【问题标题】:CreateWindowEx function fails but GetLastError() returns ERROR_SUCCESSCreateWindowEx 函数失败但 GetLastError() 返回 ERROR_SUCCESS
【发布时间】:2012-01-22 22:54:31
【问题描述】:

我正在尝试使用原生 Windows 消息队列系统(不带 .NET)使用 C/C++ 创建一个简单的窗口。我按照 MSDN 教程编写了一些创建空窗口的基本代码:

void main()
    {
    HINSTANCE hinst;
    HWND hwndMain;
    WNDCLASSEX wnd;
    MSG msg;

    hinst = GetModuleHandle( NULL );
    memset( &wnd, 0, sizeof( wnd ) );
    wnd.cbSize = sizeof( wnd );
    wnd.lpszClassName = "MainWClass";
    wnd.lpfnWndProc = MainWProc;
    wnd.hInstance = hinst;
    int result = RegisterClassEx( &wnd );
    if( !result )
    {
        printf("RegisterClassEx error: %d\r\n", GetLastError() );
    }

    hwndMain = CreateWindowEx
        (
        0, //extended styles
        wnd.lpszClassName, //class name
        "Main Window", //window name
        WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL | WS_MINIMIZEBOX, //style tags
        CW_USEDEFAULT, //horizontal position
        CW_USEDEFAULT, //vertical position
        CW_USEDEFAULT, //width
        CW_USEDEFAULT, //height
        (HWND) NULL, //parent window
        (HMENU) NULL, //class menu
        (HINSTANCE) wnd.hInstance, //some HINSTANCE pointer
        NULL //Create Window Data?
        );

    if( !hwndMain )
    {
        printf("Oh shi- %d\n", GetLastError() );
    }
    ShowWindow( hwndMain, SW_SHOWDEFAULT );
    UpdateWindow( hwndMain );
}

当我运行/调试程序时,CreateWindowEx 返回 0,这意味着它失败了。这会触发错误消息“Oh shi- [error code]”。最令人困惑的部分是错误消息打印到控制台:

哦shi-0

GetLastError()返回的错误码为0,即ERROR_SUCCESS!

我完全不知所措;怎么了?我好纠结……

附: 我在 Windows 7 32 位上使用 Visual C++ Express 2010。我在其他地方编写了一个 Windows 过程,但它只是在所有情况下都返回 0。但是,如果有人想看它,我会很乐意展示它。

我已将我的 Visual C++ 项目的项目默认字符集更改为“未设置”。我不需要在我的东西前面加上 L。

编辑:添加 wnd.hInstance = hinst;

编辑:删除了不必要的 (WNDPROC) 演员表

编辑:为 RegisterClassEx 添加错误检查

原来问题出在 Visual C++ Express(或至少不是代码本身)。我将代码复制到另一个项目,它工作。

【问题讨论】:

  • 我想您是故意不将 Visual Studio 向导/模板用于教育目的?此外,void main() 不是有效的 C++。
  • 是的,我想在开始使用向导之前大致了解如何使用本机 Windows 消息队列系统。我的 main() 函数的 void 返回类型应该与 CreateWindowEx() 函数无关,即使它不应该是有效的。
  • 我并不是说你应该在不理解向导代码的情况下使用它! :-) 只是这些样板文件很难理解,而 没有尝试正确输入所有内容。
  • @Joshua:为了学得好,你需要停止使用阻碍学习的技术。 void main 就是其中之一。从绝对意义上讲,它并没有那么糟糕(它只是阻止代码使用 g++ 和其他编译器进行编译),但它是 Microsoft 锁定编码模式的接受,当您接受其中任何一个时,你可能会接受更多,等等,那些编码模式真的很糟糕(例如,向导中的错误处理为窗口生成的代码,不工作)。特别是,停止使用那些 C casts。立刻。他们隐藏错误。干杯&hth.,
  • @CodyGray,您说的是哪个现代版本的 Windows? Windows 7 至少仍然支持非 Unicode 应用程序...如果一半正在使用的应用程序停止工作,一切都会崩溃。

标签: c++ c winapi visual-c++ user-interface


【解决方案1】:

所有现代版本的 Windows 在内部都使用 Unicode,默认情况下,Visual Studio 项目 #define _UNICODE/UNICODE,这会导致您的应用程序链接到 Windows 标头的 Unicode 版本。

但是,当您将应用程序编译为 Unicode 时,字符(以及“字符串”)类型是不同的。他们现在不是char,而是wchar_t。这意味着您必须通过在字符串前面加上 L 前缀来显式声明您的字符串字面量为长字符串。

或者,Windows 标头将所有这些隐藏在宏后面,但不再需要,因为 Windows 已经使用 Unicode 很长时间了,而且这种情况不太可能改变。

除此之外,您在 WNDCLASSEX 结构的初始化中遗漏了几件事,例如 hInstance 成员。这些东西都必须完美设置,否则事情就会失败。同样,RegisterClass(Ex)CreateWindow(Ex) 函数必须传递与窗口类名称对应的完全相同的字符串 值,否则它们会假定您在谈论两个不同的东西。错别字不可原谅!

我强烈建议您使用 Visual Studio 向导创建一个空白(但可以工作!)项目模板。

正确的样板代码如下所示:

#include <windows.h>
#include <tchar.h>

// Define these here to minimize typos, or preferably, load them from a
// resource file at the top of the main function
#define MYCLASSNAME    TEXT("MainWndClass")
#define MYWINDOWNAME   TEXT("Main Window")

