为什么 64 位 Visual Studio C 中的 CreateWindow 在创建时会自行销毁?

rob*_*rtv 2 c winapi visual-studio

我有一些示例代码,它只是在 Windows 10 中的屏幕上放置一个窗口。该程序在 32 位下运行良好:窗口回调过程在窗口创建时发送消息。

在 32 位下,前三个消息是:

WM_GETMINMAXINFO
WM_NCCREATE
WM_NCCALCSIZE
Run Code Online (Sandbox Code Playgroud)

然后从那里开始直到构​​建窗口。

但是,在 64 位下,这不会发生;相反,这些是发送的消息:

WM_GETMINMAXINFO
WM_NCCREATE
WM_NCDESTROY
Run Code Online (Sandbox Code Playgroud)

如您所见,在创建窗口后,操作系统立即发送一条消息来销毁它!

这是实际的代码:

/*****************************************************************************/
/*      This sample demonstrates how to continuously acquire pictures        */
#include <windows.h>
#include <stdio.h>
#define _NIWIN 

#define PB_QUIT     101       /* id for quit application push button */

// Window proc
LRESULT CALLBACK ImaqSmplProc(HWND hWnd, UINT iMessage, UINT wParam, LONG lParam);

// windows GUI globals
static HINSTANCE    hInst;
static HWND         ImaqSmplHwnd;
static HWND         HStop, HGrab, HQuit, HIntfName, HFrameRate;


static long        CanvasWidth = 512;  // width of the display area
static long        CanvasHeight = 384; // height of the display area
static long        CanvasTop = 10;     // top of the display area
static long        CanvasLeft = 10;    // left of the display area
static long        AcqWinWidth;
static long        AcqWinHeight;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                       LPSTR lpszCmdLine, int nCmdShow)
{
    CHAR        ImaqSmplClassName[] = "Imaq Sample";
    WNDCLASS    ImaqSmplClass;
    MSG         msg;
   

    // register the main window
    hInst = hInstance;
    if (!hPrevInstance)
    {
        ImaqSmplClass.style         = CS_HREDRAW | CS_VREDRAW;
        ImaqSmplClass.lpfnWndProc   = (WNDPROC) ImaqSmplProc;
        ImaqSmplClass.cbClsExtra    = 0;
        ImaqSmplClass.cbWndExtra    = 0;
        ImaqSmplClass.hInstance     = hInstance;
        ImaqSmplClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        ImaqSmplClass.hCursor       = LoadCursor (NULL, IDC_ARROW);
        ImaqSmplClass.hbrBackground = GetStockObject(LTGRAY_BRUSH);
        ImaqSmplClass.lpszMenuName  = 0;
        ImaqSmplClass.lpszClassName = ImaqSmplClassName;
    
        if (!RegisterClass (&ImaqSmplClass))
            return (0);
    }

    // creates the main window
    ImaqSmplHwnd = CreateWindow(ImaqSmplClassName, "LLGrab", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                            CW_USEDEFAULT, CW_USEDEFAULT, 680, 440, NULL, NULL, hInstance, NULL);

    // creates the quit application button
    if (!(HQuit = CreateWindow("Button","Quit",BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE,
                                550,152,80,40,ImaqSmplHwnd,(HMENU)PB_QUIT,hInstance,NULL)))
      return(FALSE);
    
    // Display the main window
    ShowWindow(ImaqSmplHwnd, SW_SHOW);
    UpdateWindow(ImaqSmplHwnd);
        
    while (GetMessage (&msg, NULL, 0, 0))
    {
        TranslateMessage (&msg) ;
        DispatchMessage (&msg) ;
    }

    return (int)(msg.wParam);
}

// Message proc
LRESULT CALLBACK ImaqSmplProc(HWND hWnd, UINT iMessage, UINT wParam, LONG lParam)
{
    WORD            wmId;   
    LRESULT theResult;
    
    switch (iMessage)
    {
        case WM_COMMAND:
            wmId    = LOWORD(wParam);
            switch (wmId)
            {
                case PB_QUIT:
                    PostQuitMessage(0);
                    break;
            }
            break;
        case WM_NCDESTROY:
            break;
        case WM_DESTROY:
            PostQuitMessage(0);

        default:
            theResult = DefWindowProc(hWnd, iMessage, wParam, lParam);
            return theResult;
            break;
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Rem*_*eau 7

您的ImaqSmplProc()窗口过程声明不正确,导致您将错误数据传递给DefWindowProc().

对于WM_NCCREATElParam持有一个指针,您在 64 位以下将其截断为 32 位值,因此DefWindowProc()无法WM_NCCREATE正确处理,从而导致CreateWindow()破坏窗口。如果您调试代码,您可能会在处理错误值时看到DefWindowProc()返回。FALSEWM_NCCREATElParam

如果应用程序处理此消息,它应返回 TRUE 以继续创建窗口。如果应用程序返回 FALSE,则 CreateWindow 或 CreateWindowEx 函数将返回一个 NULL 句柄。

为了解决这个问题,wParamlParam参数必须分别声明为WPARAMLPARAM

LRESULT CALLBACK ImaqSmplProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
Run Code Online (Sandbox Code Playgroud)

这与 的实际签名匹配WNDPROC

LRESULT CALLBACK WindowProc(
  _In_ HWND   hwnd,
  _In_ UINT   uMsg,
  _In_ WPARAM wParam,
  _In_ LPARAM lParam
);
Run Code Online (Sandbox Code Playgroud)

WPARAMLPARAM分别声明为UINT_PTRLONG_PTR。这意味着它们是指针大小的整数,因此在 32 位和 64 位下具有不同的大小。而UINTLONG在 32 位和 64 位上具有相同的大小。请参阅Windows 数据类型。您的原始代码没有考虑到这种差异。

您使用了类型转换来使编译器不向您发出错误声明的警告。分配ImaqSmplProc给时,您需要摆脱类型转换ImaqSmplClass.lpfnWndProc

ImaqSmplClass.lpfnWndProc   = ImaqSmplProc;
Run Code Online (Sandbox Code Playgroud)

任何时候你发现自己在使用类型转换,问问自己为什么,你可能做错了什么。除非绝对需要,否则不要使用类型转换。不要仅仅为了让编译器保持沉默而使用类型转换,它会出于某种原因抱怨有问题/错误的事情。