2017-04-22 48 views
5

Niedawna aktualizacja twórców do systemu Windows 10 zepsuła mój kod aplikacji, która używa Win32 API GetWindowLong().GetWindowLong() - Zmiana w zachowaniu wprowadzona przy pomocy aktualizacji twórców łamie moją aplikację win32

Przed aktualizacją dla twórców systemu Windows 10 jeden proces (na przykład proces A) mógł wywoływać funkcje API GetWindowWord()/GetWindowLong() na uchwycie okna innego procesu (np. Proces B), nawet jeśli proces B (główny wątek) był zablokowane w niektórych wywołaniach systemu (np. oczekiwanie na zwolnienie muteksa). Tak więc proces A był w stanie przesłać kwerendę do zarezerwowanej pamięci okna, którego właścicielem jest proces B, z powodzeniem wykorzystując te interfejsy API pomimo zablokowania procesu B.

Jednak w przypadku Aktualizacji twórców zastosowanych w systemie Windows 10 proces A zostaje zablokowany, gdy wywołuje te interfejsy API w oknie należącym do procesu B, gdy proces B (główny wątek) jest zablokowany.

Symulowałem ten scenariusz, tworząc dwie samodzielne aplikacje Win32 reprezentujące proces A i proces B. W systemie Windows 10 z zainstalowaną aktualizacją twórców proces A powieszony, gdy nazywał się GetWindowLong()/GetWindowWord() w oknie należącym do do przetwarzania B, podczas gdy proces B (główny wątek) czekał na muteksie. Innymi słowy, wywołania GetWindowLong()/GetWindowWord() nigdy nie powróciły, powodując zawieszenie procesu A.

Jednak podczas testowania tego samego scenariusza z moimi autonomicznymi aplikacjami w systemie Windows 10 bez Aktualizacji twórców lub wcześniejszej wersji, takiej jak Windows 7, wywołania funkcji GetWindowLong()/GetWindowWord() w procesie A powrócić pomyślnie, nawet gdy proces B oczekuje na zwolnienie muteksu.

Aby zademonstrować powyższy problem, oto kod zarówno dla Procesu A, jak i dla Procesu B. Aby zobaczyć problem, uruchom proces A i proces B. Następnie znajdź uchwyt okna okna Procesu B (np. Za pomocą Spy ++), a następnie wklej go do pola edycji okna procesu A. Następnie kliknij OK. Wyświetlane jest okno komunikatu wyświetlające wartość LONG ustawioną w dodatkowej pamięci okna procesu B (za pomocą SetWindowLong()). Jak dotąd, tak dobrze. Teraz przejdź do okna procesu B i spraw, aby zawiesił się, klikając przycisk "Zablokuj". Spowoduje to, że proces "B" (główny wątek GUI) będzie czekał na muteksie, który nigdy nie zostanie zwolniony, a zatem proces B zawiesi się.

TERAZ, wróć do procesu A i ponownie kliknij OK (zakładając, że pole edycyjne ma nadal ten sam uchwyt okna procesu B, który wkleiłeś wcześniej).

TERAZ, tu jest różnica w zachowaniu:

W Windows 10 Bez twórców Update i we wcześniejszych wersjach systemu Windows, takich jak Windows 7, jak poprzednio (czyli gdy proces B nie zawiesza się), wyświetlony zostanie komunikat wyświetlania wartość LONG ustawiona w dodatkowej pamięci okna procesu B (za pomocą SetWindowLong()) jest pokazana.

W systemie Windows 10 Z aktualizacją Kreator, proces A zawiesza się, ponieważ wywołanie SetWindowLong() wykonane przy użyciu uchwytu okna procesu B nigdy nie wraca, powodując zawieszenie procesu A.

Pls sugerują, jak obejść zmianę zachowania w aktualizacji Windows 10 Creators Update, aby moja aplikacja nie zawiesiła się. Wszelkie pomysły/pomoc będą mile widziane.

Oto kod dla procesu A.

/* Process A */ 
#include <windows.h> 
#include <stdio.h> 
#include <commctrl.h> 

int count = 0; 
int count1 = 0; 
TCHAR str[1000]; 

