CreateProcess時不顯示或者不建立窗口 (或用虛擬桌面實現後臺調用外部程序)

從網上搜了不少總結了三種方法,但願對你們有用:
網上常見的兩種方法(詳細說明參考:http://blog.163.com/zhucongdzkd@126/blog/static/1399971932010780309154/)。

【方法一:】
將 CreateProcess()的參數dwCreationFlags指定爲CREATE_NO_WINDOW,即以不建立窗口方式建立DOS進程。
【參考代碼:】
if (!CreateProcess(NULL, szCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))

【方法二:】
指定STARTUPINFO結構中WORD wShowWindow爲SW_HIDE(可是必定要有這一句: si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESHOWWINDOW; ),即以不顯示窗口方式建立DOS進程。
【參考代碼:】
STARTUPINFO si;
PROCESS_INFORMATION pi;
::ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
::ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(NULL, szCommand, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))

【方法三:】
將這個DOS窗口放到另一個桌面上,實現隱藏。
【參考代碼:】
si.lpDesktop="NewDesktop";
if (!CreateProcess(NULL, szCommand, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi))
方法三的詳細原理:
參考http://blog.163.com/madengyao_super/blog/static/2859822020093249493150/ 。



===============================================================================
【用虛擬桌面實現後臺調用外部程序】
最近須要實現一個無線通訊的功能,X他XX的,該死的硬件廠商居然不提供接口函數,只提供一個EXE可執行文件-_-!
這樣就須要我在程序裏調用他的這個EXE可執行文件。
調用EXE文件,能夠用WINEXEC()、SHELLEXECUTE()和CreateProcess()等函數來實現,我這裏就用CreateProcess()來調用。
可是一個軟件,兩個EXE文件,這叫什麼??實在沒辦法,我想在打開的時候不讓用戶看到這個執行文件:首先調用FINDWINDOW來查找窗口的句柄,以後再用SendMessage()來隱藏窗口,可是仍是會有一瞬主窗口被顯示出來的,或許你會說我BT吧,可是我實在是不忍心看到……
那麼怎麼解決這個問題呢,首先我固然在CreateProcess()上面尋找方法,惋惜,它只有一個參數能夠設置窗口的默認顯示方式,可是一旦這個窗口本身重設了顯示方式,它就沒有任何做用了。
繼續查找文檔,這時我看到CreateProcess()的一個參數TStartupInfo中有 lpDesktop這麼一個屬性,按照MSDN的說法,若是該指針爲NULL,那麼新建的Process將在當前Desktop上啓動,而若是對其賦了一個Desktop的名稱後,Process將在指定的Desktop上啓動,恩,看來不錯,就從它入手了;
首先,創建一個虛擬的Desktop。
const
DesktopName: PChar = 'NewDesktop';
FDesktop:= CreateDesktop(DesktopName, nil, nil, 0, GENERIC_ALL, nil);
而後,在CreateProcess的時候,指定程序在我新生成的Desktop上運行:
var
SI: TStartupInfo;
begin
FillChar(SI, SizeOf(SI), 0);
SI.cb:= SizeOf(SI);
SI.lpDesktop:= DesktopName;
SI.wShowWindow:= SW_HIDE;
SI.dwFlags:= STARTF_USESHOWWINDOW;
SI.hStdError:= 0;
SI.hStdInput:= 0;
SI.hStdOutput:= 0;
  if not CreateProcess(PChar('……'), nil, nil, nil, True, CREATE_NEW_CONSOLE + HIGH_PRIORITY_CLASS, nil,
                       PChar('……'), SI, FProceInfo) then
    begin
      Application.MessageBox('Error', 'Error', $10);
      Exit;
    end;
end;

再用FindWindow去找程序的主窗口.
開始我直接寫下了這樣的代碼:
WindowHandle:= FindWindow(nil, '……');

可是,這樣是找不到不在當前Desktop中的Window的,那怎麼辦呢?
這個時候,我忽然看到一位同事在上班時間偷偷打遊戲,我問他:「你不怕被抓到??」
他說:「給你看一個工具!」
原來是一個叫「玩遊戲一鍵隱藏」的小工具,仔細想一想,他應該是利用各桌面之間的切換來達到這種效果的,因而又開始查看MSDN,終於看到能夠用SetThreadDesktop()函數,這個函數能夠設置當前Thread工做所在的Desktop,因而我在以上代碼前又加了一句:
if not SetThreadDesktop(FDesktop) then
    begin
      Exit;
    end;
可是,程序運行後,該函數卻返回了false,說明方法調用失敗了,再仔細看MSDN,發現有這麼一句話:
[color=red]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).[/color]

對不起,個人E文水平實在有夠嗆-_-!!
具體是什麼意思我就不太清楚了,PASSION幫我翻譯的時候,我也沒怎麼記得,只記得是要切換Desktop的線程要「乾淨」。
很差意思,我對這個「乾淨」的理解就是一個新的線城,因而抱着試一試的心情,我寫下了:
  TFindWindowThread = class(TThread)
  private
    FDesktop, FWindowHandle: THandle;
  protected
    procedure Execute(); override;
  public
    constructor Create(Suspended: Boolean; const ADesktop: THandle); reintroduce;
    property WindowHandle: THandle read FWindowHandle;
  end;

而主程序中的代碼變成這樣:
FindWindowThread:= TFindWindowThread.Create(False, FDesktop);
  try
    FindWindowThread.WaitFor;
    WindowHandle:= FindWindowThread.WindowHandle;
  finally
    FindWindowThread.Free;
  end;
  if WindowHandle = 0 then
    begin
      Application.MessageBox('Error', 'Error', $10);
      Exit;
    end;

呵呵,成功,這樣果真能夠順利的找到窗口Handle了。
好了,這樣就幾乎完美的實現了一個後臺調用程序的功能,它對最終客戶來講將是徹底透明的,客戶根本感受不到後臺還有另外一個程序在工做。
 windows

相關文章
相關標籤/搜索