读书 - Delphi下深入Windows核心编程 第一天

技术交流,DH讲解.

晚上突然有这个想法,该踏踏实实把所有的书读一遍,好吧.踏实一些.
这本书,我也只有电子版,要下的人可以去盒子,园地还有我的网盘都有.

第一章 DLL和数据共享
首先建立一个DLL工程:
image
image
导出函数的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;

好的第一章就这样,准备睡觉.明天不能迟到了...

posted @ 2010-01-20 22:54  HuangJacky  阅读(1463)  评论(0编辑  收藏  举报
AdminLogin