ホーム » MFC (ページ 6)
「MFC」カテゴリーアーカイブ
コンソール AP でレジストリ読み取り
コンソール AP で,レジストリを読み取るために以下の様な関数を用意した.
int Reg_GetInt (HKEY hKey,LPCTSTR subKey,LPCTSTR ent,const int defValue=0)
{
DWORD value = defValue ;
#ifdef _ATL_VER
{
CRegKey reg ;
reg.Open(hKey,subKey) ;
#if (_ATL_VER >= 0x700)
reg.QueryDWORDValue(ent,value) ;
#else
reg.QueryValue(value,ent) ;
#endif
}
#else
{
HKEY hSecKey = NULL ;
LONG openStatus = ::RegOpenKeyEx(hKey,subKey,0,KEY_READ,&hSecKey) ;
if (hSecKey == NULL) { return value ; }
DWORD type = REG_NONE ;
DWORD count = sizeof(DWORD) ;
LONG queryStatus = ::RegQueryValueEx(hSecKey,ent,NULL,&type,(LPBYTE)&value,&count) ;
::RegCloseKey(hSecKey) ;
}
#endif
return int(value) ;
}
*::Reg~ などのエラーチェックは省いている.
呼出すのはこんな感じ.
int value = ::Reg_GetInt(HKEY_CURRENT_USER,_T(“Software\\Iwao\\Test\\RegAcc”),_T(“T_RegKey_DW”)) ;
CWinApp::GetProfileInt などが使えないかとやってみたが,…
1. AfxGetApp() を呼びだしても,NULL で戻ってくる.
次の様に領域を確保.
CWinApp theApp;
2. SetRegistryKey(key) が直接呼びだせないので,
class C_SRK_WinApp : public CWinApp {
public:
void SetRegKey (LPCTSTR key) { SetRegistryKey(key) ; }
} ;
変数の確保は次の様に変更.
C_SRK_WinApp theApp ;
3. SetRegistryKey の中で ASSERT(m_pszAppName != NULL)
以下を追加して,CWinApp を初期化.
AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0) ;
4. これで CWinApp::GetProfileInt は利用できるようになるが,CWinApp::m_pszProfileName が邪魔.
C_SRK_WinApp* app = (C_SRK_WinApp*)AfxGetApp() ;
int val = app->GetProfileInt(_T(“Test\\RegAcc”),_T(“T_RegKey_DW”),0) ;
HKCU\Software\Iwao\(m_pszProfileName)\Test\RegAcc となってしまう.
5. m_pszProfileName を _T(“”) とすることにより,意図した値を取得できる様にはなった.
C_SRK_WinApp* app = (C_SRK_WinApp*)AfxGetApp() ;
if (app != NULL) {
AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0) ;
app->SetRegKey(_T(“Iwao”)) ;
free((void*)app->m_pszProfileName) ;
app->m_pszProfileName = _tcsdup(_T(“”)) ;
int val = app->GetProfileInt(_T(“Test\\RegAcc”),_T(“T_RegKey_DW”),0) ;
}
コンソール AP で CWinApp::GetProfileInt などを使うことは,やめ.
COleDataSource を利用した Drag
以前 DoDragDrop で少し調べたが,その時は途中になっていた.
その時の内容で,例えば CListBox に表示されてるファイルリストのすべてを,CListBox の外側でドラッグするすることはできる.
void CDragLBDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
CStringArray sa ;
::ToStringArray(FileNames,&sa) ;
::StringArrayToDoDragFiles(sa) ;
}
CListCtrl であれば,OnBegindragXxxx が利用できるので,
void CDragLVDlg::OnBegindragListFiles(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
{
CStringArray sa ;
for (int index=0 ; index<m_ListFiles.GetItemCount() ; index++) {
if (m_ListFiles.GetItemState(index,LVIS_SELECTED) == LVIS_SELECTED) {
tstring str = FileNames[index] ;
sa.Add(str.c_str()) ;
}
}
if (sa.GetSize() > 0) {
::StringArrayToDoDragFiles(sa) ;
}
}
*pResult = 0;
}
今回やりたかったのは,CListBox の項目のドラッグ.
ドラッグ部分は CListCtrl とほぼ同様となると思われるが,ドラッグ開始のタイミングが取れない.
CDragListBox と開始時の動きは似ているが,MFC のコードを見るとちょっと難しそう.
いろいろと検索すると,CListBox のサブクラス化で対応するみたいとわかったが,手順が...
動的なサブクラス化と言うらしい.
Inside Visual C++ には,囲み記事に CNonNumericEdit がある.
利用する手順はわかるが,作成する手順がわからない
今も利用はしている CView からの VXxxxView ファミリがあるが,10 年以上前で手順は覚えてない.
いろいろ探して「MFC クラスウィザード」を利用することがわかった.
今まで,よく見ていたダイアログなのに...
後は,WM_LBUTTONDOWN を処理する様にして,
void CMyListBox::OnLButtonDown(UINT nFlags, CPoint point)
{
CListBox::OnLButtonDown(nFlags, point);
int sel = GetCurSel() ;
if (sel == LB_ERR) { return ; }
CString str ;
GetText(sel,str) ;
CStringArray sa ;
sa.Add(str) ;
::StringArrayToDoDragFiles(sa) ;
}
ダイアログに追加したリスボックスで,これを利用する様にダイアログのヘッダを修正.
MSDN CTRLTEST サンプル
テクニカル ノート 14
標準コントロールからのコントロールの派生
[MSVC] MFCを使用しての動的サブクラス化
Create Client Windows, Drag and Drop Between Listboxes
2014/08/11 追記
COleDataSource を利用して,他の AP に CF_HDROP で渡そうとすると,
—————————
Microsoft Visual C++ Debug Library
—————————
Debug Assertion Failed!
Program: …Documents\Visual Studio 2010\Projects\TestVC10\Debug\T_SC.exe
File: f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\cmdtarg.cpp
Line: 43
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)
—————————
COleDataSource の使い方が間違っていた.
開放する時に delete ではうまくない.
データ オブジェクトとデータ ソース : 作成と破棄
モードレスダイアログの動作が,…
VC 7 以降,MDI AP のモードレスダイアログが独立して動作してしまう現象があった.
その AP は,VC 4 の頃作成したもので CMainFrame::OnCreate 内でモードレスダイアログを作成している.
VC 6 までは特に問題なかったが,VC 7 以降タスクバーにアイコンが 2 つ存在する状態になってしまっていた.
過去に何度か対応方法を調べたが,わからずそのままとなっていた.
今日別の事を調べていて,「モードレスダイアログで親子にならなくする」方法が目に留まった.
CDialog::Create で,デフォルトの NULL ではなく,GetDesktopWindow() を与えるというもの.
VC 7 AP のダイアログの情報を,Spy++ で見ると,親ウィンドウが (なし) になっている.
今度はデバッガで,Create の付近を追いかけると AfxGetMainWnd() で NULL がかえっている.
ThrdCore.cpp より
CWnd* CWinThread::GetMainWnd()
{
if (m_pActiveWnd != NULL)
return m_pActiveWnd;
if (m_pMainWnd != NULL)
return m_pMainWnd;
return CWnd::GetActiveWindow();
}
CXxxApp::InitInstance で以下の部分を修正.
CMainFrame* pMainFrame = new CMainFrame;
m_pMainWnd = pMainFrame; // ここを追加
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
CSplitterWnd – 2
SDI で,MDI の「新しいウィンドウを開く」で 4 つのビューを表示した様な動作が欲しかったので,調べてみた.
動的な分割ウィンドウである程度の所まではできそう.
1. MainFrm.h の CMainFrame に以下を追加. CSplitterWnd m_wndSplitter; virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext); 2. CMainFrame::OnCreateClient の追加. BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { { if (!m_wndSplitter.Create(this,2, 2,CSize(10, 10), pContext, WS_CHILD | WS_VISIBLE /* | WS_HSCROLL | WS_VSCROLL */ | SPLS_DYNAMIC_SPLIT)) { TRACE0("Failed to create split bar "); return FALSE; } return TRUE; } } スクロールバーを付加したくなかったので,6 つ目のウィンドウスタイルを変更している. テスト用に分割動作をコマンドとして実装. void CMainFrame::OnSplit() { if (IsSplit) { if (m_wndSplitter.GetColumnCount() > 1) { m_wndSplitter.DeleteColumn(1) ; } if (m_wndSplitter.GetRowCount() > 1) { m_wndSplitter.DeleteRow (1) ; } IsSplit = !IsSplit ; } else { CRect rect ; m_wndSplitter.GetClientRect(&rect) ; if (m_wndSplitter.GetColumnCount() == 1) { m_wndSplitter.SplitColumn(rect.Width ()/2) ; } if (m_wndSplitter.GetRowCount() == 1) { m_wndSplitter.SplitRow (rect.Height()/2) ; } IsSplit = !IsSplit ; } }
2 x 2 の分割ウィンドウのコントロールの ID (Spy++ で確認)
左上(0,0) E900 AFX_IDW_PANE_FIRST
右上(0,1) E901
左下(1,0) E910
右下(1,1) E911
ビューの OnDraw を以下の様に書き換えると
void CSpltWView::OnDraw(CDC* pDC) { int row = -1 ; int clm = -1 ; CMainFrame* mw = (CMainFrame*)AfxGetMainWnd() ; CSplitterWnd* sw = &mw->m_wndSplitter ; CString str ; if (sw->IsChildPane(this,&row,&clm)) { int id = sw->IdFromRowCol(row,clm) ; CString tmp1 ; tmp1.Format(_T("row = %d , clm = %d"),row,clm) ; CString tmp2 ; tmp2.Format(_T("IdFromRowCol = %04X"), id) ; str += tmp1 + _T("\r\n") ; str += tmp2 + _T("\r\n") ; } { UINT nID = GetDlgCtrlID() ; CString tmp ; tmp.Format(_T("GetDlgCtrlID = %04X"),nID) ; str += tmp + _T("\r\n") ; } { CWnd* pw = GetParent() ; CString tmp1 ; tmp1.Format(_T("Splitter = %08x "),sw->GetSafeHwnd()) ; CString tmp2 ; tmp2.Format(_T("Parent = %08x "),pw->GetSafeHwnd()) ; str += tmp1 + _T("\r\n") ; str += tmp2 + _T("\r\n") ; } CRect rect ; GetClientRect(rect) ; rect.top = rect.left = 10 ; pDC->DrawText(str,rect,0) ; }
MFC の ソースを眺めていると CView::GetParentSplitter があったので,間接的にはそれを使えば良さそう.
ビューでどの位置かを求めるだけなら, short texNo = 0 ; { UINT nID = GetDlgCtrlID() ; switch (nID) { case AFX_IDW_PANE_FIRST + 0x00 : texNo = 1 ; break ; case AFX_IDW_PANE_FIRST + 0x01 : texNo = 2 ; break ; case AFX_IDW_PANE_FIRST + 0x10 : texNo = 3 ; break ; case AFX_IDW_PANE_FIRST + 0x11 : texNo = 4 ; break ; } }
MFC DLL → Static – 2
VC 6 などで生成したプロジェクトを,VC 7 ~ VC 11 まで順に変換して利用してきたもの.
「共有 DLL で MFC を使う」から「スタティック ライブラリで MFC を使用する」に変更してビルドすると,
C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\atlmfc\include\afx.h(24):
fatal error C1189: #error : Building MFC application with /MD[d]
(CRT dll version) requires MFC shared dll version. Please #define _AFXDLL or do not use /MD[d]
プロジェクトの「プロパティページ」-「構成プロパティ」-「C/C++」-「コード生成」-「ランタイム ライブラリ」を
「マルチスレッド DLL (/MD)」から「マルチスレッド (/MT)」に変更.
ビルドは通る様になったが,起動時メインフレーム表示直後にアプリケーションエラー.
プロジェクトのプロパティで,「リンカ」-「デバッグ」-「デバッグ情報の生成」で「はい (/DEBUG)」に.
ビルドして実行すると,
C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\atlmfc\src\mfc\docsingl.cpp
CSingleDocTemplate::SetDefaultTitle(CDocument* pDocument) の
ENSURE(strDocName.LoadString(AFX_IDS_UNTITLED));
プロジェクトのプロパティ,「リソース」-「プリプロセッサの定義」に “_AFXDLL;” があったので削除.
2021/02/17
VC 7 以降に変換した段階で,ソースの「プリプロセッサの定義」が引き継がれてしまう?
そのため,それは削除した方が良さそう.
* 間違ってプロジェクトの「プリプロセッサの定義」を削除しないこと.
VC 12 LNK1104 , VC 14.2 LNK2019
OCX x64
MFC を利用した x86 の ocx の 64 ビット化.
しばらくやってないので,Win XP 32 ビット環境の VC 6 で,スケルトンの作成から.
ActiveX Controls Inside Out を見ながら,スケルトンを作成.
そのままビルドして,ActiveX コントロール テスト コンテナ で,挿入してテスト.
OnDraw のEllipse を DrawText に書換え.
void C????Ctrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid) { // pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH))); // pdc->Ellipse(rcBounds); v_tstring buildMacros = ::Debug_GetMacroCC_v() ; CString macroStr = ::String_Join(buildMacros,_T("\r\n")).c_str() ; CRect rect = rcBounds ; pdc->DrawText(macroStr,rect,0) ; }
Debug_GetMacroCC_v は次の様なコード.
inline std::vector Debug_GetMacroCC_v (void) { std::vector typeAry ; #ifdef _WIN64 typeAry.push_back(_T("_WIN64 ")) ; #elif _WIN32 typeAry.push_back(_T("_WIN32 ")) ; #endif // ... #ifdef _MFC_VER typeAry.push_back(_T("_MFC_VER ")+::utot(_MFC_VER,16)+_T(" ")) ; #endif #ifdef _UNICODE typeAry.push_back(_T("_UNICODE ")) ; #endif // ... return typeAry ; }
今度は,Win 7 x64 環境で,VC 9 を利用しての確認.
「ActiveX コントロール テスト コンテナ」は,以前のバージョンとは異なり標準では存在しない.
検索するとサンプルにあるとのこと.
Win 7 環境には C:\…\VC2010Samples\C++\MFC\ole\TstCon があったので VC 10 でビルド.
VC 9 でスケルトンから作成して,OnDraw を同様に書換え.
また,構成マネージャを使用して x64 を追加.
それぞれの Release 版でビルド.
コントロールの登録は,管理者として起動した「コマンドプロンプト」で行っている.
それぞれの ocx のフォルダに移動し,regsvr32 ~.ocx として登録.
この時,ocx により?自動的に regsvr32 の 64 / 32 ビットそれぞれで登録されているみたい.
Excel 2010 で試すには,コントロールの挿入のために開発タブを有効にする必要がある.
MFC 追加でメモリリーク
MFC を利用しないコードを VC 6 でテストしていて,
コンソール AP として作成したスケルトンに,::oGetFileSize を追加.
MFC を利用した,::GetFileSize と動作を比べるために,
プロジェクトの設定を変更(共有 DLL で MFC を使用).
main 関数が存在するソースに Afx.h などの include を追加.
ビルドして実行すると,メモリリークが発生するようになった.
main 関数内を全てコメントにしてもあまり変わらない.
MFC サポートありで,コンソール AP を作成.
同様に oGetFileSize と GetFileSize を追加.
ビルド,実行すると特に問題ない.
StdAfx.h 内の Afx.h などを tmain のソース内に移動.
リビルドすると,メモリリーク発生.
どうも,Afx.h より前の iostream などが関係している.
また,リビルドしないと現象が変わらない.
StdAfx.h に iostream の include を追加
#define VC_EXTRALEAN
#include <iostream>
#include <afx.h>
Detected memory leaks! Dumping objects -> {51} normal block at 0x00032440, 33 bytes long. Data: < C > 00 43 00 CD CD CD CD CD CD CD CD CD CD CD CD CD {50} normal block at 0x00034F68, 40 bytes long. Data: < |L > 14 7C 4C 10 16 00 00 00 00 00 00 00 00 00 00 00 Object dump complete. スレッド 0x1218 終了、終了コード 0 (0x0)。 プログラム 'C:\...\T_Con2\Debug\T_Con2.exe' はコード 0 (0x0) で終了しました。
iostream のインクルードを afx.h より後にすることで対応.
CLI + MFC
CLR コンソール アプリケーションを作成.
Console::WriteLine を追加.
#include “stdafx.h”
using namespace System;
int main(array ^args)
{
Console::WriteLine(L”Hello World”);
#ifdef _WIN32
Console::WriteLine(L”WIN32″);
#endif
#ifdef __cplusplus_cli
Console::WriteLine(L”CLI”);
#endif
#ifdef __CLR_VER
Console::WriteLine(__CLR_VER);
#endif
#ifdef _MFC_VER
Console::WriteLine(_MFC_VER);
#endif
return 0;
}
実行すると,
C:\…>”C:\…\debug\ConAp.exe”
Hello World
WIN32
CLI
20050727
これに MFC の機能を追加しようとしたがわからなかったので.MFC コンソール AP を作成して /clr を追加することにした.
#include “stdafx.h”
#include “ConMFC.h”
//
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
//
using namespace System;
// 唯一のアプリケーション オブジェクトです。
CWinApp theApp;
//
using namespace std;
//
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
//
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) {
_tprintf(_T(“致命的なエラー: MFC の初期化ができませんでした。\n”));
nRetCode = 1;
}
else {
// TODO: アプリケーションの動作を記述するコードをここに挿入してください。
}
Console::WriteLine(L”Hello World”);
#ifdef _WIN32
Console::WriteLine(L”WIN32″);
#endif
#ifdef __cplusplus_cli
Console::WriteLine(L”CLI”);
#endif
#ifdef __CLR_VER
Console::WriteLine(__CLR_VER);
#endif
#ifdef _MFC_VER
Console::WriteLine(_MFC_VER);
#endif
return nRetCode;
}
実行すると,
C:\…>”C:\…\debug\ConMFC.exe”
Hello World
WIN32
CLI
20050727
2048
CLR のバージョンは,2.0.50727
MFC は 0x0800
VC 定義済みマクロ
MDI のステータスバー
以下の情報と,The MFC Answer Book とで.
すべての MDI 子ウィンドウのステータス バーを作成する方法
How to Create a Status Bar in Every MDI Child Window
https://www.betaarchive.com/wiki/index.php?title=Microsoft_KB_Archive/121946
static UINT indicators[] = { ID_INDICATOR_MDI } ; として,ID をシンボルブラウザで追加.
ビルド,実行すると,CChildFrame::OnCreate のステータスバーの Create で失敗.
String Table で定義されてないとダメな様で適当な文字列を追加.
ビューで,OnUpdateIndicatorMDI を追加(表示されないメニューに ID を追加して ClassWizard で).
pCmdUI->SetText(_T(“ステータスバーに表示する情報”)) ;
これで実行すると,追加した文字列の幅で表示された.
CChildFrame::OnCreate の,
ステータスバーを生成した後,SetPaneInfo(0,ID,SBPS_STRETCH,0)
これにより,ウィンドウ幅いっぱいに.
幅は広がったが表示される文字列は,128 文字まで?
DImage の利用
Shell Extension を利用した画像表示.ダイアログにファイルをドロップすると表示する様に.
1. CStatic を用意.ピクチャコントロールを配置して ClassWizard で変数を追加.
2. DImageS の領域を確保.通常はヘッダに.
3. OnDropFiles で
// CString dropFile = …. ;
ImageS.SetFileName(dropFile) ;
ImageS.Draw(&m_Image) ;
{
m_Image.ShowWindow(SW_HIDE) ;
m_Image.ShowWindow(SW_SHOW) ;
}
// …
4. OnPaint で
ImageS.Draw(&m_Image) ;
DImage では,ImageDMF が追加される.
DImage.zip
HTML の表示
SVG をダイアログ上に表示したくなったので,CHtmlView などを利用してみた.
Spy++ で見ると,クラス名が “Internet Explorer_Server” となる.
HtmlView.zip
SDI で CHtmlView を利用
1. AppWizard のステップ 6 で,CHtmlView を選択する.
2. ビューのソースの CXxxxView::OnInitialUpdate() を,適当なアドレスに修正する.
Navigate2(_T(“https://mish.myds.me/wordpress/dev/“),NULL,NULL);
ダイアログで,WebBrowser Control を利用
1. プロジェクトにコンポーネントを追加する.
2. 追加したコントロールの変数を割り当てる(ここでは,m_WebBrowse とした).
3. OnInitDialog などで
m_WebBrowse.Navigate(_T(“https://mish.myds.me/wordpress/dev/“),NULL,NULL,NULL,NULL) ;
ダイアログで,CHtmlView を利用
1. CHtmlView* の変数を定義する(ここでは,WebView とした).
2. OnInitDialog で
CRect rect ;
GetClientRect(&rect) ;
rect.top = 25 ;
CRuntimeClass* pClass= RUNTIME_CLASS(CHtmlView) ;
WebView = (CHtmlView*)(pClass->CreateObject()) ;
WebView->Create(NULL,NULL,WS_VISIBLE|WS_CHILD,rect,this,0,NULL) ;
WebView->SendMessage(WM_INITIALUPDATE) ;
WebView->Navigate(_T(“https://mish.myds.me/wordpress/dev/“)) ;
コード分析による修正
VC 9
~\CharFnc.hxx(155) : warning C6284: オブジェクトがパラメータ '3' として渡されました。 ~\CharFnc.hxx(155) : warning C6284: オブジェクトがパラメータ '5' として渡されました。 str.Format(_T("(%*s,%*s)"),width, ToString(value.x) ,width, ToString(value.y) ) ; str.Format(_T("(%*s,%*s)"),width,LPCTSTR(ToString(value.x)),width,LPCTSTR(ToString(value.y))) ; 間違って LPCSTR としてしまうと,_UNICODE では error C2440: '' : 'CString' から 'LPCSTR' に変換できません。 LPSTR としてしまうと, error C2440: '' : 'CString' から 'LPSTR' に変換できません。
BDoc ファミリ
コンパイルで以下の様なエラーになった場合
c:\…\DocIB.cxx(77) : error C2065: ‘BDocCSV_D’ : 定義されていない識別子です。
c:\…\DocIB.cxx(77) : error C2146: 構文エラー : ‘;’ が、識別子 ‘bdcsv’ の前に必要です。
c:\…\DocIB.cxx(77) : error C2065: ‘bdcsv’ : 定義されていない識別子です。
c:\…\DocIB.cxx(78) : error C2228: ‘.Read’ : 左側がクラス、構造体、共用体ではありません。
c:\…\DocIB.cxx(79) : error C2228: ‘.BDoc_Draw’ : 左側がクラス、構造体、共用体ではありません。
c:\…\DocIB.cxx(79) : error C2653: ‘BDoc_Draw’ : 識別子がクラス名でも名前空間名でもありません。
DocIB.cxx のインクルードする場所をもう少し前にすることによりエラーはなくなるが,…
実行時,いろいろな所でダウンする(表面化しないこともあり).
BDoc_Draw と BDocCSV_D の定義の不整合が原因.
BDoc ファミリや,AccessItemND を見直さなければならないがちょっと時間が取れない.
ひとまず,App.h に BDoc_D.hxx をインクルードすることにより対応する.
#include “MetaFile.hxx”
#include “MemoryDC.hxx”
#include “BDoc_D_.hxx”
CFont::CreatePointFont でアサート
—————————
Microsoft Visual C++ Debug Library
—————————
Debug Assertion Failed!
Program: C:\WINDOWS\explorer.exe
File: wingdix.cpp
Line: 269
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)
—————————
ここ 1 年位のコードで何個かあったので,…
呼び元で与えられた pDC が CMetaFileDC の場合,m_hAttribDC が NULL となるため.
コードを書いた段階では画面やプリンタだったが,今回 Text → EMF で表面化した.
修正は以下の様な感じ.
// font.CreatePointFont(120,faceName,pDC) ;
if (pDC->m_hAttribDC == NULL) { font.CreatePointFont(120,faceName,NULL) ; }
else { font.CreatePointFont(120,faceName,pDC) ; }
CCheckListBox の使い方
Shell Extension InfoTip の追加
Shell Extension の作成 InfoTip
1.プロジェクトの作成
MFC App Wizard(dll)
「スタティックライブラリを使用した DLL」を選択
「オートメーション」のサポート
2.クラスファクトリの作成
Class Wizard で,クラスの新規作成
基本クラスは CCmdTarget
「タイプ ID で作成」
3.ヘッダ部に INTERFACE_PART の追加
BEGIN_INTERFACE_PART(QueryInfoT,IQueryInfo)
STDMETHOD (GetInfoFlags) (DWORD* pdwFlags) ;
STDMETHOD (GetInfoTip ) (DWORD dwFlags,LPWSTR* ppwszTip) ;
END_INTERFACE_PART (QueryInfoT)
…
BEGIN_INTERFACE_PART(PersistFile,IPersistFile)
…
4.必要な変数を追加
InfoTip の場合,FileName など
5.ソースに INTERFACE_MAP の追加
BEGIN_INTERFACE_MAP(CI_ShellExt,CCmdTarget)
INTERFACE_PART (CI_ShellExt,IID_IQueryInfo,QueryInfoT)
INTERFACE_PART (CI_ShellExt,IID_IPersistFile,PersistFile)
END_INTERFACE_MAP ()
6.CI_ShellExt::XQueryInfoT::~ の実装
幾つかはある程度決まったコードになる.
CI_ShellExt::XQueryInfoT::GetInfoTipは,ppwszTipに表示する文字列を返す.
METHOD_PROLOGUE pThisでCCmdTargetの派生クラスにアクセス可能になる.
レジストリへの登録
[HKEY_CURRENT_USER\Software\Classes\CLSID\{56D044C5-9F47-4CD3-B130-9E52BE7460F3}]
@=”I.ShellExt”
[HKEY_CURRENT_USER\Software\Classes\CLSID\{56D044C5-9F47-4CD3-B130-9E52BE7460F3}\InProcServer32]
@=”C:\\Documents and Settings\\All Users\\Documents\\VC_TEST\\IShlExt\\Release\\I_Shl_E.dll”
“ThreadingModel”=”Apartment”
[HKEY_CURRENT_USER\Software\Classes\CLSID\{56D044C5-9F47-4CD3-B130-9E52BE7460F3}\ProgID]
@=”I.ShellExt”
拡張子の登録例
[HKEY_CURRENT_USER\Software\Classes\.Test_ShellExt\ShellEx\{00021500-0000-0000-C000-000000000046}]
@=”{56D044C5-9F47-4CD3-B130-9E52BE7460F3}”
{56D044C5-9F47-4CD3-B130-9E52BE7460F3} の部分は,ソースの IMPLEMENT_OLECREATE の前の行のコメントより
今回使用したコードは,IShlExt.zip
参考にしたのは,
Debugging with the Shell
MSDN Magazine 2000/04 新しいinfotip,アイコンオーバーレイ,シェルエクステンション
Windows 2000 UI Innovations: Enhance Your User’s Experience with New Infotip and Icon Overlay Shell Extensions
MSDN Magazine 2000/07 ハイパーテキストテンプレートファイルのカスタマイズによるエクスプローラの表示の拡張
Windows 95 ユーザーインターフェイス プログラミング
ExtractImage を実装してエラーになった場合
I_ShlExt.obj:error LNK2001:外部シンボル “_IID_IExtractImage” は未解決です
#include <initguid.h> を I_ShlExt.cpp の StdAfx.h の次に追加.
HBITMAP から画像サイズ取得
昨日,クリップボードからの取得で画像サイズを求める方法がわからなかった.
HBITMAP hbm = (HBITMAP)::GetClipboardData(CF_BITMAP) ; if (hbm != NULL) { CSize bmpSize(10,10) ; { BITMAP bm ; ::ZeroMemory(&bm,sizeof(BITMAP)) ; if (::GetObject(hbm, sizeof(BITMAP),&bm)!=0) { bmpSize.cx = bm.bmWidth ; bmpSize.cy = bm.bmHeight; } } ... }
CBitmap で保持している場合は,
CBitmap bitmap ; if (bitmap.LoadBitmap(bmp_ID)) { BITMAP bm ; bitmap.GetBitmap(&bm) ; CRect rect = CRect(0,0,bm.bmWidth,bm.bmHeight) ; ... }
HENHMETAFILE では,
HENHMETAFILE hemf = (HENHMETAFILE)::GetClipboardData(CF_ENHMETAFILE) ; if (hemf !=NULL) { ENHMETAHEADER emh ; ZeroMemory(&emh,sizeof(ENHMETAHEADER)) ; emh.nSize = sizeof(ENHMETAHEADER) ; if (::GetEnhMetaFileHeader(hemf,sizeof(emh),&emh) != 0) { CRect rectB ; rectB.top = emh.rclBounds.top ; rectB.left = emh.rclBounds.left ; rectB.right = emh.rclBounds.right ; rectB.bottom= emh.rclBounds.bottom ; CRect rectF ; rectF.top = emh.rclFrame .top ; rectF.left = emh.rclFrame .left ; rectF.right = emh.rclFrame .right ; rectF.bottom= emh.rclFrame .bottom ; } }
ダイアログ.exe サイレントモード
ダイアログベースで,コマンドライン引数により何も表示しないで終了する exe
次の様なコードで,AfxWinMain でエラーになってしまう.
BOOL CReszPicApp::InitInstance()
{
…
CReszPicDlg dlg;
m_pMainWnd = &dlg;
if (_tcslen(m_lpCmdLine) != 0) {
dlg.EnumDIB() ;
dlg.ResizeDIB() ;
return FALSE ;
}
INT_PTR nResponse = dlg.DoModal();
…
}
—————————
Microsoft Visual C++
—————————
ハンドルされていない例外 は ReszPic.exe (MFC42D.DLL) にあります: 0xC0000005: Access Violation。
—————————
OK
—————————
WinMain.cpp
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
…
// Perform specific initializations
if (!pThread->InitInstance()) {
if (pThread->m_pMainWnd != NULL) {
TRACE0(“Warning: Destroying non-NULL m_pMainWnd\n”);
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode = pThread->ExitInstance();
goto InitFailure;
}
…
}
m_pMainWnd への設定より前に,サイレントモードでの動作を行えば良い.
CReszPicDlg dlg;
// m_pMainWnd = &dlg;
if (_tcslen(m_lpCmdLine) != 0) {
dlg.EnumDIB() ;
dlg.ResizeDIB() ;
return FALSE ;
}
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
Message クラスなどで,AfxGetMainWnd()->GetSafeHwnd()==NULL の時の判断が抜けていたので修正.
AfxGetMainWnd()==NULL は多くの所で対応しているが,
AfxGetMainWnd()->GetSafeHwnd()==NULL は抜けがかなりありそう.
CView の OnFilePrint を抜き出し
OnFilePrint の関係を DH_Print クラスとして抜き出しました.
ダイアログベースの exe や,ビューの表示対象と異なるオブジェクトの印刷などに利用できると思います.
利用方法はこんな感じ.
class PrintTest : public DH_Print { public: virtual void OnPrint (CDC* pDC, CPrintInfo* pInfo) { CRect rect = pInfo->m_rectDraw ; { pDC->TextOut(0,0,_T("ここが印刷のためのコードです.")) ; } return ; } virtual BOOL OnPreparePrinting ( CPrintInfo* pInfo) { pInfo->SetMaxPage(5) ; return DoPreparePrinting(pInfo) ; } virtual CString GetDocumentTitle (void) { return _T("通常はドキュメント名を戻します.") ; } } ;
印刷が押されると,
void CAboutDlg::OnPrint() { PrintTest pt ; if (pt.OnFilePrint()) { OnOK() ; } }
2020/07/13 Google ドライブ上の Print.zip へのリンク
Print.zip
2020/12/17 公開しているファイルだけでは利用できなかったので修正
Print_2020_12.zip
クラス名 Print は DH_Print に変更.
印刷時のデフォルトのドキュメント名を ” DH_Print : %Y/%m/%d %H:%M:%S ” に変更.
DH_Print クラスを利用したサンプル.
PrtMF_2020_12.zip
DH_Print 利用のソースが zip の中にあり.