How do I wait to delete a file until after the program I started has finished using it?

后端 未结 2 697
野性不改
野性不改 2021-01-07 12:54

I have been looking for a way to open a file saved to my computer via a Delphi app with its appropriate application. The file is stored in a Varbinary field in a SQL databas

相关标签:
2条回答
  • 2021-01-07 13:24

    Trying to detect that the displaying process has closed is brittle and fraught with problems, as you learnt in your previous question. Often times, it's hard to find the process that is used to view the file, and even if you can, there's no certainty the closing the view of the file will close the process. The process may be used to view other files which the user leaves open. I think the lesson that you should take from that is that the system does not want you to do what you are trying to do.

    So, what's the better way to solve the problem? I think the best you can do is to create the temporary files in the temporary directory and not attempt to delete them when the user has finished with them. You could:

    • Remember the files you created and when you create, say the 21st file, delete the first one you made. Then delete the 2nd when you create the 22nd and so on.
    • Or, delete all temporary files on startup. This would remove files from a previous session.
    • Or run a separate tidy up thread that, every ten minutes, say, deleted files that were created more than an hour ago.

    You get the idea. The point is that it is an intractable problem to detect when the viewer has finished with the file, in full generality. So you need to think creatively. Find a different way around the road block.

    0 讨论(0)
  • 2021-01-07 13:24

    Hers a snip from a unit I use for a similar purpose. I found these functions online somewhere over the the years so I take no credit and make no promises.

    I personally use the WaitExec() function to launch a pdf (retrieved from a database) in Acrobat for editing and then re-save it to our database when done.

    I have used the two other functions at other times as well so I know they all work to one degree or another but I think WaitExec() worked best in an interactive mode, while Launch() worked better from a thread or non-interactive mode.

    The IsFileInUse function can tell you if the file you created is in use by any other processes and may be a viable option as well.

    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;
    
    0 讨论(0)
提交回复
热议问题