|
Below is what is suppose to be a valid but minimal MFC Application using SDI. It consists of
two files, the first is a header file, the second is a C++ file.
Header File
#define IDR_MAINFRAME 100
class MainApp : public CWinApp {
public:
virtual BOOL InitInstance();
};
C++ File
#include <afxwin.h>
#include "Main.h"
#define OEMRESOURCE
MainApp myApp;
class MyDocument : public CDocument {
DECLARE_DYNCREATE( MyDocument )
};
IMPLEMENT_DYNCREATE( MyDocument, CDocument )
class MyView : public CView {
DECLARE_DYNCREATE( MyView )
protected:
void OnDraw(CDC *);
};
IMPLEMENT_DYNCREATE( MyView, CView )
void
MyView::OnDraw(CDC *pDC)
{
}
BOOL
MainApp::InitInstance ()
{
SetRegistryKey (TEXT("Programming Windows 95 with MFC"));
LoadStdProfileSettings ();
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate (
IDR_MAINFRAME,
RUNTIME_CLASS (CDocument),
RUNTIME_CLASS (CFrameWnd),
RUNTIME_CLASS (MyView)
);
AddDocTemplate (pDocTemplate);
RegisterShellFileTypes (TRUE);
CCommandLineInfo cmdInfo;
ParseCommandLine (cmdInfo);
if (!ProcessShellCommand (cmdInfo))
return FALSE;
m_pMainWnd->DragAcceptFiles ();
return TRUE;
}
However, when I compile and run it, it dies in the call to DragAcceptFiles. I do not understand why. I am hoping that somebody here can tell me what is wrong, or the best way to debug this program.
Thanks
Bob
|
|
|
|
|
BobInNJ wrote: it dies in the call to DragAcceptFiles
Hello Bob, when you are experiencing errors saying things like "it doesn't work","it breaks","it dies" is not appropriate. You need to post actual error information. GetLastError() numbers, Exception text, assert information like the line of code containing the assert, etc. Hopefully you get the idea.
BobInNJ wrote: or the best way to debug this program.
I don't know what that means. Debugging is somewhat universal, you break into the execution at a line of code and analyze the state of the process using debugger tools (call stack, memory values).
Also, in case you are trying to implement drop files without a net maybe this will help.[^]
led mike
|
|
|
|
|
 Mike,