// Global variable to keep track of your hInstance
HINSTANCE g_hInstance;

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
   // If you don't process any of the messages yourself, you
   // must pass them to DefWindowProc for default handling.
   return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                       LPTSTR lpCmdLine, int nCmdShow)
{     
    // Save the instance handle in a global variable.
    g_hInstance = hInstance;

    // Register your window class.
    // (A full-featured app will probably want to set additional members.)
    WNDCLASSEX wcex = {0};
    wcex.cbSize = sizeof(wcex);
    wcex.lpfnWndProc = WndProc;
    wcex.hInstance = hInstance;
    wcex.lpszClassName = MYCLASSNAME;
    if (!RegisterClassEx(&wcex))
    {
        MessageBox(NULL, TEXT("Call to RegisterClassEx failed!"), NULL, MB_OK);
        return 1;
    }

    // Create your main window.
    HWND hwndMain = CreateWindowEx(0, MYCLASSNAME, MYWINDOWNAME, WS_OVERLAPPEDWINDOW,
                                   CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL,
                                   hInstance, NULL);
    if (!hwndMain)
    {
        MessageBox(NULL, TEXT("Call to CreateWindowEx failed!"), NULL, MB_OK);
        return 1;
    }

    // Show your main window.
    ShowWindow(hwndMain, nCmdShow);
    UpdateWindow(hwndMain);

    // Run the main message loop.
    BOOL bRetVal;
    MSG msg;
    while ((bRetVal = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (bRetVal == -1)
        {
            MessageBox(NULL, TEXT("Error encountered in message loop!"), NULL, MB_OK);
            return 1;
        }
        else
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}

【讨论】:

  • 我已将项目默认字符集更改为“未设置”。我也没有 Visual Studio 的非 Express 版本,这就是为什么我不能使用向导 =( 我在其他地方有一个调度消息循环,但我没有包含它,因为它无论如何都不会工作,因为 CreateWindowEx 在第一次没有成功地点。
  • 您不想要“未设置”。你想要Unicode;这是默认设置。所有版本的 Windows NT 都是本机 Unicode。使用非 Unicode(“ANSI”)应用程序的最后一个 Windows 版本是 Windows 98。我怀疑你还在运行它。除非您编译为 Unicode,否则事情将无法正常工作。如果您不喜欢所有的TEXT() 宏等,您可以在字符串文字前加上L。不过,对于 Express 来说还算公平。我发布的示例代码应该可以开箱即用。
  • @CodyGray,最后一个不支持 Unicode 的 Windows 版本是 Windows ME。无论如何,所有基于 Windows 2000 的系统都支持 Unicode 和非 Unicode 应用程序。
【解决方案2】:
wnd.lpfnWndProc = (WNDPROC) MainWProc;

我们看不出你需要使用演员表的真正原因,但它很可疑。如果没有发现任何问题,Windows 从 GetLastError() 返回 0。如果窗口过程被破坏,就会发生这种情况。喜欢这个:

LRESULT CALLBACK MainWProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    return 0;
}

Windows 发送 WM_NCCREATE 消息以请求创建窗口。如果该消息没有得到处理,那么将没有窗口。并且没有错误。修复:

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

根据需要进行调整以自定义窗口。只需确保为您不想自己处理的每条消息调用 DefWindowProc() 即可。并保持 Petzold 近在咫尺,以避免简单的错误。并失去演员阵容。

【讨论】:

  • 感谢您的反馈!我删除了 WNDPROC 演员,但问题仍然存在。原来问题出在 Visual C++(或至少不是代码),因为它在我将代码复制到另一个项目后才起作用。附言当我说窗口过程在所有情况下都返回 0 时,我并不是说它只有一行返回 0;代码。我有一个有效的窗口过程 case-switch,默认情况下返回 DefWndowProc。谢谢!
  • 太好了,这帮了大忙!您知道默认窗口过程是否会在通过“x”关闭时终止消息循环?我的消息循环只翻译和发送消息,所以我的假设是它会在发送 PostQuitMessage 时退出。我不知道这是否在单击“x”时发送(或其他方法,如 Alt+F4)。
  • @NickMiller PostQuitMessage当你的窗口是X'd 时调用。您将收到该窗口的WM_CLOSE 消息,您需要在该处理程序中调用PostQuitMessage
  • WM_DESTROY 是最好的。不再有主窗口 => 退出程序。
【解决方案3】:

我遇到了同样的问题。

就我而言,只是我使用了没有管理员权限的视觉 sutdio。 我发现在这种情况下我无法调试我的应用程序。 在没有管理员权限的调试模式下,CreateWindowEx 像你一样返回 null 错误代码结果为 0。 但是如果你去你的构建目录,你可以使用你的应用程序(不是在调试模式下)。 因此,如果您也是这种情况,只需以管理员权限启动 visula studio 即可。

我认为几乎有一种方法可以使用具有管理员权限的 Visual Studio 调试模式,而无需每次都使用管理员密码启动 Visual stuido。我不知道该怎么做,但我认为这是可能的。

【讨论】:

    【解决方案4】:

    就我而言,我必须手动处理 WNC_NCCREATE。 DefWindowProc 为 WNC_NCCREATE 返回零,我用以下方法修复了这个问题:

    LRESULT CALLBACK MainWProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (uMsg == WNC_NCCREATE) 
            return true;
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    

    【讨论】:

      猜你喜欢
      • 2011-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-05
      相关资源
      最近更新 更多