//Note1: some localized message was translated directly, so it will probably be diffferent on your computer, but the meaning is the same.
//Note2: this program is build under .NET Framework 4.5, Release, x64.
I am designing a program that can backup the flash drive silently and automatically.
In the copy method, I use code like:
//Note: the Cout(string) method is a logging method, acturally equivalent to Console.WriteLine(string)
public void CopyMoveDisk(string fromdir, string todir, int times = 0)
{
try
{
if (PauseToken)
{
Cout("Copy: paused");
STTimer t = new STTimer(o => { CopyMoveDisk(fromdir, todir, times); }, null, 5000, -1);
Thread.Sleep(6000);
t.Dispose();
return;
}
else
{
try
{
if (!Directory.Exists(todir))
Directory.CreateDirectory(todir);
var filessmall = from f in Directory.GetFiles(fromdir)
where new FileInfo(f).Length <= 100 * Math.Pow(2, 20)
orderby new FileInfo(f).Length
select f;
var filesbig = from f in Directory.GetFiles(fromdir)
where new FileInfo(f).Length > 100 * Math.Pow(2, 20)
orderby new FileInfo(f).Length
select f;
Cout("Copy: small file region");
// Copy the small files (<= 100MiB)
foreach (string file in filessmall)
{
s.CopyingFile = todir + Path.GetFileName(file);
if (File.Exists(todir + Path.GetFileName(file)))
{
if (new FileInfo(file).Length != new FileInfo(todir + Path.GetFileName(file)).Length)
{
Cout($"Copy true: {file} => {todir + Path.GetFileName(file)}: Length = {new FileInfo(todir + Path.GetFileName(file)).Length}");
File.Copy(file, todir + Path.GetFileName(file), true);
}
else
{
Cout($"Exist: {file} => {todir + Path.GetFileName(file)}: Length = {new FileInfo(todir + Path.GetFileName(file)).Length}");
}
}
else
{
Cout($"Copy: {file} => {todir + Path.GetFileName(file)}");
File.Copy(file, todir + Path.GetFileName(file), true);
}
s.CopyingFile = string.Empty;
}
Cout("Copy: small file region end");
Cout($"Copy: directory region: {times} time(s)");
// Copy the sub directories
foreach (string sub in Directory.GetDirectories(fromdir))
{
if (!sub.Contains("System Volume Information"))
{
Cout($"Copy: {sub + "\\"} => {todir + Path.GetFileName(sub) + "\\"}");
CopyMoveDisk(sub + "\\", todir + Path.GetFileName(sub) + "\\", times + 1);
}
}
Cout($"Copy: directory region end: {times} time(s)");
Cout("Copy: big file region");
// Copy the big files (> 100MiB)
foreach (string file in filesbig)
{
s.CopyingFile = todir + Path.GetFileName(file);
if (File.Exists(todir + Path.GetFileName(file)))
{
if (new FileInfo(file).Length != new FileInfo(todir + Path.GetFileName(file)).Length)
{
Cout($"Copy true: {file} => {todir + Path.GetFileName(file)}: Length = {new FileInfo(todir + Path.GetFileName(file)).Length}");
File.Copy(file, todir + Path.GetFileName(file), true);
}
else
{
Cout($"Exist: {file} => {todir + Path.GetFileName(file)}: Length = {new FileInfo(todir + Path.GetFileName(file)).Length}");
}
}
else
{
Cout($"Copy: {file} => {todir + Path.GetFileName(file)}");
File.Copy(file, todir + Path.GetFileName(file), true);
}
s.CopyingFile = string.Empty;
}
Cout("Copy: big file region end");
}
catch (Exception ex)
{
Cout($"Copy: -----Exception {ex} {Environment.NewLine}----------");
}
}
}
catch (Exception ex)
{
Cout($"Copy: -----Exception {ex} {Environment.NewLine}{s}{Environment.NewLine}----------");
}
}
After viewing the code, let's imagine a situation:
The user insert a SD Card reader. When the files are copying, the SD Card somehow become disconnected but the reader remains inserted (probably because of poor contact).
Whenever you inserted a Card reader without a card, the Windows Explorer will warn you with "Please insert the disk into 'SD Card (E:\)'."
But when this situation happened to be on this program, the method didn't throw the exception immerdiately. Instead, it showed a messagebox like:
|---USBBackup.exe - No disks|
| There're no disks in the drive. Please insert the disk in the drive E: |
|Cancel| |Retry| |Continue|
If I clicked the cancel button (or the continue button?) then will throw an exception:
System.IO.IOException: The floppy disk in the drive is not correct.
Insert %2 (volume serial number: %3) to drive %1.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.File.InternalCopy(String sourceFileName, String destFileName, Boolean overwrite, Boolean checkHost)
at USBBackup.Form1.CopyMoveDisk(String fromdir, String todir, Int32 times)
//Note3: Somehow the messagebox won't show on my Windows 10 VM.
//Instead, it will throw a excetion directly:
System.IO.IOException: The device is not ready.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.File.InternalCopy(String sourceFileName, String destFileName, Boolean overwrite, Boolean checkHost)
at USBBackup.Form1.CopyMoveDisk(String fromdir, String todir, Int32 times)
//But it showed on my Windows 7 VM.
//Or probably because I installed the .Net Framework 4.8 on the Win10 VM, but 4.5 on the Win7 VM.
Now that I don't want this program to disturb the user, what can I do to avoid showing the messagebox?
You will need to call the SetErrorMode API with SEM_FAILCRITICALERRORS
to disable the system messageboxes and set it back afterwards.
Create an internal class NativeMethods
:
internal static class NativeMethods
{
[DllImport("kernel32.dll")]
internal static extern ErrorModes SetErrorMode(ErrorModes uMode);
[Flags]
internal enum ErrorModes : uint
{
SYSTEM_DEFAULT = 0x0,
SEM_FAILCRITICALERRORS = 0x0001,
SEM_NOALIGNMENTFAULTEXCEPT = 0x0004,
SEM_NOGPFAULTERRORBOX = 0x0002,
SEM_NOOPENFILEERRORBOX = 0x8000
}
}
An example usage:
void BackupFiles()
{
var prevMode = NativeMethods.SetErrorMode(NativeMethods.SEM_FAILCRITICALERRORS);
try
{
// attempt to copy the files
// from the removable device
}
finally
{
NativeMethods.SetErrorMode(prevMode);
}
}