Einzelnen Beitrag anzeigen

Nintendo

Registriert seit: 16. Feb 2009
82 Beiträge
 
#15

AW: Laden einer alten 16 Bit dll unter WinXP?

  Alt 6. Dez 2013, 11:28
Zitat von JamesTKirk:
Warum sollte man das brauchen? Free Pascal erzeugt normale Win32/64/CE DLLs, die von jedem anderem Windows Program auch nutzbar sind (zumindest solange man sich daran hält keine FPC spezifischen Typen zu verwenden, aber das gilt analog für Delphi und C++ auch...)
Ich besitze Graphic Vision von Jason Burgon. Das grafische Gegenstück zu Free Vision. Das ist nicht so einfach nach 32 Bit zu portieren, weil der Code viele Assembleranweisungen enthält. Wollte dieses Paket für Freepascal verfügbar machen, da es ja nun mal den Go32 Port, die Dos Seite noch gibt. Mag ja sein, das DOS hoffnungslos veraltet ist. Dennoch gibt es im Widerspruch zu dieser Aussage das FreeDOS Projekt und verschiedene DOS Emulatoren. Klar, auf so einem Emulator läuft dann auch die 16 Bit Version klaglos, aber ich wollte nun dieses Graphic Vision für Freepascal verfügbar machen. Dazu brauche ich die Dll, die ich aber, weil Go32 keine Dlls unterstützt, von Hand (per eigenem Code) in den Speicher laden muss, von wo aus ich dann Routinen aufrufen will, wiederum mit eigenem Code, die Graphic Vision steuern. Ereignisse sollten sich mit Callback Funktionen beherrschen lassen. Die Dll dazu muss ich noch schreiben, mit Funktionen, die auf die GVision Objekte zugreifen und Daten entgegen nehmen.

Lizenzrechtlich ist zu sagen, das jeder Herrn Jason Burgon eine Emailschicken kann, mit der Bitte, ihm die GVision zu schicken. Ich hab das per Anfrage wegen des von ihm nicht mehr verfügbaren Vertriebes gemacht, wollte das Paket auch kaufen. Er hat mir dann das Paket kostenlos geschickt.
War dazu auf seiner Webseite und habe "Order" angeklickt.

Er plant eine Portierung nach Freepascal, aber nur für die Windows Plattform. Um für Go32 die Portierung zu sparen, das Umschreiben des Codes, will ich das Problem per Dll lösen, denn Borland Pascal, das ich besitze, erlaubt das compilieren von Dlls für Dos unter 16Bit DPMI.

Die damit verbundene Notwendigkeit, mich mit dem Aufbau einer ausführbaren Datei zu beschäftigen, hilft mir vielleicht später bei anderen Programmprojekten. Das bloße Umschreiben von DOS Code hilft mir dagegen weniger, außer natürlich betreffs Assembler, denn wegen der anderen Wortbreite müssen die Assemblerteile angepasst werden. Ich fühle mich aber nicht fit genug dafür, da sich die Assembler Syntax doch von der in BPascal unterscheidet. Wäre höchstens ein Versuch Wert mit Freepascal bis 1.9.2. Dieser Compiler konnte noch den 16 Bit Assemblercode einfach lesen und in passende 32 Bit Befehle übersetzen. Leider machen das spätere FPC Compiler nicht mehr so einfach. Aber ich habe noch so einen alten FPC Compiler im Netz gefunden.

Wegen Opensource mache ich mir da momentan wenig Sorgen, da Herr Burgon bei höflicher Anfrage kulant ist und die Graphic Vision dem Interessenten zusendet, womit dieser Interessent dann auch den Quellcode hat. Allerdings nur wenn er den korrekt installiert. Dazu ist dann allerdings eine legale Borland Pascal Distri erforderlich, weil nur da die Quellcodes der Laufzeitbibliotheken dabei sind, die zur Installation der Quellcodes Voraussetzung sind. Zur Not könnte man sich aber vielleicht mit der Kenntnis der Baumstruktur der Directories behelfen. Es gibt Borland Pascal zum Download im Internet. Wie legal das da ist, weiß ich nicht. Ich habe aber mehrmals Borland, wie auch Emba auf diese Downloads hingewiesen aber nie eine Antwort erhalten. Die Downloadlinks funktionieren noch immer. Wenn also der Eigentümer die Situation duldet....?

