CreateWindowExA failing on Windows 8 (Pro 64-bit)

1.4k views Asked by At

Is there a known issue with calling CreateWindowExA on Windows 8 (64-bit) for a 64-bit application?

Context: I'm using the FOX Toolkit (FOX STABLE 1.6.46). When compling and running the most trivial Hello World sample ("hello"), the call to CreateWindowExA in file FXWindow.cpp:1345 returns a zero HWND handle (but GetLastError() doesn't report an error). This only happens in one specific configuration:

OS        | OS Platform | App compiled for | CreateWindowExA succeeds? |
Windows 7 |    32-bit   |      32-bit      |         YES               |
Windows 7 |    64-bit   |      32-bit      |         YES               |
Windows 7 |    64-bit   |      64-bit      |         YES               |
Windows 8 |    64-bit   |      32-bit      |         YES               |
Windows 8 |    64-bit   |      64-bit      | NO! (returns NULL)        |

Is there anything different about CreateWindowExA with the last configuration. Please note that the window procedure is the same in all cases, and that the messages it receives are the following, in that order:

  • WM_GETMINMAXINFO (forwarded to DefWindowProc)
  • WM_NCCREATE (forwarded to DefWindowProc)

In the last configuration, it goes on with WM_NCDESTROY and then CreateWindowExA returns NULL.

In all other configurations, WM_NCCALCSIZE is sent and finally WM_CREATE.

3

There are 3 answers

2
Daniel Gehriger On BEST ANSWER

I've found the source problem: FOX incorrectly defines the function signature of the window procedure as

long CALLBACK wndproc(FXID hwnd,unsigned iMsg,unsigned int wParam,long lParam);

(with FXID typedef'd to void*), so on 64-bit Windows, wParam and lParam are only 32-bit wide, whereas they should be 64-bit. The correct function signature (using FOX types) is:

FXival CALLBACK wndproc(FXID hwnd,unsigned int iMsg,FXuval wParam,FXival lParam); 

So why did it work in 64-bit Windows up to Windows 7? As MSDN says:

The lParam of WM_NCCREATE contains a pointer to the CREATESTRUCT structure that contains information about the window being created. The members of CREATESTRUCT are identical to the parameters of the CreateWindowEx function.

It so happens that on Windows 7 (64-bit) and below, that structure was always allocated in memory below 4GB, and even though the pointer value was truncated to 32-bit, it still pointed to the correct location. As of Windows 8, that structure is allocated anywhere in the 64-bit memory range, and truncating it is likely to produce an incorrect pointer.

There is just one thing I'm unsure about: CALLBACK being __stdcall, arguments are pushed onto the stack from right to left. So, given the incorrect declaration of the windproc, is it still retrieving the correct iMsg and hwnd parameters?

0
MikhailL On

Confirm: 64-bit Windows 8.1 does not call WM_CREATE (while 32-but one do, like both 32- and 64-bit Windows 7). The troubles happen under CreateWindows*(), when message loop is yet irrelevant.

The note of 32-bit address is fair. In following code SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR)this) LONG_PTR can be mistakenly replaced with LONG which Windows 7 forgives but Windows 8 does not.

Luckily, WM_NCCREATE still is sent.

After intercepting WM_CREATE/WM_NCCREATE one must "return DefWindowProc()", not "return 0".

I used this mechanics to pass a pointer (last argument of CreateWindow()) to my WndProc, which in turn associated this pointer with hwnd. The desire is to pack all window-related data and functions into a class and make code better structured. The idea looked robust, but I refused it eventually. The reason is a cost of GetWindowLongPtr() which is called on every WndProc call. It is ~100 instructions on 32-bit and ~70 on 64-bit version. The alternative is static pointer to dynamic object (or maybe hash table if WndProc serves several object instances). Not that nice maybe, but it works faster and will continue working even if WM_NCCREATE will disappear too.

0
hmmm On

I had same problem. Then I solved it using CreateWindowEx(..) function without "A". This may be beneficial to others who should use this function