Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   ShareMem ohne ShareMem.dll und Co. (https://www.delphipraxis.net/135578-sharemem-ohne-sharemem-dll-und-co.html)

himitsu 13. Jun 2009 15:36


ShareMem ohne ShareMem.dll und Co.
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hier habt ihr noch ein kleines "Abfallprodukt" meines Hier im Forum suchenhimXML :angel:

Es schleift praktisch jeden Speichermanager von einer Delphi-EXE in eine Delphi-DLL durch.

Dabei ist es egal, ob nun der Standard-SpeicherManager verwendet wird oder ein Anderer.

Und es wird auch keine zusätzliche Datei benötigt, wie es bei Delphi-Referenz durchsuchenShareMem der Fall ist.


Systemvorausstzung:
die DLL (bzw. die himiSM.pas) benötigt mindestens ein Delphi, wo TMemoryManagerEx verfügbar ist
und die EXE sollte vermutlich mindestens mit Delphi 4 erstellt werden.


Diese Unit muß in die DLL aufgenommen werden:
> erster Uses-Eintrag in deren .DPR
Delphi-Quellcode:
Unit himiSM_DLLInit;

Interface

Implementation
  {$WARN SYMBOL_DEPRECATED OFF}

  Uses Windows;

  Var Name:   Array[0..29] of AnsiChar = 'himiShareMem_xxxxxxxxxxxxxxxx'#0;
    i, i2:    Integer;
    Map:      THandle;
    View:     PAnsiChar;
    MemMgr:   TMemoryManager;
    MemMgrEx: TMemoryManagerEx;
    OldMemMgr: TMemoryManagerEx;

Initialization
  GetMemoryManager(OldMemMgr);

  i2 := GetCurrentProcessId;
  For i := 15 downto 0 do Begin
    If Byte(i2 and $0F) <= 9 Then Name[i + 13] := AnsiChar((i2 and $0F) + Ord('0'))
    Else Name[i + 13] := AnsiChar((i2 and $0F) - 10 + Ord('A'));
    i2 := i2 shr 4;
  End;
  Map := OpenFileMappingA(FILE_MAP_READ, False, @Name);
  View := MapViewOfFile(Map, FILE_MAP_READ, 0, 0, 0);
  If (Map <> 0) and Assigned(View) Then
    If Assigned(PPointer(View + 3 * SizeOf(Pointer))^) Then Begin
      CopyMemory(@MemMgrEx, View, SizeOf(MemMgrEx));
      SetMemoryManager(MemMgrEx);
    End Else Begin
      CopyMemory(@MemMgr, View, SizeOf(MemMgr));
      SetMemoryManager(MemMgr);
    End;
  UnmapViewOfFile(View);
  CloseHandle(Map);

Finalization
  SetMemoryManager(OldMemMgr);

End.
Und zum Laden der DLL wird dann "einfach" nur noch statt LoadLibrary das LoadLibrarySM verwendet :mrgreen:
Delphi-Quellcode:
Function LoadLibrarySM(LibFileName: PChar): HMODULE;
  Var Name: Array[0..29] of AnsiChar;
    i, i2: Integer;
    Map:   THandle;
    View:  PAnsiChar;
    MemMgr: {$IF Declared(TMemoryManagerEx)}TMemoryManagerEx{$ELSE}TMemoryManager{$IFEND};
    Error: HRESULT;

  Begin
    Error := ERROR_INVALID_FUNCTION;
    Name := 'himiShareMem_xxxxxxxxxxxxxxxx'#0;
    i2 := GetCurrentProcessId;
    For i := 15 downto 0 do Begin
      If Byte(i2 and $0F) <= 9 Then Name[i + 13] := AnsiChar((i2 and $0F) + Ord('0'))
      Else Name[i + 13] := AnsiChar((i2 and $0F) - 10 + Ord('A'));
      i2 := i2 shr 4;
    End;
    Map := CreateFileMappingA(INVALID_HANDLE_VALUE, nil, FILE_MAP_READ, 0, 6 * SizeOf(Pointer), @Name);
    View := MapViewOfFile(Map, FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, 0);
    _DLL := 0;
    Try
      If (Map <> 0) and Assigned(View) Then Begin
        GetMemoryManager(MemMgr);
        ZeroMemory(View, 6 * SizeOf(Pointer));
        CopyMemory(View, @MemMgr, SizeOf(MemMgr));
        Result := LoadLibrary(LibFileName);
        Error := GetLastError;
      End Else Begin
        Result := 0;
        Error := ERROR_ACCESS_DENIED;
      End;
    Finally
      UnmapViewOfFile(View);
      CloseHandle(Map);
      SetLastError(Error);
    End;
  End;

Info:
Es macht übrigens auch nichts aus, wenn in einem von Beiden (EXE und DLL) keiner der Codes eingebaut ist ... in diesem Falls wird einfach nur keine Umleitung eingerichtet.

Also wenn die DLL mit deim "einfachen" LoadLibrary geladen wird oder die DLL den Code nicht enthält.



Anhang:
himiSM_DLLInit.pas * » * kommt in die DLL
himiSM_LoadLibrary.pas * » * kommt in die EXE (oder eine andere DLL) und enthällt die LoadLibrarySM

Apollonius 13. Jun 2009 15:41

Re: ShareMem ohne ShareMem.DLL
 
Gute Idee! :thumb:
Nach LoadLibrary könntest du noch GetLastError speichern und am Ende der Funktion wieder setzen (die Funktionen im Finally-Block könnten sonst am Fehlercode herumpfuschen).

himitsu 13. Jun 2009 15:51

Re: ShareMem ohne ShareMem.dll und Co.
 
Stimmt, hab ich garnicht dran gedacht :oops:

(kommt davon, wenn man im eigenem Code eine Exception wirft und es extra hierfür mit als eigenständige Funktion umbaut)


Und schon geändert :angel:

igel457 13. Sep 2009 22:36

Re: ShareMem ohne ShareMem.dll und Co.
 
Es geht auch einiges einfacher, auch wenn dann manuell eine Funktion aufgerufen werden muss. Allerdings entstehen so auch keine "wilden" Abhängigkeiten zur Windows-API.

Dll:
Delphi-Quellcode:
var
  old_mgr: TMemoryManagerEx;
  new_mgr: TMemoryManagerEx;
  mem_mgt: Boolean = false;

procedure InitMemMgr(mgr: TMemoryManagerEx);
begin
  if not mem_mgt then
  begin
    mem_mgt := true;
    GetMemoryManager(old_mgr);
   
    new_mgr := mgr;
    SetMemoryManager(new_mgr);
  end;
end;

exports
  InitMemMgr;

initialization

finalization
  if mem_mgt then
    SetMemoryManager(old_mgr);
Host:
Delphi-Quellcode:
type
  TInitMemMgrProc = procedure(mgr: TMemoryManagerEx);

var
  proc: TInitMemMgrProc;
  handle: THandle;
  mgr: TMemoryManagerEx;

[...]
handle := LoadLibrary('foo');
@proc := GetProcAddress(handle, 'InitMemMgr');
if Assigned(proc) then
begin
  GetMemoryManager(mgr);
  proc(mgr);
end;
PS: Ich frage mich ja, warum Borland das mit einer extra DLL gelöst hat... Gibt es da einen Grund? Die DLL-Daten und somit die Pointer auf die Memorymanagement-Funktionen werden bei jedem Laden der DLL doch instanziert und somit an die aufrufende Anwendung angepasst... Gibt es da einen guten Grund?

himitsu 13. Sep 2009 23:43

Re: ShareMem ohne ShareMem.dll und Co.
 
Du hast da aber ein "kleines" Problem:
Und zwar wenn in der DLL Initialisations-Abschnitte anderer Units existieren.
z.B. wenn die SysUtils oder andere Units in der DLL eingebunden sind, dann würde der Speicher-Manager der DLL schon vor Aufruf deiner Init-Prozedur verwendet und du würdest einfach diesen Manager mit dem anderen überschreiben.
> also es ist im "alten" Manager schon Speicher reserviert, welcher eventuell versucht wird über den "neuen" Manager zu verändern.


Wenn du nun nicht sicherstellen kannst, daß deine Änderung des Speichermanagers korrekt abläuft, dann solltest du vorher wenigstens, z.B. via GetMemoryManagerState (seit FastMM), feststellen, ob die "alte" Speicherverwaltung noch ungenutzt ist und dann entweder nicht diese ersetzen und/oder eine Fehlermeldung ausgeben.



Ach ja, warum Borland es in einer DLL verlagert hat:
- diese DLL wurde statisch geladen
- sie wird also schon vor der EXE geladen
und ebenfalls vor einer anderen DLL, wo sie eingebunden wurde
- sie wird auch als Letztes wieder entladen
- und da diese DLL immer als Erstes geladen und Letzes entladen wird, kann man ihre Speicherverwaltung ohne Probleme für alles Andere verwenden

- ein winziger Nachteil ist, daß man dann alles, was die Speicherverwaltung betrifft, in diese DLL auslagern muß,
welches nun auch eventuell in der EXE verwendete Fehlerüberwachungen anginge ... drum hab ich auch versucht die Verwaltung der EXE in die DLL durchzuschleifen, wärend Borlands ShareMem den Manager der DLL in die EXE einfügt


PS: so wild ist es garnicht ... es wird nur vor dem Start der DLL ein gemeinsamer Speicherbereich besorgt (in diesem Fall eine MMF) und dort der Speichermanager der EXE gespeichert
und der Rest st eine Prüfung, ob auch wirklich ein externer Manager vorhanden ist und nur dann wird er ersetzt und außerdem wurde auf die unterschiedlichen Manager-Verwaltungen eingegangen ... heißt, man kann die DLL genauso mit einer Delphi7-EXE (TMemoryManager) laufen lassen, wie mit z.B. einer EXE von Delphi 2009 (TMemoryManagerEx).

himitsu 15. Okt 2009 16:11

Re: ShareMem ohne ShareMem.dll und Co.
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hab 'nen kleinen Fehler behoben (da war noch 'ne alte Variable im Code, welche nicht mehr deklariert war)

Außerdem wurde die Unit himiSM_LoadLibrary so geändert, daß die nun auch in älteren Delphis läuft, welche noch kein TMemoryManagerEx kennen.

Und zusätzlich hab ich noch eine weitere Prozedur eingefügt.

entweder man läßt seine DLL via LoadLibrarySM

oder initialisiert Einiges mit PreLoadSM
- hier kann man dann seine DLL mit LoadLibrary laden
- allerdings bleibt dann der SharingSpeicher erhalten (sind aber nur 24 Byte)


ach ja, es gibt im Anhang auch eine kleine Demo dazu


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:45 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz