【问题标题】:Access Violation Error while calling CreateWindowEx Function调用 CreateWindowEx 函数时访问冲突错误
【发布时间】:2012-06-03 09:35:46
【问题描述】:

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

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

【问题讨论】:

  • 为什么要将WinProc 转换为(WNDPROC)?此外,您似乎忘记了对 RegisterClassEx 的呼叫。
  • 如果WinProc 具有正确的签名,您不应该收到警告。投射隐藏错误/警告,而不是解决它们。不要这样做!请注意,您将需要一个消息循环,并且默认情况下不会显示您的窗口(需要WS_VISIBLEShowWindow 某处)。我认为您应该花一些时间来完成教程。
  • @tenfour:已编辑!但仍然得到那个错误!我学习了几个教程!我的程序运行正常!但过了一段时间它开始给我这个错误。 (这不是我的完整代码!)
  • 使用调试器并找出它崩溃的地方。哪一行代码。您粘贴的代码无法帮助您。
  • 这很可能是你的WinProc 中的一个错误,正如我已经提到的......

标签: c windows winapi visual-c++


【解决方案1】:

Tenfour 已经在 cmets 中指出了这一点,但它需要再重复大约 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);
    }
    

【讨论】:

  • 一些小注意事项:'ANSI' WinMain() 在 Unicode 构建中可以正常工作,并且在“窗口创建失败”MessageBox() 调用后可能会返回错误。跨度>
  • 其实用 TEXT 宏是没有意义的。最好只针对 Unicode。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-19
  • 1970-01-01
  • 2022-11-03
  • 2019-07-16
  • 1970-01-01
  • 1970-01-01
  • 2018-11-11
相关资源
最近更新 更多