在我开始使用程序之后,如何等待删除文件?

时间:2013-11-01 19:44:50

标签: delphi winapi file-io

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

我只需知道外部应用程序的状态,因为我计划在文件关闭后删除它,我只需要编写它,因为我很确定我无法将存储在SQL表中的文件加载到内存中(通过MemoryStream,FileStream等)并直接从我的Delphi应用程序启动其相关程序。 (I've asked about that separately.

2 个答案:

答案 0 :(得分:4)

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

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

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

你明白了。关键在于,在完全普遍的情况下检测查看器何时完成文件是一个棘手的问题。所以你需要创造性地思考。在路障附近找到不同的方式。

答案 1 :(得分: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;