LRESULT CALLBACK WindowFunc(HWND,UINT,WPARAM,LPARAM); 

HWND g_hwndEdit, g_hwndButton; 

#define ID_EDIT (3456) 

#define ID_OK (3457) 

TCHAR szWinName[] = TEXT("MyWin"); 

HINSTANCE g_hInst = NULL; 

int WINAPI WinMain(HINSTANCE hThisInst,HINSTANCE hPrevInst,LPSTR lpszArgs,int nWinMode) 
{ 
    HWND hwnd; 
    MSG msg; 
    WNDCLASSEX wcl; 
    g_hInst = hThisInst; 
    wcl.cbSize = sizeof(WNDCLASSEX); 
    wcl.hInstance = hThisInst; 
    wcl.lpszClassName = szWinName; 
    wcl.lpfnWndProc = WindowFunc; 
    wcl.style = CS_HREDRAW|CS_VREDRAW; 
    wcl.hIcon = LoadIcon(NULL,IDI_APPLICATION); 
    wcl.hIconSm = NULL; 
    wcl.hCursor = LoadCursor(NULL,IDC_ARROW); 
    wcl.lpszMenuName = NULL; 
    wcl.cbClsExtra = 0; 
    wcl.cbWndExtra = 44; 
    wcl.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); 
    if(!RegisterClassEx(&wcl)) return 0; 
    hwnd = CreateWindowEx(
     WS_EX_WINDOWEDGE, 
     szWinName, 
     "Process A", 
     WS_OVERLAPPEDWINDOW, 
     CW_USEDEFAULT, 
     CW_USEDEFAULT, 
     CW_USEDEFAULT, 
     CW_USEDEFAULT, 
     HWND_DESKTOP, 
     NULL, 
     hThisInst, 
     NULL 
     );  
    ShowWindow(hwnd,nWinMode); 
    UpdateWindow(hwnd); 

    while(GetMessage(&msg,NULL,0,0)) 
    { 
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 
    return msg.wParam; 
} 
LRESULT CALLBACK WindowFunc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) 
{ 
    LONG l;  
    HWND hwndOther = hwnd; 
    char s[] = "Paste the window handle (in HEX) of Process B's window on which you wish to call GetWindowLong() in the edit field and click on OK."; 

    HDC hdc; 
    PAINTSTRUCT ps; 
    static int cxClient = 0, cyClient = 0; 

    char btnText[1001]; 

    switch(message){ 

    case WM_CREATE: 

     g_hwndEdit = CreateWindow ("edit", NULL, 
         WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | 
            WS_BORDER | ES_LEFT, 
         200, 200, 200, 200, hwnd, (HMENU)ID_EDIT, 
         g_hInst, NULL) ; 

     g_hwndButton = CreateWindow(      
         "Button", 
         "OK", 
         WS_CHILD|WS_VISIBLE, 
         500, 
         200, 
         150, 
         50, 
         hwnd, 
         (HMENU)ID_OK, 
         g_hInst, 
         NULL 
         ); 

     return 0; 

    case WM_SIZE: 

     cxClient = LOWORD(lParam); 
     cyClient = HIWORD(lParam); 

     return 0; 

    case WM_PAINT: 

     hdc = BeginPaint(hwnd, &ps); 

     TextOut(hdc, 10, 100, s, strlen(s)); 

     EndPaint(hwnd, &ps); 

     return 0; 


    case WM_COMMAND: 
     { 
      if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_OK) 
      { 
       GetWindowText(g_hwndEdit, btnText, 1000); 
       sscanf(btnText, "%x", &hwndOther); 
       l = GetWindowLong(hwndOther, 24); 
       sprintf(str, "The LONG value at offset 24 of the window with handle 0x%x is %d.", hwndOther, l); 
       MessageBox(hwnd, str, "", 0); 
      } 
     } 

     break; 

    case WM_DESTROY: 

     PostQuitMessage(0); 
     return 0;   
    } 
    return DefWindowProc(hwnd,message,wParam,lParam); 
} 

A oto kod dla Process B:

/* Process B */ 
#include <windows.h> 
#include <stdio.h> 
#include <commctrl.h> 