Habe zudem in einem Internetforum gelesen, das man wohl bei Emba oder Borland höflich anfragen kann und dann auf Wunsch das Paket zugesendet bekommt. Möglicherweise völlig kostenlos. Das wäre dann der völlig legale Weg.

Aus diesen Gründen bevorzuge ich das Schreiben einer Unit, die meine spätere Dll in den Speicher liest, Das Wissen zum Aufbau der Exe dürfte auch in anderer Hinsicht in der Windows Programmierung nützlich sein. Diese Unit soll auch den Code zum Aufruf meiner Dll Funktionen enthalten.


Aktuell allerdings habe ich mir zum Testen erst mal eine 32 Bit Dll mit einer einzigen Exportfunktion geschrieben.

Delphi-Quellcode:
library Test32dll;

//Uses Klausel wie vom Delphi Dll Experten eingerichtet

function helloWorld; stdcall;
begin
  Writeln('Hello World from a DLL!');
  Writeln('Zurück mit <<ENTER>>... ');
  Readln;
end;

exports
 helloWorld;
Delphi-Quellcode:
unit ULoadDll;

interface

uses
  Windows; { wegen VirtualAlloc() }

(* MemoryModule.h *)

const
  {
  ERROR_RESOURCE_DATA_NOT_FOUND    =
  ERROR_RESOURCE_LANG_NOT_FOUND    =
  ERROR_RESOURCE_NAME_NOT_FOUND    =
  ERROR_RESOURCE_TYPE_NOT_FOUND    =
  }

  IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;

  IMAGE_SIZEOF_SHORT_NAME = 8;

  { Indizes in Sections Array }
  IMAGE_DEFAULT_CODE = 0;
  IMAGE_DEFAULT_DATA_READWRITE = 1;
  IMAGE_DEFAULT_DATA_READONLY = 2;
  IMAGE_IMPORT_TABLE = 3;
  IMAGE_EXPORT_TABLE = 4; //meine Funktionen
  IMAGE_RESOURCE_TABLE = 5;
  IMAGE_UNINITIALIZED_DATA = 6;
  IMAGE_CPP_RUNTIME_SUPPORT = 7;
  IMAGE_DATA_THREAD_LOCAL_STORAGE = 8;
  IMAGE_BASE_RELOCATIONS = 9;
  IMAGE_SDATA_FORIA64_ONLY = 10;
  IMAGE_SRDATA_FORIA64_ONLY = 11;
  IMAGE_PDATA_FORIA64_ONLY = 12;
  IMAGE_DEBUG_CODEVIEW_SYMBOLS = 13;
  IMAGE_DEBUG_CODEVIEW_TYPERECORDS = 14;
  IMAGE_DEBUG_PRECOMPILED_HEADERS = 15;
  IMAGE_CONTAINS_LINKER_DIREKTIVES = 16;
  IMAGE_DELAY_LOAD_IMPORT_DATA = 17;

