Peter Dons Tychsen wrote:
Hello Wine.
On original Windows, the messages WM_ACTIVATEAPP are sent using PostMessage() and not SendMessage() like in the current Wine implementation.
I have thoroughly verified that this is the case on Windows-XP (that it uses PostMessage()).
This error can lead to fatal focusing problems, as some programs call more focus related functions when they receive this message. This leads to recursive calls into set_active_calls. It does not crash, but leads to unexpected results such as lost focus for mouse and keyboard.
This fixes the focus for the game "Wild Metal" if you switch focus from the game to the desktop, and then back again.
http://www.rockstargames.com/classics/
Cheers,
/Pedro
This patch makes 45 msg.c tests in user32 fail for me. Could you double check your findings? Also when you say "thoroughly verified" have you used Wine tests or your own program?
Vitaliy
Hello again,
1) Yes i did testing on Windows-XP. I did it by putting together various examples and by checking with InSendMessage() for all cases. This clearly showed that WM_ACTIVATEAPP was always posted and never sent.
2) Yes, i tested with the msg.c test-cases. The patch does "break" some tests in it. Please read note below:
However, the test system for msg.c in user32 i severely broken. If i upgrade the test system, it will require lots of more changes to prevent even more test-cases from breaking.
The problem is in the ok_sequence_() function of the test system. It is missing a call to:
while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessage(&msg);
This is needed to make sure that all messages are pulled from the queue before it starts comparing with the expected messages (especially for posted message - sent messages are always received).
I think is is also the reason why these tast-cases are a bit flaky. On machine the number of passed cases vary allot (because it ends up being timing dependent).
If i introduce this in this patch, i will either be flamed for destroying working test cases or for changing too much. The change (which i think needs to be done) breaks around 100 test cases (allot of small changes). Mostly because they now get all the messages which were previously stuck on the queue.
I think the missing line should be added in a patch by itself along with a fix for all the broken cases. Trying to blend it in with this patch will end up a mess.
/Pedro
On Thu, 2007-08-02 at 20:34 -0600, Vitaliy Margolen wrote:
Peter Dons Tychsen wrote:
Hello Wine.
On original Windows, the messages WM_ACTIVATEAPP are sent using PostMessage() and not SendMessage() like in the current Wine implementation.
I have thoroughly verified that this is the case on Windows-XP (that it uses PostMessage()).
This error can lead to fatal focusing problems, as some programs call more focus related functions when they receive this message. This leads to recursive calls into set_active_calls. It does not crash, but leads to unexpected results such as lost focus for mouse and keyboard.
This fixes the focus for the game "Wild Metal" if you switch focus from the game to the desktop, and then back again.
http://www.rockstargames.com/classics/
Cheers,
/Pedro
This patch makes 45 msg.c tests in user32 fail for me. Could you double check your findings? Also when you say "thoroughly verified" have you used Wine tests or your own program?
Vitaliy
"Peter Dons Tychsen" donpedro@tdcadsl.dk wrote:
- Yes i did testing on Windows-XP. I did it by putting together various
examples and by checking with InSendMessage() for all cases. This clearly showed that WM_ACTIVATEAPP was always posted and never sent.
Looks like you misunderstood the InSendMessage behaviour and that led to some confusion.
Wine test harness for message sequences works just fine, there is no need to blame it.
On Sat, 2007-08-04 at 12:04 +0900, Dmitry Timoshkov wrote:
"Peter Dons Tychsen" donpedro@tdcadsl.dk wrote:
- Yes i did testing on Windows-XP. I did it by putting together various
examples and by checking with InSendMessage() for all cases. This clearly showed that WM_ACTIVATEAPP was always posted and never sent.
Hello Dmitry!
Thanks for your comments,
Looks like you misunderstood the InSendMessage behaviour and that led to some confusion.
I sure did! I must have been tired or something :-). When i went back and tested it again i did verify that it worked differently than i expected. Even the InSendMessageEx() did not work as i expected. The MSDN documentation also seems vague on this. Anyway, you are right.
Wine test harness for message sequences works just fine, there is no need to blame it.
I'm not blaming it for anything. I am just pointing out that it does not work if you want to test for posted messages. It has a bit bit-mask which describes if the tested message is sent or posted. However the framework does not make sure that posted messages are actually received before it starts comparing. Look at the way the ok_sequence_() works. It compares with the messages which are picked up in the WinProc() function.
1) It does not make sure that all messages are received before comparing. I believe the best (maybe the only?) way of doing this would be to do:
while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessage(&msg);
I don't see how you otherwise can make sure that all messages are received before comparing. Currently the existing test cases almost only checks for sent messages and not posted ones. This i probably why the current cases wok as well as they do.
2) It does not actually check if the message was posted or sent. This would make sense. I was too quick in assuming that this could be done with InMessageSend(). I was wrong. Maybe another mechanism can be used?
I will go back and figure out a better way of checking these things if i can. I still think the test system needs a fix-up.
Cheers,
/Pedro
On Sat, 2007-08-04 at 12:04 +0900, Dmitry Timoshkov wrote:
"Peter Dons Tychsen" donpedro@tdcadsl.dk wrote:
- Yes i did testing on Windows-XP. I did it by putting together various
examples and by checking with InSendMessage() for all cases. This clearly showed that WM_ACTIVATEAPP was always posted and never sent.
Looks like you misunderstood the InSendMessage behaviour and that led to some confusion.
Wine test harness for message sequences works just fine, there is no need to blame it.
I have found a new way of proving that ACTIVATEAPP must be sent using the PostMessage call and not the SendMessage call. I have also found a way to extend the unit test system to cover this.
After realizing my bummer with InSendMessage() i set out to create a new function that would do what i want: Check if a message was sent with PostMessage() or SendMessage() on windows. I could not find any function that would do this so i made my own hack (below):
This piece of code, if inserted into the test system, could (on original Windows) show us which messages we are incorrectly posting or sending. I can probably also be used for other test purposes. I works by checking the call stack for the calls SendMessageA() and SendMessageW(). Works like charm on Windows-XP. Will need some work to also work inside Wine, but that is not important for now.
Usage:
1) Call call_stack_check_init(). 2) Call called_from_send_message() inside the Proc handler.
Cheers,
/Pedro
------ CODE BEGIN --------
void call_stack_check_init(void) { HANDLE process = GetCurrentProcess(); SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); SymInitialize(process, NULL, TRUE); }
#define SYM_MAX_NAME_SIZE 1000 BOOL call_stack_check_search(char *func) { STACKFRAME64 call_stack; CONTEXT context; HANDLE thread = GetCurrentThread(); SYMBOL_INFO *sym = (SYMBOL_INFO *) malloc(sizeof(SYMBOL_INFO) + SYM_MAX_NAME_SIZE); CHAR name[SYM_MAX_NAME_SIZE]; HANDLE process = GetCurrentProcess(); /* Setup symbol buffer */ ZeroMemory(sym, sizeof(SYMBOL_INFO) + SYM_MAX_NAME_SIZE); sym->SizeOfStruct = sizeof(SYMBOL_INFO); sym->MaxNameLen = SYM_MAX_NAME_SIZE;
/* Get thread context */ ZeroMemory(&context, sizeof(context)); context.ContextFlags = CONTEXT_FULL; __asm call x; __asm x: pop eax; __asm mov context.Eip, eax; __asm mov context.Ebp, ebp; __asm mov context.Esp, esp;
/* Setup stack walk offset */ ZeroMemory(&call_stack, sizeof(call_stack)); call_stack.AddrPC.Offset = context.Eip; call_stack.AddrStack.Offset = context.Esp; call_stack.AddrFrame.Offset = context.Ebp; call_stack.AddrPC.Mode = AddrModeFlat; call_stack.AddrStack.Mode = AddrModeFlat; call_stack.AddrFrame.Mode = AddrModeFlat;
while(1) { DWORD64 disp = 0;
/* Walk that rusty old stack... */ if(!StackWalk64( IMAGE_FILE_MACHINE_I386, process, thread, &call_stack, NULL, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) return FALSE;
/* Get the next symbol on stack */ if(SymFromAddr(GetCurrentProcess(), call_stack.AddrPC.Offset, &disp, sym)) { /* Make it readable */ UnDecorateSymbolName(sym->Name, name, SYM_MAX_NAME_SIZE, UNDNAME_COMPLETE | UNDNAME_NO_THISTYPE | UNDNAME_NO_SPECIAL_SYMS | UNDNAME_NO_MEMBER_TYPE | UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS);
/* Check for match */ if(!strcmp(func, name)) return TRUE; } }
return FALSE; } BOOL called_from_send_message(void) { if (call_stack_check_search("SendMessageW")) return TRUE; if (call_stack_check_search("SendMessageA")) return TRUE;
return FALSE; }
----- CODE END -----
On Sun, 2007-08-05 at 21:12 +0200, Peter Dons Tychsen wrote:
On Sat, 2007-08-04 at 12:04 +0900, Dmitry Timoshkov wrote:
"Peter Dons Tychsen" donpedro@tdcadsl.dk wrote:
- Yes i did testing on Windows-XP. I did it by putting together various
examples and by checking with InSendMessage() for all cases. This clearly showed that WM_ACTIVATEAPP was always posted and never sent.
Looks like you misunderstood the InSendMessage behaviour and that led to some confusion.
Wine test harness for message sequences works just fine, there is no need to blame it.
I have found a new way of proving that ACTIVATEAPP must be sent using the PostMessage call and not the SendMessage call. I have also found a way to extend the unit test system to cover this.
After realizing my bummer with InSendMessage() i set out to create a new function that would do what i want: Check if a message was sent with PostMessage() or SendMessage() on windows. I could not find any function that would do this so i made my own hack (below):
This piece of code, if inserted into the test system, could (on original Windows) show us which messages we are incorrectly posting or sending. I can probably also be used for other test purposes. I works by checking the call stack for the calls SendMessageA() and SendMessageW(). Works like charm on Windows-XP. Will need some work to also work inside Wine, but that is not important for now.
Usage:
- Call call_stack_check_init().
- Call called_from_send_message() inside the Proc handler.
Cheers,
/Pedro
------ CODE BEGIN --------
void call_stack_check_init(void) { HANDLE process = GetCurrentProcess(); SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); SymInitialize(process, NULL, TRUE); }
#define SYM_MAX_NAME_SIZE 1000 BOOL call_stack_check_search(char *func) { STACKFRAME64 call_stack; CONTEXT context; HANDLE thread = GetCurrentThread(); SYMBOL_INFO *sym = (SYMBOL_INFO *) malloc(sizeof(SYMBOL_INFO) + SYM_MAX_NAME_SIZE); CHAR name[SYM_MAX_NAME_SIZE]; HANDLE process = GetCurrentProcess();
/* Setup symbol buffer */ ZeroMemory(sym, sizeof(SYMBOL_INFO) + SYM_MAX_NAME_SIZE); sym->SizeOfStruct = sizeof(SYMBOL_INFO); sym->MaxNameLen = SYM_MAX_NAME_SIZE;
/* Get thread context */ ZeroMemory(&context, sizeof(context)); context.ContextFlags = CONTEXT_FULL; __asm call x; __asm x: pop eax; __asm mov context.Eip, eax; __asm mov context.Ebp, ebp; __asm mov context.Esp, esp;
/* Setup stack walk offset */ ZeroMemory(&call_stack, sizeof(call_stack)); call_stack.AddrPC.Offset = context.Eip; call_stack.AddrStack.Offset = context.Esp; call_stack.AddrFrame.Offset = context.Ebp; call_stack.AddrPC.Mode = AddrModeFlat; call_stack.AddrStack.Mode = AddrModeFlat; call_stack.AddrFrame.Mode = AddrModeFlat;
while(1) { DWORD64 disp = 0;
/* Walk that rusty old stack... */ if(!StackWalk64( IMAGE_FILE_MACHINE_I386, process, thread, &call_stack, NULL, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) return FALSE; /* Get the next symbol on stack */ if(SymFromAddr(GetCurrentProcess(), call_stack.AddrPC.Offset, &disp,
sym)) { /* Make it readable */ UnDecorateSymbolName(sym->Name, name, SYM_MAX_NAME_SIZE, UNDNAME_COMPLETE | UNDNAME_NO_THISTYPE | UNDNAME_NO_SPECIAL_SYMS | UNDNAME_NO_MEMBER_TYPE | UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS);
/* Check for match */ if(!strcmp(func, name)) return TRUE; }
}
return FALSE; } BOOL called_from_send_message(void) { if (call_stack_check_search("SendMessageW")) return TRUE; if (call_stack_check_search("SendMessageA")) return TRUE;
return FALSE; }
----- CODE END -----
By the way, this code does prove (if run on Windows-XP), that ACTIVATEAPP should be posted and not sent.
/pedro
On Sun, 2007-08-05 at 21:13 +0200, Peter Dons Tychsen wrote:
On Sun, 2007-08-05 at 21:12 +0200, Peter Dons Tychsen wrote:
On Sat, 2007-08-04 at 12:04 +0900, Dmitry Timoshkov wrote:
"Peter Dons Tychsen" donpedro@tdcadsl.dk wrote:
- Yes i did testing on Windows-XP. I did it by putting together various
examples and by checking with InSendMessage() for all cases. This clearly showed that WM_ACTIVATEAPP was always posted and never sent.
Looks like you misunderstood the InSendMessage behaviour and that led to some confusion.
Wine test harness for message sequences works just fine, there is no need to blame it.
I have found a new way of proving that ACTIVATEAPP must be sent using the PostMessage call and not the SendMessage call. I have also found a way to extend the unit test system to cover this.
After realizing my bummer with InSendMessage() i set out to create a new function that would do what i want: Check if a message was sent with PostMessage() or SendMessage() on windows. I could not find any function that would do this so i made my own hack (below):
This piece of code, if inserted into the test system, could (on original Windows) show us which messages we are incorrectly posting or sending. I can probably also be used for other test purposes. I works by checking the call stack for the calls SendMessageA() and SendMessageW(). Works like charm on Windows-XP. Will need some work to also work inside Wine, but that is not important for now.
Usage:
- Call call_stack_check_init().
- Call called_from_send_message() inside the Proc handler.
Cheers,
/Pedro
------ CODE BEGIN --------
void call_stack_check_init(void) { HANDLE process = GetCurrentProcess(); SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); SymInitialize(process, NULL, TRUE); }
#define SYM_MAX_NAME_SIZE 1000 BOOL call_stack_check_search(char *func) { STACKFRAME64 call_stack; CONTEXT context; HANDLE thread = GetCurrentThread(); SYMBOL_INFO *sym = (SYMBOL_INFO *) malloc(sizeof(SYMBOL_INFO) + SYM_MAX_NAME_SIZE); CHAR name[SYM_MAX_NAME_SIZE]; HANDLE process = GetCurrentProcess();
/* Setup symbol buffer */ ZeroMemory(sym, sizeof(SYMBOL_INFO) + SYM_MAX_NAME_SIZE); sym->SizeOfStruct = sizeof(SYMBOL_INFO); sym->MaxNameLen = SYM_MAX_NAME_SIZE;
/* Get thread context */ ZeroMemory(&context, sizeof(context)); context.ContextFlags = CONTEXT_FULL; __asm call x; __asm x: pop eax; __asm mov context.Eip, eax; __asm mov context.Ebp, ebp; __asm mov context.Esp, esp;
/* Setup stack walk offset */ ZeroMemory(&call_stack, sizeof(call_stack)); call_stack.AddrPC.Offset = context.Eip; call_stack.AddrStack.Offset = context.Esp; call_stack.AddrFrame.Offset = context.Ebp; call_stack.AddrPC.Mode = AddrModeFlat; call_stack.AddrStack.Mode = AddrModeFlat; call_stack.AddrFrame.Mode = AddrModeFlat;
while(1) { DWORD64 disp = 0;
/* Walk that rusty old stack... */ if(!StackWalk64( IMAGE_FILE_MACHINE_I386, process, thread, &call_stack, NULL, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) return FALSE; /* Get the next symbol on stack */ if(SymFromAddr(GetCurrentProcess(), call_stack.AddrPC.Offset, &disp,
sym)) { /* Make it readable */ UnDecorateSymbolName(sym->Name, name, SYM_MAX_NAME_SIZE, UNDNAME_COMPLETE | UNDNAME_NO_THISTYPE | UNDNAME_NO_SPECIAL_SYMS | UNDNAME_NO_MEMBER_TYPE | UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ACCESS_SPECIFIERS);
/* Check for match */ if(!strcmp(func, name)) return TRUE; }
}
return FALSE; } BOOL called_from_send_message(void) { if (call_stack_check_search("SendMessageW")) return TRUE; if (call_stack_check_search("SendMessageA")) return TRUE;
return FALSE; }
----- CODE END -----
By the way, this code does prove (if run on Windows-XP), that ACTIVATEAPP should be posted and not sent.
/pedro
Oh dear... scrap that last comment. With the comments from Alexandre this no longer valid. It does not prove anything... back to the drawing board Peter... :-)
/Pedro
Peter Dons Tychsen donpedro@tdcadsl.dk writes:
This piece of code, if inserted into the test system, could (on original Windows) show us which messages we are incorrectly posting or sending. I can probably also be used for other test purposes. I works by checking the call stack for the calls SendMessageA() and SendMessageW(). Works like charm on Windows-XP. Will need some work to also work inside Wine, but that is not important for now.
This is silly. It can't possibly work right since there is no reason that messages are sent using the exported functions, it won't work on anything not compiled with frame pointer, and there's absolutely no need for anything like that at all. A posted message doesn't call the window proc directly, it goes through the message queue, so it's completely trivial to check for it between GetMessage and DispatchMessage.
On Sun, 2007-08-05 at 23:23 +0200, Alexandre Julliard wrote:
Peter Dons Tychsen donpedro@tdcadsl.dk writes:
This piece of code, if inserted into the test system, could (on original Windows) show us which messages we are incorrectly posting or sending. I can probably also be used for other test purposes. I works by checking the call stack for the calls SendMessageA() and SendMessageW(). Works like charm on Windows-XP. Will need some work to also work inside Wine, but that is not important for now.
This is silly. It can't possibly work right since there is no reason that messages are sent using the exported functions, it won't work on anything not compiled with frame pointer, and there's absolutely no need for anything like that at all.
Hello again,
For the tests i am running there is always a frame pointer. You might be right that there are other (better) ways to solve this problem, but is still think this little function could come in handy when black-box testing original DLLs. You are of course right about the point of the exported functions. It slipped my mind... I am not sure the word "silly" applies however... :-).
A posted message doesn't call the window proc directly, it goes through the message queue, so it's completely trivial to check for it between GetMessage and DispatchMessage.
In this case we would need to use PeekMessage(), but yes, you are right. I scrapped this solution to begin with as it required too much many changes in the test suite (and probably also because i liked the more complicated version - should have stuck with the simple one :-))
I always try to follow the "keep it simple" rule-sets, but it is not always i succeed in doing it... :-(.
I will go back and redo the tests with this approach, now that you have torpedoed my idea, which i probably deserved.
Thanks,
/Pedro
On Mon, 2007-08-06 at 01:26 +0200, Peter Dons Tychsen wrote:
I will go back and redo the tests with this approach, now that you have torpedoed my idea, which i probably deserved.
OK. To recover from my earlier brain-dead idea, i think i have came up with a much simpler solution, based on your recommendations.
Please review this idea.
The patch adds support for checking posted messages by emptying the queue, and marking all dispatched messages as sent instead of posted.
As soon as it is introduced however, it will breaks some test-cases which then needs fixing, as more messages are checked. Not sure about the workload in this.
Unfortunately this does seem to indicate that my original idea that WM_ACTIVATEAPP should be posted is wrong (scratch that silly patch). Sorry for the mess. :-)
/Pedro