int count = 0; 
int count1 = 0; 
TCHAR str[1000]; 

LRESULT CALLBACK WindowFunc(HWND,UINT,WPARAM,LPARAM); 

TCHAR szWinName[] = TEXT("MyWin"); 

HINSTANCE g_hInst = NULL; 

HANDLE g_hThread, g_hMutex; 

HWND g_hwndButton; 

#define ID_BUTTON (3456) 

//worker thread fn 
DWORD WINAPI ThreadFunc(LPVOID p) 
{ 
    g_hMutex = CreateMutex(NULL, TRUE, "HELLO_MUTEX"); 
    // this worker thread now owns the above created mutex and goes into an infinite loop so that 
    // the mutex is never released 
    while (1){} 
    return 0; 
} 

// main (GUI) thread 
int WINAPI WinMain(HINSTANCE hThisInst,HINSTANCE hPrevInst,LPSTR lpszArgs,int nWinMode) 
{  

    HANDLE hThread; 
    DWORD threadld; 
    // create a worker thread that will create a mutex and then will go into an infinite loop making sure that the mutex is never released 
    // and thus when the main (GUI) thread calls WaitForSingleObject() on this mutex handle, it is going to block forever. 
    hThread = CreateThread(NULL, 
          0, 
          ThreadFunc, 
          0, 
          0, 
          &threadld); 

    // make the main (GUI) thread sleep for 5 secs so that by the time it wakes up, the worker thread will have created the mutex and gone into an infinite loop 
    Sleep(5000); 
    HWND hwnd; 
    MSG msg; 
    WNDCLASSEX wcl; 
    g_hInst = hThisInst; 
    wcl.cbSize = sizeof(WNDCLASSEX); 
    wcl.hInstance = hThisInst; 
    wcl.lpszClassName = szWinName; 
    wcl.lpfnWndProc = WindowFunc; 
    wcl.style = CS_HREDRAW|CS_VREDRAW; 
    wcl.hIcon = LoadIcon(NULL,IDI_APPLICATION); 
    wcl.hIconSm = NULL; 
    wcl.hCursor = LoadCursor(NULL,IDC_ARROW); 
    wcl.lpszMenuName = NULL; 
    wcl.cbClsExtra = 0; 
    wcl.cbWndExtra = 44; 
    wcl.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); 
    if(!RegisterClassEx(&wcl)) return 0; 
    hwnd = CreateWindowEx(
     WS_EX_WINDOWEDGE, 
     szWinName, 
     "Process B", 
     WS_OVERLAPPEDWINDOW, 
     CW_USEDEFAULT, 
     CW_USEDEFAULT, 
     CW_USEDEFAULT, 
     CW_USEDEFAULT, 
     HWND_DESKTOP, 
     NULL, 
     hThisInst, 
     NULL 
     ); 
    SetWindowLong(hwnd, 24, 135678); 
    ShowWindow(hwnd,nWinMode); 
    UpdateWindow(hwnd); 

    while(GetMessage(&msg,NULL,0,0)) 
    { 
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 
    return msg.wParam; 
} 
LRESULT CALLBACK WindowFunc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) 
{ 
    char strr[1000];  
    char s[] = "Click on the \"Block\" button below to make the main (GUI) thread block by waiting on a mutex forever since the mutex will never be released."; 
    HWND hwndOther = hwnd; 

    HDC hdc; 
    PAINTSTRUCT ps; 
    static int cxClient = 0, cyClient = 0; 

    switch(message){ 

    case WM_CREATE: 


     sprintf(strr, "Window created - handle is %x.\n", hwnd); 
     OutputDebugString(strr); 

     g_hwndButton = CreateWindow(      
         "Button", 
         "Block", 
         WS_CHILD|WS_VISIBLE, 
         10, 
         120, 
         50, 
         50, 
         hwnd, 
         (HMENU)ID_BUTTON, 
         g_hInst, 
         NULL 
         ); 

     return 0; 

    case WM_SIZE: 

     cxClient = LOWORD(lParam); 
     cyClient = HIWORD(lParam); 

     return 0; 

    case WM_PAINT: 

     hdc = BeginPaint(hwnd, &ps); 

     TextOut(hdc, 10, 100, s, strlen(s)); 

     EndPaint(hwnd, &ps); 

     return 0; 

    case WM_COMMAND: 
     { 
      if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_BUTTON) 
      {     
       MessageBox(hwnd, "Main (GUI) Thread going in blocking state by waiting for mutex forever now", "", 0); 
       WaitForSingleObject(g_hMutex, INFINITE);    
      } 
     } 

     break; 

    case WM_DESTROY: 

     PostQuitMessage(0); 
     return 0;   
    } 
    return DefWindowProc(hwnd,message,wParam,lParam); 
} 
+0

