zoukankan      html  css  js  c++  java
  • 读书 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;

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

  • 相关阅读:
    R语言学习——数据分析
    java学习——类之YuanZhu
    java学习——类之YuanZhu
    java学习——构造类之3!+5!=126
    C# WebQQ协议群发机器人(二)
    Unity5.1 新的网络引擎UNET(七) UNET 单人游戏转换为多人
    转:Oracle GoldenGate学习之Goldengate介绍
    cvReadTrainData
    使用Handler在子线程中更新UI
    初识MVC
  • 原文地址:https://www.cnblogs.com/huangjacky/p/1652794.html
Copyright © 2011-2022 走看看