技术交流,DH讲解. 晚上突然有这个想法,该踏踏实实把所有的书读一遍,好吧.踏实一些. 这本书,我也只有电子版,要下的人可以去盒子,园地还有我的网盘都有. 第一章 DLL和数据共享 首先建立一个DLL工程: 导出函数的2种方式:
function Export1():Integer ; export;
begin
//add code here
end;
就是在函数后面写个export.
function Export1():Integer ;
begin
//add code here
end;
function Export2():Cardinal ;stdcall;
begin
//add code here
end;
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;
interface
uses
SysUtils;
implementation
var
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;
好的第一章就这样,准备睡觉.明天不能迟到了...
|
请发表评论