Windows操作系統是一個多任務系統,每個任務都有相應的進程對應。熟悉windows系統的用戶知道,每個進程都有自己獨立的內存地址和內存空間。這對進程間之間的數據相互訪問和相互交換帶來一定的不便,但是在實際應用中有時要在進程間進行數據交換。windows系統提供了許多方法實現進程間的數據交換,比如我們可以通過磁盤文件來進行交換,這實際是最簡單可行的辦法,但運行速度大打折扣,特別是對於大批量數據交換時。另外我們可以通過Windows系統提供的CreateFileMaping、OpenFileMapping、MapViewofFile等API函數來實現進程間的數據交換,但實現方法相對複雜。
本文將介紹另外兩種進程間數據交換的方法,一種是通過剪貼板的進程數據交換方法,另一種是直接內存讀取的進程數據交換方法。最後將用Delphi語言舉例加以實現應用。
一、通過剪貼板的進程數據交換方法
剪貼板是windows系統專門提供用來不同應用程序之間的數據交換,用戶只要通過“複製”和“粘帖”兩個動作就可以實現應用程序之間的數據交換。用戶可以自定義剪貼板格式實現數據交換的目的。
其方法步驟爲:
1、數據發送進程向剪貼板“複製”用戶自定義的數據內容
2、數據發送進程向數據接受進程發送數據交換的消息。
3、數據接受進程收到消息以後向剪貼板“粘貼”用戶自定義的數據內容。
這種方法的實現過程如圖一所示:
該方法涉及到的重要API函數主要有:
1、RegisterClipboardFormat
這個函數是向系統剪貼板註冊新的剪貼板數據格式其函數調用格式爲:
UINT RegisterClipboardFormat( LPCTSTR lpszFormat // 新剪貼板數據格式的名稱 ); |
如果該名稱的剪貼板格式已經註冊函數返回已經的標示號,否則函數返回一個新的標示號。
2、GlobalAlloc、GlobalLock
這兩個函數前者是用於從當前進程的內存空間分配一定字節數的內存,後者是對指定的內存加鎖。它們的函數調用格式分別爲:
HGLOBAL GlobalAlloc( UINT uFlags, //分配到的內存屬性 DWORD dwBytes // 需要內存分配的空間大小 ); LPVOID GlobalLock( HGLOBAL hMem // 需要加鎖的內存“句柄” ); |
3、Clipboard的Open、SetAsHandle、Close、GetAsHandle
這四個函數是Delphi語言Clipboard全局變量提供專門用於對剪貼板數據“複製”和“粘帖”動作所要用到的函數。在Clipbrd.pas單元中實現。其函數格式略。
二、直接內存讀取的進程數據交換方法
這種方法是通過Windows系統一系列API函數調用直接讀取另一個進程的內存,來實現數據交換的目的。
其方法步驟爲:
1、數據發送方進程數據接受方進程發送數據交換的消息,Wparam參數值爲數據發送方進程標示號,Lparam參數值爲要發送的數據的地址。
2、數據接受方進程收到消息以後直接讀取數據發送方進程內存地址的數據。
這種方法的實現過程如圖二所示:
該方法涉及的重要API函數主要有:
1、GetCurrentProcessId
這個函數主要獲取當前運行進程的進程標示號,其函數格式爲:
DWORD GetCurrentProcessId(VOID) |
2、openProcess
這個函數主要獲得一個已經運行的進程的“進程句柄”,以便在另外的API函數調用。其函數格式爲:
HANDLE OpenProcess( DWORD dwDesiredAccess, // 要打開的進程的訪問模式 BOOL bInheritHandle, // 該進程句柄是否在子線程中繼承 DWORD dwProcessId // 進程的標示號 ); |
3、ReadProcessMemory
這個函數是直接內存讀取的進程數據交換方法最核心的一個函數,它實現從另一個進程中讀取內存數據。其函數格式爲:
BOOL ReadProcessMemory( HANDLE hProcess, //進程句柄 LPCVOID lpBaseAddress, // 要讀取內存的起始地址 LPVOID lpBuffer, //存放讀取的數據的變量 DWORD nSize, //需要讀取的字節數 LPDWORD lpNumberOfBytesRead // 實際讀取到的字節數 ); |
三、兩種進程數據交換方法的應用舉例
爲了更加詳細地介紹上述兩種方法,下面利用Delphi語言結合實際的例子來闡述它的應用。該例子分別用通過剪貼板的進程數據交換的方法和直接內存讀取的進程數據交換方法實現兩個進程間的交換一個記錄數據,該記錄的數據格式爲:
TFriend=packed record name :array[1..8] of char; age :byte; address :array[1..30] of char; end; |
另外定義兩條消息和幾個需要用到的常量:
1.WM_Datasend_byClpbrd=WM_user+1000; //用於通過剪貼板的進程數據交換的方法的消息; 2.WM_DataSend_byMemory=WM_user+1001;// 直接內存讀取的進程數據交換方法的消息; 3.receivewindowcaption='DataReceiver'; //接收數據進程窗口的標題; 4.receivewindowclassname='Data Receive Window';//接受數據進程窗口的類別名稱; 5.userDefineformat='process data exchange'; //自定義剪貼板數據格式名稱; |
以上常量和記錄結構是發送進程和接受進程的兩個程序都要用到的數據。可以單獨存放在一個文件中用uses引用,或用{$I }的方法引用。
(一)數據發送進程程序的實現
1、在Delphi的IDE環境中新建Application
2、在Form1中加入一個Tspinedit、兩個Tedit和兩個Tbutton控件(分別爲Button1和button2),兩個Edit控件分別供輸入姓名和地址,SpinEdit控件供輸入年齡。
3、重載Button1的onClick程序用於剪貼板數據交換,如下所示:
procedure TForm1.Button1Click(Sender: TObject); var wnd :Hwnd; Friend :TFriend; hmem :Thandle; datapointer :pointer; CF_User :Uint; begin //尋找數據接受進程的窗口 wnd:=findwindow(receivewindowclassname,receivewindowcaption); //如果找到了向剪貼板複製數據並且向數據接受進程發送消息 if wnd<>0 then begin //申請內存 hmem:=globalAlloc(GMEM_MOVEABLE,sizeof(friend)); //給記錄賦值 strpcopy(@friend.name,edit1.text); friend.age:=byte(spinedit1.Value); strpcopy(@friend.address,edit2.text); //獲取申請到的內存地址並且把記錄的值導入內存 DataPointer := GlobalLock(hmem); //把記錄數據複製到Global內存中 move(friend,datapointer^,sizeof(friend)); //註冊或獲取新的剪貼板數據格式標號 CF_User:=registerclipboardformat(userdefineformat); //向剪貼板複製數據 clipboard.Open; clipboard.SetAsHandle(CF_USER,hmem); clipboard.Close; //向接收進程窗口發送消息 sendmessage(wnd,WM_Datasend_byClpbrd,0,0); end; end; |
4、重載button2的OnClick程序用於直接內存讀取的數據交換,如下所示:
procedure TForm1.Button2Click(Sender: TObject); var wnd :Hwnd; Friend :TFriend; Begin //尋找數據接受進程的窗口 wnd:=findwindow(receivewindowclassname,receivewindowcaption); //如果找到了向數據接收進程窗口發送消息 if wnd<>0 then begin //對記錄賦值 strpcopy(@friend.name,edit1.text); friend.age:=byte(spinedit1.Value); strpcopy(@friend.address,edit2.text); //發送消息,把發送進程的進程標示號作爲Wparam //把記錄地址作爲Lparam進行傳遞 sendmessage(wnd,WM_DataSend_byMemory, getcurrentprocessID,lparam(@Friend)); end; end; |
5、保存、編譯、運行
(二)數據接受進程程序的實現
1、在Delphi的IDE環境新建Application;
2、在Form1中加入三個Tedit控件,接受發送過來的數據;
3、重載Form1的CreateParams程序,重新定義Form1的類,程序如下:
procedure TForm1.CreateParams(var Params: TCreateParams); begin inherited CreateParams(Params); params.Caption:=receivewindowcaption; Params.WinClassName :=receivewindowclassname; end; |
4、重載在Form1的wndproc程序接受兩種方法發送過來的數據,程序如下:
procedure Tform1.wndproc(var message:Tmessage); var processhnd :Thandle; Friend :TFriend; numread :dword; hmem :Thandle; clipdata :pointer; CF_User :Uint; Begin Case message.msg of //處理剪貼板方式發送過來的消息 WM_Datasend_byClpbrd: Begin //註冊或獲取新的剪貼板數據格式標號 CF_User:= registerclipboardformat(userdefineformat); if clipboard.HasFormat(CF_User) then begin //獲取剪貼板的內容,並且分別賦值給三個Edit hmem:=clipboard.GetAsHandle(CF_user); clipdata:=globallock(hmem); if clipdata<>nil then begin move(clipdata^,Friend,sizeof(Friend)); edit1.text:=Friend.name; edit2.text:=inttostr(Friend.age); edit3.text:=Friend.address; end; end; message.Result:=1; End; //處理直接內存讀取發送過來的消息 WM_DataSend_byMemory: Begin //獲取發送進程的“進程句柄”,設置訪問模式爲“讀” processhnd:=openprocess(PROCESS_VM_READ, false,message.WParam); //讀取發送進程的內存數據 readprocessmemory(processhnd, ptr(message.lparam), @friend,sizeof(friend),numread); edit1.text:=Friend.name; edit2.text:=inttostr(Friend.age); edit3.text:=Friend.address; closehandle(processhnd); message.result:=1; End; Else //對於其它消息依然按照原來的方法進行消息處理 Inherited wndproc(message); End; |
5、保存、編譯、運行
到這裏爲止已經實現上述進程間記錄數據的交換的兩種方法。運行結果如圖三所示:
圖三
以上程序僅僅是上面介紹的兩種方法最簡單應用,用戶可以根據自己的實際需要加以拓展應用。上述程序在Windows 2000+Delphi 5.0的環境下運行通過。
轉自:http://industry.ccidnet.com/art/322/20031027/68697_1.html
下面是我寫的剪貼板交換數據的代碼 點擊下載