type
  HMEMORYMODULE = Pointer;

  HMEMORYRSRC = Pointer;

  HCUSTOMMODULE = Pointer;

  LPVOID = Pointer;

  LPCTSTR = PChar;
  LPTSTR = PChar;

  PUINT = ^UINT;
  UINT = Longint;

  PULONG = ^ULONG;
  ULONG = LongWord; { in delphi 3 -> Longint }

  PDWORD = ^DWORD;
  DWORD = LongWord; { in delphi 3 -> Longint }

  PIMAGE_DOS_HEADER = ^IMAGE_DOS_HEADER;
  IMAGE_DOS_HEADER = record // DOS .EXE header
    e_magic : WORD; // Magic number { MZ for exe }
    e_cblp : WORD; // Bytes on last page of file
    e_cp : WORD; // Pages in file
    e_crlc : WORD; // Relocations
    e_cparhdr : WORD; // Size of header in paragraphs
    e_minalloc : WORD; // Minimum extra paragraphs needed
    e_maxalloc : WORD; // Maximum extra paragraphs needed
    e_ss : WORD; // Initial (relative) SS value
    e_sp : WORD; // Initial SP value
    e_csum : WORD; // Checksum
    e_ip : WORD; // Initial IP value
    e_cs : WORD; // Initial (relative) CS value
    e_lfarlc : WORD; // File address of relocation table
    e_ovno : WORD; // Overlay number
    e_res : array[0..3] of WORD; // Reserved words
    e_oemid : WORD; // OEM identifier (for e_oeminfo)
    e_oeminfo : WORD; // OEM information; e_oemid specific
    e_res2 : array[0..9] of WORD; // Reserved words
    e_lfanew : Longint; // File address of new exe header
  end;
 
  PIMAGE_FILE_HEADER = ^IMAGE_FILE_HEADER;
   IMAGE_FILE_HEADER = record
    Machine : WORD;
    NumberOfSections : WORD;
    TimeDateStamp : DWORD;
    PointerToSymbolTable : DWORD;
    NumberOfSymbols : DWORD;
    SizeOfOptionalHeader : WORD;
    Characteristics : WORD;
  end;

  TLocation = record
   case DWORD of
    0: (PhysicalAddress: DWORD);
    1: (VirtualSize: DWORD);
  end;

  IMAGE_SECTION_HEADER = record
    Name : array[0..IMAGE_SIZEOF_SHORT_NAME-1] of BYTE;
    Misc : TLocation;
    VirtualAddress : DWORD;
    SizeOfRawData : DWORD;
    PointerToRawData : DWORD;
    PointerToRelocations: DWORD;
    PointerToLinenumbers: DWORD;
    NumberOfRelocations : WORD;
    NumberOfLinenumbers : WORD;
    Characteristics : DWORD;
  end;

  PIMAGE_DATA_DIRECTORY = ^IMAGE_DATA_DIRECTORY;
   IMAGE_DATA_DIRECTORY = record
    VirtualAddress: DWORD;
    Size: DWORD;
  end;

  PIMAGE_BASE_RELOCATION = ^IMAGE_BASE_RELOCATION;
   IMAGE_BASE_RELOCATION = record
    VirtualAddress: DWORD;
    SizeOfBlock: DWORD;
  end;

  PIMAGE_OPTIONAL_HEADER32 = ^IMAGE_OPTIONAL_HEADER32;
   IMAGE_OPTIONAL_HEADER32 = record
    //
    // Standard fields.
    //

    Magic : WORD;
    MajorLinkerVersion : BYTE;
    MinorLinkerVersion : BYTE;
    SizeOfCode : DWORD;
    SizeOfInitializedData : DWORD;
    SizeOfUninitializedData : DWORD;
    AddressOfEntryPoint : DWORD;
    BaseOfCode : DWORD;
    BaseOfData : DWORD;

    //
    // NT additional fields.
    //

    ImageBase : DWORD;
    SectionAlignment : DWORD;
    FileAlignment : DWORD;
    MajorOperatingSystemVersion : WORD;
    MinorOperatingSystemVersion : WORD;
    MajorImageVersion : WORD;
    MinorImageVersion : WORD;
    MajorSubsystemVersion : WORD;
    MinorSubsystemVersion : WORD;
    Win32VersionValue : DWORD;
    SizeOfImage : DWORD;
    SizeOfHeaders : DWORD;
    CheckSum : DWORD;
    Subsystem : WORD;
    DllCharacteristics : WORD;
    SizeOfStackReserve : DWORD;
    SizeOfStackCommit : DWORD;
    SizeOfHeapReserve : DWORD;
    SizeOfHeapCommit : DWORD;
    LoaderFlags : DWORD;
    NumberOfRvaAndSizes : DWORD;
    DataDirectory : array[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES-1] of IMAGE_DATA_DIRECTORY;
  end;

  PIMAGE_NT_HEADERS32 = ^IMAGE_NT_HEADERS32;
   IMAGE_NT_HEADERS32 = record
    Signature : DWORD;
    FileHeader : IMAGE_FILE_HEADER;
    OptionalHeader : IMAGE_OPTIONAL_HEADER32;
  end;
  
  TThunkCharacterisics = record
  case DWORD of
   0: (Characteristics : DWORD); { 0 for terminating null import descriptor        }
   1: (OriginalFirstThunk: DWORD); { RVA to original unbound IAT (PIMAGE_THUNK_DATA) }
  end;

  PIMAGE_IMPORT_DESCRIPTOR = ^IMAGE_IMPORT_DESCRIPTOR;
   IMAGE_IMPORT_DESCRIPTOR = record
    Thunk : TThunkCharacterisics;
    TimeDateStamp : DWORD; { 0 if not bound,                                }
                                            { -1 if bound, and real date\time stamp          }
                                            {     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) }
                                            { O.W. date/time stamp of DLL bound to (Old BIND)      }

    ForwarderChain : DWORD; { -1 if no forwarders                           }
    Name : DWORD;
    FirstThunk : DWORD; { RVA to IAT (if bound this IAT has actual addresses)  }
  end;
  
  TCode = record
  case LongWord of
   0 : (Offset,Segment: Word);
   1 : (LinearAddr: LongWord);
  end;
  
  PIMAGE_EXPORT_DIRECTORY = ^IMAGE_EXPORT_DIRECTORY;
   IMAGE_EXPORT_DIRECTORY = record
    Characteristics : DWORD;
    TimeDateStamp : DWORD;
    MajorVersion : WORD;
    MinorVersion : WORD;
    Name : DWORD;
    Base : DWORD;
    NumberOfFunctions : DWORD;
    NumberOfNames : PDWORD;
    AddressOfFunctions : PDWORD; { RVA from base of image }
    AddressOfNames : PDWORD; { RVA from base of image }
    AddressOfNameOrdinals : PDWORD; { RVA from base of image }
  end;

  TSections = Array[0..17] of IMAGE_SECTION_HEADER;

  PDllFileStruct = ^TDllFileStruct;
  TDllFileStruct = record
    dosheader : IMAGE_DOS_HEADER;
    dosStub : array [0..27] of Byte;
    peHeader : IMAGE_NT_HEADERS32;
    SectionHeader: IMAGE_SECTION_HEADER;
    Code : array of Byte;
  end;

  PIMAGE_RESOURCE_DIRECTORY_ENTRY = ^IMAGE_RESOURCE_DIRECTORY_ENTRY;
   IMAGE_RESOURCE_DIRECTORY_ENTRY = record
    Name: DWORD;
    OffsetToData: DWORD;
  end;

  PIMAGE_RESOURCE_DATA_ENTRY = ^IMAGE_RESOURCE_DATA_ENTRY;
   IMAGE_RESOURCE_DATA_ENTRY = record
    OffsetToData: ULONG;
    Size : ULONG;
    CodePage : ULONG;
    Reserved : ULONG;
  end;

  PIMAGE_RESOURCE_DIR_STRING_U = ^IMAGE_RESOURCE_DIR_STRING_U;
   IMAGE_RESOURCE_DIR_STRING_U = record
    Length: WORD;
    NameString: array[0..1] of WORD; { WCHAR -> 16 Bit UNICODE-CHAR }
  end;

  PIMAGE_RESOURCE_DIRECTORY = ^IMAGE_RESOURCE_DIRECTORY;
   IMAGE_RESOURCE_DIRECTORY = record
    Characteristics: DWORD;
    TimeDateStamp: DWORD;
    MajorVersion: WORD;
    MinorVersion: WORD;
    NumberOfNamedEntries: WORD;
    NumberOfIdEntries: WORD;
    { dynamic array }
    DirectoryEntries: array of IMAGE_RESOURCE_DIRECTORY_ENTRY; { EntryCount is NumberOfIdEntries}
  end;

