假设我有2个应用程序A和B。每个应用程序在主线程中创建一个窗口,而没有其他线程。
当按下应用程序A窗口的“关闭”按钮时,将发生以下情况:
WM_CLOSE消息并按以下方式进行处理:DestroyWindow(hWnd_A);
return 0;
WM_DESTROY应用程序A上的行为类似于:SendMessage(hWnd_B, WM_REGISTERED_MSG, 0, 0); //key line!!
PostQuitMessage(0);
return 0;
WM_REGISTERED_MSG应用程序B上运行:SendMessage(hWnd_A, WM_ANOTHER_REGISTERED_MSG, 0, 0);
return 0;
WM_ANOTHER_REGISTERED_MSG应用程序A上运行:OutputDebugString("Cannot print this");
return 0;
SendMessage的调用向应用程序A的窗口过程发送了一个非排队消息,该消息在应用程序B的主线程上下文中对其进行处理 。实际上,在第4点中没有显示带有OutputDebugString的调试输出。SendMessage中的SendMessageTimeout标志替换为SMTO_BLOCK的key line会使整个事情实际上被阻塞。 (请参阅SendMessage的documentation)SendMessage进行的窗口过程的简单直接调用吗? SendMessage如何知道何时发送已排队或未排队的消息? WM_ANOTHER_REGISTERED_MSG。我期望的是,当发送该消息时,A的线程应该正在等待其对SendMessage的调用返回。最佳答案
所描述的行为确实很好。
How does
SendMessageknow when to send queued or non-queued messages?
Some functions that send nonqueued messages are ... SendMessage ...
SendMessage 总是总是发送非排队消息。SendMessage 文档中:However, the sending thread will process incoming nonqueued messages while waiting for its message to be processed.
SendMessage调用中调用窗口过程。并处理从另一个线程通过SendMessage发送的传入消息。这是如何实现的?SendMessage消息调用到另一个线程窗口时,它将进入内核模式。内核模式始终记住用户模式堆栈指针。然后我们切换到内核堆栈。当我们从内核返回到用户模式时-内核通常返回到用户模式从那里调用它并保存到堆栈的位置。但是存在和异常(exception)。其中之一:NTSYSCALLAPI
NTSTATUS
NTAPI
KeUserModeCallback
(
IN ULONG RoutineIndex,
IN PVOID Argument,
IN ULONG ArgumentLength,
OUT PVOID* Result,
OUT PULONG ResultLenght
);
void
KiUserCallbackDispatcher
(
IN ULONG RoutineIndex,
IN PVOID Argument,
IN ULONG ArgumentLength
);
KiUserCallbackDispatcher调用RtlGetCurrentPeb()->KernelCallbackTable[RoutineIndex](Argument, ArgumentLength)-通常这是user32.dll中的某些功能。该函数已经调用了相应的窗口程序。从窗口过程中,我们可以回叫内核-因为KeUserModeCallback分配了其他内核框架-我们将在此框架内进入内核,而不会损坏先前的内核。当窗口过程返回时-再次调用特殊的api__declspec(noreturn)
NTSTATUS
NTAPI
ZwCallbackReturn
(
IN PVOID Result OPTIONAL,
IN ULONG ResultLength,
IN NTSTATUS Status
);
KeUserModeCallback内的先前内核堆栈。所以我们终于从调用KeUserModeCallback的地方返回。然后我们回到用户模式,恰好从我们调用内核的那一点开始,就在同一堆栈上。GetMessage的调用中,窗口过程究竟如何称为?正是通过这个。调用流为:GetMessage...
--- kernel mode ---
KeUserModeCallback...
push additional kernel stack frame
--- user mode --- (stack below point from where GetMessage enter kernel)
KiUserCallbackDispatcher
WindowProc
ZwCallbackReturn
-- kernel mode --
pop kernel stack frame
...KeUserModeCallback
--- user mode ---
...GetMessage
SendMessage完全相同。SendMessage将message_1发送到thread_B时,我们进入内核,信号为gui event_B,thread_B等待了它。并开始在gui event_A上等待当前线程。如果thread_B执行在thread_B中调用的消息检索代码(调用GetMessage或PeekMessage)KeUserModeCallback。结果执行了它的窗口过程。在这里,它调用SendMessage将一些message_2发送回thread_A。结果,我们将event_A设置为在其中thread_A等待并在event_B上开始等待。 thread_A将被唤醒并调用KeUserModeCallback。它将用此消息调用Window过程。当它返回时(假设这次我们不再调用SendMessage),我们再次发信号给event_B并开始等待event_A。SendMessage返回,然后从窗口过程返回-完成句柄原始message_1。将设置为event_A。 thread_A唤醒并从SendMessage返回。调用流程将是下一个:thread_A thread_B
----------------------------------------------------
GetMessage...
wait(event_B)
SendMessage(WM_B)...
set(event_B)
wait(event_A)
begin process WM_B...
KeUserModeCallback...
KiUserCallbackDispatcher
WindowProc(WM_B)...
SendMessage(WM_A)...
set(event_A)
wait(event_B)
begin process WM_A...
KeUserModeCallback...
KiUserCallbackDispatcher
WindowProc(WM_A)...
...WindowProc(WM_A)
ZwCallbackReturn
...KeUserModeCallback
set(event_B)
...end process WM_A
wait(event_A)
...SendMessage(WM_A)
...WindowProc(WM_B)
ZwCallbackReturn
...KeUserModeCallback
set(event_A)
...end process WM_B
wait(event_B)
...SendMessage(WM_B)
...GetMessage
WM_DESTROY消息时-窗口仍然有效,并调用处理传入的消息。我们可以实现下一个演示:首先,我们不需要两个过程。具有2个线程的绝对足够的单个进程。不需要特殊的注册消息。为什么不使用say WM_APP作为测试消息?WM_CREATE中的WM_CREATE上,仅返回-1(用于失败创建窗口)WM_DESTROY中的SendMessage(hwnd_A, WM_APP, 0, hwnd_B)(以lParam身份传递自己的所有权)WM_APP并调用SendMessage(hwnd_B, WM_APP, 0, 0) WM_APP(因此递归调用了WindowProc,在堆栈下面是WM_DESTROY SendMessage返回的SendMessage内的调用WM_DESTROY返回的ULONG WINAPI ThreadProc(PVOID hWnd);
struct WNDCTX
{
HANDLE hThread;
HWND hWndSendTo;
};
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WNDCTX* ctx = reinterpret_cast<WNDCTX*>(GetWindowLongPtrW(hWnd, GWLP_USERDATA));
switch (uMsg)
{
case WM_NULL:
DestroyWindow(hWnd);
break;
case WM_APP:
DbgPrint("%x:%p>WM_APP:(%p, %p)\n", GetCurrentThreadId(), _AddressOfReturnAddress(), wParam, lParam);
if (lParam)
{
DbgPrint("%x:%p>Send WM_APP(0)\n", GetCurrentThreadId(), _AddressOfReturnAddress());
LRESULT r = SendMessage((HWND)lParam, WM_APP, 0, 0);
DbgPrint("%x:%p>SendMessage=%p\n", GetCurrentThreadId(), _AddressOfReturnAddress(), r);
PostMessage(hWnd, WM_NULL, 0, 0);
}
else
{
DbgPrint("%x:%p>Cannot print this\n", GetCurrentThreadId(), _AddressOfReturnAddress());
}
return GetCurrentThreadId();
case WM_DESTROY:
if (HANDLE hThread = ctx->hThread)
{
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
if (HWND hWndSendTo = ctx->hWndSendTo)
{
DbgPrint("%x:%p>Send WM_APP(%p)\n", GetCurrentThreadId(), _AddressOfReturnAddress(), hWnd);
LRESULT r = SendMessage(hWndSendTo, WM_APP, 0, (LPARAM)hWnd);
DbgPrint("%x:%p>SendMessage=%p\n", GetCurrentThreadId(), _AddressOfReturnAddress(), r);
}
break;
case WM_NCCREATE:
SetLastError(0);
SetWindowLongPtr(hWnd, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams));
if (GetLastError())
{
return 0;
}
break;
case WM_CREATE:
if (ctx->hWndSendTo)
{
return -1;
}
if (ctx->hThread = CreateThread(0, 0, ThreadProc, hWnd, 0, 0))
{
break;
}
return -1;
case WM_NCDESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
static const WNDCLASS wndcls = {
0, WindowProc, 0, 0, (HINSTANCE)&__ImageBase, 0, 0, 0, 0, L"lpszClassName"
};
ULONG WINAPI ThreadProc(PVOID hWndSendTo)
{
WNDCTX ctx = { 0, (HWND)hWndSendTo };
CreateWindowExW(0, wndcls.lpszClassName, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, &ctx);
return 0;
}
void DoDemo()
{
DbgPrint("%x>test begin\n", GetCurrentThreadId());
if (RegisterClassW(&wndcls))
{
WNDCTX ctx = { };
if (CreateWindowExW(0, wndcls.lpszClassName, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, &ctx))
{
MSG msg;
while (0 < GetMessage(&msg, 0, 0, 0))
{
DispatchMessage(&msg);
}
}
UnregisterClassW(wndcls.lpszClassName, (HINSTANCE)&__ImageBase);
}
DbgPrint("%x>test end\n", GetCurrentThreadId());
}
d94>test begin
6d8:00000008884FEFD8>Send WM_APP(0000000000191BF0)
d94:00000008880FF4F8>WM_APP:(0000000000000000, 0000000000191BF0)
d94:00000008880FF4F8>Send WM_APP(0)
6d8:00000008884FEB88>WM_APP:(0000000000000000, 0000000000000000)
6d8:00000008884FEB88>Cannot print this
d94:00000008880FF4F8>SendMessage=00000000000006D8
6d8:00000008884FEFD8>SendMessage=0000000000000D94
d94>test end
WM_APP时,thread_B的最有趣的外观堆栈跟踪
关于windows - 两个应用程序之间相互的SendMessage-ing如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48528666/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/