技術交流,DH講解.
晚上突然有這個想法,該踏踏實實把所有的書讀一遍,好吧.踏實一些.
這本書,我也只有電子版,要下的人可以去盒子,園地還有我的網盤都有.
第一章 DLL和資料共用
首先建立一個DLL工程:
匯出函數的2種方式:
function Export1():Integer ; export;begin //add code hereend;
就是在函數後面寫個export.
function Export1():Integer ;begin //add code hereend;function Export2():Cardinal ;stdcall;begin //add code hereend;exports Export1,Export2;
在exports後面把所有要匯出的函數名字寫上.
exe調用Dll也有2種方式:
顯式調用:
function Export1():Integer;external 'Project2.dll';implementation
定義一個函數,然後external 說明這個函數來之哪個dll.這樣就不用實現了.
隱式調用:
type TExport2 = function : Cardinal;stdcall;procedure TForm1.FormCreate(Sender: TObject);var hLib:Cardinal; Export2:TExport2;begin hLib:=LoadLibrary('Project2.dll'); Export2:=TExport2(GetProcAddress(hLib,'Export2')); //執行函數 Export2; FreeLibrary(hLib);end;
先用LoadLibary載入Dll然後GetProcAddress來取得函數指標,然後執行,最後釋放dll控制代碼,記得.
既然說到了調用,這裡就要說呼叫慣例了:
register 函數體 從左至右,優先使用寄存器(EAX,EDX,ECX),然後使用堆棧
pascal 函數體 從左至右,通過堆棧傳遞
cdecl 調用者 從右至左,通過堆棧傳遞(與C\C++預設呼叫慣例相容)
stdcall 函數體 從右至左,通過堆棧傳遞(與VC中的__stdcall相容)
safecall 函數體 從右至左,通過堆棧傳遞(同stdcall)
我們看見傳參不一樣我調用的方式要是不一樣,那麼參數就可能不對了,函數最後的結果肯定不一樣了.
如果我們想在Dll初始化幹點兒事,結束時幹點兒事,怎麼辦?
//Dll工程檔案的末尾的Begin End中間begin //這裡寫代碼就是初始化了,但是這樣只能初始化幹事情end.
只能初始化...那怎麼辦?
解決方案又有2個,哈哈:
第一個,我們添加一個引用單元,然後這個單元initialization和finalization進行初始化和收尾工作塞.
uses Unit2 in 'Unit2.pas';//Dll添加引用單元
unit Unit2;interfaceuses SysUtils;implementationvar L:TObject;initialization L:=TObject.Create;//初始化工作finalization L.Free; //收尾工作end.
第二個,調用main函數,是的,dllmain(),不過Delphi裡面不是叫這個,叫dllproc().
procedure MyDllMain(n:Integer);begin case n of DLL_PROCESS_ATTACH: begin //進程載入Dll的時候 end; DLL_PROCESS_DETACH: begin //進程卸載Dll的時候 end; DLL_THREAD_ATTACH: begin //線程載入Dll的時候 end; DLL_THREAD_DETACH: begin //線程卸載Dll的時候 end; end;end;begin DllProc:=MyDllMain; MyDllMain(DLL_PROCESS_ATTACH);end.
當Dll被一個進程載入之後,DLL的變數的空間就是這個進程裡面了.也就是如果2個進程載入一個DLL,那麼DLL的全域變數就有2個備份了,互不相干,是吧.
要多個DLL共用一個全域變數怎麼辦?接下來要說的就是用記憶體對應檔.
type TData = record A:Integer; //其他資料 end; PData = ^TData;var hMap:Cardinal; data:PData;procedure MyDllMain(n:Integer);begin case n of DLL_PROCESS_ATTACH: begin hMap:=OpenFileMapping(FILE_MAP_ALL_ACCESS,False,'名字'); if hMap = 0 then//沒有就建立 begin //記憶體映射的話,第一個參數必須是$FFFFFFFF,檔案對應的話就是檔案控制代碼 //第二個參數:安全,一般為nil //第三個參數:對應檔的屬性,我們要可讀可寫 //第四個參數:要映射資料大小的高4個位元組 //第五個參數:要映射資料大小的低4個位元組 //第六個參數:唯一的名字 hMap:=CreateFileMapping($FFFFFFFF,nil,PAGE_READWRITE,0,SizeOf(TData),'名字'); if hMap=0 then//建立失敗 Exit; end; //映射資料 data:=MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,0);//全部映射出來 if data = nil then//映射失敗 Exit; end; DLL_PROCESS_DETACH: begin if Boolean(data) then begin UnmapViewOfFile(data); CloseHandle(hMap); end; end; end;end;
現在就映射好了,只要對data進行操作就會寫到共用記憶體區了.因為可能多個DLL回去寫,所以這裡我們需要弄個互斥對象來同步.我就不示範了.這書上的代碼裡面會有的.
還有一個地方,映射的時候都只有一個變數一個結構體,結構體裡面不要有指標,也就是像String這樣的,因為指標指向的地址又是在進程空間中了.如果有多個結構體要共用,就需要先把多個結構體定義成一個結構體.
其他共用資料的方式:
1 SendMessage 或者 PostMessage,然後靠LPARAM和WPARAM來傳遞,但是LPARAM和WPARAM只是2個Integer,也就是傳遞的資料也就只有2個數,地址,指標沒戲,因為你指標還是進程空間裡面的,沒有映射出來.
2 WM_COPYDATA訊息可以傳一個指標出來.
function SendCopyData(h:HWND;s:AnsiString):Integer ;var p:PAnsiChar; bufsize:Integer; data:TCopyDataStruct;begin bufsize:=Length(s) + 1; p:=AllocMem(bufsize); StrCopy(p,PAnsiChar(s)); with data do begin cbData:=bufsize; dwData:=12315;//自己定義 lpData:=p; end; Result:=SendMessage(h,WM_COPYDATA,0,Integer(@data))end;
好的第一章就這樣,準備睡覺.明天不能遲到了...