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

调用CreateWindowEx函数时发生访问冲突错误,c,windows,winapi,visual-c++,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.hbrBackgro

我想使用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);
  }
但我得到了访问冲突错误。
为什么?

Tenfour已经在评论中指出了这一点,但它需要再重复6到8次:永远不要将指向窗口过程函数的指针投射到
WNDPROC
。事实上,除非你确切地知道为什么你需要投,否则不要投任何东西。他问你为什么要选演员,你的回答是:

因为没有收到警告

事实上,这正是问题所在!强制转换所做的就是告诉编译器“闭嘴,我知道我在做什么!”因为您按下了“覆盖”按钮,所以不再收到警告。但这些警告之所以出现,是因为他们试图告诉你你的代码被破坏了。编译器在那里帮助你。如果忽略它,或者更糟的是,翻转覆盖位并告诉它闭嘴,你不会走得很远。正如多产的Win32博主雷蒙德·陈(Raymond Chen)所言。(这是一个非常常见的错误,他还写了关于它的文章,并对其进行了分析。)

人们被迫转换函数指针的最常见原因是编译器试图警告他们函数签名不正确。记录了窗口过程函数的正确签名,如下所示:

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
    函数执行相同的操作。表示它设置了最后一个错误:

    如果函数失败,返回值为
    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);
    }
    

Tenfour已经在评论中指出了这一点,但需要再重复6到8次:不要将指向窗口过程函数的指针投射到
WNDPROC
。事实上,除非你确切地知道为什么你需要投,否则不要投任何东西。他问你为什么要选演员,你的回答是:

因为没有收到警告

事实上,这正是问题所在!强制转换所做的就是告诉编译器“闭嘴,我知道我在做什么!”因为您按下了“覆盖”按钮,所以不再收到警告。但这些警告之所以出现,是因为他们试图告诉你你的代码被破坏了。编译器在那里帮助你。如果忽略它,或者更糟的是,翻转覆盖位并告诉它闭嘴,你不会走得很远。正如多产的Win32博主雷蒙德·陈(Raymond Chen)所言。(这是一个非常常见的错误,他还写了关于它的文章,并对其进行了分析。)

人们被迫转换函数指针的最常见原因是编译器试图警告他们函数签名不正确。记录了窗口过程函数的正确签名,如下所示:

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
    函数执行相同的操作。表示它设置了最后一个错误:

    如果函数失败,返回值为
    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);
    }
    

为什么要将
WinProc
转换为
(WNDPROC)
?另外,您似乎忘记了调用
RegisterClassEx
。如果
WinProc
具有正确的签名,则不应收到警告。铸造隐藏错误