ホーム » MFC (ページ 2)
「MFC」カテゴリーアーカイブ
::IsFontInstalled (LPCTSTR pszFace)
EnumFont… を調べていて,::IsFontInstalled を見つけた.
MFC 6 位に追加されたみたいだが,static 宣言されている.
VC 6 RTM では存在していなさそう.
正確なタイミングはわからないが,VC 6 SP6 には存在していて …\MFC\SRC\CCDATA.CPP にある,
フォントがインストールされているかのチェックに利用できそうなので,関数として定義予定.
CComboBox Get…Text() ?
CComboBox の選択項目の取得には GetLBText がある.
が,エディットボックス部分の取得方法がそこには書かれていない?
検索しても,あまりうまく引っかからない.
::GetWindowText(m_CtrlComboBox.GetSafeHwnd(),…) でいけそうなことは確認した.
Spy++ で見ると,エディットボックスの部分は「子ウィンドウ」になっている?
いろいろと調べると次の様なものもあり.
WindowsX.h
#define ComboBox_GetText(hwndCtl, lpch, cchMax) GetWindowText((hwndCtl), (lpch), (cchMax))
CComboBox の DDX_CBString を追加して,デバッガで追いかけると,::GetWindowText を呼出している.
MFC による Windows 95 プログラミングの 358 ページに次の様にあった.
コンボボックスの操作
… CWnd から継承している GetWindowText,SetWindowText 関数は,予想通り,エディットコントロール内のテキストを取得,設定する.
MFC だと次の様にできる.
CString str ;
m_CtrlComboBox.GetWindowText(str) ;
AfxGetInstanceHandle
コンソール AP を変更していて「MFC を使用」にすると AfxGetInstanceHandle で ASSERT .
---------------------------
Microsoft Visual C++ Debug Library
---------------------------
Debug Assertion Failed!
Program: c:\Temp\TOutline\tolmba\Debug.060\tolmba.exe
File: afxwin1.inl
Line: 19
For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.
(Press Retry to debug the application)
---------------------------
中止(A) 再試行(R) 無視(I)
---------------------------
この単体テスト用プロジェクトは,最初 MFC を使用しないでコードを書いていた.
が,幾つかの機能を付けていくと MFC を使用した既存のコードが必要になった.
その中で AfxGetInstanceHandle() を呼出している所があり,そこで ASSERT .
次の様に MFC の初期化 ::AfxWinInit を追加して対応.
int _tmain (int argc,TCHAR* argv[])
{
#ifdef _MFC_VER
if (!::AfxWinInit(::GetModuleHandle(NULL),NULL,::GetCommandLine(),0)) {
return 1 ;
}
#endif
_tsetlocale(LC_ALL,_T("")) ;
// ...
return 0 ;
}
VC 14.x での MFC のソース?
VC 2019 など最新でない VC を使用していると,MFC ソースにステップイン できなくなることがある.
大抵は,インストールされている最新の MFC の場所を指定すれば良さそう.
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.37.32822\atlmfc\include
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.37.32822\atlmfc\src\mfc
VC のアップデートがあり更新したためか,一部で「ビルド時のものと異なる」と表示されるようになった.
---------------------------
Microsoft Visual Studio
---------------------------
ソース ファイル:C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.37.32822\atlmfc\include\cstringt.h
モジュール:C:\Windows\SysWOW64\mfc140ud.dll
プロセス:[28036] GLSmth.exe
ソース ファイルがモジュールがビルドされたときのものと異なります。デバッガーでこのファイルを使用しますか?
---------------------------
はい(Y) いいえ(N)
---------------------------
VC 2022 17.6.?
先日 VS 2022 の更新版 があったのでアップデート.
個人的なツールの 3D ビューア をビルドすると,
ALYac Gen:Variant.Tedy.373496
Arcabit Trojan.Tedy.D5B2F8
BitDefender Gen:Variant.Tedy.373496
Cylance Unsafe
DeepInstinct MALICIOUS
Emsisoft Gen:Variant.Tedy.373496 (B)
eScan Gen:Variant.Tedy.373496
GData Gen:Variant.Tedy.373496
MAX Malware (ai Score=81)
McAfee Artemis!AE3A9CE560AE
McAfee-GW-Edition Artemis
Trellix (FireEye) Gen:Variant.Tedy.373496
TrendMicro-HouseCall TROJ_GEN.R002H09EQ23
VIPRE Gen:Variant.Tedy.373496
VC 2019 などでビルドしたものは問題ない.また VC 2022 更新前のものも問題なかった.
今まで VS の更新版が出てすぐにそれでビルドすることは少なかった.
テストが不十分なこともあり,リリース用は主に VC 2017 を使用している.
更新版によっては,この様なことがあるのかもしれない.
落ち着くまでしばらく時間がかかるのか?
MFC ウィンドウ位置の保存
以前から,実装しようとしていつも見返してしまうので…
単純にウィンドウ位置を保存するタイミングは OnDestroy が良さそう.
他には,ダイアログで「OK」を押された場合( OnOK )などもある.
ウィンドウ位置を保存するコードは次の様な感じ.
if (!wnd->IsIconic()) {
CRect wRect ;
wnd->GetWindowRect(&wRect) ;
// SaveRect(SecDialog,entry,wRect) ;
}
起動時は
CRect rect ; rect.SetRectEmpty() ;
// rect = GetRect(SecDialog,entry,rect) ;
if (!rect.IsRectNull()) {
// ::SystemParametersInfo(SPI_GETWORKAREA,0,&workArea,0) ;
// 範囲の補正
wnd->MoveWindow(rect) ;
}
CMainFrame を持つ AP では CMainFrame::OnCreate で,ダイアログの場合は OnInitDialog で MoveWindow する.
CComboBox::SetItemHeight (-1,…)
高 DPI 対応で修正していて,コンボボックス(特に,オーナードロー?)の高さが他に比べて不自然.
ドキュメントを見ると,CB_SETITEMHEIGHT と思うが,指定方法がよくわからない.
「コンボボックス 高さ 変更」で 検索する と「できない」と記述されている所もある.
CB_SETITEMHEIGHT で検索していると,-1 を指定しているものがある.
さらに,CB_GETITEMHEIGHT のドキュメントを見ると,次の様にある.
選択フィールドの高さを取得するには、このパラメーターは -1 である必要があります
どうも CB_SETITEMHEIGHT の表現がうまくないものと思う.
次の様なコードで対応.
{
CRect rect ;
m_CtrlStrText.GetClientRect(&rect) ;
FontFace.SendMessage(CB_SETITEMHEIGHT,-1,rect.Height()) ;
FontFace.SendMessage(CB_SETITEMHEIGHT, 0,rect.Height()*15/10) ;
}
2023/05/16
ドロップダウンの幅がうまくなかったが,共通コードを見るといろいろ考慮しなければならない部分が多い.
そのため,次の様なコードで対応.
{
new_w = now_w ;
// new_w = now_w * 15 / 10 ;
new_w = int(new_w * ::GetDPI_scale(this->GetSafeHwnd())) ; // new_w*(::GetDpiForWindow()/96.)
FontFace.SendMessage(CB_SETDROPPEDWIDTH,new_w) ;
}
間違っているかもしれないが,CB_SETITEMHEIGHT を簡単にテストしたことのまとめ.
通常のコンボボックスの場合 | -1 で,全ての項目の高さを設定できる |
オーナードロー 固定 | -1 で,エディットボックス部分の高さを指定 0 で,ドロップ部分の項目の高さを指定 |
オーナードロー 可変 | -1 で,エディットボックス部分の高さを指定 0 以上で,ドロップ部分のそれぞれの高さを指定 |
CWinApp::m_pszRegistryKey
VC で MFC プロジェクトを作成すると,CXxxApp::InitInstance が次の様になる.
SetRegistryKey(_T(“アプリケーション ウィザードで生成されたローカル アプリケーション”));
ドキュメントにある様に,会社名などを指定する.
The registry key is usually the name of a company.
あまりやってはいけないことだろうと思うが,ドキュメントを見ていると,その設定した値 CWinApp::m_pszRegistryKey を直接変更できる.
そこのサンプルコードが… 何年も前から変わっていない.
CWinApp::SetRegistryKey の動作は,新しいキーに変更して m_pszPforileName も変更している.
MFC を使用するコンソール AP での SetRegistryKey の使用は次の所.
https://dev.mish.work/wordpress/2021/07/21/console-ap-setregistrykey/
MFC タイトルバーの変更
ダイアログベースであれば C…Dlg::OnInitDialog() に次の様なコードを追加.
{
tstring str_title = ::GetWindowText(this->GetSafeHwnd()) ;
str_title+= _T(" ") + ::Get_ModuleVersion() + ::Get_BuildStrMSC() ;
SetWindowText(str_title.c_str()) ;
}
SDI や MDI の場合は,
MainFrm.h に OnUpdateFrameTitle を追加.
virtual void OnUpdateFrameTitle (BOOL bAddToTitle);
MainFrm.cpp に次の様な OnUpdateFrameTitle を追加.
// SDI
void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
CFrameWnd::OnUpdateFrameTitle(bAddToTitle) ;
{
tstring str_title = ::GetWindowText(this->GetSafeHwnd()) ;
str_title+= _T(" ") + ::Get_ModuleVersion() + ::Get_BuildStrMSC() ;
SetWindowText(str_title.c_str()) ;
}
}
// MDI
void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
CMDIFrameWnd::OnUpdateFrameTitle(bAddToTitle) ;
{
tstring str_title = ::GetWindowText(this->GetSafeHwnd()) ;
str_title = ::MDI_Add_VerBuildStr(str_title.c_str()) ;
SetWindowText(str_title.c_str()) ;
}
}
高 DPI CToolBar のリサイズ
CToolBar を使用した 自前のコード .
CDialogBar や CStatusBar はスケーリングしてくれるのに,CToolBar は対応しない?
以前は設定などで変更可能なコードにしていたが,DPI を求める方法に.
if (newExtendSize == CSize(0,0)) {
double dpi_s = ::GetDPI_scale(toolBar->GetSafeHwnd()) ; // ::GetDpiForWindow()/96.
if (dpi_s > 1) {
CToolBarCtrl& tbCtrl = toolBar->GetToolBarCtrl() ;
CSize orgSize = tbCtrl.GetButtonSize() ;
CSize newSize = orgSize ;
newSize.cx = int(orgSize.cx*dpi_s) ;
newSize.cy = int(orgSize.cy*dpi_s) ;
newExtendSize.cx = newSize.cx - orgSize.cx ;
newExtendSize.cy = newSize.cy - orgSize.cy ;
resizeType = 'R' ;
}
}
あとは既存コードの,ビットマップのリサイズと CToolBar::SetSizes を呼出せば良さそう.
CMFCToolBar は DPI を正しく処理しているみたいで,その部分のコード.
…\VC\…\atlmfc\src\mfc\afxtoolbar.cpp の CMFCToolBar::LoadToolBarEx .
まだ幾つか問題はあるが,今回の対応部分としてはこれで良さそう.
更にツールバー上のコンボボックスのスケーリングは次の様にした.
{
int width = 50 ;
{
double dpi_s = 1. ;
dpi_s = ::GetDPI_scale(AfxGetMainWnd()->GetSafeHwnd()) ;
dpi_s = ::GetDPI_scale( m_wndToolBar.GetSafeHwnd()) ;
dpi_s = ::GetDPI_scale( this->GetSafeHwnd()) ;
if (dpi_s > 1.) {
width = int(width*dpi_s) ;
}
}
m_wndToolBar.SetButtonInfo (19,ID_COMBO_ANGLE,TBBS_SEPARATOR,width) ;
CRect rectCombo ;
m_wndToolBar.GetItemRect (19,&rectCombo) ;
rectCombo.top = 1 ;
rectCombo.bottom = rectCombo.top + 300 ;
if (!m_ComboAngle.Create(CBS_DROPDOWN | WS_VSCROLL | WS_VISIBLE,rectCombo,&m_wndToolBar,ID_COMBO_ANGLE)) {
TRACE0("Failed to ComboBox\n");
return -1;
}
m_ComboAngle.SetFont(m_wndToolBar.GetFont()) ;
m_ComboAngle.AddString(_T(" 0.")) ;
m_ComboAngle.AddString(_T("10.")) ;
// ...
m_ComboAngle.AddString(_T("90.")) ;
}
ダイアログバーのイベント処理
あまり使わないと,すぐ忘れてしまう…
1999/10 に作成したプロジェクトを変更していて,ダイアログバーにコントロールを追加.
ClassWizard で CMainFrame に対してイベント処理を追加すれば良い.
今回の SDI.exe は,ビューでの処理のみなので「再描画」のコマンドメッセージを Post している.
void CMainFrame::OnUpdateRx()
{
PostMessage(WM_COMMAND,ID_REDRAW,0) ;
}
ビューでは次の様にしてコントロールにアクセスしている.
CMainFrame* pMain = (CMainFrame*)AfxGetApp()->m_pMainWnd ;
CDialogBar* dlgBar= &pMain->m_wndMyDialogBar ;
CComboBox* font = (CComboBox*) dlgBar->GetDlgItem(IDC_FONT_NAME) ;
CEdit* text = (CEdit*) dlgBar->GetDlgItem(IDC_STRING) ;
CEdit* e_rx = (CEdit*) dlgBar->GetDlgItem(IDC_RX) ;
CEdit* e_ry = (CEdit*) dlgBar->GetDlgItem(IDC_RY) ;
ダイアログバーの追加手順は次の所.
https://dev.mish.work/wordpress/2021/12/27/vc-6-dialog-bar/
ShowWindow SW_HIDE SW_SHOW
ダイアログに「ピクチャーコントロール」を追加して,それの再描画.
以前よくやっていた方法.
void CFOutLineDlg::OnHeight()
{
if (m_CountSpin.m_hWnd == NULL) { return ; }
UpdateData(TRUE) ;
InvalidateRect(NULL) ;
}
これだと描画対象以外のコントロールも再描画するのでちらついてしまう.
一度 ShowWindow で SW_HIDE して SW_SHOW することで,対象がコントロールだけになる.
void CFOutLineDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
if (m_CountSpin.m_hWnd == NULL) { return ; }
::FitWindow(this,&m_Image,5,TRUE,FALSE) ;
m_Image.ShowWindow(SW_HIDE) ;
m_Image.ShowWindow(SW_SHOW) ;
}
error C2061 , C2091 , C2809 , C2556
1997/06 に作成したプロジェクトをビルドすると…
--------------------Configuration: FontFam - Win32 Release--------------------
FontFam.exe - 0 error(s), 0 warning(s)
--------------------Configuration: FontFam - Win32 Debug--------------------
Compiling...
FontFDlg.cpp
c:\program files (x86)\microsoft visual studio\vc98\include\new(35) : error C2061: syntax error : identifier 'THIS_FILE'
c:\program files (x86)\microsoft visual studio\vc98\include\new(35) : error C2091: function returns function
c:\program files (x86)\microsoft visual studio\vc98\include\new(35) : error C2809: 'operator new' has no formal parameters
c:\program files (x86)\microsoft visual studio\vc98\include\new(36) : error C2061: syntax error : identifier 'THIS_FILE'
c:\program files (x86)\microsoft visual studio\vc98\include\new(37) : error C2091: function returns function
c:\program files (x86)\microsoft visual studio\vc98\include\new(37) : error C2556: 'void *(__cdecl *__cdecl operator new(void))(unsigned int,const struct std::nothrow_t &)' : overloaded function differs only by return type from 'void *(__cdecl *__cd
ecl operator new(void))(unsigned int)'
c:\program files (x86)\microsoft visual studio\vc98\include\new(35) : see declaration of 'new'
c:\program files (x86)\microsoft visual studio\vc98\include\new(41) : error C2061: syntax error : identifier 'THIS_FILE'
c:\program files (x86)\microsoft visual studio\vc98\include\new(42) : error C2091: function returns function
c:\program files (x86)\microsoft visual studio\vc98\include\new(42) : error C2556: 'void *(__cdecl *__cdecl operator new(void))(unsigned int,void *)' : overloaded function differs only by return type from 'void *(__cdecl *__cdecl operator new(void))
(unsigned int)'
c:\program files (x86)\microsoft visual studio\vc98\include\new(35) : see declaration of 'new'
c:\program files (x86)\microsoft visual studio\vc98\include\new(42) : error C2809: 'operator new' has no formal parameters
c:\program files (x86)\microsoft visual studio\vc98\include\new(42) : error C2065: '_P' : undeclared identifier
Error executing cl.exe.
FontFam.exe - 11 error(s), 0 warning(s)
以前は <memory> の インクルードを追加 することで対応していた.
原因は,幾つかのインクルードが次のコードより後に存在しているため?
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
buf = string ( buf.c_str () )
先日の共通のコードでのバグ.
tstring ask_cli (LPCTSTR msg=_T(""))
{
tstring cli ;
while(true) {
tstring buf ;
buf.resize(1000) ;
std::terr << msg ;
std::tin.getline(&buf[0],std::streamsize(buf.size())) ;
// buf = ::QuotM_Del_All(buf.c_str()) ;
if (buf == _T("q")) { break ; }
else if (buf == _T("Q")) { break ; }
if (buf.empty()) { continue ; }
cli = buf.c_str() ;
break ;
}
return cli ;
}
ループを抜けるために “q” や “Q” と比較している部分が機能しない(break しない).
原因は “q\0\0\0…” と “q\0” を比べているため.
そのため,比較する前に次の様にするとうまく機能する.
{
tstring tmp = buf.c_str() ;
buf = tmp ;
}
MFC であれば CString::ReleaseBuffer(-1) が使えるが…
buf = tstring(buf.c_str()) とすることで対応.
Win11 に入っている MFC*.DLL
先日手に入った Win11 には,予め幾つかの AP が入っていた.
その中には,MFC140u.dll などが入っていたので,改めて新規インストールした環境で調べてみた.
Win11 Pro
Win11 Ent
やはり MFC40 と MFC42 みたい.
https://jml.mish.work/index.php/cpp/ref-vcredist-xxx-exe.html
MFC 14 以降の CCheckListBox で…
何年か前,幾つかのプロジェクトを VC 14 に対応しようとして CCheckListBox の表示がうまくなかった.
CCheckListBox を使っているプロジェクトは限られているので,~MFC 12 にしていた.
今回 いろいろとあり VC 2017 ~ 2022 に対応することに.
MFC が更新されているのかわからないが,以前のものより少し動作は良くなっている?
相変わらず,表示直後にずれているのと,高さが MFC 12 以前に比べ詰まっている.
「MFC CCheckListBox ずれる」で検索すると,OnInitDialog() などで 高さを指定 すれば良いとあった.
{
CRect rect ;
m_CtrlSExtDllNow.GetWindowRect(&rect) ; // 他のコントロールの高さを利用
m_CtrlListSExt.SetItemHeight(0,rect.Height()) ;
}
面倒だったのでエディットボックスの高さを利用している.
これで MFC 12 などで作成したものと同じ様な表示になった.
CHttpFile Read
何年か前に Web 上の html を 読み取るコード を書いた.
それを 今回書き直し.
これらのコードでは「シフトJIS」で書かれたページ以外ではうまく処理できない?
以前利用した時は IIS のサーバで,html 内の一部のコードを読み取るだけなので,特に問題にはならかなった.
今回も,単純に html が更新されたかどうかだったので,それなりに処理できていた.
今度は,html だけでなく画像などバイナリにも対応したくなった.
CInternetFile::ReadString の部分を
{
CString buf ;
while (pFile->ReadString(buf)) {
tstring tmp = ::To_tstring(LPCSTR(LPCTSTR(buf))) ;
r_buf.push_back(tmp) ;
}
read_dt = ::String_Join_Line(r_buf) ;
}
CInternetFile::Read に変更.
{
FILE* ofp = ::Open_File(dt_file.c_str(),_T("wb")) ;
v_char readBuf ; readBuf.resize(1024) ;
while (true) {
UINT r_size = pFile->Read(&readBuf[0],1024) ;
::fwrite(&readBuf[0],1,r_size,ofp) ;
// if (r_size < 1024)
if (r_size < 1)
{
break ;
}
}
::fclose(ofp) ;
}
GetActiveView
いつも面倒と思う CView の取得.
https://dev.mish.work/wordpress/2022/05/20/drop-lnk-on-mdi-exe/
普通は,CDocument から CView を操作することはあまりないものと思う.
が,今回はテスト用のコードで CView での表示情報を更新したくなった.
{
CFrameWnd* pFrameWnd = (CFrameWnd*)AfxGetMainWnd() ;
CFrameWnd* pActiveFrame = pFrameWnd->GetActiveFrame() ;
{
CXxxView* pView = (CXxxView*)pFrameWnd->GetActiveView() ;
if (pActiveFrame != NULL) {
pView = (CXxxView*)pActiveFrame->GetActiveView() ;
}
if (pView != NULL) {
PartsA sel_pa = *(pView->GetSelectP()) ;
{
PartsA err_pa = ::xx_get_error_xx(...) ;
sel_pa += err_pa ;
}
*(pView->GetSelectP()) = sel_pa ;
}
}
UpdateAllViews(NULL) ;
}
それを関数に.
CView* Get_active_view (void)
{
CFrameWnd* pFrameWnd = (CFrameWnd*)AfxGetMainWnd() ;
CFrameWnd* pActiveFrame = pFrameWnd->GetActiveFrame() ;
{
CView* pView = pFrameWnd->GetActiveView() ;
if (pActiveFrame != NULL) {
pView = pActiveFrame->GetActiveView() ;
}
return pView ;
}
return NULL ;
}