查了很多资料涉及到Notification Broker的都只有触发一种状态的代码,但是为了一个功能改进,我需要一种能获取多种电话状态(呼出、呼入、通话中……)的方法,同时由于程序是需要主动触发的,所以用RegistryNotifyApp做主动Broker,查了MSDN却发现没有对RegistryNotifyApp如何在多种事件中启动做出过多说明,就继续翻其他MSDN,终于看到在NOTIFICATIONCONDITION的解释里有这含义模糊的两行文字:
dwMask
Applies only to DWORD values. This mask is applied to the changed registry value before comparison. By specifying a bit mask, the Notifications Broker notifies the clients only when specific bits in the registry value changes.
This mask is not applied to TargetValue. If dwMask is 0, TargetValue is treated as a string (type REG_SZ), otherwise it is treated as type REG_DWORD.
TargetValue
If the changed value is type REG_SZ, then comparison is done between psz and the changed value. If the changed value is type REG_DWORD, then dwMask is applied to the changed value and then the result is compared to dw. If the changed value is neither REG_SZ nor REG_DWORD, then notification is sent without any comparison. You must set dwMask to -1 to test against the whole doubleword.
可以看出,dwMask=-1好像会有什么好玩的事情发生,立即写代码测试:
#define SN_PHONECALLTALKING_ROOT HKEY_LOCAL_MACHINE #define SN_PHONECALLTALKING_PATH TEXT("System//State//Phone") #define SN_PHONECALLTALKING_VALUE TEXT("Status") #define SN_PHONECALLTALKING_BITMASK 536870912 #define SN_PHONECALLCALLING_BITMASK 131072 #define SN_PHONEINCOMINGCALL_BITMASK 65536 #define MY_NOTIFICATION_NAME TEXT("testnotification") DWORD GetCurrentNotificationStatus() { DWORD lpPhoneTalking = 0; HRESULT hr; hr = RegistryGetDWORD(SN_PHONECALLTALKING_ROOT, SN_PHONECALLTALKING_PATH, SN_PHONECALLTALKING_VALUE, &lpPhoneTalking); if(SUCCEEDED(hr)) { if(lpPhoneTalking & SN_PHONECALLTALKING_BITMASK){ return SN_PHONECALLTALKING_BITMASK; }else if(lpPhoneTalking & SN_PHONECALLCALLING_BITMASK){ return SN_PHONECALLCALLING_BITMASK; }else if(lpPhoneTalking & SN_PHONEINCOMINGCALL_BITMASK){ return SN_PHONEINCOMINGCALL_BITMASK; }else{ return lpPhoneTalking; } }else{ return 0; } } HRESULT RegisterNotification() { HRESULT hr = NULL; NOTIFICATIONCONDITION nc; nc.dwMask = -1; nc.ctComparisonType = REG_CT_ANYCHANGE; nc.TargetValue.dw = 0x01; hr = RegistryNotifyApp(SN_PHONECALLTALKING_ROOT, SN_PHONECALLTALKING_PATH, SN_PHONECALLTALKING_VALUE, MY_NOTIFICATION_NAME, szExeFile, NULL, NULL, 0x00, RNAF_NONAMEONCMDLINE, &nc); return hr; } int _tmain(int argc, _TCHAR* argv[]) { GetModuleFileName(NULL,szExeFile,256); HRESULT hr = RegisterNotification(); LPCTSTR currStatus; DWORD dStatus; dStatus=GetCurrentNotificationStatus(); switch(dStatus){ case SN_PHONECALLCALLING_BITMASK: currStatus=L"Calling"; break; case SN_PHONECALLTALKING_BITMASK: currStatus=L"Talking"; break; case SN_PHONEINCOMINGCALL_BITMASK: currStatus=L"Incoming"; break; default: currStatus=(LPCTSTR)dStatus; } MessageBox(GetActiveWindow(),currStatus,TEXT("test"),MB_SETFOREGROUND | MB_ICONINFORMATION | MB_OK); return 0; }
执行程序,查找注册表果然注册了一个新的Broker“testnotification”,然后测试呼叫电话和打出电话和接听,成功获取了三种状态。
因此,如果你想在自己的Notification Broker程序里实现监控某种事件的整个状态,用dwMask = -1配合ctComparisonType = REG_CT_ANYCHANGE使用即可。
首先感谢看雪论坛嵌入开发版块的加百力和宇宙青年Yonsm对我提问帖子的解答,没有他俩我现在大概还没思绪呢。
以及感谢今天下午MobileSide开发群里的山夕兄的帮助。 解决的关键如下图所示,这是微软搞的一个小陷阱,偷懒一点不抓完控件都不知道你该找那个窗口的Handle…… 分别对应关系: 1.拨号 2.通话中(本文主角) 3.通话结束 这3个窗口里的类名控件名全都一样,唯一不同的就是那些按钮的文本名,还有通话状态窗口和通话结束窗口是动态切换的,也就是平时状态下你只能看到通话结束窗口,而通话的时候通话结束窗口是被干掉了一堆MS_PHONE_BUTTON按钮控件的。 “打开扬声器”按钮的标识是IDC_SPEAKER(分析CProg就知道),用GetDlgCtrlID可以获得其ID为23016 #define IDC_SPEAKER 23016 然后我们要获得发往这个窗口和指定按钮的消息,所以要对其Subclass子类化,WINCE没有SetWindowsHook等相关函数,只能用SetWindowLong来实现。 不说废话了,还是贴代码省事,今天忙一天又搞Dll Injection又搞回来又搞回去最后发现还是搞回来就行,累死我了…… 代码演示了对“打开扬声器”按钮和“打开扬声器”菜单进行控制,代码里的一些ID来自实际分析,非官方标准声明。
代码:
#define IDM_SPEAKER 21426 #define IDC_SPEAKER 23016 unsigned int timerID; static WNDPROC s_OldWndProc = NULL; static WNDPROC s_OldSpkBtnProc = NULL; HANDLE s_hExit = NULL; HWND lpSpeakerPhoneBtn=NULL; LRESULT CALLBACK NewSpkBtnProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if(timerID!=0){ KillTimer(NULL,timerID); timerID=0; } switch (uMsg) { case WM_LBUTTONDOWN: { SetSpeakerPhone(); break; } } return CallWindowProc(s_OldSpkBtnProc, hwnd, uMsg, wParam, lParam); } LRESULT CALLBACK NewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if(timerID!=0){ KillTimer(NULL,timerID); timerID=0; } switch (uMsg) { case WM_COMMAND: { //SetWindowText(lpCallStatWin,TEXT("WM_COMMAND")); if(LOWORD(wParam)==IDM_SPEAKER) SetSpeakerPhone(); break; } case WM_SIZE: { if(wParam==SIZE_MINIMIZED) _debug(L"SIZE_MINIMIZED"); _debug(L"WM_SIZE:wParam:%08x,lParam:%08x",wParam,lParam); break; } case WM_SYSCOMMAND: { _debug(L"SYSCOMMAND"); break; } case WM_ACTIVATE: { _debug(L"WM_ACTIVATE:wParam:%08x,lParam:%08x",wParam,lParam); break; } case WM_KILLFOCUS: { _debug(L"WM_KILLFOCUS:wParam:%08x,lParam:%08x",wParam,lParam); break; } case WM_DESTROY: { if (s_hExit) { //_debug(L"Posting exit msg..."); SetEvent(s_hExit); } break; } } return CallWindowProc(s_OldWndProc, hwnd, uMsg, wParam, lParam); } int SetHook(HWND lpHookHwnd){ s_hExit = CreateEvent(NULL, FALSE, FALSE, NULL); if (lpHookHwnd!=NULL) { lpCallStatWin=lpHookHwnd; s_OldWndProc = (WNDPROC)GetWindowLong(lpHookHwnd, GWL_WNDPROC); SetWindowLong(lpHookHwnd, GWL_WNDPROC, (DWORD)NewWndProc); s_OldSpkBtnProc = (WNDPROC)GetWindowLong(lpSpeakerPhoneBtn, GWL_WNDPROC); SetWindowLong(lpSpeakerPhoneBtn, GWL_WNDPROC, (DWORD)NewSpkBtnProc); WaitForSingleObject(s_hExit, INFINITE); SetWindowLong(lpHookHwnd, GWL_WNDPROC, (DWORD)s_OldWndProc); SetWindowLong(lpSpeakerPhoneBtn, GWL_WNDPROC, (DWORD)s_OldSpkBtnProc); } return 0; } HWND FindPhoneHandle(){ HWND lpNextWindow = NULL; HWND lpForeground = NULL; TCHAR lpClassName[64]; int lpControlID; //LPWSTR lpControlText; //LPSTR test; lpForeground =GetForegroundWindow(); if(lpForeground==NULL) return 0; lpNextWindow = GetWindow(lpForeground, GW_CHILD); lpNextWindow = GetWindow(lpNextWindow, GW_HWNDFIRST); while(lpNextWindow!=0){ GetClassName( lpNextWindow,lpClassName,64); //_debug(L"Main Handle:%d,ClassName:%s",lpForeground,lpClassName); if(!_tcscmp(lpClassName,TEXT("MS_PHONE_BUTTON"))){ //确认是电话界面里的按钮,开始判断IDC_SPEAKER lpControlID=GetDlgCtrlID(lpNextWindow); if(lpControlID==IDC_SPEAKER){ //_debug(L" Speaker found"); lpSpeakerPhoneBtn=lpNextWindow; HWND lpRealHandle=GetParent(lpNextWindow); //_debug(L" ParentHandle:%d,lpForeground:%d",lpRealHandle,lpForeground); if(lpRealHandle!=NULL){ return lpRealHandle; } return lpForeground; } } lpNextWindow = GetWindow(lpNextWindow, GW_HWNDNEXT); } return 0; } void CALLBACK TimeProc(HWND hwnd, UINT message, UINT idTimer, DWORD dwTime) { HWND lpCallStatusWindow; lpCallStatusWindow=FindPhoneHandle(); if(lpCallStatusWindow!=0){ SetHook(lpCallStatusWindow); } } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { //NOTIFICATIONCONDITION condition; timerID = SetTimer(NULL,1,1000,TimeProc); MSG msg; while(GetMessage(&msg,0,0,0)) { //收到wm_timer消息,处理它 //_debug(L"msg.message:%d",msg.message); if(msg.message==WM_TIMER){ DispatchMessage(&msg); } } return 0; } |