0

我一直在寻找一种方法来打开通过 Delphi 应用程序及其适当的应用程序保存到我的计算机上的文件。该文件存储在 SQL 数据库的 Varbinary 字段中,并被加载到内存流中,然后通过 TMemoryStream 的 SavetoFile 方法保存。我想要完成的是在不知道该应用程序可执行文件的文件路径的情况下在其适当的应用程序中打开保存的文件。我使用 ShellExecuteEx 取得了一些成功,但某些应用程序不返回 HProcess(例如 Windows Live 照片库),所以我不能(或至少不知道如何)等待应用程序关闭之前当没有返回句柄时继续前进。有没有办法确保我在调用 ShellExecuteEx 时收到句柄?如果这不是最好的方法,我应该怎么做?

我只需要知道外部应用程序的状态,因为我计划在文件关闭后删除它,我只需要编写它,因为我相当确定我无法将存储在 SQL 表中的文件加载到内存中(顺便说一下MemoryStream、FileStream 等)并直接从我的 Delphi 应用程序启动其相关程序。(我已经单独问过了。

4

2 回答 2

4

正如您在上一个问题中所了解的那样,试图检测显示过程已关闭是脆弱且充满问题的。很多时候,很难找到用于查看文件的进程,即使可以,也不能确定关闭文件的视图是否会关闭进程。该过程可用于查看用户打开的其他文件。我认为你应该从中吸取的教训是,系统不希望你做你想做的事。

那么,解决问题的更好方法是什么?我认为您能做的最好的事情是在临时目录中创建临时文件,而不是在用户完成后尝试删除它们。你可以:

  • 记住你创建的文件,当你创建时,比如第 21 个文件,删除你创建的第一个文件。然后在创建 22 时删除 2,依此类推。
  • 或者,在启动时删除所有临时文件。这将从以前的会话中删除文件。
  • 或者运行一个单独的整理线程,例如每十分钟删除一个多小时前创建的文件。

你明白了。关键是,检测查看器何时完成文件是一个棘手的问题,完全通用。所以你需要创造性地思考。在路障周围寻找不同的方式。

于 2013-11-01T20:06:40.080 回答
1

她的片段来自我用于类似目的的单元。这些年来,我在网上的某个地方发现了这些功能,所以我不相信也不做任何承诺。

我个人使用 WaitExec() 函数在 Acrobat 中启动 pdf(从数据库中检索)进行编辑,然后在完成后将其重新保存到我们的数据库中。

我在其他时候也使用过另外两个函数,所以我知道它们都在某种程度上工作,但我认为 WaitExec() 在交互模式下效果最好,而 Launch() 在线程或非交互模式下效果更好.

IsFileInUse 函数可以告诉您您创建的文件是否正在被任何其他进程使用,并且可能也是一个可行的选项。

uses SysUtils, Windows, ShellAPI, Forms, Registry, Classes, Messages, Printers,
    PSAPI, TlHelp32, SHFolder;



function IsFileInUse(fName: string): boolean;
var
    HFileRes: HFILE;
begin
    Result := False;
    if not FileExists(fName) then
        Exit;
    HFileRes := CreateFile(pchar(fName), GENERIC_READ or GENERIC_WRITE,
        0 {this is the trick!}, nil, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL, 0);
    Result := (HFileRes = INVALID_HANDLE_VALUE);
    if not Result then
        CloseHandle(HFileRes);
end;

function Launch(sCommandLine: string; bWait: Boolean; AppHandle: HWND): Boolean;
var
    SEI: TShellExecuteInfo;
    Mask: Longint;
begin
    Mask := SEE_MASK_NOCLOSEPROCESS;
    FillChar(SEI, Sizeof(SEI), #0);
    SEI.cbsize := Sizeof(SEI);
    SEI.wnd := AppHandle;
    SEI.fmask := Mask;

    //if FExeStyleString<>'' then SEI.LPVERB:=pchar(FExeStyleString);
    SEI.LPFile := pchar(sCommandline);

    //SEI.LPParameters := pchar(FExeParameters);
    //SEI.LPDirectory  := pchar(FExepath);
    SEI.nshow := SW_SHOWNORMAL; // SW_SHOWMINIMIZED, SW_SHOWMAXIMIZED
    ShellexecuteEx(@SEI);

    if bWait then
        WaitforSingleObject(SEI.hProcess, INFINITE);
    Result := True;
end;

function WaitExec(const CmdLine:AnsiString;const DisplayMode:Integer):Integer;
  {Execute an app, wait for it to terminate then return exit code.  Returns -1
   if execution fails. DisplayMode is usually either sw_ShowNormal or sw_Hide.}
 var
   S:TStartupInfo;
   P:TProcessInformation;
   M:TMsg;
   R:DWord;
 begin
   FillChar(P,SizeOf(P),#0);
   FillChar(S,Sizeof(S),#0);
   S.cb := Sizeof(S);
   S.dwFlags := STARTF_USESHOWWINDOW;
   S.wShowWindow := DisplayMode;
   if not CreateProcess(nil,
     PChar(CmdLine),                { pointer to command line string }
     nil,                           { pointer to process security attributes }
     nil,                           { pointer to thread security attributes }
     False,                         { handle inheritance flag }
     CREATE_NEW_CONSOLE or          { creation flags }
     NORMAL_PRIORITY_CLASS,
     nil,                           { pointer to new environment block }
     nil,                           { pointer to current directory name }
     S,                             { pointer to STARTUPINFO }
     P)                             { pointer to PROCESS_INF }
   then begin
    ShowMessage('Create Process failed. Save this message for IT: ' + CmdLine);
    Result:=-1
   end
   else begin
//     WaitforSingleObject(P.hProcess,INFINITE);
//   The following replacement better satisfies DDE requirements
     repeat
       R := MsgWaitForMultipleObjects(1, // One event to wait for
       P.hProcess, // The array of events
       FALSE, // Wait for 1 event
       INFINITE, // Timeout value
       QS_ALLINPUT); // Any message wakes up
       if R>WAIT_OBJECT_0 then begin
         M.Message := 0;
         while PeekMessage(M,0,0,0,PM_REMOVE) do begin
           TranslateMessage(M);
           DispatchMessage(M);
         end
       end;

     until R=WAIT_OBJECT_0;

     // put value into Result.... non zero = success
     GetExitCodeProcess(P.hProcess,DWord(Result));

     CloseHandle(P.hProcess);
     CloseHandle(P.hThread);
     P.hProcess:=0;
     P.hThread:=0;
   end;
end;
于 2013-11-01T20:49:07.913 回答