I'm trying to launch a modal dialog from a DLL loaded by an MFC application. I'm using VS2010 and both the EXE and DLL use MFC in a static library.
I call DoModal() in my DLL to launch the dialog, with the parent being a CWnd* pointing to the main window from the MFC app. The dialog resource is in the DLL.
This eventually leads to the MFC library function CWnd::CreateDlgIndirect, which has this debug check:
#ifdef _DEBUG
if ( AfxGetApp()->IsKindOf( RUNTIME_CLASS( COleControlModule ) ) )
{
TRACE(traceAppMsg, 0, "Warning: Creating dialog from within a COleControlModule application is not a supported scenario.\n");
}
#endif
AfxGetApp() returns NULL so the code in the debug check fails. If I compile in release, the dialog appears, but doesn't seem to work (all the fields are empty even though I set defaults, some button's don't appear).
I've tried adding AFX_MANAGE_STATE(AfxGetStaticModuleState()); to the top of the function that launches the dialog, and it doesn't make any difference.
What am I missing?
Edit: here's the code I use to call the dialog.
HMODULE oldResMod = AfxGetResourceHandle();
AFX_MANAGE_STATE(AfxGetStaticModuleState());
AfxSetResourceHandle(GetThisModule());
CWnd wndParent;
wndParent.Attach(parent);
CExportOptionsDlg dlg(&wndParent);
dlg.project_name = project->GetName();
if (dlg.DoModal() != IDOK)
{
wndParent.Detach();
AfxSetResourceHandle(oldResMod);
return false; // cancelled
}
// ... (get some data from the dialog members) ...
wndParent.Detach();
AfxSetResourceHandle(oldResMod);
return true; // OK
Check that you've actually created a CWinApp somewhere in your current module (DLL/EXE).
Every module should have one, and only one, CWinApp object. Typically, you would make the CWinApp object a global variable such that it is created and destroyed when the module is loaded and unloaded, respectively.
Related
I have an MFC dialog based application that has 2 Dialogs: Main Dialog CMyDlgand Second dialog CMyDlg2.
On the main Dialog I add a Button "Go dialog 2". So I added a handler for the button so that when clicked it pops up the second dialog. Everything works fine But on the second Dialog I have added a Rich Edit Control from toolbox. I Added for it a variable. I also added a class for the second dialog.
Now If I run the Application I get the dialog one and if I pressed "Go to dialog 2" I got what I want. But I need at some point to change the font of the rich edit control but my program crashes.
So I overrided OnInitDialog and inside it do some changes to the control but program crashes. After debugging I found that the handle of rich edit is null?!
So how and where can I change the color or do some initializations to the control?
(I called AfxInitRichEdit2() in OnInitInstance())
BOOL CMyDlg2::OnInitDialog() {
m_richEdit.SetWindowText("Hello there!"); // program crashes because the handle m_richEdit is null.
return TRUE;
}
And this is the handler of button that creates the Dialog2 and that contains the rich edit control:
void CMyDlg::OnBnClickedButton1(){
CMyDlg2 theDlg;
theDlg.DoModal();
// TODO: Add your control notification handler code here
}
If I create the rich edit control programmatically then everything works fine because I create it at OnInitDialog and then it works fine but I need the one that is I added using the wizard toolbox.
*** The thing is that if I write:
m_richEdit.SetWindowText(""); // program crashes but if I wirte:
GetDlgItem(IDC_RICHEDIT221).SetWindowText(""); it works fine?
You probably have the following code inserted by wizard:
void DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_RICHEDIT22, m_richEdit);
}
This tells the dialog to associate m_richEdit with the dialog control IDC_RICHEDIT22. But this association is not performed until the base class method CDialog::OnInitDialog(); is called.
BOOL CMyDlg2::OnInitDialog()
{
//this line should work:
GetDlgItem(IDC_RICHEDIT22)->SetWindowText("Hello");
//this line won't work:
//m_richEdit.SetWindowText("Hello there!"); <- richedit's handle is NULL
//this line will subclass m_richEdit
//plus run other initialization
CDialog::OnInitDialog();
//m_richEdit is ready
m_richEdit.SetWindowText("Hello there!");
return TRUE;
}
It's recommended to put CDialog::OnInitDialog() int the first line, to make sure the initialization is done.
GetDlgItem works because the control IDC_RICHEDIT22 exists in the dialog template and you have a valid dialog handle. You are basically making a simple call based on WinAPI's GetDlgItem:
HWND hedit = ::GetDlgItem(m_hWnd, IDC_RICHEDIT22);
::SetWindowText(hedit, "Hello world");
There is no additional initialization needed.
But m_richEdit is just a C++ object, declared as CRichEditCtrl m_richEdit; The constructor for this C++ class doesn't do much besides setting m_hWnd to NULL.
Once it's associated with a valid window handle, we can begin using its windows methods such as CRichEdit::SetWindowText
I'm making a MFC application using the Doc/View architecture with Visual Studio 2017, and for some reason I get that error whenever I call GetDC() inside this function:
void CDigitRecognizerView::ClearScreen(void)
{
CDC* dc;
dc = GetDC(); // debug assertion error here
CBrush brush;
brush.CreateSolidBrush(0xFFFFFF);
dc->SelectObject(&brush);
CRect rect;
GetWindowRect(&rect);
dc->FillRect(&rect, &brush);
CDigitRecognizerDoc* pDocument = GetDocument();
ReleaseDC(dc);
}
This is the message map macro defined in the app class:
BEGIN_MESSAGE_MAP(CDigitRecognizerApp, CWinApp)
ON_COMMAND(ID_APP_ABOUT, &CDigitRecognizerApp::OnAppAbout)
ON_COMMAND(ID_FILE_NEW, &CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen)
ON_COMMAND(ID_EDIT_CLEARSCREEN, CDigitRecognizerView::ClearScreen)
END_MESSAGE_MAP()
So whenever I select the "Clear Screen" option from the menu of the app, ClearScreen() gets called but I can't get the DC of the View, it crashes.
I have looked at the variables in the debugger and the window handle seems OK so I don't know really.
I am also wondering what other way I could call a function of the View class from the App class whenever I select a menu option because this doesn't seem to work.
How did you get the CWinApp message-map pointing to a CDigitRecognizerView function? I think the "wizard" wouldn't do this. Did you add the handler manually?
As for accessing Doc/View instances from the CWinApp class, there are some functions available:
GetFirstDocTemplatePosition() / GetNextDocTemplate(), members of CWinApp class. Alternatively you can simply store the pDocTemplate instance created in the InitInstance() function. Then call:
GetFirstDocPosition() / GetNextDoc(), members of the CDocTemplate class, and finally:
GetFirstViewPosition() / GenNextView(), members of the CDocument class
But this is normally not needed (the events could be handled in the Doc/View classes), unless you want to perform some operation(s) on all (or some of the) DocTemplate/Doc/View instances (which rather implies that you are developing an MDI application).
The below code, creating a Property Sheet, works without problem in an Application File.
.......
CProp_Sheet Pr_Sheet(_T("PS"));
CPr_Page_1 Pr_Page_1;
CPr_Page_2 Pr_Page_2;
Pr_Sheet.AddPage(&Pr_Page_1);
Pr_Sheet.AddPage(&Pr_Page_2);
m_pMainWnd = &Pr_Sheet;
int nResponse = Pr_Sheet.DoModal();
However, when I put the same code in a DLL, and call it from a menu item in another Application, m_pMainWnd cannot be used (the menu in the Application is to remain visible behind the Property Sheet).
I have tried to create a handle
CWnd* m_pWnd = &Pr_Sheet;
and use m_pWnd in the CProp_Sheet constructor, but although compiling correctly at run time failure always occurs at
CProp_Sheet::OnInitDialog().
Thanks in advance for any comments.
I'm using VS2010 and Windows 7, and my app is SDI shared DLL, upgraded from VC6. After installing my application, if the user double-clicks the registered file type, the application crashes at the MFC function:
void CRecentFileList::Add(LPCTSTR lpszPathName, LPCTSTR lpszAppID)
{
// ...
#if (WINVER >= 0x0601)
// ...
#ifdef UNICODE
// ...
#endif
ENSURE(SUCCEEDED(hr)); // Crash here: "hr = 0x800401f0 CoInitialize has not been called."
This is called from the InitInstance() function:
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
//CString str = cmdInfo.m_strFileName + '\n';
//MessageBox(NULL,str, "MyApp", MB_OK|MB_ICONWARNING);
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
The user's chosen file is correctly passed through (as I checked with the MessageBox).
The hr = 0x800401f0 seems to be a COM problem (here), but I'm not using COM or ATL. The assertion is the same as this, but from a different cause. The Germans had the same problem as me (here), but I can't understand the google translation (here)!! I don't think it's a WINVER issue (here) and I don't want to parse my own stuff (like this), just have the application open when a user double clicks a file.
Thanks for any help you can offer :)
The comment you inserted in your code contains the answer:
// Crash here: "hr = 0x800401f0 CoInitialize has not been called."
The HRESULT value is telling you that you need to call the CoInitialize function in order to initialize the COM library for your application's thread.
Of course, the message is a little bit outdated. As you'll see in the above-linked documentation, all new applications should call the CoInitializeEx function instead. No worries, though: it does essentially the same thing as its older brother.
As the "Remarks" section of the documentation indicates:
CoInitializeEx must be called at least once, and is usually called only once, for each thread that uses the COM library. [. . . ] You need to initialize the COM library on a thread before you call any of the library functions except CoGetMalloc, to get a pointer to the standard allocator, and the memory allocation functions. Otherwise, the COM function will return CO_E_NOTINITIALIZED.
You say that you're not using COM, but this is incorrect. You may not be using it explicitly, but Windows and the MFC framework are definitely using it "behind the scenes". All of the file type registration functions rely on COM. The skeleton code produced by the MFC project wizard in Visual Studio 2010 would have automatically inserted the appropriate COM registration code, but since you upgraded an existing project from VC++ 6, you appear to be missing this vital step.
In MFC, the AfxOleInit function also initializes COM for the current apartment of the calling app, just as the OleInitialize function does internally. Make sure that your overridden InitInstance function contains a call to one of these functions.
For example, in a fresh new MFC project created by the VS 2010 wizard, the InitInstance function looks something like this:
BOOL CTestApp::InitInstance()
{
// InitCommonControlsEx() is required on Windows XP if an application
// manifest specifies use of ComCtl32.dll version 6 or later to enable
// visual styles. Otherwise, any window creation will fail.
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// Set this to include all the common control classes you want to use
// in your application.
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
// Initialize OLE libraries
if (!AfxOleInit()) // ** MAKE SURE THAT YOU CALL THIS!! **
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
AfxEnableControlContainer();
// . . .
// a bunch more boring initialization stuff...
// The one and only window has been initialized, so show and update it
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
I loaded the form but only buttons without functions
HMODULE hModule = LoadLibrary(L"Tools.dll");
if (hModule != NULL)
{
AfxSetResourceHandle(hModule);
CDialog dgl(MAKEINTRESOURCE(199), NULL);
dgl.DoModal();
}
so how I can load a full function of form
and I don't have the DLL source code
To show Dialog box from MFC dll , like scenario - you have exported function in DLL and from that function you call DoModel().This template actually stored in DLL module.You need to switch module state for current handle to be used.You can do this by using :
AFX_MANAGE_STATE(AfxGetStaticModuleState());
AFX_MODULE_STATE AfxGetStaticModuleState()
->The AFX_MODULE_STATE structure contains global data for the module,that is the portion of the module state that is pushed or popped.
IN DLL code will be like this :
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CMyDlg objMyDlg;
iRet = objMyDlg.DoModal();
This is possible only if you are sure that dialog class implementation is MFC based and the class is exported from Tools.dll. You can try inspect your .dll with Dependency Walker utility.
Please note the compiler mangles constructor name. This is what I got for the following declaration.
class __declspec(dllexport) TestDialog : public CDialog
{
public:
TestDialog()
:CDialog(10)
{
}
};
Mangled constructor name: ??_7TestDialog##6B#
Probably you will be able to recreate dialog class header based on the results of your inspection. You should also make sure you have the same version of MFC both for Tools.dll and your application.