yoy.be "Why-o-Why"
Delay a file copy until the next reboot
2005-10-21 10:43
i327
[permalink]
I have been looking for a while how to 'delay' a file-copy until the next reboot.
In searching the [[Microsoft Windows]] documentation, I have found the SetupApi functions, and they appear to be just what I need to make it work. Ported to Delphi, the code looks a little like this. The code I first had had some more exception-raising code in stead of a return value of boolean here, but feel free to build it up some more. I also provide a callBack routine that does a little more than absolutely neccessary, but that might come in handy of you want to do more with this single FileQueue. Note that I chose not to use the default callback routine here, which would handle all events with the proper dialogs and UI things.
interface
uses
Windows;
type
HSPFILEQ = THandle;
function SetupOpenFileQueue: HSPFILEQ stdcall;
function SetupCloseFileQueue(QueueHandle: HSPFILEQ): BOOL stdcall;
function SetupQueueCopy(QueueHandle: HSPFILEQ;
SourceRootPath: PChar;
SourcePath: PChar; //optional
SourceFilename: PChar;
SourceDescription: PChar;
SourceTagfile: PChar; //optional
TargetDirectory: PChar; //optional
TargetFileName: PChar; //optional
CopyStyle: DWORD
) : BOOL stdcall;
type
PSP_FILE_CALLBACK_A=function(CONTEXT:pointer;NOTIFICATION:cardinal;PARAM1:cardinal;PARAM2:cardinal):cardinal stdcall;
function SetupCommitFileQueue(Owner: HWND;
QueueHandle: HSPFILEQ; //optional
MsgHandler: PSP_FILE_CALLBACK_A;
Context: pointer): BOOL stdcall;
implementation
function SetupOpenFileQueue; external 'setupapi.dll';
function SetupCloseFileQueue; external 'setupapi.dll';
function SetupQueueCopy; external 'setupapi.dll' name 'SetupQueueCopyA';
function SetupCommitFileQueue; external 'setupapi.dll' name 'SetupCommitFileQueue';
// CopyStyle values for copy and queue-related APIs
const
SP_COPY_DELETESOURCE = $0000001; // delete source file on successful copy
SP_COPY_REPLACEONLY = $0000002; // copy only if target file already present
SP_COPY_NEWER = $0000004; // copy only if source newer than or same as target
SP_COPY_NEWER_OR_SAME = SP_COPY_NEWER;
SP_COPY_NOOVERWRITE = $0000008; // copy only if target doesn't exist
SP_COPY_NODECOMP = $0000010; // don't decompress source file while copying
SP_COPY_LANGUAGEAWARE = $0000020; // don't overwrite file of different language
SP_COPY_SOURCE_ABSOLUTE = $0000040; // SourceFile is a full source path
SP_COPY_SOURCEPATH_ABSOLUTE = $0000080; // SourcePathRoot is the full path
SP_COPY_IN_USE_NEEDS_REBOOT = $0000100; // System needs reboot if file in use
SP_COPY_FORCE_IN_USE = $0000200; // Force target-in-use behavior
SP_COPY_NOSKIP = $0000400; // Skip is disallowed for this file or section
SP_FLAG_CABINETCONTINUATION = $0000800; // Used with need media notification
SP_COPY_FORCE_NOOVERWRITE = $0001000; // like NOOVERWRITE but no callback nofitication
SP_COPY_FORCE_NEWER = $0002000; // like NEWER but no callback nofitication
SP_COPY_WARNIFSKIP = $0004000; // system critical file: warn if user tries to skip
SP_COPY_NOBROWSE = $0008000; // Browsing is disallowed for this file or section
SP_COPY_NEWER_ONLY = $0010000; // copy only if source file newer than target
SP_COPY_SOURCE_SIS_MASTER = $0020000; // source is single-instance store master
SP_COPY_OEMINF_CATALOG_ONLY = $0040000; // (SetupCopyOEMInf only) don't copy INF--just catalog
SP_COPY_REPLACE_BOOT_FILE = $0080000; // file must be present upon reboot (i.e., it's needed by the loader); this flag implies a reboot
SP_COPY_NOPRUNE = $0100000; {// never prune this file}
// Operation/queue start/end notification. These are ordinal values.
const
SPFILENOTIFY_STARTQUEUE = $00000001;
SPFILENOTIFY_ENDQUEUE = $00000002;
SPFILENOTIFY_STARTSUBQUEUE = $00000003;
SPFILENOTIFY_ENDSUBQUEUE = $00000004;
SPFILENOTIFY_STARTDELETE = $00000005;
SPFILENOTIFY_ENDDELETE = $00000006;
SPFILENOTIFY_DELETEERROR = $00000007;
SPFILENOTIFY_STARTRENAME = $00000008;
SPFILENOTIFY_ENDRENAME = $00000009;
SPFILENOTIFY_RENAMEERROR = $0000000a;
SPFILENOTIFY_STARTCOPY = $0000000b;
SPFILENOTIFY_ENDCOPY = $0000000c;
SPFILENOTIFY_COPYERROR = $0000000d;
SPFILENOTIFY_NEEDMEDIA = $0000000e;
SPFILENOTIFY_QUEUESCAN = $0000000;
// These are used with SetupIterateCabinet().
SPFILENOTIFY_CABINETINFO = $00000010;
SPFILENOTIFY_FILEINCABINET = $00000011;
SPFILENOTIFY_NEEDNEWCABINET = $00000012;
SPFILENOTIFY_FILEEXTRACTED = $00000013;
SPFILENOTIFY_FILEOPDELAYED = $00000014;
// These are used for backup operations
SPFILENOTIFY_STARTBACKUP = $00000015;
SPFILENOTIFY_BACKUPERROR = $00000016;
SPFILENOTIFY_ENDBACKUP = $00000017;
// Extended notification for SetupScanFileQueue(Flags=SPQ_SCAN_USE_CALLBACKEX)
SPFILENOTIFY_QUEUESCAN_EX = $00000018;
SPFILENOTIFY_STARTREGISTRATION = $00000019;
SPFILENOTIFY_ENDREGISTRATION = $00000020;
//IFDEF _SETUPAPI_VER >= $0501
// Extended notification for SetupScanFileQueue(Flags=SPQ_SCAN_USE_CALLBACK_SIGNERINFO)
SPFILENOTIFY_QUEUESCAN_SIGNERINFO = $00000040;
//ENDIF
// Copy notification. These are bit flags that may be combined.
SPFILENOTIFY_LANGMISMATCH = $00010000;
SPFILENOTIFY_TARGETEXISTS = $00020000;
SPFILENOTIFY_TARGETNEWER = $00040000;
// File operation codes and callback outcomes.
const
FILEOP_COPY = 0;
FILEOP_RENAME = 1;
FILEOP_DELETE = 2;
FILEOP_BACKUP = 3;
FILEOP_ABORT = 0;
FILEOP_DOIT = 1;
FILEOP_SKIP = 2;
FILEOP_RETRY = FILEOP_DOIT;
FILEOP_NEWPATH = 4;
//callback context
type
TCallBackRaiseError=(
cbrNone,
cbrCopyError,
cbrSourceNotFound,
cbrDeleteError,
cbrRenameError);
TCallBackContext=record
MediaNeededOnce,DelaySuccess:boolean;
RaiseError:TCallBackRaiseError;
end;
function QueueFileCopyCallBack(Context: pointer; Notification: cardinal;
Param1: cardinal; Param2: cardinal): cardinal stdcall;
var
ctx:^TCallBAckContext;
begin
ctx:=Context;
case Notification of
SPFILENOTIFY_DELETEERROR:
begin
ctx.RaiseError:=cbrDeleteError;
Result:=FILEOP_ABORT;
end;
SPFILENOTIFY_RENAMEERROR:
begin
ctx.RaiseError:=cbrRenameError;
Result:=FILEOP_ABORT;
end;
SPFILENOTIFY_COPYERROR:
begin
ctx.RaiseError:=cbrCopyError;
Result:=FILEOP_ABORT;
end;
SPFILENOTIFY_NEEDMEDIA:
begin
if ctx.MediaNeededOnce then
begin
ctx.RaiseError:=cbrSourceNotFound;
Result:=FILEOP_ABORT;
end
else
begin
ctx.MediaNeededOnce:=true;
Result:=FILEOP_DOIT;
end;
end;
SPFILENOTIFY_FILEOPDELAYED:
begin
ctx.DelaySuccess:=true;
Result:=FILEOP_DOIT;
end;
else
Result:=FILEOP_DOIT;//default: continue
end;
end;
function QueueFileCopy(SrcPath,SrcFile,DestPath,DestFile:string;
DeleteSource:boolean):boolean;
var
fq:HSPFILEQ;
c:cardinal;
ctx:TCallBackContext;
begin
//init callback context
ctx.MediaNeededOnce:=false;
ctx.DelaySuccess:=false;
ctx.RaiseError:=cbrNone;
fq:=SetupOpenFileQueue;
c:= //SP_COPY_REPLACEONLY or
SP_COPY_SOURCE_ABSOLUTE or
//SP_COPY_FORCE_IN_USE or
//SP_COPY_IN_USE_NEEDS_REBOOT or
SP_COPY_NOSKIP;
if DeleteSource then c:=c or SP_COPY_DELETESOURCE;
if not(SetupQueueCopy(
fq,
PChar(SrcPath),
nil,
PChar(SrcFile),
nil,
nil,
PChar(DestPath),
PChar(DestFile),
c
)
) then RaiseLastOSError;
SetupCommitFileQueue(GetDesktopWindow,fq,@QueueFileCopyCallBack,@ctx);
//returns false on abort! don't RaiseLastOSError;
SetupCloseFileQueue(fq);
Result:=ctx.RaiseError=cbrNone;
end;