function LoadLibraryFromFile(Filename: String): TDllFileStruct;

function MemoryLoadLibraryEx(var ALibrary: TDllFileStruct): HMEMORYMODULE;

procedure MemoryFreeLibrary(var ALibrary: HMEMORYMODULE);

(**


* Find the location of a resource with the specified type and name.


*)



function MemoryFindResource(module: HMEMORYMODULE; lpName,lpType: LPCTSTR): HMEMORYRSRC;


(**


* Find the location of a resource with the specified type, name and language.


*)



function MemoryFindResourceEx(module: HMEMORYMODULE; lpName,lpType: LPCTSTR; language: WORD): HMEMORYRSRC;




(**


* Get the size of the resource in bytes.

*)


function MemorySizeofResource(module: HMEMORYMODULE; resource: HMEMORYRSRC): DWORD;


(**


* Get a pointer to the contents of the resource.


*)



function MemoryLoadResource(module: HMEMORYMODULE; resource: HMEMORYRSRC): LPVOID;


(**


* Load a string resource.


*)



function MemoryLoadString(module: HMEMORYMODULE; id: UINT; buffer: LPTSTR; maxsize: Longint): Longint;


(**


* Load a string resource with a given language.


*)



function MemoryLoadStringEx(module: HMEMORYMODULE; id: UINT; buffer: LPTSTR; maxsize: Longint; language: WORD): Longint;








