[SOLVED] How to tell if dll is 32-bit or 64-bit lazarus / 34-Windows ---------------------------------------------------------------------- Zoran · 2017-05-11 06:33 In a program I search for a dll in some standard paths in file system. When the program finds the dll with the name I want, is there a way to detect whether this dll is 64-bit or 32-bit before trying to load it dynamically (so I can skip it if it doesn't match the program bitness)? ---------------------------------------------------------------------- rvk · 2017-05-11 07:24 Besides the fact there shouldn't be mismatching DLL in your path... You can find the answer here: How to determine if dll file was compiled as x64 or x86 bit using either Delphi or Lazarus ---------------------------------------------------------------------- balazsszekely · 2017-05-11 08:28 Hi Zoran, I wrote this small function a few years ago. Works both wit exe and dll files: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses JwaWindows; function GetPEType(const APath: WideString): Byte;const  PE_UNKNOWN = 0;  PE_16BIT   = 1;  PE_32BIT   = 2;  PE_64BIT   = 3;var  hFile, hFileMap: THandle;  PMapView: Pointer;  PIDH: PImageDosHeader;  PINTH: PImageNtHeaders;  Base: LongWord;begin  Result := PE_UNKNOWN;   hFile := CreateFileW(PWideChar(APath), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);  if hFile = INVALID_HANDLE_VALUE then  begin    CloseHandle(hFile);    Exit;  end;   hFileMap  := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);  if hFileMap = 0 then  begin    CloseHandle(hFile);    CloseHandle(hFileMap);    Exit;  end;   PMapView := MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);  if PMapView = nil then  begin    CloseHandle(hFile);    CloseHandle(hFileMap);    Exit;  end;   PIDH := PImageDosHeader(PMapView);  if PIDH^.e_magic <> IMAGE_DOS_SIGNATURE then  begin    CloseHandle(hFile);    CloseHandle(hFileMap);    UnmapViewOfFile(PMapView);    Exit;  end;   Base := LongWord(PIDH);  PINTH := PIMAGENTHEADERS(Base + LongWord(PIDH^.e_lfanew));  if PINTH^.Signature <> IMAGE_NT_SIGNATURE then  begin    CloseHandle(hFile);    CloseHandle(hFileMap);    UnmapViewOfFile(PMapView);    Exit;  end;   if PINTH^.Signature = $4550 then  begin    case PINTH^.OptionalHeader.Magic of      $10b: Result := PE_32BIT;      $20b: Result := PE_64BIT    end;  end  else  Result := PE_16BIT;   CloseHandle(hFile);  CloseHandle(hFileMap);  UnmapViewOfFile(PMapView);end;  procedure TForm1.Button1Click(Sender: TObject);var  PEType: Byte;begin  PEType := GetPEType(WideString('C:\mydll.dll'));  case PEType of    0: ShowMessage('unknown PE');    1: ShowMessage('16 bit PE');    2: ShowMessage('32 bit PE');    3: ShowMessage('64 bit PE');  end;end; ---------------------------------------------------------------------- Zoran · 2017-05-11 11:21 Thank you both. GetMem, I tested your function and it works well. I'll use it, thanks again. ---------------------------------------------------------------------- Zoran · 2017-05-12 08:01 GetMem, I added your function to Wiki: http://wiki.freepascal.org/Mult iplatform_Programming_Guide#Detecting_bitness_of_external_library_befo re_loading_it ---------------------------------------------------------------------- balazsszekely · 2017-05-12 08:48 QuoteGetMem, I added your function to Wiki: http://wiki.freepascal.org /Multiplatform_Programming_Guide#Detecting_bitness_of_external_library _before_loading_itThank you @Zoran! ---------------------------------------------------------------------- Thaddy · 2017-05-12 10:15 Same problem exists on Linux btw. Not always, but on 32/64 bit mixed. ---------------------------------------------------------------------- Zoran · 2017-05-15 06:10 Now I noticed that compiler issues hints on these two lines: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   Base := LongWord(PIDH);  PINTH := PIMAGENTHEADERS(Base + LongWord(PIDH^.e_lfanew));  And I don't know is there a reason why you cut Pointer to LongWord (in a 64-bit application this might lose significant bits)? Wouldn't it be better: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  ...  Base: Pointer;... Base := PIDH; PINTH := PIMAGENTHEADERS(Base + LongWord(PIDH^.e_lfanew));  In my test, both versions (with Base as LongWord and as Pointer) seem to work always... but... What do you think? ---------------------------------------------------------------------- balazsszekely · 2017-05-15 08:31 Hi Zoran, QuoteAnd I don't know is there a reason why you cut Pointer to LongWordInitially I wrote that function for delphi, where an explicit typecast needed, otherwise the function won't compile. The error message( both with D2007 and XE4): "E2015 Operator not applicable to this operand type". QuoteNow I noticed that compiler issues hints on these two lines:Because we casting a pointer to a fixed size variable. The compiler knows that the size of pointers can have different values on different platforms, while the size of the variable stays the same(or not, it depends on a lot of things), hence the warning, nothing to worry about in my opinion. Quotein a 64-bit application this might lose significant bitsYou have a valid point here, Longword is enough for a 32 bit system, but NativeUInt(PtrUint) or QWord is more appropriate for 64 bit. Your pointer alternative is also good if you don't mind dropping the delphi compatibility support. PS: I also find this: https://0x2a.wtf/projects/pesp . Supports both Windows and Linux. It's pure pascal. You get a lot more info then bitness: Import/Export tables, Entry Point, etc...This is also an answer to @Thaddy's reply. ---------------------------------------------------------------------- Zoran · 2017-05-17 08:00 Okay, thanks. I updated the wiki to use Pointer. Now, another thing, this function can never return PE_16BIT, take a look at these lines (see added comments): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i ... if PINTH^.Signature <> IMAGE_NT_SIGNATURE then // this is asked again later in code, to return 16-bit begin   CloseHandle(hFile);   CloseHandle(hFileMap);   UnmapViewOfFile(PMapView);   Exit; end;  if PINTH^.Signature = $4550 then // $4550 is same as IMAGE_NT_SIGNATURE, which was already tested above. begin   case PINTH^.OptionalHeader.Magic of     $10b: Result := PE_32BIT;     $20b: Result := PE_64BIT   end; end else   Result := PE_16BIT; // so, this line will never execute!!!!!  CloseHandle(hFile); CloseHandle(hFileMap ); UnmapViewOfFile(PMapView);  end; // the function ends here.  Personally, I don't need 16-bit testing, but the wiki should be corrected. Either by removing the first test which prevents returning 16-bit, or by removing 16 bit result. What do you think? ---------------------------------------------------------------------- balazsszekely · 2017-05-17 08:14 QuoteNow, another thing, this function can never return PE_16BIT, take a look at these lines (see added comments):You're correct! It's a logical error, thank you for noticing it. Feel free to modify the code anyway you like, personally I don't think 16 bit support is needed nowadays. ---------------------------------------------------------------------- Zoran · 2017-05-17 11:02 Quote from: GetMem on May 17, 2017, 10:14:19 amQuoteNow, another thing, this function can never return PE_16BIT, take a look at these lines (see added comments):You're correct! It's a logical error, thank you for noticing it. Feel free to modify the code anyway you like, personally I don't think 16 bit support is needed nowadays. Thanks. I updated the wiki. ---------------------------------------------------------------------- ASerge · 2017-05-19 21:16 Multiple CloseHandle calls can be excluded: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses Windows;  type  TPEBitness = (pebUnknown, peb16, peb32, peb64); function GetPEBitness(const APath: WideString): TPEBitness;const  IMAGE_NT_OPTIONAL_HDR32_MAGIC = $10b;  IMAGE_NT_OPTIONAL_HDR64_MAGIC = $20b;var  HFile, HFileMap: THandle;  PMapView: Pointer;  PIDH: PImageDosHeader;  PINTH: PImageNtHeaders;begin  Result := pebUnknown;  HFile := CreateFileW(PWideChar(APath), GENERIC_READ, FILE_SHARE_READ, nil,    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);  if HFile = INVALID_HANDLE_VALUE then    Exit;  HFileMap  := CreateFileMapping(HFile, nil, PAGE_READONLY, 0, 0, nil);  CloseHandle(HFile);  if HFileMap = 0 then    Exit;  PMapView := MapViewOfFile(HFileMap, FILE_MAP_READ, 0, 0, 0);  CloseHandle(HFileMap);  if PMapView = nil then    Exit;  PIDH := PImageDosHeader(PMapView);  if PIDH^.e_magic = IMAGE_DOS_SIGNATURE then  begin    PINTH := PImageNtHeaders(PAnsiChar(PMapView) + PIDH^._lfanew);    if PINTH^.Signature <> IMAGE_NT_SIGNATURE then    Result := peb16    else      case PINTH^.OptionalHeader.Magic of      IMAGE_NT_OPTIONAL_HDR32_MAGIC:          Result := peb32;        IMAGE_NT_OPTIONAL_HDR64_MAGIC:          Result := peb64;      end;  end;  UnmapViewOfFile(PMapView);end; ---------------------------------------------------------------------- Zoran · 2017-05-19 22:03 Thank you, ASerge. Are you sure that it works well for 16-bit. Are you sure that it will not in some case return peb16 when it is not a valid 16-bit dll? I'm asking because in the first version (see this post), there was a protective block, and I'm affraid that it was maybe inserted when the function returned 16-bit when it was not a valid 16-bit dll (I don't know, but it just looks like that...). ---------------------------------------------------------------------- ASerge · 2017-05-19 23:04 Quote from: Zoran on May 20, 2017, 12:03:44 amAre you sure that it works well for 16-bit if PIDH^.e_magic = IMAGE_DOS_SIGNATURE then // Executable   if PINTH^.Signature <> IMAGE_NT_SIGNATURE then // But for NT ---------------------------------------------------------------------- ASerge · 2017-05-19 23:19 Test Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1;{$APPTYPE CONSOLE}  // Code above paste here function PEBitnessAsText(V: TPEBitness): string;const  CValue: array [TPEBitness] of string = (    'Unknown', '16 bit', '32 bit', '64 bit');begin  Result := CValue[V];end; procedure WritelnBitness(const FileName: WideString);begin  Writeln(FileName, ' is ', PEBitnessAsText(GetPEBitness(FileName)));end; const  CDlls: array [1..5] of WideString = (    'c:\Windows\SysWOW64\netmsg.dll',    'c:\Windows\System32\netmsg.dll',    'c:\Windows\Sysnative\netmsg.dll',    'c:\Windows\twain.dll',    'c:\Windows\twain_32.dll');var  i: Integer;begin  for i := Low(CDlls) to High(CDlls) do    WritelnBitness(CDlls[i]);  Readln;end. Result with 64 compiler: c:\Windows\SysWOW64\netmsg.dll is 32 bit c:\Windows\System32\netmsg.dll is 64 bit c:\Windows\Sysnative\netmsg.dll is Unknown c:\Windows\twain.dll is 16 bit c:\Windows\twain_32.dll is 32 bit Result with 32 compiler: c:\Windows\SysWOW64\netmsg.dll is 32 bit c:\Windows\System32\netmsg.dll is 32 bit c:\Windows\Sysnative\netmsg.dll is 64 bit c:\Windows\twain.dll is 16 bit c:\Windows\twain_32.dll is 32 bit ---------------------------------------------------------------------- balazsszekely · 2017-05-20 06:09 Quote@Zoran I'm asking because in the first version (see this post), there was a protective block, and I'm affraid that it was maybe inserted when the function returned 16-bit when it was not a valid 16-bit dll (I don't know, but it just looks like that...). No, that was a logical error. My original code would fail to detect a 16 bit PE as you correctly pointed out in one of your previous post. I don't think it's a way to detect a corrupted 16/32/64 PE other then checking if a valid  IMAGE_DOS_SIGNATURE,  IMAGE_NT_SIGNATURE, etc... exist. I meant PE_UNKNOWN for special cases when the path to file is invalid or the memory map could not be crated or something else. Quote@ASerge Multiple CloseHandle calls can be excluded. Yes, your code is more compact, however I prefer a slightly verbose approach. It's much easier this way  to figure out what the code does at a later time. For example instead of: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if (Something) then  if (SomethingElse) then    DoThis  else    DoThat;I always do: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if (Something) thenbegin  if (SomethingElse) then    DoThis  else    DoThat;end;Which is code redundancy, but visually is more appealing to me, but I guess is just a personal preference. ---------------------------------------------------------------------- Thaddy · 2017-05-20 07:10 That's why I like editors with a line length of at least 1024. So I definitely know that my Pascal code intended to have a linebreak and not white  space, but I guess is just a personal preference. ---------------------------------------------------------------------- ASBzone · 2019-02-07 01:16 Quote from: ASerge on May 20, 2017, 01:19:49 amTest Result with 64 compiler: c:\Windows\SysWOW64\netmsg.dll is 32 bit c:\Windows\System32\netmsg.dll is 64 bit c:\Windows\Sysnative\netmsg.dll is Unknown c:\Windows\twain.dll is 16 bit c:\Windows\twain_32.dll is 32 bit Result with 32 compiler: c:\Windows\SysWOW64\netmsg.dll is 32 bit c:\Windows\System32\netmsg.dll is 32 bit c:\Windows\Sysnative\netmsg.dll is 64 bit c:\Windows\twain.dll is 16 bit c:\Windows\twain_32.dll is 32 bit Thanks again for this routine, ASerge.  It has come in very handy. ---------------------------------------------------------------------- 440bx · 2019-02-07 03:27 Quote from: GetMem on May 20, 2017, 08:09:54 amMy original code would fail to detect a 16 bit PE as you correctly pointed out in one of your previous post. just FYI, there is no such thing as a 16bit PE.  twain.dll is 16bit but, it is not a PE file.  It is an NE file.  Also, In a PE file, the first DWORD of the IMAGE_NT_HEADERS is "PE00" (in string form - $00004550 in binary form).  In an NE file, at the same location, there is a WORD (not a dword) with the signature "NE".  See attached screenshot for a "dump" of twain.dll's header. It's "risky" to conclude the DLL is 16 bit without checking that its signature is "NE". It would probably be wise too to ensure that Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i PINTH := PImageNtHeaders(PAnsiChar(PMapView) + PIDH^._lfanew);is readable before dereferencing it just in case the dll is corrupted.  The same applies to PIDH (after all, if the dll is corrupted, it could have a file size of zero.) The magic value "IMAGE_ROM_OPTIONAL_HDR_MAGIC  0x107" is known/valid.  For that value the code will return pebUnknown, which is reasonable since it is quite unlikely that an application would want to load such a file but, strictly speaking, it is not "unknown". The above comments are on sale for $0.02 ETA: @Serge: Do you know why the result is different for c:\Windows\Sysnative\netmsg.dll ?  the 64bit version says "unknown" and the 32bit version says 64 bit.  Do you know if Windows is playing some kind of trick on 32bit executables when they look at files in the Sysnative directory or is there some other reason for the difference ? ---------------------------------------------------------------------- rvk · 2019-02-07 05:30 Quote from: 440bx on February 07, 2019, 04:27:10 amDo you know why the result is different for c:\Windows\Sysnative\netmsg.dll ?  the 64bit version says "unknown" and the 32bit version says 64 bit.  Do you know if Windows is playing some kind of trick on 32bit executables when they look at files in the Sysnative directory or is there some other reason for the difference ?The Sysnative doesn't really exist. It's a redirection which is only available in 32 bit processes. So not available in 64 bit. Also see http://www.samlogic.net/articles/sysnative-folder-64-bit- windows.htm for an explanation. And QuoteNote that 64-bit applications cannot use the Sysnative alias as it is a virtual directory not a real one.https://docs.microsoft.com/nl-nl/windows/desktop/WinProg64/file- system-redirector ---------------------------------------------------------------------- 440bx · 2019-02-07 07:25 Quote from: rvk on February 07, 2019, 06:30:56 amThe Sysnative doesn't really exist. It's a redirection which is only available in 32 bit processes. So not available in 64 bit. Thank you.    I had read the documentation about file system redirection but, I did not encounter a reference to "sysnative" unitl now.  Good to know the tricks MS has pulled out of its sleeve and thrown in the binary bucket.