Interaktion mit anderen Programmen
Ein anderes Programm aus der eigenen Anwendung starten
Sowohl in der Win16-API, als auch in der Win32-API existiert für den Aufruf anderer Programme die Funktion ShellExecute. Die Win16-API stellt dafür außerdem die Funktion WinExec bereit, deren Aufruf etwas einfacher ist, die aber nur ausführbare Programme starten kann und keine verknüpften Dokumente:
winexec('C:\Program.exe', SW_SHOWNORMAL); |
function OpenFile(FileName:string):integer; var FName, ExeName : PChar; begin ExeName:=StrAlloc(255); FName:=StrAlloc(255); StrPCopy(FName, FileName); if FindExecutable(FName, nil, ExeName)<32 then begin FileName:=StrPas(ExeName)+' '+StrPas(FName); StrPCopy(ExeName, FileName); Result:=WinExec(ExeName, SW_ShowNormal); end else Result:=0; StrDispose(FName); StrDispose(ExeName); end; |
ShellExecute(MainForm.Handle, 'open', <Programmdatei>, <Aufrufparameter>, '', SW_SHOWNORMAL); |
ShellExecute(MainForm.Handle, 'print', <Dokumentdatei>, '', '', SW_SHOWNORMAL); |
- Mail und Internet-Client unter Win 95 aufrufen
Mail : Einfach als Programmaufruf "mailto:Name@domain" angeben, also:
ShellExecute(MainForm.Handle, 'open', 'mailto:Name@domain', nil, nil, SW_SHOWNORMAL); {oder mit Betreff und Mailtext:} ShellExecute(MainForm.Handle, 'open', 'mailto:Name@domain'+'?subject=Gruesse&body=Hallo Welt!', nil, nil, SW_SHOWNORMAL); |
Internet-Client:
var URL:string; [...] URL:='http://www.pics-software.de'; ShellExecute(MainForm.Handle, 'open', PChar(URL), nil, nil, SW_SHOWNORMAL); |
ShellExecute returns a value greater than 32 if successful, or an error value that is less than or equal to 32 otherwise. The return value is cast as an HINSTANCE for backward compatibility with 16-bit Microsoft Windows applications. It is not a true HINSTANCE, however. The only thing that can be done with the returned HINSTANCE is to cast it to an integer and compare it with the value 32 or one of the error codes below. [MSDN April 1999]
- Start eines Programms und Warten auf dessen Ende
Die Funktion ExecAndWait funktioniert sowohl in Delphi1, als auch in allen 32Bit-Delphi-Versionen. Hier wird im 16Bit-Teil WinExec und GetModuleUsage und im 32Bit-Teil CreateProcess und WaitForSingleObject verwendet:
uses WinTypes, WinProcs, SysUtils; { WindowState is one of the SW_xxx constants. Look up ShowWindow in the API help for a list.} function ExecAndWait(const Filename, Params: string; WindowState: word): boolean; {$IFDEF WIN32} var SUInfo: TStartupInfo; ProcInfo: TProcessInformation; CmdLine: string; begin { Enclose filename in quotes to take care of long filenames with spaces. } CmdLine := '"' + Filename + '" ' + Params; FillChar(SUInfo, SizeOf(SUInfo), #0); with SUInfo do begin cb := SizeOf(SUInfo); dwFlags := STARTF_USESHOWWINDOW; wShowWindow := WindowState; end; Result := CreateProcess(NIL, PChar(CmdLine), NIL, NIL, FALSE, CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, NIL, PChar(ExtractFilePath(Filename)), SUInfo, ProcInfo); { Wait for it to finish. } if Result then WaitForSingleObject(ProcInfo.hProcess, INFINITE); {$ELSE} var InstanceID : THandle; Buff: array[0..255] of char; begin StrPCopy(Buff, Filename + ' ' + Params); InstanceID := WinExec(Buff, WindowState); if InstanceID < 32 then { a value less than 32 indicates an Exec error } Result := FALSE else begin Result := TRUE; repeat Application.ProcessMessages; until Application.Terminated or (GetModuleUsage(InstanceID) = 0); end; {$ENDIF} end; |
Ein anderes Programm aus der eigenen Anwendung beenden
Ein Beispiel, wie man eine andere Anwendung aus dem eigenen Programm heraus beendet, findet man in der Unit CloseApp. Man braucht dazu das Instanzen-Handle der zu beendenden Anwendung.
Konsolen-Ausgaben aus DOS-Fenstern in eigenen Programmen einlesen
Mein Programm soll z.B. tasm32 aufrufen. Jetzt kann es sein, daß tasm32 Fehler in der Console anzeigt. Wie komme ich an die ran? Die Ausgabe in eine Datei umleiten wäre gut, funktioniert aber nicht, wenn ich das Umleitungszeichen mit als Parameter übergebe. Aber wie gehts ?
Bevor Du tasm32 startest, erzeuge einfach die Datei(en), in die die Ausgabe umgelenkt werden soll. (Das kann übrigens auch eine (Named)Pipe sein.) Diese(s) Handle(s) sollte(n) vererbbar sein.
Dann gibst Du in der StartupInfo Struktur beim CreateProcess diese(s) Handle an. Alle Handles, die nicht umgelenkt werden sollen, werden mit GetStdHandle() belegt. Dann noch in Flags USE_STD_HANDLES setzen und CreateProcess() aufrufen. Und ab geht die Post...
Informationen zur Benutzung von CreateProcess findet man unter dem Thema Ein anderes Programm aus der eigenen Anwendung starten.
Den Namen der Programmdatei (*.exe) einer Applikation aus einem Fensterhandle ermitteln
Kann mir vielleicht jemand sagen, wie ich zu einem Fensterhandle (oder dem entsprechenem Prozeß) den Dateinamen der ausgeführten EXE-Datei bekomme?
Leider gehen Windows 9x/2000 hier einen völlig anderen Weg als Windows NT 4.0. Erstgenannte verwenden dafür den "TProcessEntry32"-Typen aus der Unit "TlHelp32". Die Funktionen aus dieser Unit sind aber nur in der Kernel32-DLL von Windows 9x/2000 definiert. Hier ein Beispiel von Christian Kästner:
uses TlHelp32, ShellApi; var ProcID : DWord; SSHandle : THandle; Continue : boolean; ProcEntry : TProcessEntry32; begin GetWindowThreadProcessID(FindWindow('Fenstertitel',nil), @ProcID); SSHandle := CreateToolhelp32Snapshot(TH32CS_SnapProcess, 0); ProcEntry.dwSize := Sizeof(ProcEntry); FileList.Items.Clear; // gehe alle Prozesse durch (m.H. der TlHelp32 - Unit) Continue := Process32First(SSHandle, ProcEntry); while Continue do begin // gesuchter Prozess (ProcID) in der Liste aller Prozesse gefunden? if ProcEntry.th32ProcessID = ProcID then FileList.Items.Add(ProcEntry.szExeFile); Continue := Process32Next(SSHandle, ProcEntry); end; CloseHandle(SSHandle); end; |
interface type TPIDlist = array[0..1000] of DWORD; function EnumProcesses (pidList : PInteger; cb : Integer; var cbNeeded : Integer): boolean; stdcall; function GetModuleBaseName (hProcess : THandle; module : HInst; BaseName : Pchar; size : Integer) : Integer; stdcall; implementation const psapidll = 'psapi.dll'; var PID : TPIDlist; function EnumProcesses; external psapidll; function GetModuleBaseName; external psapidll name 'GetModuleBaseNameA'; function GetProcessList(var PIDlist : TPIDlist): integer; var cb, cbNeeded : integer; begin cbNeeded := 0; cb := SizeOf(PIDlist); FillChar(PIDlist, cb, 0); if not EnumProcesses(@PIDlist, cb, cbNeeded) then cbNeeded := 0 else cbNeeded := cbNeeded div SizeOf(DWord); Result := cbNeeded; end; procedure GetProcessNames; var numProcesses : integer; ProcHandle : THandle; ExeName : string; begin numProcesses := GetProcessList(PIDlist); for i := 0 to numProcesses-1 do begin ProcHandle := OpenProcess(PROCESS_QUERY_INFORMATION, False, PIDlist[i]); if ProcHandle <> 0 then begin try if GetModuleBaseName(ProcHandle, 0, @szName, sizeof (szName)) > 0 then ExeName := szName else ExeName := 'System'; finally CloseHandle(ProcHandle) end; end else if PIDlist[i] = 0 then ExeName := 'System idle'; end; |
Wie bekommt man Zugriff auf alle aktuell geöffneten Fenster?
Diese Unit demonstriert die API-Funktionen
Sie können auch ein komplettes Beispielprojekt (4 kB) mit dieser Unit vom Server laden. Dafür habe ich lange in den Tiefen der API graben müssen und bin mächtig stolz auf das Ergebnis. Also bitte bei Weitergabe des Codes die Autoren angeben!
Den Anzeige-Zustand und die Position eines Fensters ermittelt man mit der API-Funktion GetWindowPlacement. Die folgende Funktion GetWindowState gibt den Anzeigezustand des Fensters mit dem Handle Wnd zurück:
function GetWindowState(Wnd:HWnd):integer; var WPlacement : PWINDOWPLACEMENT; begin GetMem(WPlacement,SizeOf(TWINDOWPLACEMENT)); WPlacement^.Length:=SizeOf(TWINDOWPLACEMENT); if GetWindowPlacement(Wnd,WPlacement) then Result:=WPlacement^.showCmd else Result:=-1; FreeMem(WPlacement); end; |
Wie erhält man das Handle des Controls, das den Eingabefokus hat?
Für Fenster in einem Prozess des eigenen Programms hilft ein einfacher Aufruf der API-Funktion "GetFocus". Für Fenster in fremden Prozessen muß man zuerst eine Beziehung zwischen dem eigenen Prozess und dem Prozess, der das aktive Control enthält, herstellen. Das erledigt diese Funktion der man das Handle des aktiven Top-Level-Fensters übergeben muß. Das Handle des aktiven Top-Level-Fensters erhält man z.B. mit "GetForegroundWindow".
function GetFocussedWindow(ParentWnd:HWnd):HWnd; var OtherThreadID,Buffer : DWord; begin OtherThreadID:=GetWindowThreadProcessID(ParentWnd, @Buffer); if AttachThreadInput(GetCurrentThreadID, OtherThreadID, true) then begin Result:=GetFocus; AttachThreadInput(GetCurrentThreadID, OtherThreadID, false); end else Result:=0; end; |
Wie kann man einen Tastendruck an ein anderes Fenster schicken?
Dazu benötigt man das Handle des Fensters und schickt an dieses dann zwei Nachrichten: WM_KeyDown und WM_KeyUp. Im folgenden Beispiel wird im Fenster "Form1" das Menü mit der Taste F10 aktiviert:
var W : HWnd; begin W := FindWindow(NIL,'Form1'); //"Form1" heißt hier das Fenster mit dem Menü if W <> 0 then begin PostMessage(W, wm_KeyDown, vk_F10,0); //Simuliert F10 PostMessage(W, wm_KeyUp, vk_F10,0); end; ... end; |
Keybd_Event(vk_Shift,0,0,0); Keybd_Event(vk_Tab,0,0,0); Keybd_Event(vk_Tab,0,KEYEVENTF_KEYUP,0); Keybd_Event(vk_Shift,0,KEYEVENTF_KEYUP,0); |