Registriert seit: 3. Sep 2004
4.629 Beiträge
Delphi 10.2 Tokyo Starter
|
AW: EllipsisCharacter funktioniert nicht
22. Feb 2017, 00: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 GdipCreateFromHDC oder GdipCreateFontFamilyFromName fehlschlägt? Dann ist Graphics
bzw. TempFont
ungültig. Mag sein, dass die GDI+ hier gnädig ist, aber andere APIs z.b. CloseHandle 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;
Geändert von Zacherl (22. Feb 2017 um 00:40 Uhr)
|