Thank you for the response. The error message I get is in the form of a Error Box. The title
of the Box says Microsoft Visaul C++ Debug Library". It also has the line Debug Assertion Failed!. The box also has button on it for Abort, Retry and Ignore.
It gets to the statement:
if (!ProcessShellCommand (cmdInfo))
but it does not get to the statement:
m_pMainWnd->DragAcceptFiles ();
Therefore, I step into the routine ProcessShellCommand. Here is the call stack for the
program after it dies:
> sdi1.exe!AfxMessageBox(unsigned int nIDPrompt=61700, unsigned int nType=0, unsigned int nIDHelp=4294967295) Line 163 + 0x17 bytes C++
sdi1.exe!CSingleDocTemplate::OpenDocumentFile(const wchar_t * lpszPathName=0x00000000, int bMakeVisible=1) Line 113 C++
sdi1.exe!CDocManager::OnFileNew() Line 848 C++
sdi1.exe!CWinApp::OnFileNew() Line 22 C++
sdi1.exe!CWinApp::ProcessShellCommand(CCommandLineInfo & rCmdInfo={...}) Line 26 C++
sdi1.exe!MainApp::InitInstance() Line 46 + 0xc bytes C++
sdi1.exe!AfxWinMain(HINSTANCE__ * hInstance=0x00400000, HINSTANCE__ * hPrevInstance=0x00000000, wchar_t * lpCmdLine=0x00020908, int nCmdShow=1) Line 37 + 0xd bytes C++
sdi1.exe!wWinMain(HINSTANCE__ * hInstance=0x00400000, HINSTANCE__ * hPrevInstance=0x00000000, wchar_t * lpCmdLine=0x00020908, int nCmdShow=1) Line 30 C++
sdi1.exe!__tmainCRTStartup() Line 263 + 0x2c bytes C
sdi1.exe!wWinMainCRTStartup() Line 182 C
kernel32.dll!7c817067()
[Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]
Also, it would seem to me that this is a very simple MFC program and that for somebody who understands MFC should be able to tell me what is wrong off the top of his (or her) head. Maybe, I am wrong about this. My limited experience with MFC SDI programs is that they are full of unexpected surprises.
Thanks
Bob
|
|
|
|
|
In addition to led mike's reply...
You have not created a window anywhere and haven't set m_pMainWnd
to anything so m_pMainWnd->DragAcceptFiles() is certainly going to fail.
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Mark,
Thanks for the response. First, the program never reaches the statement:
m_pMainWnd->DragAcceptedFiles();
Therefore, I do not think that me not setting m_pMainWnd is the problem. Second, my program
is based upon the program in Chapter 8 for Jeff Prosise book, "Programming Windows 95 with MFC" the first edition. In his program, which I could not get to work either, he did not set m_pMainWnd. It is my understanding (which might be all wrong) that when I call the constructor (via new) of CSingleDocTemplate then the window is created automatically and the pointer m_pMainWnd is set. Am I wrong about this?
Also, please look at my last post to see a more detailed explanation on how my program bombs out.
Thanks
Bob
|
|
|
|
|
BobInNJ wrote: the program never reaches the statement:
m_pMainWnd->DragAcceptedFiles();
But you originally stated "it dies in the call to DragAcceptFiles"
BobInNJ wrote: when I call the constructor (via new) of CSingleDocTemplate then the window is created automatically and the pointer m_pMainWnd is set
Son of a gun, you're right.
Maybe put a breakpoint at the beginning of CSingleDocTemplate::OpenDocumentFile()
and step through - what line of code in there is asserting?
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Mark,
Thanks for the prompt response.I do nto remeber saying that it dies in DragAcceptFiles. If I did, I was wrong. Sorry about that. In any case, here is a partial listing of the routine it dies in.
<br />
CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,<br />
BOOL bMakeVisible)<br />
{<br />
CDocument* pDocument = NULL;<br />
CFrameWnd* pFrame = NULL;<br />
BOOL bCreated = FALSE;
BOOL bWasModified = FALSE;<br />
<br />
if (m_pOnlyDoc != NULL)<br />
{<br />
pDocument = m_pOnlyDoc;<br />
if (!pDocument->SaveModified())<br />
return NULL;
<br />
pFrame = (CFrameWnd*)AfxGetMainWnd();<br />
ASSERT(pFrame != NULL);<br />
ASSERT_KINDOF(CFrameWnd, pFrame);<br />
ASSERT_VALID(pFrame);<br />
}<br />
else<br />
{<br />
pDocument = CreateNewDocument();<br />
ASSERT(pFrame == NULL);
bCreated = TRUE;<br />
}<br />
<br />
if (pDocument == NULL)<br />
{<br />
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);<br />
return NULL;<br />
}<br />
ASSERT(pDocument == m_pOnlyDoc);<br />
The statement that is gennerating the message box is:
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
I am thinking that CreateNewDocument should not be returning NULL but it is.
Thanks
Bob
|
|
|
|
|
|
Mark,
Thanks for the response. The place it is dying in is:
if (pDocument == NULL)
{
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
return NULL;
}
ASSERT(pDocument == m_pOnlyDoc);
That is, pDocument is NULL so the body of the if statment is entered and the call to AfxMessageBox happens. I hope this is clear.
Bob
|
|
|
|
|
Awesome, thanks.
It looks like the only way that can happen is if there's already
a document created at the time of the call (there can only be ONE document
in SDI, by definition.
I'm not sure where the pre-existing document is coming from...
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Actually, I'm missing something - I'm not seeing where it's possible for the assertion
to fail.
What are the values of pDocument and m_pOnlyDoc when that line
(ASSERT(pDocument == m_pOnlyDoc);) is reached??
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Mark Salsbery wrote: Actually, I'm missing something - I'm not seeing where it's possible for the assertion
to fail.
Dude I don't think those base classes should be in those RUNTIME_CLASS macros CDocument , CFrameWnd
BobInNJ wrote: pDocTemplate = new CSingleDocTemplate (
IDR_MAINFRAME,
RUNTIME_CLASS (CDocument),
RUNTIME_CLASS (CFrameWnd),
RUNTIME_CLASS (MyView)
);
led mike
|
|
|
|
|
Rut roh - Good point.
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
Mark,
Thanks for the response. The code fragment we are talking about is:
<br />
if (pDocument == NULL)<br />
{<br />
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);<br />
return NULL;<br />
}<br />
ASSERT(pDocument == m_pOnlyDoc);<br />
The ASSERT statement is never reached due to the fact that pDocument is NULL. Whe the if statement is reached, m_pOnlyDoc is NULL also.
Bob
|
|
|
|
|
If you comment out that statement, does the program work otherwise?
"Love people and use things, not love things and use people." - Unknown
"The brick walls are there for a reason...to stop the people who don't want it badly enough." - Randy Pausch
|
|
|
|
|
David,
Thanks for the response. Commenting out the statement:
m_pMainWnd->DragAcceptFiles();
has no effect. Is this (above) the statement you are refering to.
Bob
|
|
|
|
|
BobInNJ wrote: Is this (above) the statement you are refering to.
Yes. Can you use the debugger to step over each statement to find the offending one?
"Love people and use things, not love things and use people." - Unknown
"The brick walls are there for a reason...to stop the people who don't want it badly enough." - Randy Pausch
|
|
|
|
|
David,
Thanks for the response. I have walked through with the debugger. The details of what I found are in my thread with Mark on this issue. Please look there.
Bob
|
|
|
|
|
But you then need to step into CreateNewDocument() .
"Love people and use things, not love things and use people." - Unknown
"The brick walls are there for a reason...to stop the people who don't want it badly enough." - Randy Pausch
|
|
|
|
|
David and/or Mark,
Please consider the following code fragement:
<br />
CObject* CRuntimeClass::CreateObject()<br />
{<br />
ENSURE(this);<br />
<br />
if (m_pfnCreateObject == NULL)<br />
{<br />
TRACE(traceAppMsg, 0,<br />
_T("Error: Trying to create object which is not ")<br />
_T("DECLARE_DYNCREATE \nor DECLARE_SERIAL: %hs.\n"),<br />
m_lpszClassName);<br />
return NULL;<br />
}<br />
When I trace through this, pfnCreateObject is NULL. Therefore, I am thinking that one of my classes
is not properly declared. Do you agree with my reasoning?
Bob
|
|
|
|
|
In addition to David Crow's comments -
you CAN step into MFC source code and put breakpoints in it
(just in case you didn't know )
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
I found a problem:
Please consider the following code fragment:
<br />
CSingleDocTemplate* pDocTemplate;<br />
pDocTemplate = new CSingleDocTemplate (<br />
IDR_MAINFRAME,<br />
RUNTIME_CLASS (CDocument),<br />
RUNTIME_CLASS (MyWindow),<br />
RUNTIME_CLASS (MyView)<br />
);<br />
AddDocTemplate (pDocTemplate);<br />
RegisterShellFileTypes (TRUE);<br />
It should have been:
<br />
CSingleDocTemplate* pDocTemplate;<br />
pDocTemplate = new CSingleDocTemplate (<br />
IDR_MAINFRAME,<br />
RUNTIME_CLASS (MyDocument),<br />
RUNTIME_CLASS (MyWindow),<br />
RUNTIME_CLASS (MyView)<br />
);<br />
AddDocTemplate (pDocTemplate);<br />
RegisterShellFileTypes (TRUE);<br />
After making this change, the code bombs out later. Any other ideas?
Thanks
Bob
|
|
|
|
|
...and the reason for it not working was due to fact that MFC CDocument class has Runtime class information because it is declared using DECLARE_DYNAMIC whereas your MyDocument class actually has functionality to allow dynamic creation of objects using CRuntimeClass because of DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE.
So where does it break now?
Sohail
modified 21-Apr-21 21:01pm.
|
|
|
|
|
It now dies inside the following MFC routine:
<br />
void CSingleDocTemplate::SetDefaultTitle(CDocument* pDocument)<br />
{<br />
CString strDocName;<br />
if (!GetDocString(strDocName, CDocTemplate::docName) ||<br />
strDocName.IsEmpty())<br />
{<br />
ENSURE(strDocName.LoadString(AFX_IDS_UNTITLED));<br />
}<br />
pDocument->SetTitle(strDocName);<br />
}<br />
In particular, with the call to ENSURE. Here is the call stack when it dies:
> sdi1.exe!CSingleDocTemplate::SetDefaultTitle(CDocument * pDocument=0x003d95e8) Line 207 C++
sdi1.exe!CSingleDocTemplate::OpenDocumentFile(const wchar_t * lpszPathName=0x00000000, int bMakeVisible=1) Line 141 C++
sdi1.exe!CDocManager::OnFileNew() Line 848 C++
sdi1.exe!CWinApp::OnFileNew() Line 22 C++
sdi1.exe!CWinApp::ProcessShellCommand(CCommandLineInfo & rCmdInfo={...}) Line 26 C++
sdi1.exe!MainApp::InitInstance() Line 50 + 0xc bytes C++
sdi1.exe!AfxWinMain(HINSTANCE__ * hInstance=0x00400000, HINSTANCE__ * hPrevInstance=0x00000000, wchar_t * lpCmdLine=0x00020908, int nCmdShow=1) Line 37 + 0xd bytes C++
sdi1.exe!wWinMain(HINSTANCE__ * hInstance=0x00400000, HINSTANCE__ * hPrevInstance=0x00000000, wchar_t * lpCmdLine=0x00020908, int nCmdShow=1) Line 30 C++
sdi1.exe!__tmainCRTStartup() Line 263 + 0x2c bytes C
sdi1.exe!wWinMainCRTStartup() Line 182 C
kernel32.dll!7c817067()
[Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]
I am hoping somebody here can tell me what I am doing wrong. I am finding programs using the SDI model to be very hard to write and debug.
Thanks
Bob
|
|
|
|
|
BobInNJ wrote: I am finding programs using the SDI model to be very hard to write and debug.
I would start with a wizard-generated SDI app instead of
code from a book or author. Subtle changes between MFC versions
could make that problematic - the wizard code should run right away
so you can get right to working on actual code
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|