Jeśli to naprawdę jest coś, co zmieniło się w CU, jest mało prawdopodobne, aby cokolwiek można z tym zrobić. –

+1

naprawdę nici gui nie może czekać i "wisiał". musi na stałe uruchomić pętlę wiadomości. więc rozwiązanie bardzo prosto - nie czekaj, bez przetwarzania komunikatów, w gui thread – RbMm

+0

Dzięki RbMm za wskazanie mnie we właściwym kierunku. Twoja sugestia implikowała użycie MsgWaitForMultipleObjects(), która nie spowoduje zawieszenia wątku GUI w Procesie B, a zatem GetWindowLong() wykonane w Procesie A na uchwycie okna Procesu B powróci normalnie bez zwisania. –

Odpowiedz

0

Chyba znalazłem rozwiązanie, które jest w użyciu MsgWaitForMultipleObjects() wewnątrz Process B tak, że ciągle szuka wiadomości w kolejce, jak również oprócz czekania na muteksie. W ten sposób wywołanie GetWindowLong() wykonane w procesie A na uchwycie okna procesu B powróci normalnie bez blokowania, a zatem problem zostanie rozwiązany.

Oto zaktualizowany kod Process B, zmiana jest w "Bloku" Kliknij przycisk obsługi WM_COMMAND przypadku (Proces Kod pozostaje taka sama):

#include <windows.h> 
#include <stdio.h> 
#include <commctrl.h> 

int count = 0; 
int count1 = 0; 
TCHAR str[1000]; 

LRESULT CALLBACK WindowFunc(HWND,UINT,WPARAM,LPARAM); 

TCHAR szWinName[] = TEXT("MyWin"); 

HINSTANCE g_hInst = NULL; 

HANDLE g_hThread, g_hMutex; 

HWND g_hwndButton; 

#define ID_BUTTON (3456) 

//worker thread fn 
DWORD WINAPI ThreadFunc(LPVOID p) 
{ 
    g_hMutex = CreateMutex(NULL, TRUE, "HELLO_MUTEX"); 
    // this worker thread now owns the above created mutex and goes into an infinite loop so that 
    // the mutex is never released 
    while (1){} 
    return 0; 
} 

// main (GUI) thread 
int WINAPI WinMain(HINSTANCE hThisInst,HINSTANCE hPrevInst,LPSTR lpszArgs,int nWinMode) 
{  

    HANDLE hThread; 
    DWORD threadld; 
    // create a worker thread that will create a mutex and then will go into an infinite loop making sure that the mutex is never released 
    // and thus when the main (GUI) thread calls WaitForSingleObject() on this mutex handle, it is going to block forever. 
    hThread = CreateThread(NULL, 
          0, 
          ThreadFunc, 
          0, 
          0, 
          &threadld); 

    // make the main (GUI) thread sleep for 5 secs so that by the time it wakes up, the worker thread will have created the mutex and gone into an infinite loop 
    Sleep(5000); 
    HWND hwnd; 
    MSG msg; 
    WNDCLASSEX wcl; 
    g_hInst = hThisInst; 
    wcl.cbSize = sizeof(WNDCLASSEX); 
    wcl.hInstance = hThisInst; 
    wcl.lpszClassName = szWinName; 
    wcl.lpfnWndProc = WindowFunc; 
    wcl.style = CS_HREDRAW|CS_VREDRAW; 
    wcl.hIcon = LoadIcon(NULL,IDI_APPLICATION); 
    wcl.hIconSm = NULL; 
    wcl.hCursor = LoadCursor(NULL,IDC_ARROW); 
    wcl.lpszMenuName = NULL; 
    wcl.cbClsExtra = 0; 
    wcl.cbWndExtra = 44; 
    wcl.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); 
    if(!RegisterClassEx(&wcl)) return 0; 
    hwnd = CreateWindowEx(
     WS_EX_WINDOWEDGE, 
     szWinName, 
     "Process B", 
     WS_OVERLAPPEDWINDOW, 
     CW_USEDEFAULT, 
     CW_USEDEFAULT, 
     CW_USEDEFAULT, 
     CW_USEDEFAULT, 
     HWND_DESKTOP, 
     NULL, 
     hThisInst, 
     NULL 
     ); 
    SetWindowLong(hwnd, 24, 135678); 
    ShowWindow(hwnd,nWinMode); 
    UpdateWindow(hwnd); 

    while(GetMessage(&msg,NULL,0,0)) 
    { 
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 
    return msg.wParam; 
} 

