The perfect implementation of external programs (Delphi)

xiaoxiao2021-03-05  22

Recently, some features need to call other software to complete, and that software only has executable files, there is no source code, fortunately, it is not difficult, just need to start after my program Open the software, when you need it, set some text to one of the text boxes, and then click on a button.

Speaking here, I believe that you also have some initial imagination of this feature, yes, the basic idea is:

1) Call CREATEPROCESS () open the target program.

2) Use FindWindow () to find the window handle of the target program.

3) Find the handle of the text box, and the messageId of the button, set the text with the SendMessage () method, and trigger the event.

Ok, this is really simple, but when I realize it, I found that the result of this is: When my program starts and open the target program, its Splash window, and the main window will be displayed, even if When I use FindWindow () to find the main window handle, call SendMessage (WindowHandle, Sw_hide) to hide the window, or a momentary window is displayed, this effect is really pursuing perfect, I can't bear to see.

So how do you solve this problem, first of all, I will find a method in CreateProcess (), but unfortunately, it only has a parameter to set the default display mode of the window, but once this window resets the display method, it has no effect. . . . Continue to find a document, then I see a parameter tStartupInfo in CreateProcess () in tstartupinfo, according to MSDN, if the pointer is null, then the new Process will start on the current Desktop, and if it is assigned After the name of Desktop, Process will start on the designated Desktop, Well, it seems good, it will start from it:

1) First, create a virtual Desktop,

Const DesktopName = 'MyDesk'; fdesktop: = CreateDesktop (DesktopName, NIL, NIL, 0, Generic_all, nil); Multiple Desktop can be created in Windows, you can use SwitchDesktop () to switch which Desktop is displayed, before Windows simulates the form of Linux, which can be switched in multiple virtual Desktop, in fact, the program is also implemented for Windows itself, the virtual Desktop function of the Windows itself is implemented, and the startup screen of Windows, and the screensaver screen are also virtual Desktop. Implemented, well, there are not many introductions this area, if you are interested, you can see more details in MSDN:

Http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/enumdesktops.asp

2) When CreateProcess, the specified program runs on my newly generated Desktop: Var StartInfo: TStartupinfo; Fillcha (StartInfo, SizeOf (StartInfo), 0); startInfo.cb: = sizeof (startInfo); startInfo.lpdesktop: = PChar (DesktopName); // specify the name of the Desktop to StartInfo.wShowWindow: = SW_HIDE; StartInfo.dwFlags: = STARTF_USESHOWWINDOW; StartInfo.hStdError: = 0; StartInfo.hStdInput: = 0; StartInfo.hStdOutput: = 0; if not CreateProcess (PChar (FileName), nil, nil, nil, true, CREATE_NEW_CONSOLE HIGH_PRIORITY_CLASS, nil, PChar (ExtractFilePath (FilePath)), StartInfo, FProceInfo) then begin MessageBox (Application.Handle, 'Error when init voice (5). ', Pchar (Application.Title), MB_ICONWARNING; EXIT;

3) Use FindWindow to find the main window of the program

I started to write this code directly:

For i: = 0 TO 60 Do Begin // Wait 30 Seconds for Open THE Main WINDOW WINDOWHANDLE: = FINDWINDOW (NIL, 'WindowCaption'); if WINDOWHANDE <> 0 THEN BEGIN BREAK; END; SLEEP (500); end; but , Practice prove, this is not found in the current Desktop, what should I do?

The answer is that you can use the setthreadDesktop () function, this function can set the desktop where the current Thread work is located, so I added another sentence before the above code:

If not setthreadDesktop (fdesktop) THEN BEGIN EXIT; but, after the program runs, the function returns FALSE, and the method call failed, then look at MSDN carefully, and found that there is such a sentence:

The SetThreadDesktop Function Will Fail if The Calling Thread Has Any Windows or Hooks on Its Current Desktop (Unless The HDesktop Parameter Is A Handle To The Current Desktop).

Oh, there is a need for any UI in the thread that you need to switch Desktop, and I am calling this method in the main thread of the program. Of course, I will fail, I know this, I just need one "Clean" thread let it bind to the new Desktop, let it find the WindowHandle I have to find with the FindWindow () method, can't you, so, this step is needed to use a thread, thread code is as follows: TFindWindowThread = class (TThread) private FDesktop: THandle; FWindowHandle: THandle; protected procedure Execute (); override; public constructor Create (ACreateSuspended: Boolean; const ADesktop: THandle); reintroduce; property WindowHandle: THandle read FWindowHandle; end ;

{TFindWindowThread} procedure TFindWindowThread.Execute (); var I: Integer; begin // make the current thread find window on the new desktop if not SetThreadDesktop (FDesktop) then begin exit; end; for I:! = 0 to 60 do begin // wait 30 seconds for open the main window FWindowHandle: = FindWindow (nil, PChar ( 'WindowCaption')); if FWindowHandle <> 0 then begin break; end; Sleep (500); end; end; constructor TFindWindowThread.Create ( Acreatesuspended: boolean; const adsktop: thandle; begin inherited create (acreateSuspend); fdesktop: = adsktop;

And this becomes the main program code: FindWindowThread: = TFindWindowThread.Create (false, FDesktop); try FindWindowThread.WaitFor; FMainWindowHandle: = FindWindowThread.WindowHandle; finally FindWindowThread.Free; end; if FMainWindowHandle = 0 then begin MessageBox (Application .Handle, 'Error When Init Voice (6).', Pchar (Application.title), MB_ICONWARNING;

Oh, success, so it can be found smoothly.

4) Finally, use this main window handle to find the editbox Handle inside, if this: feditWindow: = FindWindowEx (FmainWindowHandle, 0, Pchar ('Edit'), NIL); I have specified this text box here. Classname, this name can be obtained with SPY . The initialization work is over, if it is smooth, the program is truly running in the background. So function calls, or the same as the general approach:

if (FMainWindowHandle = 0) or (FEditWindow = 0) then begin exit; end; SendMessage (FEditWindow, WM_SETTEXT, 0, LongInt (@AText [1])); SendMessage (FMainWindowHandle, WM_COMMAND, $ 8012, $ 0); where $ 8012 this The number is also the resource ID obtained with SPY .

Finally, don't forget to close the program, and release the virtual Desktop: if fProceinfo.hprocess <> 0 THEN BEGINFO.HPROCESS <> 0 dam, 0); end; if fdesktop <> 0 the begin closedesktop (fdesktop);

Ok, this is almost perfectly realizing the function of the back-stage calling program. It will be completely transparent to the final customer. The customer does not feel that there is still another program in the background. Is it very cool, so many of the programs of others can be used directly (of course, they have to travel on the basis of copyright).

There is any improvement, or exchange, you can Mail to: Tonyki [AT] citiz.net

转载请注明原文地址:https://www.9cbs.com/read-33290.html

New Post(0)