implementation

uses
  {$ifdef DOS}
  Dos,
    {$ifdef FPC}
    Go32,
    {$else}

    {$endif}
  {$endif}
  SysUtils, Classes;



function LoadLibraryFromFile(Filename: String): TDllFileStruct;
var
  S: TFileStream;
  H: TDllFileStruct;
  CodeSize: Longint;
begin
  S := TFileStream.Create(Filename, fmOpenRead);
  S.Read(H, Sizeof(H)-4);
  CodeSize := S.Size - S.Position;
  SetLength(H.Code, CodeSize);
  S.Read(H.Code, CodeSize);
  S.Free;
  Result := H;
end;

const
  _MEM_COMMIT = $00001000; { Wert zu Windows äquivalent }
  _MEM_RESERVE = $00002000; { Wert zu Windows äquivalent }

  _PAGE_NO_ACCESS = $01; { Wert zu Windows äquivalent }
  _PAGE_READONLY = $02; { Wert zu Windows äquivalent }
  _PAGE_READWRITE = $04; { Wert zu Windows äquivalent }
  _PAGE_WRITECOPY = $08; { Wert zu Windows äquivalent }

  _PAGE_EXECUTE = $10; { Wert zu Windows äquivalent }
  _PAGE_EXECUTE_READ = $20; { Wert zu Windows äquivalent }
  _PAGE_EXECUTE_READWRITE = $40; { Wert zu Windows äquivalent }
  _PAGE_EXECUTE_WRITECOPY = $80; { Wert zu Windows äquivalent }

function _VirtualAlloc(DestAddr: Pointer; DestSize: Longint; AllocType, ProtectFlags: DWORD): HMemoryModule;
begin
  //in Freepascal mit GO32 - Memmngr
  //in Delphi-WDOSX mit DPMI - MemMngr
  //implementieren
end;

function MemoryLoadLibraryEx(var ALibrary: TDllFileStruct): HMEMORYMODULE;
var
  Delta : Longint;
  LibSize: Longint;
  LibAddr: Longint;
  AddrPtr: HMemoryModule;
  EffAddr: Longint;
  MemSize: Longint;
  SectionsCount: Integer;
