![]() |
Problem ShellExecute / CreateProcess + Wait mit Vista UAC
Hallo DPler,
mal wieder eine Frage die sich um Vista, UAC und die kleinen Details in Delphi bewegt. Ich möchte ein Programm im aktuellen Rechtekontext einer Anwendung ausführen und auf das Ende der Ausführung warten. zu startende Anwendung benötigt Admin-Rechte (Vista Manifest eingebunden). Das Problem ist, wenn ich CreateProcess(Ex) verwende, schmeißt mir Vista in meinem Programm eine Exception raus. Fehler 740, also ElevationRequired. Ich habe im MSDN von der unterschiedlichen Behandlung von ShellExecute und CreateProcess gelesen, insbesondere die besondere Behandlung von RequireElevation von ShellExecute. Gibt es ein "echtes" ShellExecuteAndWait ohne Aufruf des CreateProcess? Forumsuche zeigt nur Prä-Vista Beispiele, die alle CreateProcess nutzen... Gruß winkel79 |
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Das Sollte helfen:
Delphi-Quellcode:
function ExecAndWait(Filename, Params: Widestring;
WindowState: word = SW_SHOWNORMAL): boolean; var ShExecInfo : SHELLEXECUTEINFOW; r : Cardinal; //http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/shellexecuteex.asp //http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/structures/shellexecuteinfo.asp begin Result := false; if Filename = '' then exit; if not WideFileExists(FileName) then begin Log (true,P_ERROR,WideFormat(_('Kann die angegebene Datei nicht finden: %s'),[FileName])); exit; end; ShExecInfo.cbSize := sizeof(SHELLEXECUTEINFOW); ShExecInfo.fMask := SEE_MASK_NOCLOSEPROCESS; ShExecInfo.lpVerb := 'open'; ShExecInfo.lpFile := PWideChar(Filename); ShExecInfo.lpParameters := PWideChar(Params); ShExecInfo.lpDirectory := PWideChar(WideExtractFileDir(Filename)); ShExecInfo.nShow := WindowState; Result := ShellExecuteExW(@ShExecInfo); try if Result then begin r := WaitForSingleObject(ShExecInfo.hProcess, INFINITE); if r <> WAIT_OBJECT_0 then Log(true,P_FATAL,IntToStr(r)+#10+WideFormat(_('Fehler beim Beenden von "%s".'),[Filename])); end else Log(true,P_FATAL,SysErrorMessage(GetLastError)+#10+WideFormat(_('Fehler beim Starten von "%s".'),[Filename])); finally CloseHandle(ShExecInfo.hProcess); end; end; |
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Hallo Sven,
danke für das Codebeispiel. Es funktioniert teilweise richtig. Teilweise, weil ich nun das Problem habe, daß die UAC unter Vista in der Taskbar kommt. Das Problem habe ich in einem anderen Thread beschrieben und gelöst: ![]() Wie kann ich dem ShellExecuteEx das Fensterhandle bzw. HWND von GetForegroundWindow übergeben um dieses UAC Feature zu umgehen? Wie verhält es sich mit SHELLEXECUTEINFOW unter Win98? Um abwärtskompatibel zu bleiben vielleicht eine Platform-Weiche? Bleibt hierbei bis auf SHELLEXECUTEINFOA und ShellExecuteExA alles gleich? Danke für Eure Hilfe! Gruß winkel79 |
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
So, die Herren ;)
Vielleicht kann jemand (Sven?) noch mal einen kurzen Blick über den Code werfen. Tests unter Win98 (in VM), Vista und XP laufen soweit. Mit und ohne UAC und der richtigen Einblendung (siehe anderer Thread).
Delphi-Quellcode:
Die Logaufrufe von Sven hab ich entfernt bzw. auskommentiert. Die Function Win32IsUnicode ist lediglich ein globaler
function ExecAndWait(Filename, Params: Widestring;
WindowState: word = SW_SHOWNORMAL): boolean; var ShExecInfoW: SHELLEXECUTEINFOW; ShExecInfoA: SHELLEXECUTEINFOA; r : Cardinal; //http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/shellexecuteex.asp //http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/structures/shellexecuteinfo.asp begin Result := false; if Filename = '' then exit; if not WideFileExists(FileName) then begin // Log (true,P_ERROR,WideFormat(_('Kann die angegebene Datei nicht finden: %s'),[FileName])); exit; end; if Win32IsUnicode then begin ShExecInfoW.Wnd := GetForegroundWindow; ShExecInfoW.cbSize := sizeof(SHELLEXECUTEINFOW); ShExecInfoW.fMask := SEE_MASK_NOCLOSEPROCESS; ShExecInfoW.lpVerb := 'open'; ShExecInfoW.lpFile := PWideChar(Filename); ShExecInfoW.lpParameters := PWideChar(Params); ShExecInfoW.lpDirectory := PWideChar(WideExtractFileDir(Filename)); ShExecInfoW.nShow := WindowState; Result := ShellExecuteExW(@ShExecInfoW); end else begin ShExecInfoA.Wnd := GetForegroundWindow; ShExecInfoA.cbSize := sizeof(SHELLEXECUTEINFOA); ShExecInfoA.fMask := SEE_MASK_NOCLOSEPROCESS; ShExecInfoA.lpVerb := 'open'; ShExecInfoA.lpFile := PChar(AnsiString(Filename)); ShExecInfoA.lpParameters := PChar(AnsiString(Params)); ShExecInfoA.lpDirectory := PChar(AnsiString(WideExtractFileDir(Filename))); ShExecInfoA.nShow := WindowState; Result := ShellExecuteExA(@ShExecInfoA); end; try if Result then begin if Win32IsUnicode then r := WaitForSingleObject(ShExecInfoW.hProcess, INFINITE) else r := WaitForSingleObject(ShExecInfoA.hProcess, INFINITE); end; finally if Win32IsUnicode then CloseHandle(ShExecInfoW.hProcess) else CloseHandle(ShExecInfoA.hProcess); end; end;
Delphi-Quellcode:
Danke für Eure Hilfe!
(Win32Platform = VER_PLATFORM_WIN32_NT)
Gruß winkel79 |
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Soweit alles ok. Ich hatte Dir auf die schnelle nur die Unicode-Variante geschrieben,da wir alles unter Windows ME sowieso nicht mehr unterstützen.
|
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Zitat:
Danke für Deine Hilfe, Sven! :dp: P.S.: Sollte man die ShellExecuteInfoW/A nicht besser vor der Nutzung nullen? Ich werd das mal sicherheitshalber noch einbauen: Edit1: Hab des jetzt einfach mal per
Delphi-Quellcode:
bzw.
ZeroMemory(@ShExecInfoW, SizeOf(ShExecInfoW));
Delphi-Quellcode:
noch schnell genullt. Sollte passen.
ZeroMemory(@ShExecInfoA, SizeOf(ShExecInfoA));
|
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
sehr schön, genau was ich gesucht habe! Was muss ich ändern damit nicht gewartet sondern nur ausgeführt wird?
|
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Zitat:
|
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Um das ganze Delphi 2009 tauglich zu machen, müssen folgende Typecasts von PChar nach PAnsiChar korrigiert werden:
Delphi-Quellcode:
Desweiteren kann man getrost WideExtractFileDir und WideFileExists durch die "regulären", seit D2009 unicode-tauglichen, Varianten ExtractFileDir und FileExists ersetzt werden.
ShExecInfoA.lpFile := PAnsiChar(AnsiString(Filename));
ShExecInfoA.lpParameters := PAnsiChar(AnsiString(Parameters)); ShExecInfoA.lpDirectory := PAnsiChar(AnsiString(ExtractFileDir(Filename))); Gruß, Stefan |
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
man könnte aber auch statt
Delphi-Quellcode:
einfach ein
ShExecInfoW: SHELLEXECUTEINFOW;
// oder ShExecInfoA: SHELLEXECUTEINFOA;
Delphi-Quellcode:
verwenden und bei PChar (ohne diese TypeCasts) bleiben :stupid:
ShExecInfo: SHELLEXECUTEINFO;
Statt Wide-/AnsiString dann natürlich auch nur String. ok, hatte das überlesen Zitat:
|
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Zitat:
|
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
jupp, hatte ich auch schon germerkt :angel2:
dann aber zumindestens noch 'ne Version, die mit dem arbeitet, was Delphi kann: (bis D2007 in ANSI und ab WinNT und D2009 in Unicode)
Delphi-Quellcode:
[edit] noch schnell ein {$IFDEF UNICODE} eingefügt ... wenn Delphi eh standardmäßig kein Unicode kann, braucht man den Code ja hierbei nicht. :angel2:
function ExecAndWait(Filename, Params: String;
WindowState: Word = SW_SHOWNORMAL): boolean; var {$IFDEF UNICODE} ShExecInfoW: SHELLEXECUTEINFOW; {$ENDIF} ShExecInfoA: SHELLEXECUTEINFOA; // MSDN: ShellExecuteEx, ShellExecuteInfo begin Result := false; if (Filename = '') or not FileExists(FileName) then exit; {$IFDEF UNICODE} if Win32IsUnicode then begin ShExecInfoW.Wnd := GetForegroundWindow; ShExecInfoW.cbSize := SizeOf(SHELLEXECUTEINFOW); ShExecInfoW.fMask := SEE_MASK_NOCLOSEPROCESS; ShExecInfoW.lpVerb := 'open'; ShExecInfoW.lpFile := PWideChar(WideString(Filename)); ShExecInfoW.lpParameters := PWideChar(WideString(Params)); ShExecInfoW.lpDirectory := PWideChar(WideString(ExtractFileDir(Filename))); ShExecInfoW.nShow := WindowState; Result := ShellExecuteExW(@ShExecInfoW); try if Result then WaitForSingleObject(ShExecInfoW.hProcess, INFINITE); finally CloseHandle(ShExecInfoW.hProcess); end; end else {$ENDIF} begin ShExecInfoA.Wnd := GetForegroundWindow; ShExecInfoA.cbSize := sizeof(SHELLEXECUTEINFOA); ShExecInfoA.fMask := SEE_MASK_NOCLOSEPROCESS; ShExecInfoA.lpVerb := 'open'; ShExecInfoA.lpFile := PAnsiChar(AnsiString(Filename)); ShExecInfoA.lpParameters := PAnsiChar(AnsiString(Params)); ShExecInfoA.lpDirectory := PAnsiChar(AnsiString(ExtractFileDir(Filename))); ShExecInfoA.nShow := WindowState; Result := ShellExecuteExA(@ShExecInfoA); try if Result then WaitForSingleObject(ShExecInfoA.hProcess, INFINITE); finally CloseHandle(ShExecInfoA.hProcess); end; end; end; |
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Nu isses perfekt! :thumb:
|
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Hallo,
wie kann ich die Parameter auslesen, die das auszuführende Programm nach Ausführung zurückgibt? Vielen Dank! :bounce2: |
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Was für Parameter meinst du? ExitCode? Handle?
|
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
ExitCode denke ich. Das externe Programm führt eine Berechnung durch und gibt eine 1 zurück wenn alles ok ist und eine andere Zahl, wenn ein Fehler auftritt (die Zahl entspricht dann dem auszugebenden Error Code). Nur falls eine 1 ausgegeben wird, soll das ursprüngliche Programm (von dem das externe Programm gestartet wurde) weiterlaufen. Wenn ich es richtig verstanden habe, dann bräuchte ich ein Handle doch nur, um aus dem startenden Programm bei dem externen Programm zur Laufzeit Veränderung etc. durchzuführen.
|
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Hast du das externe Programm selbst geschrieben? Wenn du wirklich den ExitCode meinst, ist das eine komische Verwendung. Normalerweise wird dein Programm mit ExitProcess(0) << ExitCode 0 beendet, um zu signalisieren, dass alles korrekt beendet wurde. ExitCode 1 bedeutet, dass ein Fehler aufgetreten ist.
Naja guck dir mal die ![]() |
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Hallo zusammen,
im Bezug auf die Antwort #12 hier im Thread and ein paar davor habe ich einige Fragen: Was findet man in "ShExecInfoA.hProcess" wieder? Ist es die Prozess-ID? Wenn ja: ist es die, die auch im Taskmanager angezeigt wird? Ich bekomme nämlich zwei unterschriedliche als Ergebnis. Wenn die gleich sein müssten: was könnte da falsch laufen? Wenn "ShExecInfoA.hProcess" nicht die Prozess-ID darstellt: wie komme ich an diese dran? |
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Wie das Präfix h andeutet, handelt es sich um ein Prozess-Handle. Ab Windows XP SP 1 kommst du mit
![]() |
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Danke für die Antwort!
Die Verwendung von GetProcessId scheint allerdings problematisch zu sein, da es diese Funktion eerst ab XP SP1 gibt. Habe noch das hier gefunden: GetWindowThreadProcessId, aber auch danach habe ich zwei unterschiedliche Werte. Gibt es denn keine andere Möglichkeit vom Handle zu ProzessID zu gelangen? |
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
NtQueryInformationProcess, aber ich bin nicht sicher, ob die ProcessID in Win2000 damit ermittelt werden kann.
|
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Zitat:
|
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Zitat:
|
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Danke für die schnelle Antwort.
|
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
eigenes Thema erstellt...
|
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Hallo,
muss das Handle auch irgendwann geschlossen werden, wenn ich den ![]()
Delphi-Quellcode:
Oder kümmert sich das System selber darum, wenn das Programm dann wirklich mal vom Benutzer geschlossen wird?
function ExecAndWait(Filename, Params: String;
WindowState: Word = SW_SHOWNORMAL): boolean; var {$IFDEF UNICODE} ShExecInfoW: SHELLEXECUTEINFOW; {$ENDIF} ShExecInfoA: SHELLEXECUTEINFOA; // MSDN: ShellExecuteEx, ShellExecuteInfo begin Result := false; if (Filename = '') or not FileExists(FileName) then exit; {$IFDEF UNICODE} if (Win32Platform = VER_PLATFORM_WIN32_NT) then begin ShExecInfoW.Wnd := GetForegroundWindow; ShExecInfoW.cbSize := SizeOf(SHELLEXECUTEINFOW); ShExecInfoW.fMask := SEE_MASK_NOCLOSEPROCESS; ShExecInfoW.lpVerb := 'open'; ShExecInfoW.lpFile := PWideChar(WideString(Filename)); ShExecInfoW.lpParameters := PWideChar(WideString(Params)); ShExecInfoW.lpDirectory := PWideChar(WideString(ExtractFileDir(Filename))); ShExecInfoW.nShow := WindowState; Result := ShellExecuteExW(@ShExecInfoW); end else {$ENDIF} begin ShExecInfoA.Wnd := GetForegroundWindow; ShExecInfoA.cbSize := sizeof(SHELLEXECUTEINFOA); ShExecInfoA.fMask := SEE_MASK_NOCLOSEPROCESS; ShExecInfoA.lpVerb := 'open'; ShExecInfoA.lpFile := PAnsiChar(AnsiString(Filename)); ShExecInfoA.lpParameters := PAnsiChar(AnsiString(Params)); ShExecInfoA.lpDirectory := PAnsiChar(AnsiString(ExtractFileDir(Filename))); ShExecInfoA.nShow := WindowState; Result := ShellExecuteExA(@ShExecInfoA); end; end; Gruss |
Re: Problem ShellExecute / CreateProcess + Wait mit Vista UA
Das Handle muss geschlossen werden. Aber wenn du es nicht brauchst, kannst du auch einfach auf das Flag SEE_MASK_NOCLOSEPROCESS verzichten, dann wird das bereits von ShellExecuteEx selbst gemacht.
|
AW: Problem ShellExecute / CreateProcess + Wait mit Vista UAC
Hi ihr Lieben,
wie kann ich nun mit obiger Funktion eine Datei starten? Eingebunden hab ich sie noch bekommen, wenngleich ich aufgrund des Unicode's da Sorgen hatte (nutze Delphi XE) und mir nicht klar ist, welche uses ich ggf. noch nutzen muss, damit der die Funktionen "WideFileExists" z.B. findet. Ihr bemerkt schon, ich starte gerade erst mit dem ganzen Kram. Jedenfalls führte z.B. folgende Zeile natürlich zu keinem Erfolg, ich begreife einfach den Funktionsaufruf nicht, den ich durchführen muss. Welche Parameter sind zu übergeben, damit das klappt? Ist der Deklaration werde ich leider nicht hinreichend schlau und bitte um Hilfe dazu. Umgebaut hatte ich das nun zu:
Delphi-Quellcode:
und versuche nun via
function ExecAndWait(Filename, Params: Widestring;
WindowState: word = SW_SHOWNORMAL): boolean; var ShExecInfoA: SHELLEXECUTEINFOA; r : Cardinal; //http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/shellexecuteex.asp //http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/structures/shellexecuteinfo.asp begin Result := false; if Filename = '' then exit; if not FileExists(FileName) then begin // Log (true,P_ERROR,WideFormat(_('Kann die angegebene Datei nicht finden: %s'),[FileName])); exit; end; ShExecInfoA.Wnd := GetForegroundWindow; ShExecInfoA.cbSize := sizeof(SHELLEXECUTEINFOA); ShExecInfoA.fMask := SEE_MASK_NOCLOSEPROCESS; ShExecInfoA.lpVerb := 'open'; ShExecInfoA.lpFile := PAnsiChar(AnsiString(Filename)); ShExecInfoA.lpParameters := PAnsiChar(AnsiString(Params)); ShExecInfoA.lpDirectory := PAnsiChar(AnsiString(ExtractFileDir(Filename))); ShExecInfoA.nShow := WindowState; Result := ShellExecuteExA(@ShExecInfoA); try if Result then begin r := WaitForSingleObject(ShExecInfoA.hProcess, INFINITE); end; finally CloseHandle(ShExecInfoA.hProcess); end; end;
Delphi-Quellcode:
ExecandWait('Ping.EXE','>> 192.168.212.253 >> testping.txt');
Es auch zu nutzen. Ich weiß, der Aufruf ist falsch, aber ich brauche hier Starthilfe, bitte. Noch toller wäre es ja, 'n Ping direk tzu nutzen, aber nach 4-6 Stunden probieren aller möglichen Sachen, die durch die bank alle nicht klappten, wollte ich es eben über das doofe ping.exe lösen. LG Shyran |
AW: Problem ShellExecute / CreateProcess + Wait mit Vista UAC
Du vermischst Ansi (SHELLEXECUTEINFOA) mit Unicode. Hast Du es einmal mit der Version aus
![]() P.S.: Willkommen in der DP :dp: [edit] Nachtrag: FileExists müsste in SysUtils stehen [/edit] |
AW: Problem ShellExecute / CreateProcess + Wait mit Vista UAC
Hi,
danke für das Willkommen! *freu* Und: Ja, habe ich probiert. Daher kam mein Einwand, dass er die "Wide"-Ausdrücke usw. beim Kompilieren nicht finden konnte und daher auch mein Umbau dazu. Nochmal deutlich: Die Funktion "WideFileExists" kannte er nicht. In den uses stehen die SysUtils drin, gerade eben nachgeschaut. Noch eine Idee? Grüßlis |
AW: Problem ShellExecute / CreateProcess + Wait mit Vista UAC
Zitat:
|
AW: Problem ShellExecute / CreateProcess + Wait mit Vista UAC
Huhu!
Wenn Du magst, schau mal im ![]() |
AW: Problem ShellExecute / CreateProcess + Wait mit Vista UAC
Tatsache! Lass das "Wide" einfach mal weg (die Funktion ist wohl selbst geschrieben), funktioniert es dann?
[edit] Nach etwas suchmaschinen: das scheint eine Funktion aus den TNTSysUtils zu sein, die hat ja längst nicht jeder. [/edit] |
AW: Problem ShellExecute / CreateProcess + Wait mit Vista UAC
In XE ist ales schon "Wide" (Unicode)
FileExists ist mit UnicodeString (also String, seit Delphi 2009) deklariert. #Und dann gibt es diese Typen und WinAPIs einmal mit A (ANSI), W (Wide aka Unicode) und ohne, wo es bis D2007 nach ANSI und ab D2009 nach Wide/Unicode gemappt (weitergeleitet) wird. Es gibt noch gesonderste Units, in welchen z.B. zusärtliche Wide- oder (seit D2009) ANSI-Versionen verschiedenster Funktionen rumliegen. So gibt es seit D2009 z.B. die Unit AnsiStrings, wo man ein StringReplace für ANSI findet, da das "normale" StringReplace ja nun für Unicode ausgelegt ist. PS: Um dich zu verwirren: AnsiUpperCase ist (standardmäßig) Unicode :stupid: (Codegear/Emba dachten es wäre so "leichter" für uns) |
AW: Problem ShellExecute / CreateProcess + Wait mit Vista UAC
Wenn dem so ist, wieso klappt das dann mit "Wide"... bei mir nicht? Ich nutze hier ja Delphi XE. Habe ich eine Einstellung übersehen?
@DeddyH: Einfach weglassen funktionierte nicht, daher hatte ich das ja "umgebaut", siehe mein Eröffnungsbeitrag hier :-) |
AW: Problem ShellExecute / CreateProcess + Wait mit Vista UAC
Welche Delphi-Version setzt Du denn ein?
|
AW: Problem ShellExecute / CreateProcess + Wait mit Vista UAC
FileExists ist Unicode (seit D2009) ... es heißt aber deswegen nicht gleich so :wink:
> XE (ist im Beitrag versteckt) PS: Du könnstes das in deinem Forenprofil mit angeben. |
AW: Problem ShellExecute / CreateProcess + Wait mit Vista UAC
Na also, deshalb frage ich mich ja, was da nicht funktioniert bzw. wieso.
|
AW: Problem ShellExecute / CreateProcess + Wait mit Vista UAC
Na so versteckt war's auch nicht, in meinen 3 Beiträgen enthielten zwei den Hinweis, welche Version (Delphi XE Prof.)ich nutze. Und wieso es nicht funktioniert, kann ich leider nicht beantworten. nach der Frühstückspause schmeiss ich den Code nochmal hinein und werde ein wenig C&P Action der Fehlermeldungen hier bringen.
Dennoch schonmal herzlichen Dank für die dargebrachte Hilfe bis hierhin. PS: Version im Profil eingestellt, guter Hinweis! |
AW: Problem ShellExecute / CreateProcess + Wait mit Vista UAC
:oops: Sry, das habe ich komplett übersehen, ich passe mich wohl langsam an Frank an :tongue:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 18:58 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz