Delphi For Android 开发笔记-附:如何Delphi中同时实现Windows、Android版的GetModuleFileName函数?

在Windows中开发DLL时,经常会需要获取当前DLL所在目录以便读取同目录下的其他文件,而目前Delphi在开发android时,其实没多大必要获取,因为整个工程只有一个so文件,而这个so文件也可以通过引用System.IOUtils,再使用TPath.GetLibraryPath()即可。

不过有时候我们确实需要用到类似Windows中GetModuleFileName和GetModuleHandle等函数, 这些我们应该如何在android中去实现呢?

1.linux中的进程管理/proc/[PID]/XXXX

在linux中乃至各种遵循posix的unix类系统,均可以在/proc/路径下面得到系统各种进程的相关信息,因此下面的代码在MacOS和iOS中其实是可以通用的,因为Posix标准就是为了可移植而定义的,对于开发人员来说,用一套代码走遍所有系统是非常高兴的事,而进程各个模块的信息则在maps文件中。

2.实现android版的GetModuleFileName

下面各种版本的实现代码:

2.1 FreePascal 代码

 1 //可支持android和linux
 2 uses
 3   {$IFDEF FPC}
 4   {$IFDEF UNIX}  
 5   //BaseUnix, Unix,
 6   dl;
 7   {$ENDIF}
 8   {$ENDIF} 
9 type
10 HMODULE = Cardinal;
11 {$IFDEF FPC} 12 {$IFDEF UNIX} 13 function GetModuleFileName(hLib: HMODULE):string; 14 var 15 dlinfo: dl_info; 16 IsLib: Boolean; 17 begin 18 19 if hLib=0 then 20 hLib := HMODULE(@GetModuleFileName); 21 22 dladdr(Pointer(hLib), @dlinfo); 23 Result:=string(dlinfo.dli_fname); 24 end; 25 {$ENDIF} 26 {$ENDIF}

2.2 delphi XE7 代码

  1 //支持android, MacOS没测试,可能会有问题但原理是正确的
  2 uses
  3   {$IFDEF POSIX}
  4   Posix.Base,
  5   Posix.Dlfcn,
  6   {$ENDIF}
  7   Classes, SysUtils;
  8 
  9 type
 10   HMODULE = Cardinal;
 11 
 12 {$IFNDEF FPC}
 13 {$IFDEF ANDROID}
 14 //Posix.Dlfcn中android版本的dladdr只返回模块名称,不返回全路径
 15 //因此需要自己实现dladdr
 16 threadvar
 17   _ModuleName: MarshaledAString;
 18 
 19 function dladdr(Lib: pointer; info: Pdl_info): Longint; cdecl;
 20 var
 21   F: Text;
 22   s, ss, curnode: string;
 23   a1, a2, curbase: UIntPtr;
 24   //i: longint;
 25   p, pp: PByte;
 26   M: TMarshaller;
 27 begin
 28   Result:=0;
 29   _ModuleName:='';
 30   if info = nil then
 31     exit;
 32   curbase:=0;
 33   curnode:='';
 34   Assign(F, '/proc/self/maps');
 35   Reset(F);
 36   if IoResult <> 0 then
 37     exit;
 38   while not Eof(F) do
 39     begin
 40       // Read the address range info
 41       ReadLn(F, ss);
 42       p:=PByte(M.AsAnsi(ss, CP_UTF8).ToPointer);
 43       // Starting address
 44       pp:=p;
 45       while not (p^ in [Byte(Ord('-')), Byte(Ord(#0))]) do
 46         Inc(p);
 47       p^ := Byte(Ord(#0));
 48 
 49       s := UTF8ToString(MarshaledAString(pp));
 50       a1 := StrToIntDef('$'+s, 0);
 51         if a1 > 0 then
 52         begin
 53           // Ending address
 54           Inc(p);
 55           pp:=p;
 56           while p^ > Byte(Ord(' ')) do
 57             Inc(p);
 58           p^ := Byte(Ord(#0));
 59 
 60           s := UTF8ToString(MarshaledAString(pp));
 61           a2 := StrToIntDef('$'+s, 0);
 62 
 63             if a2 > 0 then
 64             begin
 65               while p^ <= Byte(Ord(' ')) do Inc(p);  // Whitespace
 66               while p^ > Byte(Ord(' ')) do Inc(p);   // Skip perms
 67               while p^ <= Byte(Ord(' ')) do Inc(p);  // Whitespace
 68               while p^ > Byte(Ord(' ')) do Inc(p);   // Skip offset
 69               while p^ <= Byte(Ord(' ')) do Inc(p);  // Whitespace
 70               while p^ > Byte(Ord(' ')) do Inc(p);   // Skip dev
 71               while p^ <= Byte(Ord(' ')) do Inc(p);  // Whitespace
 72               // inode
 73               pp:=p;
 74               while p^ > Byte(Ord(' ')) do
 75                 Inc(p);
 76               p^ := Byte(Ord(#0));
 77 
 78               s := UTF8ToString(MarshaledAString(pp));
 79 
 80                 if s <> '0' then
 81                 begin
 82                     if s <> curnode then
 83                     begin
 84                       curnode:=s;
 85                       curbase:=a1;
 86                     end;
 87 
 88                     if (UIntPtr(Lib) >= a1) and (UIntPtr(Lib) < a2) then
 89                     begin
 90                       while p^ <= Byte(Ord(' ')) do Inc(p);  // Whitespace
 91                       // File name
 92                         if p^ = Byte(Ord('/')) then
 93                         begin
 94                           _ModuleName:=MarshaledAString(p);
 95                           info^.dli_fname:=MarshaledAString(_ModuleName);
 96                           info^.dli_fbase:=pointer(curbase);
 97                           info^.dli_sname:=nil;
 98                           info^.dli_saddr:=nil;
 99                           Result:=1;
100                         end;
101                       break;
102                     end;
103                 end;
104             end;
105         end;
106     end;
107   Close(F);
108 end;
109 {$ELSE}
110 function dladdr(Lib: pointer; info: Pdl_info): Longint; cdecl;
111 begin
112   Result := Posix.Dlfcn.dladdr(UIntPtr(Lib), info^);
113 end;
114 {$ENDIF}
115 {$ENDIF}
116 
117 {$IFDEF POSIX}
118 function GetModuleFileName(hLib: HMODULE):string;
119 var
120   dlinfo: dl_info;
121   IsLib: Boolean;
122 begin
123    if hLib=0 then
124       hLib := HMODULE(@GetModuleFileName);
125 
126     dladdr(pointer(hLib), @dlinfo);
127     Result := UTF8ToString(dlinfo.dli_fname);
128 end;
129 {$ENDIF}

3. 实现android版的GetModuleHandle

有了上面的dladdr实现,其实GetModuleHandle只是由原来通过地址信息获得路径名,逆处理为由路径名获取地址信息,而首地址即使so加载到内存后的句柄。