внешняя переменная недоступна для потока, пока не будет выполнен блокирующий вызов ... - PullRequest
0 голосов
/ 03 декабря 2011

У меня есть два файла кода, один содержит мою функцию WinMain() и все функции, связанные с моим главным диалоговым окном. Другой содержит обратный вызов потока и переменные функции, связанные с моей программой. Я определил HWND hWnd = NULL как глобальную переменную в MainDlg.cpp и определил extern HWND hWnd в Other.cpp (который содержит обратный вызов потока). Поток создается, когда в окно отправляется сообщение WM_INITDIALOG. Внутри обратного вызова потока переменная hWnd равна нулю до тех пор, пока не будет выполнена длительная операция, после чего она станет доступной.

MainDlg.cpp

HWND hWnd = NULL;
HANDLE hListenThread = NULL;

DWORD WINAPI ListenThread( LPVOID lpvParam );

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
    /* ... */
    if( NULL == (hWnd=CreateDialog( hInstance, MAKEINTRESOURCE(IDD_MAINDLG), NULL, (DLGPROC)WndProc)) )
    {
        MessageBox( NULL, "Error creating the main dialog!", NULL, MB_OK | MB_ICONERROR );
        return -1;
    }

    MSG msg;
    while( GetMessage(&msg,NULL,0,0) && IsWindow(hWnd) )
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

BOOL CALLBACK WndProc(HWND hWndDlg, UINT Message, WPARAM wParam, LPARAM lParam)
{

    switch(Message)
    {
        case WM_INITDIALOG:
        {
            DWORD dwListenThreadId = NULL;
            /* referencing hWnd here works fine... */
            /* ... */
            hListenThread = CreateThread(
                NULL,
                0,
                ListenThread,
                hWndDlg,
                0,
                &dwListenThreadId);
            /* ... */
        } break;
    }

    return false;
}

Other.cpp

extern HWND hWnd;
DWORD WINAPI ListenThread( LPVOID lpvParam )
{

    if( hWnd == NULL )
        MessageBox( NULL, "hWnd == NULL", NULL, MB_OK | MB_SYSTEMMODAL );

    if( hWnd != NULL )
        MessageBox( NULL, "hWnd != NULL", NULL, MB_OK | MB_SYSTEMMODAL );

    return 0;
}

Оба окна сообщений в обратном вызове ListenThread отображаются при запуске приложения. Может кто-нибудь сказать, пожалуйста, почему это происходит? Есть ли что-то, что я могу сделать, чтобы исправить это, кроме выполнения while( hWnd == NULL ); в начале моего ListenThread?

Ответы [ 2 ]

3 голосов
/ 03 декабря 2011

Сообщение WM_INITDIALOG появляется до того, как CreateDialog возвращается, и hWnd не устанавливается, пока CreateDialog не вернется.Таким образом, поток запускается до того, как CreateDialog возвращает и устанавливает глобальную переменную hWnd.

Таким образом, вы можете исправить это, переместив создание потока из сообщения WM_INITDIALOG сразу после возврата CreateDialog.

Но вам не нужно этого делать, потому что вашему коду не требуется глобальная переменная hWnd.Вы уже передаете дескриптор окна диалогов в качестве параметра процедуре запуска потока.Так что просто приведите lpvParam к HWND и используйте его.Это позволит вам избавиться от вашей глобальной переменной, что является плохой практикой.

Other.cpp

DWORD WINAPI ListenThread( LPVOID lpvParam )
{
    HWND hWnd= (HWND)lpvParam;

    if( hWnd == NULL )
        MessageBox( NULL, "hWnd == NULL", NULL, MB_OK | MB_SYSTEMMODAL );

    if( hWnd != NULL )
        MessageBox( NULL, "hWnd != NULL", NULL, MB_OK | MB_SYSTEMMODAL );

    return 0;
}
1 голос
/ 03 декабря 2011

Сообщение WM_INITDIALOG отправляется WndProc() изнутри самого CreateDialog(), а не DispatchMessage().Об этом говорится в CreateDialog() документации .Ваша переменная hWnd не назначается до тех пор, пока не выйдет CreateDialog().Если ваш поток начинает работать до выхода CreateDialog() (в зависимости от планирования задач, это не является гарантией), ваш первый MessageBox() будет вызван.Пока MessageBox() работает, CreateDialog() имеет время для выхода и назначения переменной, поэтому второй MessageBox() вызывается после того, как вы отклоните первый MessageBox().

Вам не нужноиспользовать переменную hWnd в вашем потоке вообще.Вы передаете диалог HWND параметру lpParameter CreateThread(), поэтому он появится в параметре lpvParam ListenThread(), например:

DWORD WINAPI ListenThread( LPVOID lpvParam )  
{  
    HWND hWnd = (HWND) lpvParam;  
    ...
    return 0;  
}  
...