BOOL waitWithMessageLoop(HANDLE hMutex, BOOL &bExit) 
{ 
    BOOL bContinue = TRUE; 
    bExit = FALSE; 
    while(bContinue) 
    { 
     DWORD dwReturn = ::MsgWaitForMultipleObjects(1, &hMutex, FALSE, INFINITE, QS_ALLINPUT); 
     if(dwReturn == WAIT_OBJECT_0) 
     { 
      // our mutex got released 
      bContinue = FALSE; 
     } 
     else if(dwReturn == WAIT_OBJECT_0 + 1) 
     { 
      MSG msg; 
      while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
      { 
       if (msg.message == WM_QUIT) 
       { 
        bExit = TRUE; 
        bContinue = FALSE; 
        break; 
       } 
       ::TranslateMessage(&msg); 
       ::DispatchMessage(&msg); 
      } 
     } 
     else 
     { 
      // MsgWaitForMultipleObjects() returned error 
      return FALSE; 
     } 
    } 
    return TRUE; 
} 


LRESULT CALLBACK WindowFunc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) 
{ 
    char strr[1000];  
    char s[] = "Click on the \"Block\" button below to make the main (GUI) thread block by waiting on a mutex forever since the mutex will never be released."; 
    HWND hwndOther = hwnd; 

    HDC hdc; 
    PAINTSTRUCT ps; 
    static int cxClient = 0, cyClient = 0; 

    switch(message){ 

    case WM_CREATE: 


     sprintf(strr, "Window created - handle is %x.\n", hwnd); 
     OutputDebugString(strr); 

     g_hwndButton = CreateWindow(      
         "Button", 
         "Block", 
         WS_CHILD|WS_VISIBLE, 
         10, 
         120, 
         50, 
         50, 
         hwnd, 
         (HMENU)ID_BUTTON, 
         g_hInst, 
         NULL 
         ); 

     return 0; 

    case WM_SIZE: 

     cxClient = LOWORD(lParam); 
     cyClient = HIWORD(lParam); 

     return 0; 

    case WM_PAINT: 

     hdc = BeginPaint(hwnd, &ps); 

     TextOut(hdc, 10, 100, s, strlen(s)); 

     EndPaint(hwnd, &ps); 

     return 0; 

    case WM_COMMAND: 
     { 
      if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_BUTTON) 
      {     
       MessageBox(hwnd, "Main (GUI) Thread going in blocking state by waiting for mutex forever now", "", 0); 
       // disable the "Block" button 
       EnableWindow(g_hwndButton, FALSE); 
       //WaitForSingleObject(g_hMutex, INFINITE);// do NOT use this as this cause the GetWindowLong() call made in Process A to hang 
       BOOL bExit = FALSE; 
       waitWithMessageLoop(g_hMutex, bExit); 
       if (bExit) 
       { 
        PostQuitMessage(0); 
       } 
      } 
     } 

     break; 

    case WM_DESTROY: 

     PostQuitMessage(0); 
     return 0;   
    } 
    return DefWindowProc(hwnd,message,wParam,lParam); 
} 

Dzięki --ANURAG.