最近手機上的短信存儲器快滿了,應該刪除一些短信以留出一些空間,但是有好多短信是各個MM發過來的,捨不得就這麼刪除了,想導出到電腦裏面保存起來。萬一哪天MM成了我女朋友了,有機會的時候可以給她看看,說明我是這麼珍惜跟她相關的點點滴滴。^_^於是用數據線把手機連接到電腦上,打開EasyGPRS軟件,讀取手機中的短信到列表窗口中。但是可惜的是EasyGPRS軟件沒有提供導出短信內容的功能,於是只好自己想辦法了。當然最簡單的方法是,把各條短信的接收時間,對方號碼,內容等信息在電腦上輸入一遍,保存到文件中。但是近200條短信,我可以沒有那個耐心。我當然得想跟簡單快捷的方法。我想到的方法就是,寫一個程序,把EasyGPRS已經從手機中讀取的短信內容,從列表窗口複製並保存到一個文件中。想到了就做!原本以爲比較簡單的東西,沒想到也花了我6,7個小時時間,當然是晚上下班後和早晨上班前的空餘時間。:)
先對照程序界面說說其基本功能:
找到列表控件窗口後,就可以向它發送消息取得其列數,行數和各行數據了,我剛開始時就是這麼做的。但是,發送消息取得列數和行數是正確的,卻不能取得各行的數據。這是爲什麼呢?原來,Windows編程中,允許跨越進程邊界向另一個進程中的窗口發送消息(跨進程發送消息當然也是跨線程發送消息,處理方式同在線程內部發送消息的處理方式有一些不同,詳細情況可以參考《Windows核心編程》第26章《窗口消息》)。但是,跨進程邊界發送的消息不能帶有指針類型的參數,因爲指針不能跨越進程邊界。在Windows系統中,各個進程是相互隔離的,有自己的獨立地址空間,一個進程中有效的指針,傳遞到另一個進程中不一定有效;即使有效,它也是指向另一個進程的地址空間中的某個位置了,負責處理消息的目標窗口(線程)會從那個位置取得輸入參數,或者將處理結果存放到那裏去,發送消息的進程無法正確地傳遞消息參數,或者利用指針取得消息處理時返回的結果。跨進程傳遞指針需要特殊的處理,例如使用COM+,或者Java RMI等分佈式處理系統。
由於需要一個指針作爲其參數,所以不能用普通的SendMessage()函數向另一個進程中的列表控件發送消息,取得控件的某個項目的內容。那麼如何解決這個問題,取得另一個進程中的列表控件的內容呢?我想到了前段時間學習的《Windows核心編程》一書中講到過的高級DLL操作技術,是否可以用某種DLL技術解決這個問題呢?趕緊找出書來,翻開第22章《插入DLL和掛接API》,再次閱讀第3節《使用Windows掛鉤來插入DLL》,我就找到解決辦法了。作者在這一節介紹了使用windows掛鉤在某進程中插入DLL的方法,並給出了實際的應用示例,即用Windows掛鉤向資源管理器進程Explorer.exe插入DLL,來實現保存和恢復桌面圖標位置的功能。我就稍微變通一下,使用Windows掛鉤向EasyGPRS進程插入DLL,來實現取得EasyGPRS進程中的短信列表窗口內容的功能。
首先簡單介紹以下設置Windows掛鉤的函數SetWindowsHookEx:
HHOOK SetWindowsHookEx( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId ); |
安裝掛鉤函數後,系統在截取到相應類型的消息時,就會利用目標線程調用掛鉤函數,這時候就可以直接用SendMessage()函數向目標列表控件發送消息,取得其某一行的內容了。但是問題是,取得的內容是在目標進程中的,還是無法傳遞到源進程中。我對這個問題的解決方法是在Dll中設置一個帶共享屬性的節,把取得的內容放到共享節中。這樣目標線程取得內容後,源進程也可以通過位於共享節的變量得到內容。注意:這裏共享節是位於Dll中的,而Dll是被同時映射到兩個進程中的。
寫了這麼多,才發現自己的講述比較亂,好像沒太多條理,這裏簡單總結一下。我要解決的問題是,把另一個程序中的一個列表窗口的內容,複製自己的程序中。因爲跨進程發送消息時,消息參數不能含有指針,所以不能簡單地用SendMessage()發送相關消息給另一個程序中的列表窗口,來取得其內容。最終對這個問題的解決涉及到3個方面:
2 #include <CommCtrl.h>
3 #include <shlwapi.h>
4
5 #define UM_GET_COL_INFO (WM_USER + 0x1000)
6 #define UM_GET_ITEM_INFO (WM_USER + 0x2000)
7
8 #pragma comment(lib,"shlwapi.lib")
9 #pragma comment(lib,"comctl32.lib")
10
11 #pragma data_seg("Shared")
12 HHOOK g_hhook=NULL;
13 extern "C" __declspec(dllexport) LVCOLUMN g_colinfo={0};
14 extern "C" __declspec(dllexport) LVITEM g_iteminfo={0};
15 extern "C" __declspec(dllexport) TCHAR g_szText[500]={0};
16 #pragma data_seg()
17
18 #pragma comment(linker,"/section:Shared,rws")
19
20 static HINSTANCE g_hDll;
21
22 BOOL WINAPI DllMain(HINSTANCE hInst,DWORD fdwReason,PVOID fImpLoad)
23 {
24 switch(fdwReason)
25 {
26 case DLL_PROCESS_ATTACH:
27 g_hDll = hInst;
28 break;
29 }
30 return TRUE;
31 }
32
33 static LRESULT WINAPI GetMsgProc(int ncode,WPARAM wParam,LPARAM lParam)
34 {
35 MSG* pMsg;
36 pMsg = (MSG*)lParam;
37 /* 取得列(ListView頭部)信息 */
38 if (UM_GET_COL_INFO == pMsg->message)
39 {
40 HWND hList = (HWND)(pMsg->wParam);
41 HANDLE hEvent;
42
43 ZeroMemory(g_szText,sizeof(g_szText));
44 g_colinfo.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
45 g_colinfo.fmt = LVCFMT_CENTER;
46 g_colinfo.pszText = g_szText;
47 g_colinfo.cchTextMax = 500;
48
49 ListView_GetColumn(hList,pMsg->lParam,&g_colinfo);
50
51 hEvent = OpenEvent(EVENT_ALL_ACCESS,FALSE,"MsgProcessed");
52 SetEvent(hEvent);
53 CloseHandle(hEvent);
54 }
55 /* 取得數據項信息 */
56 else if (UM_GET_ITEM_INFO == pMsg->message)
57 {
58 HWND hList = (HWND)(pMsg->wParam);
59 HANDLE hEvent;
60
61 ZeroMemory(g_szText,sizeof(g_szText));
62 g_iteminfo.mask = LVIF_TEXT;
63
64 g_iteminfo.iItem = HIWORD(pMsg->lParam);
65 g_iteminfo.iSubItem = LOWORD(pMsg->lParam);
66
67 g_iteminfo.pszText = g_szText;
68 g_iteminfo.cchTextMax = 500;
69
70 ListView_GetItem(hList,&g_iteminfo);
71
72 hEvent = OpenEvent(EVENT_ALL_ACCESS,FALSE,"MsgProcessed");
73 SetEvent(hEvent);
74 CloseHandle(hEvent);
75 }
76 return CallNextHookEx(g_hhook,ncode,wParam,lParam);
77 }
78
79 extern "C" __declspec(dllexport) BOOL WINAPI SetMyHook(DWORD dwThreadId)
80 {
81 if (0 != dwThreadId)
82 g_hhook = SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,g_hDll,dwThreadId);
83 else
84 UnhookWindowsHookEx(g_hhook);
85 return TRUE;
86 }
87
對代碼說明如下:
3 第12至17行在Dll中定義了一個名爲Shared的節。注意節中定義的變量必須初始化,否則變量不是被放在指定的節,而是放在默認的未初始化全局變量節。(編譯時編譯器會給出警告信息)。節中的後三個變量加了extern "C" __declspec(dllexport)修飾,其中__declspec(dllexport)表示需要導出指定的變量,extern "C"表示以C語言方式導出,即不給修飾名,這樣即使Dll是用C++語言編寫的,C語言編寫的客戶端程序也可以調用它。第19行定義了Shared節的屬性爲rws。r = readable,w=writeable,s=shared,即在多個進程間共享本節。共享節的意義是:Dll被多次映射時,共享節中的變量只有一份,而不是通常的對每個映射dll的進程有一份,變量在多個進程間共享,類似於C++類中的靜態成員變量被類的多個實例共享一樣。
2 #pragma comment(lib,"shlwapi.lib")
3
4 #pragma comment(lib,"MsgHookDll.lib")
5
6 extern "C" __declspec(dllimport) BOOL WINAPI SetMyHook(DWORD);
7 extern "C" __declspec(dllimport) LVCOLUMN g_colinfo;
8 extern "C" __declspec(dllimport) LVITEM g_iteminfo;
9 extern "C" __declspec(dllimport) TCHAR g_szText[500];
10
11 #define UM_GET_COL_INFO (WM_USER + 0x1000)
12 #define UM_GET_ITEM_INFO (WM_USER + 0x2000)
13
14 void CExportSMSDlg::CopyDataFromList()
15 {
16 if(NULL == m_hDestWnd) return;
17
18 HWND hList,hHeader;
19 int x,y,rows,cols;
20 DWORD dwThreadId;
21 HANDLE hEvent;
22
23 //取得行列數,清空客戶端程序列表內容的代碼省略
24
25 hEvent = CreateEvent(NULL,FALSE,FALSE,"MsgProcessed");
26 dwThreadId = GetWindowThreadProcessId(m_hDestWnd,NULL);
27 SetMyHook(dwThreadId);
28
29 // 複製ListView頭部信息的代碼省略
30
31 //複製各行數據的代碼類似於取得列表
32 for(y = 0; y < rows; y++)
33 for(x = 0; x < cols; x++)
34 {
35 PostThreadMessage(dwThreadId,UM_GET_ITEM_INFO,(WPARAM)m_hDestWnd,MAKELPARAM(x,y));
36 WaitForSingleObject(hEvent,INFINITE);
37
38
39 g_iteminfo.pszText = g_szText;
40 g_iteminfo.mask |= LVIF_STATE;
41 g_iteminfo.state |= LVIS_SELECTED;
42 if (0 == x)
43 ListView_InsertItem(hList,&g_iteminfo);
44 else
45 ListView_SetItem(hList,&g_iteminfo);
46 ListView_EnsureVisible(hList,y,FALSE);
47 ProcessMessage();
48 }
49
50 CloseHandle(hEvent);
51 SetMyHook(0);
52 }
說明如下:
{
MSG msg;
while(PeekMessage(&msg,0,0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}