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

The*_*ist 1 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);
  }
Run Code Online (Sandbox Code Playgroud)

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

Cod*_*ray 6

Tenfour已经在评论中指出了这一点,但它再次重复大约6或8次:永远不要将指针WNDPROC强制转换为窗口过程函数.事实上,除非你确切知道为什么需要施放它,否则不要施放任何东西.你回答他的评论,问你为什么要施法,这说明:

没有警告!

事实上,这正是问题所在!所有演员都会告诉编译器"闭嘴,我知道我在做什么!" 由于您按下了"覆盖"按钮,因此您不再收到警告.但那些警告是有原因的 - 他们试图告诉你你的代码被破坏了.编译器可以帮助您.你不会忽视它,或者更糟糕的是,翻转覆盖位并告诉它闭嘴.正如多产的Win32博主Raymond Chen所说,一个函数指针转换是一个等待发生的错误.(这是一个常见的错误,他也在这里这里下来.)

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

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
Run Code Online (Sandbox Code Playgroud)

当然,您可以根据自己的选择命名功能.但参数的数量,类型和返回值都需要匹配该签名.

如果不这样做,编译器将发出错误.如果你翻转覆盖位并抛弃错误,那么代码将在运行时失败,这正是你现在遇到的症状.该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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

     int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                          LPTSTR lpCmdLine, int nCmdShow);
    
    Run Code Online (Sandbox Code Playgroud)
  • 您正确检查RegisterClassEx函数的返回值,如果失败,则调用GetLastError作为调试辅助工具.你应该对CreateWindowEx函数做同样的事情.该函数的文档确实表明它设置了最后一个错误:

    如果函数失败,则返回值为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);
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 实际上使用TEXT宏毫无意义.最好只针对Unicode. (2认同)