I am happy that DavidB provided a working solution for the non-writing problem, but if possible I would prefer a light-weight script using Windows tools that are already present. Does this need a justification? I do not need a GUI for my purposes, I want to understand what happens here, and I am more familiar with scripting languages.
I see that Steve Si tried to flush data to the USB device in his StartFromUSB.cmd script for RMPrepUSB:
...
REM Flush cache (esp. if NTFS filesystem still writing cache to slow disks)
sync 2> nul
...
Obviously this does not help as you can see when you stop suppressing the error message generated by sync.exe (at least on Windows 10, 64 bit). DavidB uses this code for flushing (found in start_VM.dpr within "QEMU Starter source.zip", post #44):
procedure FlushToDisk(sDriveLetter: Char);
var
hDrive: THandle;
begin
hDrive := 0;
try
hDrive := CreateFile(PAnsiChar('\\.\' + sDriveLetter + ':'), GENERIC_READ or
GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil,
OPEN_EXISTING, 0, 0);
FlushFileBuffers(hDrive);
finally
CloseHandle(hDrive);
end;
end;
I do not understand why this flushing should be required (except for safety) as USB devices by default are not cached (see Device Manager). Moreover, it is not the host machine's writes to the USB device that always get lost but those of the virtual machine. Okay, I will spend some time investigating this, in the hope I can find a Windows command for flushing, but at the moment I wonder whether DavidB's dismounting routine does some magic the mountvol or diskpart commands omit:
function DismountVolume(ADrive: char): Boolean;
const
FSCTL_DISMOUNT_VOLUME = (9 shl 16) or (0 shl 14) or (8 shl 2) or 0;
var
VolumeName: string;
BytesReturned: Cardinal;
begin
Result := False;
VolumeName := Format('\\.\%s:', [ADrive]);
SetLength(VolumesHandles, Length(VolumesHandles) + 1);
VolumesHandles[High(VolumesHandles)] := CreateFile(PChar(VolumeName), GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if VolumesHandles[High(VolumesHandles)] = INVALID_HANDLE_VALUE then
begin
LastError := GetLastError;
Exit;
end;
Result := DeviceIoControl(VolumesHandles[High(VolumesHandles)], FSCTL_DISMOUNT_VOLUME, nil, 0,
nil, 0, BytesReturned, nil);
if not Result then
LastError := GetLastError;
end;
At least I haven't studied yet what must be known about the FSCTL_DISMOUNT_VOLUME stuff. Is there a kind of device lock if no flushing was done before dismounting?
Gerolf