Einzelnen Beitrag anzeigen

Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#9

AW: EllipsisCharacter funktioniert nicht

  Alt 21. Feb 2017, 23:10
Ich denke du verstehst nicht das dass nur ein Delphi Ding ist.
Oder brauchst du so was in C++, CSharp, VB, NET usw..
Das stimmt so nicht. Alle diese Sprachen - mit Ausnahme von C++ unterstützen - try..finally Blöcke als Sprachfeature. Das "Delphi Ding" sind Resourcenschutzblöcke bei Verwendung von Objekten:
Delphi-Quellcode:
var
  A: TObject;
begin
  A := TObject.Create;
  try
  
  finally
    A.Free;
  end;
end
Dass man sowas in .NET / C# beispielsweise nicht braucht, liegt daran, dass diese Sprachen eine automatische Referenzzählung bzw. einen Garbage-Collector besitzen, der unreferenzierte Klasseninstanzen automatisch freigibt. C++ wurde zumdem explizit für das RAII Prinzip entwickelt und alloziiert Objekte standardmäßig auf dem Stack (statt auf dem Heap). Hierdurch ist die Lebensdauer durch das aktuelle Scope limitiert und benötigt auch keine zusätzliche Finalisierung.

Es mag Leute geben, die try..finally unter Delphi ausschließlich als Resourcenschutzblock verwenden, aber dennoch sind dies zwei getrennte Dinge und man sollte den Nutzen dieses Sprachkonstrukts im Bezug auf andere Dinge keinesfalls unterschätzen. Grade auch bei Verwendung der WinAPI.

Was sieht man da sehr schön ?
Dein Code ist fehlerhaft; genauer gesagt gibt er unter Umständen Objekte frei, welche gar nicht korrekt initialisiert wurden (und du vergisst diverse Rückgabewerte zu prüfen). Exemplarisch hier mal nur folgender Abschnitt:
Delphi-Quellcode:
procedure TSkinListView.DrawEllipsisText(...);
begin
  ...
  if GdipCreateFromHDC(DC, Graphics) = Ok then
  begin
    GdipCreateFontFamilyFromName(UseFont, nil, Fam);
    if Assigned(Fam) then
    begin
      GdipCreateFont(Fam, UseSize, FontStyle, 2, TempFont);
      ...
    end;
  end;
  GdipDeleteGraphics(Graphics);
  GdipDeleteFont(TempFont);
  ...
Was passiert, wenn MSDN-Library durchsuchenGdipCreateFromHDC oder MSDN-Library durchsuchenGdipCreateFontFamilyFromName fehlschlägt? Dann ist Graphics bzw. TempFont ungültig. Mag sein, dass die GDI+ hier gnädig ist, aber andere APIs z.b. MSDN-Library durchsuchenCloseHandle würden in diesem Falle bereits einen Fehler signalisieren - oder noch schlimmer: Du gibst ein anderes, zufälliges Handle deiner Anwendung frei.

Worauf Luckie allerdings (vermutlich) hinauswollte, ist die tiefe Verschachtelung und die redundante Finalisierung im if boundingBox.Width > Width then Block. Beides kannst du nämlich mit try..finally sehr elegant vermeiden:
Delphi-Quellcode:
procedure GdipCheck(Status: GPSTATUS);
begin
  if (Status <> Ok) then raise Exception.Create('GDI+ Exception');
end

...

procedure TSkinListView.DrawEllipsisText(...);
begin
  Graphics := 0;
  Fam := nil;
  TempFont := nil;
  ...
  GdipCheck(GdipCreateFromHDC(DC, Graphics));
  try
    GdipCheck(GdipCreateFontFamilyFromName(UseFont, nil, Fam));
    GdipCheck(GdipCreateFont(Fam, UseSize, FontStyle, 2, TempFont));
    GdipCheck(GdipCreateStringFormat(0, 0, strFormat));
    ...
  finally
    if (Graphics <> 0) then GdipDeleteGraphics(Graphics);
    if (Fam <> nil) then GdipDeleteFontFamily(Fam);
    if (TempFont <> nil) then GdipDeleteFont(TempFont);
    ...
  end;
Die Exception-Hilfsfunktion ist hierbei natürlich optional und kann auch durch ein if (GdipCreateFromHDC(DC, Graphics) <> Ok) then Exit ersetzt werden, da nach dem Exit ebenfalls in jedem Falle der Finally-Block ausgeführt wird.

