1

我想使用 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);
  }

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

4

1 回答 1

6

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

因为没有收到警告!

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

人们被迫强制转换函数指针的最常见原因是编译器试图警告他们他们的函数签名不正确。窗口过程函数的正确签名记录在 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函数做同样的事情。该函数的文档确实表明它设置了最后一个错误:

    如果函数失败,则返回值为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);
    }
    
于 2012-06-03T11:35:18.843 回答