begin
  LibSize := ALibrary.peHeader.OptionalHeader.SizeOfImage;
  LibAddr := ALibrary.peHeader.OptionalHeader.ImageBase;
  AddrPtr := VirtualAlloc(
    Pointer(ALibrary.peHeader.OptionalHeader.ImageBase),
    ALibrary.peHeader.OptionalHeader.SizeOfImage,
    MEM_RESERVE,
    PAGE_READWRITE
  );
  EffAddr := Longint(AddrPtr);

  Delta := EffAddr - ALibrary.peHeader.OptionalHeader.ImageBase;

  { Dieses Delta muss zu den in den Offsets im File addiert werden }

  SectionsCount := ALibrary.peHeader.FileHeader.NumberOfSections;
  Writeln('Anzahl Sectionen: ', SectionsCount);
  EffAddr := LibAddr + ALibrary.SectionHeader.VirtualAddress;
  MemSize := ALibrary.SectionHeader.SizeOfRawData;
  {
  in Windows:
  NewAddressPointer := VirtualAlloc(EffAddr, ALibrary.SectionHeader.SizeOfRawData, MEM_COMMIT, RAGE_READWRITE);
  Aber für DOS32 Schnittstelle muss anderer Weg beschritten werden:
  }

  Result := HMEMORYMODULE(EffAddr);
  //Jetzt muss relocation folgen
  {
  Offset := SizeOf(dosheader) + SizeOf(dosStub) + SizeOf(peHeader);
  Funcns := SizeOf(SectionHeader) * 4 + Offset;
  }

end;

procedure MemoryFreeLibrary(var ALibrary: HMEMORYMODULE);
begin

  {HeapFree(GetProcessHeap(), 0, ALibrary);}
  Dispose(ALibrary);

end;

const
 DEFAULT_LANGUAGE = 0;

function MemoryFindResource(module: HMEMORYMODULE; lpName,lpType: LPCTSTR): HMEMORYRSRC;
begin
  Result := MemoryFindResourceEx(module, lpName, lpType, DEFAULT_LANGUAGE);
end;

function _MemorySearchResourceEntry(

    var root: Pointer;

    resources: PIMAGE_RESOURCE_DIRECTORY;

    key: LPCTSTR): PIMAGE_RESOURCE_DIRECTORY_ENTRY;

begin
end;

function MemoryFindResourceEx(module: HMEMORYMODULE; lpName,lpType: LPCTSTR; language: WORD): HMEMORYRSRC;
begin
end;

function MemorySizeofResource(module: HMEMORYMODULE; resource: HMEMORYRSRC): DWORD;
begin
end;

function MemoryLoadResource(module: HMEMORYMODULE; resource: HMEMORYRSRC): LPVOID;
begin
end;

function MemoryLoadString(module: HMEMORYMODULE; id: UINT; buffer: LPTSTR; maxsize: Longint): Longint;
begin
end;

function MemoryLoadStringEx(module: HMEMORYMODULE; id: UINT; buffer: LPTSTR; maxsize: Longint; language: WORD): Longint;
begin
end;

end.
Und hier noch mein Testprogramm:

Delphi-Quellcode:
program LoadDll16;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ULoadDll,
  Convert,
  LibExec in 'LibExec.pas';

var
  MyLib: TDllFileStruct;
  MemModul: HMEMORYMODULE;
begin
  { TODO -oUser -cConsole Main : Insert code here }
  MyLib := LoadLibraryFromFile('Test32dll.dll');
  Writeln('Kennzeichen ',WordToHex(MyLib.dosheader.e_magic));
  Writeln('Anzahl Sections: ', MyLib.peHeader.FileHeader.NumberOfSections);
  Writeln('Zurück mit << E N T E R >> ... ');
  ReadLn;
  MemModul := MemoryLoadLibraryEx(MyLib);
end.
Was mich jetzt wundert, ist, das bei Anzahl Section für diese kleine Dll, die ich zunächst mit Delphi (Turbo Delphi) geschrieben habe, 25954 Sections angezeigt bekomme. Was stimmt da nicht?

Das Programm sollte auch ohne die Unit LibExec übersetzbar sein, falls einer so nett ist, das mal auszuprobieren. LibExec enthält noch mal mit anderen Feldbezeichnern die Headerstrukturen und eine Klassendefinition für das Lesen der Dll. Diese Dinge werden von dem Testprogramm nicht benutzt.

Mich wundert die hohe Anzahl Sections. Und das ist wie gesagt, die 32 Bit Dll.

Die 16 Bit dll, testweise auch nur mit der Hello World Funktion, zeigt mir Null Sections an.

Hat die vielleicht doch einen etwas anderen Aufbau?

Geändert von Nintendo ( 6. Dez 2013 um 12:03 Uhr)
  Mit Zitat antworten Zitat