Noch ein kleiner Nachtrag: Aus eigener Erfahrung kann ich sagen, dass man try..finally wirklich schmerzhaft vermisst, wenn es einem nicht zur Verfügung steht. Ok, C++ hat RAII als Ersatz und da ist es wohl als persönliche Präferenz zu sehen, dass ich dieses Prinzip nicht uneingeschränkt sinnvoll finde (aus zahlreichen Gründen, die hier aber wohl über das Thema hinausschießen), aber selbst pures C wird bei Verwendung der WinAPI teilweise extrem redundant, was Finalisierungen angeht. Kleines (nicht sehr sinnvolles) Beispiel (der Einfachheit halber in Delphi-Code, aber im C-Stil, also ohne try..finally).

Variante 1 (verschachtelt):
Delphi-Quellcode:
begin
  hProcess := OpenProcess(PROCESS_ALL_ACCESS, false, 1337);
  if (hProcess <> 0) then
  begin
    Mem := VirtualAllocEx(hProcess, nil, SizeOf(Value), MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);
    if Assigned(Mem) then
    begin
      Value := 0;
      if WriteProcessMemory(hProcess, Mem, @Value, SizeOf(Value), BytesWritten) and
        (BytesWritten = SizeOf(Value)) then
      begin
        hThread := CreateRemoteThread(hProcess, nil, 0,
          GetProcAddress(GetModuleHandle('kernel32.dll'), 'ExitProcess'), Mem, CREATE_SUSPENDED, TID);
        if (hThread <> 0) then
        begin
          ResumeThread(hThread);
          // Das könnte sich hier ewig so weiterschachteln ...
        end;
        CloseHandle(hThread);
      end;
      VirtualFreeEx(hProcess, Mem, 0, MEM_RELEASE);
    end;
    CloseHandle(hProcess);
  end;
end;
Variante 2 (mit Exit/goto):
Delphi-Quellcode:
begin
  hProcess := OpenProcess(PROCESS_ALL_ACCESS, false, 1337);
  if (hProcess <> 0) then
  begin
    Mem := VirtualAllocEx(hProcess, nil, SizeOf(Value), MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);
    if not Assigned(Mem) then
    begin
      // 1 Finalisierung
      CloseHandle(hProcess);
      Exit;
    end;
    Value := 0;
    if (not WriteProcessMemory(hProcess, Mem, @Value, SizeOf(Value), BytesWritten)) or
      (BytesWritten <> SizeOf(Value)) then
    begin
      // 2 Finalisierungen
      VirtualFreeEx(hProcess, Mem, 0, MEM_RELEASE);
      CloseHandle(hProcess);
      Exit;
    end;
    hThread := CreateRemoteThread(hProcess, nil, 0,
      GetProcAddress(GetModuleHandle('kernel32.dll'), 'ExitProcess'), Mem, CREATE_SUSPENDED, TID);
    if (hThread = 0) then
    begin
      // 3 Finalisierungen
      CloseHandle(hThread);
      VirtualFreeEx(hProcess, Mem, 0, MEM_RELEASE);
      CloseHandle(hProcess);
      Exit;
    end;
    ResumeThread(hThread);
    // Und am Ende nochmal alles redundant
    CloseHandle(hThread);
    VirtualFreeEx(hProcess, Mem, 0, MEM_RELEASE);
    CloseHandle(hProcess);
  end;
end;
Dahingegen die Variante mit try..finally:
Delphi-Quellcode:
begin
  hProcess := OpenProcess(PROCESS_ALL_ACCESS, false, 1337);
  if (hProcess <> 0) then
  try
    // Mini-Nachteil: Man muss alle Variablen am Anfang nullen
    Mem := nil;
    hThread := 0;
    // Los gehts:
    Mem := VirtualAllocEx(hProcess, nil, SizeOf(Value), MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);
    if not Assigned(Mem) then
      Exit;
    Value := 0;
    if (not WriteProcessMemory(hProcess, Mem, @Value, SizeOf(Value), BytesWritten)) or
      (BytesWritten <> SizeOf(Value)) then
      Exit;
    hThread := CreateRemoteThread(hProcess, nil, 0,
      GetProcAddress(GetModuleHandle('kernel32.dll'), 'ExitProcess'), Mem, CREATE_SUSPENDED, TID);
    if (hThread = 0) then
      Exit;
    ResumeThread(hThread);
    // .. kann endlos so weitergehen, ohne immer tiefere Verschachtelung oder Redundanz
  finally
    if (hThread <> 0) then CloseHandle(hThread);
    if (Mem <> nil) then VirtualFreeEx(hProcess, Mem, 0, MEM_RELEASE);
    CloseHandle(hProcess);
  end;
end;
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)

Geändert von Zacherl (21. Feb 2017 um 23:40 Uhr)
  Mit Zitat antworten Zitat