The documentation of CWnd::CreateEx() does not say that it sets the error state.
Run under the debugger and step into the CreateEx() code.
Or post your code here.
--
David Wilkinson
Visual C++ MVP
> I am creating window with CreateEx function.
> But it fails (and returns zero ) so no window is created.
> When I call GetLastError just after CreatEx call return 0 means no
> ERROR. Why??
It makes sense to call GetLastError after calling raw Win32 API
::CreateWindowEx, not after CWnd::CreateEx.
For example, if CreateWindowEx call (inside CWnd::CreateEx body) fails,
other functions are called before CWnd::CreateEx returns FALSE.
In fact, in CWnd::CreateEx implementation in wincore.cpp (VS2008 SP1), I can
read:
<code>
...
if (!AfxUnhookWindowCreate())
PostNcDestroy(); // cleanup if CreateWindowEx fails too soon
if (hWnd == NULL)
return FALSE;
</code>
So, if CreateWindowEx failed, AfxUnhookWindowCreate is called, and
PostNcDestroy could be called, before CWnd::CreateEx returns FALSE.
I think these functions may alter the error state, making GetLastError calls
after CWnd::CreateEx meaningless.
Giovanni
As explained by Giovanni, last error is probably eaten by cleanup
code. A quick inspection of ::CreateEx shows that you can see
GetLastError() in _DEBUG builds.
My guess is that the MFC people thinking goes like this: there's two
reasons for CreateEx to fail, either there is a resource shortage, in
which case result FALSE means OutOfResources, either there is a bug in
client code, in which case it's enough to show last error only in
_DEBUG builds.
Of course, that's not very nice. It's better to employ scope guard^^^^
and preserve original error code, e.g.
void ___RestoreLastError(DWORD e)
{ SetLastError(e); }
void ___UnhookWindowCreate(CWnd& wnd)
{
if (AfxUnhookWindowCreate())
wnd.PostNcDestroy();
}
BOOL CWnd::CreateEx(....)
{
.......
DWORD dwLastError=NOERROR;
ScopeGuard GuardLastError = MakeGuard(&__RestoreLastError, ByRef
(dwLastError));
AfxHookWindowCreate(this); // wincore.cpp, line 705 (VS2008 MFC
sources)
ON_BLOCK_EXIT(&___UnhookWindowCreate, *this);
if (::AfxCtxCreateWindowEx(cs.dwExStyle, cs.lpszClass,
cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams) == NULL)
{
dwLastCreationError = GetLastError();
return FALSE;
}
ASSERT(hWnd == m_hWnd); // should have been set in send msg hook
GuardLastError.dismiss(); // Creation worked, don't touch last
error.
return TRUE;
}
Goran.
P.S. (^^^^) he who knows not of ScopeGuard, Google it; if you know C+
+, it will forever change the way you code, and for the better.
P.P.S How does the above work? (Provided it does, since it's compiled
with head-compiler and debugged with head-debugger). In short: we
register last error just after the failed call to
AfxCtxCreateWindowEx. GuardLastError object forces a call to
SetLastError. Call to SetLastError happens just before CWnd::CreateEx
exits, during the destruction of GuardLastError object. One has to be
a crafty bastard and place GuardLastError in a good place so that it
this is indeed last to be destroyed, which I am (crafty bastard, that
is). If creation works, GuardLastError is "dismissed" (in scope guard
parlance), that is, we don't touch last error value on success. Call
to AfxHookWindowCreate is matched with Unhook/PostDestroy in lines
718/719. That's assured through ON_BLOCK_EXIT with
___UnhookWindowCreate.
P.P.P.S. This seems complicated, and indeed it is in a simple
situation like this. However, judicious application of scope guard in
anything a more complicated easily makes for simpler code. And when
exceptions are present (and they are even if you don't see any throw
statements), code gets unwieldy even easier. For example, just ask
yourself how would correct code look like if AfxCtxCreateWindowEx
could throw. (BTW, I guess it can't; if it can, CWnd:::CreateEx does
not satisfy "Basic exception safety" from
http://en.wikipedia.org/wiki/Exception_handling#Exception_safety. That
wouldn't be a surprise, though; quick glance at MFC sources shows that
MFC is all but exception safe. But luckily for MFC, next contender,
Qt, ain't any better in that regard either).
[Sorry, I get tired of seeing the same bugs come back year after year, even though they
are reasonbly trivial for Microsoft to fix]
As pointed out, single-stepping into the code helps. Note also that there are a number of
places where, instead of ASSERT statements, MFC just prints out English-language messages
about problems (often with decimal values displayed in hex, because hex is so much
cooler), so be sure to look in your output window.
But ultimately, the most effective procedure is to single-step through CWnd::CreateEx with
the step-into function (F11 default shortcut) to see what is happening. Closing your eyes
and saying "MFC is perfect, I don't need to look into it" is not a viable approach to this
kind of debugging.
joe
Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm