调用CreateWindowEx函数时访问冲突错误

时间:2012-06-03 09:35:46

标签: c windows winapi visual-c++

我想使用winAPI创建一个窗口:

    int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpCmdLine,
        int nShowCmd)
 {
WNDCLASSEX wClass;
HWND hWnd;


wClass.cbClsExtra=NULL;
wClass.cbSize=sizeof(WNDCLASSEX);
wClass.cbWndExtra=NULL;
wClass.hbrBackground=(HBRUSH)COLOR_WINDOW;
wClass.hCursor=LoadCursor(NULL,IDC_ARROW);
wClass.hIcon=NULL;
wClass.hIconSm=NULL;
wClass.hInstance=hInst;
wClass.lpfnWndProc=(WNDPROC)WinProc;
wClass.lpszClassName=TEXT("Window Class");
wClass.lpszMenuName=NULL;
wClass.style=CS_HREDRAW|CS_VREDRAW|CS_DROPSHADOW ;

if(!RegisterClassEx(&wClass))
{
    int nResult=GetLastError();
    MessageBox(NULL,
        TEXT("Window class creation failed"),
        TEXT("Window Class Failed"),
        MB_ICONERROR);
}

hWnd=CreateWindowEx(NULL,
        TEXT("Window Class"),
        TEXT("My Process Explorer"),
        WS_OVERLAPPEDWINDOW,
        200,
        20,
        800,
        630,
        NULL,
        NULL,
        hInst,
        NULL);
  }

但是我收到了访问冲突错误。 为什么呢?

1 个答案:

答案 0 :(得分:6)

Tenfour已经在评论中指出了这一点,但它再次重复大约6或8次:永远不要将指向窗口过程函数的指针转换为WNDPROC 。事实上,除非您知道为什么需要投射它,否则不要投射任何。你回答他的评论,询问你为什么要投票:

  

没有收到警告!

事实上,这正是问题所在!所有演员都会告诉编译器“闭嘴,我知道我在做什么!”由于您按下了“覆盖”按钮,因此您不再收到警告。但那些警告是有原因的 - 他们试图告诉你你的代码被破坏了。编译器可以帮助您。你不会忽视它,或者更糟糕的是,翻转覆盖位并告诉它闭嘴。正如多产的Win32博主Raymond Chen所说,A function pointer cast is a bug waiting to happen。 (这是一个常见的错误,他也写了herehere。)

人们感到被迫投射函数指针的最常见原因是编译器试图警告他们他们的函数签名是不正确的。窗口过程函数的正确签名记录为here on MSDN,如下所示:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

当然,您可以命名您选择的任何功能。但参数的数量,类型和返回值都需要匹配该签名。

如果没有,编译器将发出错误。如果你翻转覆盖位并抛弃错误,那么代码将在运行时失败,这正是你现在遇到的症状。 CreateWindowEx函数说“嘿,哇,我不认识你试图传递给我的窗口程序!”

当我编写一个有效的窗口过程存根时,删除虚假强制转换,然后运行您的代码,它可以正常工作而没有错误。例如:

LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return DefWindowProc(hWnd, message, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
                   LPSTR lpCmdLine, int nShowCmd)
{
    WNDCLASSEX wClass;
    HWND hWnd;

    wClass.cbClsExtra=NULL;
    wClass.cbSize=sizeof(WNDCLASSEX);
    wClass.cbWndExtra=NULL;
    wClass.hbrBackground=(HBRUSH)COLOR_WINDOW;
    wClass.hCursor=LoadCursor(NULL,IDC_ARROW);
    wClass.hIcon=NULL;
    wClass.hIconSm=NULL;
    wClass.hInstance=hInst;
    wClass.lpfnWndProc=WinProc;
    wClass.lpszClassName=TEXT("Window Class");
    wClass.lpszMenuName=NULL;
    wClass.style=CS_HREDRAW|CS_VREDRAW|CS_DROPSHADOW ;

    if(!RegisterClassEx(&wClass))
    {
        int nResult=GetLastError();
        MessageBox(NULL,
            TEXT("Window class creation failed"),
            TEXT("Window Class Failed"),
            MB_ICONERROR);
    }

    hWnd=CreateWindowEx(NULL,
            TEXT("Window Class"),
            TEXT("My Process Explorer"),
            WS_OVERLAPPEDWINDOW | WS_VISIBLE,
            200,
            20,
            800,
            630,
            NULL,
            NULL,
            hInst,
            NULL);
}

但是,还有其他几点需要注意:

  • 您正在使用ANSI入口点WinMain,这是不正确的,因为在2012年,您绝对应该编译Unicode。您已经在使用TEXT()宏来确保在定义UNICODE时字符串文字是宽字符串,但您需要对入口点函数执行相同的操作。将定义更改为:

     int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                          LPTSTR lpCmdLine, int nCmdShow);
    
  • 您正确检查RegisterClassEx函数的返回值,如果失败,请调用GetLastError作为调试帮助。您应该使用CreateWindowEx函数执行相同的操作。 The documentation for that function表示它设置了最后一个错误:

      

    如果函数失败,则返回值为NULL。要获取扩展错误信息,请致电GetLastError

    因此,您可以将代码更改为:

    hWnd=CreateWindowEx(NULL,
            TEXT("Window Class"),
            TEXT("My Process Explorer"),
            WS_OVERLAPPEDWINDOW,
            200,
            20,
            800,
            630,
            NULL,
            NULL,
            hInst,
            NULL);
    if (!hWnd)
    {
       int nResult = GetLastError();
       MessageBox(NULL,
            TEXT("Window creation failed"),
            TEXT("Window Failed"),
            MB_ICONERROR);
    }