gpt4 book ai didi

windows - GetWindowLong() - Creators Update 引入的行为变化破坏了我的 win32 应用程序

转载 作者:可可西里 更新时间:2023-11-01 13:48:36 28 4
gpt4 key购买 nike

最近对 Windows 10 进行的创意者更新破坏了我使用 Win32 API GetWindowLong() 的应用程序代码。

在 Windows 10 Creator Update 之前,即使进程 B(主线程)被阻止,一个进程(比如进程 A)也能够在另一个进程的窗口句柄(比如进程 B)上调用 GetWindowWord()/GetWindowLong() API在某些系统调用中(例如等待释放互斥量)。因此,尽管进程 B 被阻塞,进程 A 仍能够使用这些 API 成功查询进程 B 拥有的窗口的保留内存。

但是,在 Windows 10 上应用 Creator Updates 后,当进程 B(主线程)被阻塞时,进程 A 在属于进程 B 的窗口上调用这些 API 时会被阻塞。

我通过创建 2 个代表进程 A 和进程 B 的独立 Win32 应用程序来模拟这种情况。在应用了创意者更新的 Windows 10 系统上,进程 A 在属于的窗口上调用 GetWindowLong()/GetWindowWord() 时挂起当进程 B(主线程)正在等待互斥锁时,进程 B。换句话说,对 GetWindowLong()/GetWindowWord() 的调用从未返回,从而导致进程 A 挂起。

但是,当我在没有 Creators Update 或更早版本(例如 Windows 7)的 Windows 10 系统上使用我的独立应用程序测试相同的场景时,对进程 A 中的 GetWindowLong()/GetWindowWord() API 的调用返回即使进程 B 正在等待释放互斥锁,也能成功。

为了演示上述问题,这里是进程 A 和进程 B 的代码。要查看问题,请运行进程 A 和进程 B。然后,找出进程 B 窗口的窗口句柄(例如使用 Spy++),然后将其粘贴到进程 A 窗口的编辑字段中。然后单击“确定”。显示在进程 B 的窗口的额外内存(使用 SetWindowLong())中设置的 LONG 值的消息框。到目前为止,一切都很好。现在,转到进程 B 的窗口并通过单击“阻止”按钮使其挂起。这将使进程“B”(主 GUI 线程)等待一个永远不会被释放的互斥量,因此进程 B 将挂起。

现在,返回进程 A 的窗口并再次单击“确定”(假设编辑字段仍然具有您之前粘贴的进程 B 的相同窗口句柄)。

现在,这是行为上的差异:

在没有 Creators Update 的 Windows 10 和早期的 Windows 版本(例如 Windows 7)上,和以前一样(即当进程 B 没有挂起时),一个消息框显示在进程 B 的窗口的额外内存中设置的 LONG 值(使用 SetWindowLong ()) 显示。

在带有创意者更新的 Windows 10 上,进程 A 挂起,因为使用进程 B 的窗口句柄对 SetWindowLong() 的调用永远不会返回,从而导致进程 A 挂起。

请建议我如何解决 Windows 10 Creators Update 上的这种行为变化,以便我的应用程序不会挂起。任何想法/帮助将不胜感激。

这是进程 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);
}

这是进程 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);
}

最佳答案

我想我找到了在进程 B 中使用 MsgWaitForMultipleObjects() 的解决方案,这样它除了等待互斥锁之外,还会继续在队列中寻找消息。这样,进程A对进程B的窗口句柄的GetWindowLong()调用将正常返回,不会阻塞,问题就解决了。

这是进程 B 的更新代码,更改是在“阻止”按钮单击处理 WM_COMMAND 情况下(进程 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);

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);
}

谢谢,--阿努拉格。

关于windows - GetWindowLong() - Creators Update 引入的行为变化破坏了我的 win32 应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43562901/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com