[VFD] Import the VFD project (Virtual Floppy Drive) which allows creating virtual
[reactos.git] / modules / rosapps / lib / vfdlib / vfdctl.c
diff --git a/modules/rosapps/lib/vfdlib/vfdctl.c b/modules/rosapps/lib/vfdlib/vfdctl.c
new file mode 100644 (file)
index 0000000..54c3320
--- /dev/null
@@ -0,0 +1,3272 @@
+/*
+       vfdctl.c
+
+       Virtual Floppy Drive for Windows
+       Driver control library
+       Driver and image control functions
+
+       Copyright (C) 2003-2005 Ken Kato
+*/
+
+#ifdef __cplusplus
+#pragma message(__FILE__": Compiled as C++ for testing purpose.")
+#endif // __cplusplus
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <dbt.h>
+#ifdef _MSC_VER
+#pragma warning (push, 3)
+#endif
+#include <shlobj.h>
+#include <winioctl.h>
+#ifdef _MSC_VER
+#pragma warning (pop)
+#endif
+#include <stdio.h>
+
+#include "vfdtypes.h"
+#include "vfdio.h"
+#include "vfdapi.h"
+#include "vfdlib.h"
+#include "vfdver.h"
+
+#ifndef IOCTL_DISK_GET_LENGTH_INFO
+//     Old winioctl.h header doesn't define the following
+
+#define IOCTL_DISK_GET_LENGTH_INFO                     CTL_CODE(\
+IOCTL_DISK_BASE, 0x0017, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+typedef struct _GET_LENGTH_INFORMATION {
+       LARGE_INTEGER   Length;
+} GET_LENGTH_INFORMATION, *PGET_LENGTH_INFORMATION;
+
+#endif // IOCTL_DISK_GET_LENGTH_INFO
+
+//
+//     DOS device name (\\.\VirtualFD)
+//
+#ifndef __REACTOS__
+#define VFD_DEVICE_TEMPLATE            "\\\\.\\" VFD_DEVICE_BASENAME "%u"
+#else
+#define VFD_DEVICE_TEMPLATE            "\\\\.\\" VFD_DEVICE_BASENAME "%lu"
+#endif
+#define VFD_VOLUME_TEMPLATE            "\\\\.\\%c:"
+
+#define VFD_INSTALL_DIRECTORY  "\\system32\\drivers\\"
+
+#ifdef _DEBUG
+#ifndef __REACTOS__
+extern ULONG TraceFlags = (ULONG)-1;//0;
+extern CHAR *TraceFile = NULL;
+extern ULONG TraceLine = 0;
+#else
+ULONG TraceFlags = (ULONG)-1;//0;
+CHAR const * TraceFile = NULL;
+ULONG TraceLine        = 0;
+#endif
+#endif
+
+//
+//     broadcast a WM_DEVICECHANGE system message to inform
+//     a drive letter creation / removal
+//
+#define VFD_LINK_CREATED       0
+#define VFD_LINK_REMOVED       1
+
+static void VfdBroadcastLink(
+       CHAR                    cLetter,
+       BOOL                    bRemoved)
+{
+       DWORD                   receipients;
+       DWORD                   device_event;
+       DEV_BROADCAST_VOLUME params;
+
+       if (!isalpha(cLetter)) {
+               VFDTRACE(0,
+                       ("VfdBroadcastLink: invalid parameter"))
+               return;
+       }
+
+       receipients = BSM_APPLICATIONS;
+
+       device_event = bRemoved ?
+               DBT_DEVICEREMOVECOMPLETE : DBT_DEVICEARRIVAL;
+
+       ZeroMemory(&params, sizeof(params));
+
+       params.dbcv_size                = sizeof(params);
+       params.dbcv_devicetype  = DBT_DEVTYP_VOLUME;
+       params.dbcv_reserved    = 0;
+       params.dbcv_unitmask    = (1 << (toupper(cLetter) - 'A'));
+       params.dbcv_flags               = 0;
+
+       if (BroadcastSystemMessage(
+               BSF_NOHANG | BSF_FORCEIFHUNG | BSF_NOTIMEOUTIFNOTHUNG,
+               &receipients,
+               WM_DEVICECHANGE,
+               device_event,
+               (LPARAM)&params) <= 0) {
+
+               VFDTRACE(0,
+                       ("VfdBroadcastLink: BroadcastSystemMessage - %s",
+                       SystemMessage(GetLastError())));
+       }
+}
+
+//
+//     Broadcast a VFD notify message
+//
+static __inline void VfdNotify(
+       WPARAM                  wParam,
+       LPARAM                  lParam)
+{
+       //      SendNotifyMessage causes volume locking conflict (I think)
+       //      on Windows XP while closing an image with VfdWin
+//     SendNotifyMessage(HWND_BROADCAST, uVfdMsg, wParam, lParam);
+       PostMessage(HWND_BROADCAST, g_nNotifyMsg, wParam, lParam);
+}
+
+#ifdef VFD_EMBED_DRIVER
+//
+//     Restore the VFD driver file in the system directory
+//
+
+static DWORD VfdRestoreDriver(
+       PCSTR                   sPath)
+{
+#define FUNC           "VfdRestoreDriver"
+       HRSRC                   hRes;
+       DWORD                   size;
+       HGLOBAL                 hDrv;
+       PVOID                   pData;
+       DWORD                   result;
+       HANDLE                  hFile;
+       DWORD                   ret;
+
+       //
+       //      Prepare driver binary
+       //
+
+       // use embedded driver binary
+
+#define S(s) #s
+       hRes = FindResource(g_hDllModule,
+               S(VFD_DRIVER_NAME_ID), S(VFD_DRIVER_TYPE_ID));
+#undef S
+
+       if (hRes == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": FindResource - %s",
+                       SystemMessage(ret)));
+
+               return ret;
+       }
+
+       size = SizeofResource(g_hDllModule, hRes);
+
+       if (size == 0) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": SizeofResource - %s",
+                       SystemMessage(ret)));
+
+               return ret;
+       }
+
+       hDrv = LoadResource(g_hDllModule, hRes);
+
+       if (hDrv == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": LoadResource - %s",
+                       SystemMessage(ret)));
+
+               return ret;
+       }
+
+       pData = LockResource(hDrv);
+
+       if (pData == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": LockResource - %s",
+                       SystemMessage(ret)));
+
+               return ret;
+       }
+
+       //      create the driver file
+
+       hFile = CreateFile(sPath, GENERIC_WRITE,
+               0, NULL, OPEN_ALWAYS, 0, NULL);
+
+       if (hFile == INVALID_HANDLE_VALUE) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": CreateFile(%s) - %s",
+                       sPath, SystemMessage(ret)));
+
+               return ret;
+       }
+
+       if (!WriteFile(hFile, pData, size, &result, NULL) ||
+               size != result) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": CreateFile - %s",
+                       SystemMessage(ret)));
+
+               CloseHandle(hFile);
+               return ret;
+       }
+
+       SetEndOfFile(hFile);
+       CloseHandle(hFile);
+
+       return ERROR_SUCCESS;
+}
+#endif // VFD_EMBED_DRIVER
+
+//
+//     Install the Virtual Floppy Driver
+//
+DWORD WINAPI VfdInstallDriver(
+       PCSTR                   sFileName,
+       DWORD                   nStart)
+{
+#undef FUNC
+#define FUNC           "VfdInstallDriver"
+       SC_HANDLE               hScManager;                             // Service Control Manager
+       SC_HANDLE               hService = NULL;                // Service (= Driver)
+#ifndef VFD_EMBED_DRIVER
+       CHAR                    file_path[MAX_PATH];
+       PSTR                    file_name;
+#endif //      VFD_EMBED_DRIVER
+       CHAR                    system_dir[MAX_PATH];
+       PSTR                    inst_path;
+       DWORD                   len;
+       DWORD                   ret = ERROR_SUCCESS;
+
+       //      get SystemRoot directory path
+
+//     len = GetEnvironmentVariable(
+//             "SystemRoot", system_dir, sizeof(system_dir));
+       len = GetWindowsDirectory(system_dir, sizeof(system_dir));
+
+       if (len == 0 || len > sizeof(system_dir)) {
+               VFDTRACE(0,
+                       (FUNC ": %%SystemRoot%% is empty or too long.\n"));
+
+               return ERROR_BAD_ENVIRONMENT;
+       }
+
+       inst_path = &system_dir[len];
+
+#ifdef VFD_EMBED_DRIVER
+       //
+       //      use embedded driver file
+       //
+       strcpy(inst_path++, VFD_INSTALL_DIRECTORY VFD_DRIVER_FILENAME);
+
+       ret = VfdRestoreDriver(system_dir);
+
+       if (ret != ERROR_SUCCESS) {
+               return ret;
+       }
+
+#else  // VFD_EMBED_DRIVER
+       //      Prepare driver binary's full path
+
+       if (sFileName == NULL || *sFileName == '\0') {
+
+               // default driver file is vfd.sys in the same directory as executable
+
+               len = GetModuleFileName(
+                       NULL, file_path, sizeof(file_path));
+
+               if (len == 0) {
+                       ret = GetLastError();
+
+                       VFDTRACE(0,
+                               (FUNC ": GetModuleFileName - %s",
+                               SystemMessage(ret)));
+
+                       return ret;
+               }
+
+               //      search the last '\' character
+
+               while (len > 0 && file_path[len - 1] != '\\') {
+                       len --;
+               }
+
+               //      supply the file name (vfd.sys)
+
+               file_name = &file_path[len];
+               strcpy(file_name, VFD_DRIVER_FILENAME);
+       }
+       else {
+
+               //      ensure that tha path is an absolute full path
+
+               len = GetFullPathName(
+                       sFileName,
+                       sizeof(file_path),
+                       file_path,
+                       &file_name);
+
+               if (len == 0) {
+                       ret = GetLastError();
+
+                       VFDTRACE(0,
+                               (FUNC ": GetFullPathName(%s) - %s\n",
+                               sFileName, SystemMessage(ret)));
+
+                       return ret;
+               }
+
+               if (GetFileAttributes(file_path) & FILE_ATTRIBUTE_DIRECTORY) {
+                       //      if the specified path is a directory,
+                       //      supply the file name (vfd.sys)
+
+                       file_name = &file_path[len];
+                       strcpy(file_name++, "\\" VFD_DRIVER_FILENAME);
+               }
+       }
+
+       //      Check if the file is a valid Virtual Floppy driver
+
+       ret = VfdCheckDriverFile(file_path, NULL);
+
+       if (ret != ERROR_SUCCESS) {
+               VFDTRACE(0,
+                       (FUNC ": VfdCheckDriverFile(%s)\n", file_path));
+
+               return ret;
+       }
+
+       //      if the path is under the system directory, make it relative
+       //      to the system directory
+
+       len = strlen(system_dir);
+
+       if (!_strnicmp(file_path, system_dir, len)) {
+               inst_path = &file_path[len];
+
+               while (*inst_path == '\\') {
+                       inst_path++;
+               }
+       }
+       else {
+               inst_path = &file_path[0];
+       }
+#endif //      VFD_EMBED_DRIVER
+
+       //      Connect to the Service Control Manager
+
+       hScManager = OpenSCManager(
+               NULL,                                                   // local machine
+               NULL,                                                   // local database
+               SC_MANAGER_CREATE_SERVICE);             // access required
+
+       if (hScManager == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": OpenSCManager() - %s",
+                       SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+       //      Create a new service object
+
+       hService = CreateService(
+               hScManager,                                             // service control manager
+               VFD_DEVICE_BASENAME,                    // internal service name
+               VFD_DEVICE_BASENAME,                    // display name
+               SERVICE_ALL_ACCESS,                             // access mode
+               SERVICE_KERNEL_DRIVER,                  // service type
+               nStart,                                                 // service start type
+               SERVICE_ERROR_NORMAL,                   // start error sevirity
+               inst_path,                                              // service image file path
+               NULL,                                                   // service group
+               NULL,                                                   // service tag
+               NULL,                                                   // service dependency
+               NULL,                                                   // use LocalSystem account
+               NULL                                                    // password for the account
+       );
+
+       if (!hService) {
+               // Failed to create a service object
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": CreateService() - %s",
+                       SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+cleanup:
+       //      Close the service object handle
+
+       if (hService) {
+               CloseServiceHandle(hService);
+       }
+
+       //      Close handle to the service control manager.
+
+       if (hScManager) {
+               CloseServiceHandle(hScManager);
+       }
+
+       if (ret == ERROR_SUCCESS) {
+               //      Broadcast the successful operation
+               VfdNotify(VFD_OPERATION_INSTALL, 0);
+       }
+#ifdef VFD_EMBED_DRIVER
+       else {
+               //      Delete the restored driver file
+               DeleteFile(system_dir);
+       }
+#endif // VFD_EMBED_DRIVER
+
+       return ret;
+}
+
+//
+//     Configure the Virtual Floppy Driver (change the start method)
+//
+
+DWORD WINAPI VfdConfigDriver(
+       DWORD                   nStart)
+{
+#undef FUNC
+#define FUNC           "VfdConfigDriver"
+       SC_HANDLE               hScManager;                             // Service Control Manager
+       SC_HANDLE               hService;                               // Service (= Driver)
+       DWORD                   ret = ERROR_SUCCESS;
+
+       //      Connect to the Service Control Manager
+
+       hScManager = OpenSCManager(NULL, NULL, 0);
+
+       if (hScManager == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": OpenSCManager() - %s",
+                       SystemMessage(ret)));
+
+               return ret;
+       }
+
+       //      Open the VFD driver entry in the service database
+
+       hService = OpenService(
+               hScManager,                                             // Service control manager
+               VFD_DEVICE_BASENAME,                    // service name
+               SERVICE_CHANGE_CONFIG);                 // service access mode
+
+       if (hService == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": OpenService(SERVICE_CHANGE_CONFIG) - %s",
+                       SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+       //      Change the start method of the VFD driver
+
+       if (!ChangeServiceConfig(
+                       hService,
+                       SERVICE_NO_CHANGE,
+                       nStart,
+                       SERVICE_NO_CHANGE,
+                       NULL,
+                       NULL,
+                       NULL,
+                       NULL,
+                       NULL,
+                       NULL,
+                       NULL)) {
+
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": ChangeServiceConfig() - %s",
+                       SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+cleanup:
+       //      Close the service object handle
+
+       if (hService) {
+               CloseServiceHandle(hService);
+       }
+
+       //      Close handle to the service control manager.
+
+       if (hScManager) {
+               CloseServiceHandle(hScManager);
+       }
+
+       //      Broadcast the successful operation
+
+       if (ret == ERROR_SUCCESS) {
+               VfdNotify(VFD_OPERATION_CONFIG, 0);
+       }
+
+       return ret;
+}
+
+//
+//     Remove the Virtual Floppy Driver entry from the service database
+//
+DWORD WINAPI VfdRemoveDriver()
+{
+#undef FUNC
+#define FUNC           "VfdRemoveDriver"
+       SC_HANDLE               hScManager;                             // Service Control Manager
+       SC_HANDLE               hService;                               // Service (= Driver)
+       CHAR                    file_path[MAX_PATH];
+       DWORD                   ret = ERROR_SUCCESS;
+
+       //      Get the current driver path
+
+       ret = VfdGetDriverConfig(file_path, NULL);
+
+       if (ret != ERROR_SUCCESS) {
+               return ret;
+       }
+
+       //      Connect to the Service Control Manager
+
+       hScManager = OpenSCManager(NULL, NULL, 0);
+
+       if (hScManager == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": OpenSCManager() - %s",
+                       SystemMessage(ret)));
+
+               return ret;
+       }
+
+       //      Open the VFD driver entry in the service database
+
+       hService = OpenService(
+               hScManager,                                             // Service control manager
+               VFD_DEVICE_BASENAME,                    // service name
+               DELETE);                                                // service access mode
+
+       if (hService == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": OpenService(DELETE) - %s",
+                       SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+       //      Remove driver entry from registry
+
+       if (!DeleteService(hService)) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": DeleteService() - %s",
+                       SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+cleanup:
+       //      Close the service object handle
+
+       if (hService) {
+               CloseServiceHandle(hService);
+       }
+
+       //      Close handle to the service control manager.
+
+       if (hScManager) {
+               CloseServiceHandle(hScManager);
+       }
+
+       //      Broadcast the successful operation
+
+       if (ret == ERROR_SUCCESS) {
+               VfdNotify(VFD_OPERATION_REMOVE, 0);
+
+#ifdef VFD_EMBED_DRIVER
+               //      Remove the driver file
+               DeleteFile(file_path);
+#endif //      VFD_EMBED_DRIVER
+       }
+
+       return ret;
+}
+
+//
+//     Start the Virtual Floppy Driver
+//
+DWORD WINAPI VfdStartDriver(
+       PDWORD                  pState)
+{
+#undef FUNC
+#define FUNC           "VfdStartDriver"
+       SC_HANDLE               hScManager;                     // Service Control Manager
+       SC_HANDLE               hService;                       // Service (= Driver)
+       SERVICE_STATUS  stat;
+       DWORD                   ret = ERROR_SUCCESS;
+       HCURSOR                 original;
+       int                             i;
+
+       if (pState) {
+               *pState = 0;
+       }
+
+       //      Connect to the Service Control Manager
+
+       hScManager = OpenSCManager(NULL, NULL, 0);
+
+       if (hScManager == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": OpenSCManager() - %s",
+                       SystemMessage(ret)));
+
+               return ret;
+       }
+
+       //      show an hourglass cursor
+
+       original = SetCursor(LoadCursor(NULL, IDC_WAIT));
+
+       //      Open the VFD driver entry in the service database
+
+       hService = OpenService(
+               hScManager,                                             // Service control manager
+               VFD_DEVICE_BASENAME,                    // service name
+               SERVICE_START
+               | SERVICE_QUERY_STATUS);                // service access mode
+
+       if (hService == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": OpenService(SERVICE_START) - %s",
+                       SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+       //      Start the driver
+
+       if (!StartService(hService, 0, NULL)) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": StartService() - %s",
+                       SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+       //      Wait until the driver is properly running
+
+       i = 0;
+
+       for (;;) {
+               if (!QueryServiceStatus(hService, &stat)) {
+                       ret = GetLastError();
+
+                       VFDTRACE(0,
+                               (FUNC ": QueryServiceStatus() - %s",
+                               SystemMessage(ret)));
+
+                       break;
+               }
+
+               if (stat.dwCurrentState == SERVICE_RUNNING || ++i == 5) {
+                       break;
+               }
+
+               Sleep(1000);
+       }
+
+       if (stat.dwCurrentState == SERVICE_RUNNING) {
+
+               //      Broadcast the successful operation
+
+               if (ret == ERROR_SUCCESS) {
+                       VfdNotify(VFD_OPERATION_START, 0);
+               }
+
+               //      broadcast the arrival of VFD drives
+               //      otherwise WinXP explorer doesn't recognize the VFD drives
+
+               for (i = 0; i < VFD_MAXIMUM_DEVICES; i++) {
+                       HANDLE  hDevice;
+                       CHAR    letter = 0;
+
+                       hDevice = VfdOpenDevice(i);
+
+                       if (hDevice != INVALID_HANDLE_VALUE) {
+
+                               VfdGetGlobalLink(hDevice, &letter);
+
+                               CloseHandle(hDevice);
+
+                               if (isalpha(letter)) {
+                                       VfdBroadcastLink(letter, VFD_LINK_CREATED);
+                                       VfdNotify(VFD_OPERATION_SETLINK, i);
+                               }
+                       }
+                       else {
+                               VFDTRACE(0,
+                                       (FUNC ": VfdOpenDevice(%d) - %s",
+                                       i, SystemMessage(GetLastError())));
+                       }
+               }
+       }
+       else {
+               //      somehow failed to start the driver
+
+               ret = ERROR_SERVICE_NOT_ACTIVE;
+       }
+
+       if (pState) {
+               *pState = stat.dwCurrentState;
+       }
+
+cleanup:
+       //      Close the service object handle
+
+       if (hService) {
+               CloseServiceHandle(hService);
+       }
+
+       //      Close handle to the service control manager.
+
+       if (hScManager) {
+               CloseServiceHandle(hScManager);
+       }
+
+       //      revert to the original cursor
+
+       SetCursor(original);
+
+       return ret;
+}
+
+//
+//     Stop the Virtual Floppy Driver
+//
+DWORD WINAPI VfdStopDriver(
+       PDWORD                  pState)
+{
+#undef FUNC
+#define FUNC           "VfdStopDriver"
+       SC_HANDLE               hScManager;                     // Service Control Manager
+       SC_HANDLE               hService;                       // Service (= Driver)
+       SERVICE_STATUS  stat;
+       CHAR                    drive_letters[VFD_MAXIMUM_DEVICES];
+       DWORD                   ret = ERROR_SUCCESS;
+       int                             i;
+       HCURSOR                 original;
+
+       if (pState) {
+               *pState = 0;
+       }
+
+       //      Connect to the Service Control Manager
+
+       hScManager = OpenSCManager(NULL, NULL, 0);
+
+       if (hScManager == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": OpenSCManager() - %s",
+                       SystemMessage(ret)));
+
+               return ret;
+       }
+
+       //      Show the hourglass cursor
+
+       original = SetCursor(LoadCursor(NULL, IDC_WAIT));
+
+       //      Open the VFD driver entry in the service database
+
+       hService = OpenService(
+               hScManager,                                             // Service control manager
+               VFD_DEVICE_BASENAME,                    // service name
+               SERVICE_STOP
+               | SERVICE_QUERY_STATUS);                // service access mode
+
+       if (hService == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": OpenService(SERVICE_STOP) - %s",
+                       SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+       //      Get assigned drive letters
+
+       for (i = 0; i < VFD_MAXIMUM_DEVICES; i++) {
+               HANDLE  hDevice;
+               CHAR letter;
+
+               hDevice = VfdOpenDevice(i);
+
+               if (hDevice != INVALID_HANDLE_VALUE) {
+
+                       //      remove all session local drive letters
+
+                       while (VfdGetLocalLink(hDevice, &letter) == ERROR_SUCCESS &&
+                               isalpha(letter)) {
+                               VfdSetLocalLink(hDevice, 0);
+                       }
+
+                       //      store existing persistent drive letters
+
+                       VfdGetGlobalLink(hDevice, &drive_letters[i]);
+
+                       CloseHandle(hDevice);
+               }
+               else {
+                       VFDTRACE(0,
+                               (FUNC ": VfdOpenDevice(%d) - %s",
+                               i, SystemMessage(GetLastError())));
+               }
+       }
+
+       //      Stop the driver
+
+       if (!ControlService(hService, SERVICE_CONTROL_STOP, &stat)) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": ControlService(SERVICE_CONTROL_STOP) - %s",
+                       SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+       //      Wait until the driver is stopped
+
+       i = 0;
+
+       while (stat.dwCurrentState != SERVICE_STOPPED && ++i < 5) {
+               Sleep(1000);
+
+               if (!QueryServiceStatus(hService, &stat)) {
+                       ret = GetLastError();
+
+                       VFDTRACE(0,
+                               (FUNC ": QueryServiceStatus() - %s",
+                               SystemMessage(ret)));
+
+                       break;
+               }
+       }
+
+       if (stat.dwCurrentState != SERVICE_RUNNING) {
+
+               //      broadcast the removal of persistent drive letters
+
+               for (i = 0; i < VFD_MAXIMUM_DEVICES; i++) {
+                       if (isalpha(drive_letters[i])) {
+                               VfdBroadcastLink(drive_letters[i], VFD_LINK_REMOVED);
+                               VfdNotify(VFD_OPERATION_DELLINK, i);
+                       }
+               }
+       }
+
+       if (pState) {
+               *pState = stat.dwCurrentState;
+       }
+
+cleanup:
+       //      Close the service object handle
+
+       if (hService) {
+               CloseServiceHandle(hService);
+       }
+
+       //      Close handle to the service control manager.
+
+       if (hScManager) {
+               CloseServiceHandle(hScManager);
+       }
+
+       //      Broadcast the successful operation
+
+       if (ret == ERROR_SUCCESS) {
+               VfdNotify(VFD_OPERATION_STOP, 0);
+       }
+
+       //      revert to the original cursor
+
+       SetCursor(original);
+
+       return ret;
+}
+
+//
+//     Get the Virtual Floppy Driver configuration
+//
+DWORD WINAPI VfdGetDriverConfig(
+       PSTR                    sFileName,
+       PDWORD                  pStart)
+{
+#undef FUNC
+#define FUNC           "VfdGetDriverConfig"
+       SC_HANDLE               hScManager;                             // Service Control Manager
+       SC_HANDLE               hService;                               // Service (= Driver)
+       LPQUERY_SERVICE_CONFIG config = NULL;
+       DWORD                   result;
+       DWORD                   ret = ERROR_SUCCESS;
+
+       if (sFileName) {
+               ZeroMemory(sFileName, MAX_PATH);
+       }
+
+       if (pStart) {
+               *pStart = 0;
+       }
+
+       //      Connect to the Service Control Manager
+
+       hScManager = OpenSCManager(NULL, NULL, 0);
+
+       if (hScManager == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": OpenSCManager() - %s", SystemMessage(ret)));
+
+               return ret;
+       }
+
+       //      Open the VFD driver entry in the service database
+
+       hService = OpenService(
+               hScManager,                                             // Service control manager
+               VFD_DEVICE_BASENAME,                    // service name
+               SERVICE_QUERY_CONFIG);                  // service access mode
+
+       if (hService == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": OpenService(SERVICE_QUERY_CONFIG) - %s",
+                       SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+       //      Get the length of config information
+
+       if (!QueryServiceConfig(hService, NULL, 0, &result)) {
+               ret = GetLastError();
+
+               if (ret == ERROR_INSUFFICIENT_BUFFER) {
+                       ret = ERROR_SUCCESS;
+               }
+               else {
+                       VFDTRACE(0,
+                               (FUNC ": QueryServiceConfig() - %s",
+                               SystemMessage(ret)));
+
+                       goto cleanup;
+               }
+       }
+
+       //      allocate a required buffer
+
+       config = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LPTR, result);
+
+       if (config == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": LocalAlloc(%lu) - %s\n",
+                       result, SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+       //      get the config information
+
+       if (!QueryServiceConfig(hService, config, result, &result)) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": QueryServiceConfig() - %s",
+                       SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+       //      copy information to output buffer
+
+       if (sFileName) {
+               if (strncmp(config->lpBinaryPathName, "\\??\\", 4) == 0) {
+
+                       //      driver path is an absolute UNC path
+                       strncpy(
+                               sFileName,
+                               config->lpBinaryPathName + 4,
+                               MAX_PATH);
+               }
+               else if (config->lpBinaryPathName[0] == '\\' ||
+                       (isalpha(config->lpBinaryPathName[0]) &&
+                       config->lpBinaryPathName[1] == ':')) {
+
+                       //      driver path is an absolute path
+                       strncpy(sFileName,
+                               config->lpBinaryPathName,
+                               MAX_PATH);
+               }
+               else {
+                       //      driver path is relative to the SystemRoot
+//                     DWORD len = GetEnvironmentVariable(
+//                             "SystemRoot", sFileName, MAX_PATH);
+
+                       DWORD len = GetWindowsDirectory(sFileName, MAX_PATH);
+
+                       if (len == 0 || len > MAX_PATH) {
+                               VFDTRACE(0,
+                                       (FUNC ": %%SystemRoot%% is empty or too long.\n"));
+
+                               ret = ERROR_BAD_ENVIRONMENT;
+                               goto cleanup;
+                       }
+
+                       sprintf((sFileName + len), "\\%s",
+                               config->lpBinaryPathName);
+               }
+       }
+
+       if (pStart) {
+               *pStart = config->dwStartType;
+       }
+
+cleanup:
+       //      Free service config buffer
+
+       if (config) {
+               LocalFree(config);
+       }
+
+       //      Close the service object handle
+
+       if (hService) {
+               CloseServiceHandle(hService);
+       }
+
+       //      Close handle to the service control manager.
+
+       if (hScManager) {
+               CloseServiceHandle(hScManager);
+       }
+
+       return ret;
+}
+
+//
+//     Get the Virtual Floppy Driver running state
+//
+DWORD WINAPI VfdGetDriverState(
+       PDWORD                  pState)
+{
+#undef FUNC
+#define FUNC           "VfdGetDriverState"
+       SC_HANDLE               hScManager = NULL;      // Service Control Manager
+       SC_HANDLE               hService = NULL;        // Service (= Driver)
+       SERVICE_STATUS  status;
+       DWORD                   ret = ERROR_SUCCESS;
+
+       if (pState) {
+               *pState = 0;
+       }
+
+       //      Connect to the Service Control Manager
+
+       hScManager = OpenSCManager(NULL, NULL, 0);
+
+       if (hScManager == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": OpenSCManager() - %s",
+                       SystemMessage(ret)));
+
+               return ret;
+       }
+
+       //      Open the VFD driver entry in the service database
+
+       hService = OpenService(
+               hScManager,                                             // Service control manager
+               VFD_DEVICE_BASENAME,                    // service name
+               SERVICE_QUERY_STATUS);                  // service access mode
+
+       if (hService == NULL) {
+
+               ret = GetLastError();
+
+               if (ret == ERROR_SERVICE_DOES_NOT_EXIST) {
+
+                       if (pState) {
+                               *pState = VFD_NOT_INSTALLED;
+                       }
+
+                       ret = ERROR_SUCCESS;
+               }
+               else {
+                       VFDTRACE(0,
+                               (FUNC ": OpenService(SERVICE_QUERY_STATUS) - %s",
+                               SystemMessage(ret)));
+               }
+
+               goto cleanup;
+       }
+
+       //      Get current driver status
+
+       ZeroMemory(&status, sizeof(status));
+
+       if (!QueryServiceStatus(hService, &status)) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": QueryServiceStatus() - %s",
+                       SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+       if (pState) {
+               *pState = status.dwCurrentState;
+       }
+
+cleanup:
+       //      Close the service object handle
+
+       if (hService) {
+               CloseServiceHandle(hService);
+       }
+
+       //      Close handle to the service control manager.
+
+       if (hScManager) {
+               CloseServiceHandle(hScManager);
+       }
+
+       return ret;
+}
+
+//
+//     open a Virtual Floppy drive without showing the "Insert Floppy"
+//     dialog when the drive is empty.
+//
+HANDLE WINAPI VfdOpenDevice(
+       ULONG                   nTarget)                // either a drive letter or a device number
+{
+#undef FUNC
+#define FUNC           "VfdOpenDevice"
+       CHAR                    dev_name[20];
+       UINT                    err_mode;
+       HANDLE                  hDevice;
+
+       //      format a device name string
+
+       if (isalpha(nTarget)) {
+               //      nTarget is a drive letter
+               //      \\.\<x>:
+#ifndef __REACTOS__
+               sprintf(dev_name, VFD_VOLUME_TEMPLATE, nTarget);
+#else
+               sprintf(dev_name, VFD_VOLUME_TEMPLATE, (CHAR)nTarget);
+#endif
+       }
+       else if (isdigit(nTarget)) {
+               //      nTarget is a device number in character
+               //      \\.\VirtualFD<n>
+               sprintf(dev_name, VFD_DEVICE_TEMPLATE, nTarget - '0');
+       }
+       else {
+               //      nTarget is a device number value
+               //      \\.\VirtualFD<n>
+               sprintf(dev_name, VFD_DEVICE_TEMPLATE, nTarget);
+       }
+
+       // change error mode in order to avoid "Insert Floppy" dialog
+
+       err_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
+
+       //      open the target drive
+
+       hDevice = CreateFile(
+               dev_name,
+               GENERIC_READ | GENERIC_WRITE,
+               FILE_SHARE_READ | FILE_SHARE_WRITE,
+               NULL,
+               OPEN_EXISTING,
+               FILE_FLAG_NO_BUFFERING,
+               NULL);
+
+       //      revert to the original error mode
+
+       SetErrorMode(err_mode);
+
+       if (hDevice != INVALID_HANDLE_VALUE) {
+
+               //      check if the target is a valid VFD drive
+
+               ULONG version;
+
+               if (VfdGetDriverVersion(hDevice, &version) != ERROR_SUCCESS) {
+
+                       //      Failed to get the driver version
+
+                       CloseHandle(hDevice);
+                       hDevice = INVALID_HANDLE_VALUE;
+               }
+               else if ((version & ~0x80000000) !=
+                       MAKELONG(VFD_DRIVER_MINOR, VFD_DRIVER_MAJOR)) {
+
+                       //      the driver version mismatch
+
+//                     CloseHandle(hDevice);
+//                     hDevice = INVALID_HANDLE_VALUE;
+
+                       SetLastError(ERROR_REVISION_MISMATCH);
+               }
+       }
+       else {
+               VFDTRACE(0,(
+                       "CreateFile(%s) - %s", dev_name,
+                       SystemMessage(GetLastError())));;
+       }
+
+       return hDevice;
+}
+
+//
+//     Open a Virtual Floppy Image
+//
+DWORD WINAPI VfdOpenImage(
+       HANDLE                  hDevice,
+       PCSTR                   sFileName,
+       VFD_DISKTYPE    nDiskType,
+       VFD_MEDIA               nMediaType,
+       VFD_FLAGS               nMediaFlags)
+{
+#undef FUNC
+#define FUNC           "VfdOpenImage"
+       PCSTR                   prefix;
+       CHAR                    abspath[MAX_PATH];
+       DWORD                   name_len;
+       DWORD                   result;
+       DWORD                   ret = ERROR_SUCCESS;
+
+       PVFD_IMAGE_INFO image_info = NULL;
+       PUCHAR                  image_buf = NULL;
+       ULONG                   image_size;
+       VFD_FILETYPE    file_type;
+
+       //
+       //      Check parameters
+       //
+
+       if (hDevice == NULL ||
+               hDevice == INVALID_HANDLE_VALUE) {
+               return ERROR_INVALID_HANDLE;
+       }
+
+       if (nMediaType == VFD_MEDIA_NONE ||
+               nMediaType >= VFD_MEDIA_MAX) {
+
+               VFDTRACE(0,
+                       (FUNC ": Invalid MediaType - %u\n", nMediaType));
+
+               return ERROR_INVALID_PARAMETER;
+       }
+
+
+       if (sFileName && *sFileName) {
+
+               //      check file contents and attributes
+
+               HANDLE hFile = CreateFile(sFileName, GENERIC_READ,
+                       FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+
+               if (hFile == INVALID_HANDLE_VALUE) {
+                       ret = GetLastError();
+
+                       VFDTRACE(0,
+                               (FUNC ": CreateFile(%s) - %s",
+                               sFileName, SystemMessage(ret)));
+
+                       return ret;
+               }
+
+               //      try extracting image data from zip compressed file
+
+               ExtractZipImage(hFile, &image_buf, &image_size);
+
+               if (image_buf) {
+
+                       file_type = VFD_FILETYPE_ZIP;
+
+                       //      imz file must be opened in RAM mode
+
+                       if (nDiskType == VFD_DISKTYPE_FILE) {
+
+                               VFDTRACE(0,
+                                       (FUNC ": %s is a zip compressed file",
+                                       sFileName));
+
+                               CloseHandle(hFile);
+                               ret = ERROR_INVALID_PARAMETER;
+
+                               goto exit_func;
+                       }
+               }
+               else {
+
+                       file_type = VFD_FILETYPE_RAW;
+
+                       if (nDiskType == VFD_DISKTYPE_FILE) {
+
+                               //      direct image file must not be compressed or encrypted
+
+                               BY_HANDLE_FILE_INFORMATION info;
+
+                               if (!GetFileInformationByHandle(hFile, &info)) {
+                                       ret = GetLastError();
+
+                                       VFDTRACE(0,
+                                               (FUNC ": GetFileInformationByHandle - %s",
+                                               SystemMessage(ret)));
+
+                                       CloseHandle(hFile);
+
+                                       return ret;
+                               }
+
+                               if (info.dwFileAttributes &
+                                       (FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_ENCRYPTED)) {
+
+                                       VFDTRACE(0,
+                                               (FUNC ": file is compressed/encrypted"));
+
+                                       CloseHandle(hFile);
+
+                                       return ERROR_FILE_ENCRYPTED;
+                               }
+
+                               image_size      = info.nFileSizeLow;
+                       }
+                       else {
+
+                               // prepare image data for a file based RAM disk
+
+                               image_size = GetFileSize(hFile, NULL);
+
+                               if (image_size == 0 || image_size == INVALID_FILE_SIZE) {
+                                       ret = GetLastError();
+
+                                       VFDTRACE(0,
+                                               (FUNC ": GetFileSize - %s",
+                                               SystemMessage(ret)));
+
+                                       CloseHandle(hFile);
+
+                                       return ret;
+                               }
+
+                               image_buf = (PUCHAR)LocalAlloc(LPTR, image_size);
+
+                               if (image_buf == NULL) {
+                                       ret = GetLastError();
+
+                                       VFDTRACE(0,
+                                               (FUNC ": LocalAlloc - %s",
+                                               SystemMessage(ret)));
+
+                                       CloseHandle(hFile);
+
+                                       return ret;
+                               }
+
+                               if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) != 0) {
+                                       ret = GetLastError();
+
+                                       VFDTRACE(0,
+                                               (FUNC ": SetFilePointer - %s",
+                                               SystemMessage(ret)));
+
+                                       CloseHandle(hFile);
+
+                                       goto exit_func;
+                               }
+
+                               if (!ReadFile(hFile, image_buf, image_size, &result, NULL) ||
+                                       image_size != result) {
+
+                                       ret = GetLastError();
+
+                                       VFDTRACE(0,
+                                               (FUNC ": ReadFile - %s",
+                                               SystemMessage(ret)));
+
+                                       CloseHandle(hFile);
+
+                                       goto exit_func;
+                               }
+                       }
+               }
+
+               CloseHandle(hFile);
+
+               //      Prepare absolute path in the kernel namespace
+
+               if (*sFileName == '\\' && *(sFileName + 1) == '\\') {
+
+                       // \\server\share\path\floppy.img
+
+                       prefix = "\\??\\UNC";
+                       sFileName++;                    // drip the first '\'
+               }
+               else {
+
+                       //      local path
+
+                       PSTR file_part;
+
+                       if (GetFullPathName(sFileName,
+                               sizeof(abspath), abspath, &file_part) == 0) {
+
+                               ret =  GetLastError();
+
+                               VFDTRACE(0,
+                                       (FUNC ": GetFullPathName(%s) - %s\n",
+                                       sFileName, SystemMessage(ret)));
+
+                               goto exit_func;
+                       }
+
+                       prefix = "\\??\\";
+                       sFileName = abspath;
+               }
+
+               name_len = strlen(prefix) + strlen(sFileName);
+       }
+       else {
+
+               //      filename is not specified -- pure RAM disk
+
+               nDiskType = VFD_DISKTYPE_RAM;
+               file_type = VFD_FILETYPE_NONE;
+
+               prefix = NULL;
+               name_len = 0;
+
+               //      prepare a FAT formatted RAM image
+
+               image_size = VfdGetMediaSize(nMediaType);
+
+               image_buf = (PUCHAR)LocalAlloc(LPTR, image_size);
+
+               if (image_buf == NULL) {
+                       ret = GetLastError();
+
+                       VFDTRACE(0,
+                               (FUNC ": LocalAlloc - %s",
+                               SystemMessage(ret)));
+
+                       return ret;
+               }
+
+               FormatBufferFat(image_buf, VFD_BYTE_TO_SECTOR(image_size));
+       }
+
+       if (image_size < VfdGetMediaSize(nMediaType)) {
+
+               //      image is too small for the specified media type
+
+               VFDTRACE(0,
+                       (FUNC ": Image is too small for the specified media type\n"));
+
+               ret = ERROR_INVALID_PARAMETER;
+               goto exit_func;
+       }
+
+       //      prepare VFD_IMAGE_INFO structure
+
+       image_info = (PVFD_IMAGE_INFO)LocalAlloc(LPTR,
+               sizeof(VFD_IMAGE_INFO) + name_len + 1);
+
+       if (image_info == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": LocalAlloc(%lu) - %s\n",
+                       sizeof(VFD_IMAGE_INFO) + name_len + 1,
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       ZeroMemory(image_info,
+               sizeof(VFD_IMAGE_INFO) + name_len + 1);
+
+       if (name_len) {
+               sprintf(image_info->FileName,
+                       "%s%s", prefix, sFileName);
+       }
+
+       image_info->NameLength  = (USHORT)name_len;
+
+       image_info->DiskType    = nDiskType;
+       image_info->MediaType   = nMediaType;
+       image_info->MediaFlags  = nMediaFlags;
+       image_info->FileType    = file_type;
+       image_info->ImageSize   = image_size;
+
+       if (nDiskType != VFD_DISKTYPE_FILE) {
+               //      protect flag for a RAM disk is set after
+               //      initializing the image buffer
+               image_info->MediaFlags &= ~VFD_FLAG_WRITE_PROTECTED;
+       }
+
+       VFDTRACE(0,
+               (FUNC ": Opening file \"%s\" (%lu bytes) %s %s %s %s\n",
+               name_len ? image_info->FileName : "<RAM>",
+               image_info->ImageSize,
+               (file_type == VFD_FILETYPE_ZIP) ? "ZIP image" : "RAW image",
+               VfdMediaTypeName(nMediaType),
+               (nDiskType == VFD_DISKTYPE_FILE) ? "FILE disk" : "RAM disk",
+               (nMediaFlags & VFD_FLAG_WRITE_PROTECTED) ? "Protected" : "Writable"));
+
+       //      Open the image file / create a ram disk
+
+       if (!DeviceIoControl(
+               hDevice,
+               IOCTL_VFD_OPEN_IMAGE,
+               image_info,
+               sizeof(VFD_IMAGE_INFO) + name_len,
+               NULL,
+               0,
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(IOCTL_VFD_OPEN_FILE) - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       //      initialize the RAM disk image
+
+       if (nDiskType != VFD_DISKTYPE_FILE) {
+
+               image_size &= ~VFD_SECTOR_ALIGN_MASK;
+
+               if (SetFilePointer(hDevice, 0, NULL, FILE_BEGIN) != 0) {
+                       ret = GetLastError();
+
+                       VFDTRACE(0,
+                               (FUNC ": SetFilePointer - %s",
+                               SystemMessage(ret)));
+
+                       goto exit_func;
+               }
+
+               if (!WriteFile(hDevice, image_buf, image_size, &result, NULL) ||
+                       image_size != result) {
+
+                       ret = GetLastError();
+
+                       VFDTRACE(0,
+                               (FUNC ": WriteFile - %s",
+                               SystemMessage(ret)));
+
+                       goto exit_func;
+               }
+
+               if (nMediaFlags & VFD_FLAG_WRITE_PROTECTED) {
+                       VfdWriteProtect(hDevice, TRUE);
+               }
+
+               if (!DeviceIoControl(
+                       hDevice,
+                       IOCTL_VFD_RESET_MODIFY,
+                       NULL,
+                       0,
+                       NULL,
+                       0,
+                       &result,
+                       NULL))
+               {
+                       VFDTRACE(0,
+                               (FUNC ": DeviceIoControl(IOCTL_VFD_RESET_MODIFY) - %s",
+                               SystemMessage(GetLastError())));
+               }
+       }
+
+       //      Broadcast the successful operation
+
+       if (ret == ERROR_SUCCESS) {
+               ULONG   number;
+               CHAR    root[] = "A:\\";
+
+               if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
+                       VfdNotify(VFD_OPERATION_OPEN, number);
+               }
+
+               VfdGetGlobalLink(hDevice, &root[0]);
+
+               if (isalpha(root[0])) {
+                       SHChangeNotify(SHCNE_MEDIAINSERTED, SHCNF_PATH, root, NULL);
+               }
+
+               while (VfdGetLocalLink(hDevice, &root[0]) == ERROR_SUCCESS &&
+                       isalpha(root[0])) {
+                       SHChangeNotify(SHCNE_MEDIAINSERTED, SHCNF_PATH, root, NULL);
+               }
+       }
+
+exit_func:
+       if (image_info) {
+               LocalFree(image_info);
+       }
+
+       if (image_buf) {
+               LocalFree(image_buf);
+       }
+
+       return ret;
+}
+
+//
+//     Close the virtual floppy Image
+//
+DWORD WINAPI VfdCloseImage(
+       HANDLE                  hDevice,
+       BOOL                    bForce)
+{
+#undef FUNC
+#define FUNC           "VfdCloseImage"
+       DWORD                   result;
+       DWORD                   ret = ERROR_SUCCESS;
+       int                             retry = 0;
+
+lock_retry:
+       if (!DeviceIoControl(
+               hDevice,
+               FSCTL_LOCK_VOLUME,
+               NULL,
+               0,
+               NULL,
+               0,
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(FSCTL_LOCK_VOLUME) - %s",
+                       SystemMessage(ret)));
+
+               if (ret != ERROR_ACCESS_DENIED || retry == 5) {
+                       //      error other than access denied or
+                       //      operation kept failing for 5 seconds
+                       return ret;
+               }
+
+               if (!bForce) {
+                       //      error is access denied and
+                       //      the force flag is not set
+
+                       if (retry == 0) {
+
+                               //      send the MEDIAREMOVED notification to the shell and
+                               //      see if the shell releases the target drive
+
+                               CHAR root[] = "A:\\";
+
+                               VfdGetGlobalLink(hDevice, &root[0]);
+
+                               if (isalpha(root[0])) {
+                                       SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATH, root, NULL);
+                               }
+
+                               while (VfdGetLocalLink(hDevice, &root[0]) == ERROR_SUCCESS &&
+                                       isalpha(root[0])) {
+                                       SHChangeNotify(SHCNE_MEDIAREMOVED, SHCNF_PATH, root, NULL);
+                               }
+                       }
+
+                       Sleep(1000);
+                       retry++;
+
+                       goto lock_retry;
+               }
+       }
+
+       ret = ERROR_SUCCESS;
+
+       if (!DeviceIoControl(
+               hDevice,
+               FSCTL_DISMOUNT_VOLUME,
+               NULL,
+               0,
+               NULL,
+               0,
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(FSCTL_DISMOUNT_VOLUME) - %s",
+                       SystemMessage(ret)));
+
+               return ret;
+       }
+
+       if (!DeviceIoControl(
+               hDevice,
+               IOCTL_VFD_CLOSE_IMAGE,
+               NULL,
+               0,
+               NULL,
+               0,
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               if (ret != ERROR_NOT_READY) {
+                       VFDTRACE(0,
+                               (FUNC ": DeviceIoControl(IOCTL_VFD_CLOSE_FILE) - %s",
+                               SystemMessage(ret)));
+               }
+
+               return ret;
+       }
+
+       if (!DeviceIoControl(
+               hDevice,
+               FSCTL_UNLOCK_VOLUME,
+               NULL,
+               0,
+               NULL,
+               0,
+               &result,
+               NULL))
+       {
+               //      This should not be fatal because the volume is unlocked
+               //      when the handle is closed anyway
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(FSCTL_UNLOCK_VOLUME) - %s",
+                       SystemMessage(GetLastError())));
+       }
+
+       //      Broadcast the successful operation
+       if (ret == ERROR_SUCCESS) {
+               ULONG   number;
+
+               if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
+                       VfdNotify(VFD_OPERATION_CLOSE, number);
+               }
+       }
+
+       return ret;
+}
+
+//
+//     Get Virtual Floppy image info
+//
+DWORD WINAPI VfdGetImageInfo(
+       HANDLE                  hDevice,
+       PSTR                    sFileName,
+       PVFD_DISKTYPE   pDiskType,
+       PVFD_MEDIA              pMediaType,
+       PVFD_FLAGS              pMediaFlags,
+       PVFD_FILETYPE   pFileType,
+       PULONG                  pImageSize)
+{
+#undef FUNC
+#define FUNC           "VfdGetImageInfo"
+       PVFD_IMAGE_INFO image_info;
+       DWORD                   result;
+       DWORD                   ret = ERROR_SUCCESS;
+
+       image_info = (PVFD_IMAGE_INFO)LocalAlloc(
+               LPTR, sizeof(VFD_IMAGE_INFO) + MAX_PATH);
+
+       if (image_info == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": LocalAlloc(%lu) - %s\n",
+                       sizeof(VFD_IMAGE_INFO) + MAX_PATH, SystemMessage(ret)));
+
+               return ret;
+       }
+
+       ZeroMemory(image_info, sizeof(VFD_IMAGE_INFO) + MAX_PATH);
+
+       //      Query file information
+
+       if (!DeviceIoControl(
+               hDevice,
+               IOCTL_VFD_QUERY_IMAGE,
+               NULL,
+               0,
+               image_info,
+               sizeof(VFD_IMAGE_INFO) + MAX_PATH,
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               if (ret != ERROR_MORE_DATA) {
+                       VFDTRACE(0,
+                               (FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_FILE) - %s",
+                               SystemMessage(ret)));
+
+                       goto cleanup;
+               }
+       }
+
+       //      copy obtained information to output buffer
+
+       if (sFileName) {
+
+               //      if filename is too long, clip it
+
+               if (image_info->NameLength >= MAX_PATH) {
+                       image_info->NameLength = MAX_PATH - 1;
+               }
+
+               // ensure the name is properly terminated
+
+               image_info->FileName[image_info->NameLength] = '\0';
+
+               if (strncmp(image_info->FileName, "\\??\\UNC", 7) == 0) {
+                       *sFileName = '\\';
+                       strcpy(sFileName + 1, image_info->FileName + 7);
+               }
+               else if (strncmp(image_info->FileName, "\\??\\", 4) == 0) {
+                       strcpy(sFileName, image_info->FileName + 4);
+               }
+               else {
+                       strcpy(sFileName, image_info->FileName);
+               }
+       }
+
+       if (pDiskType) {
+               *pDiskType = image_info->DiskType;
+       }
+
+       if (pMediaType) {
+               *pMediaType = image_info->MediaType;
+       }
+
+       if (pMediaFlags) {
+               *pMediaFlags = image_info->MediaFlags;
+       }
+
+       if (pFileType) {
+               *pFileType = image_info->FileType;
+       }
+
+       if (pImageSize) {
+               *pImageSize = image_info->ImageSize;
+       }
+
+cleanup:
+       if (image_info) {
+               LocalFree(image_info);
+       }
+
+       return ret;
+}
+
+//
+//     Get current media state (opened / write protected)
+//
+DWORD WINAPI VfdGetMediaState(
+       HANDLE                  hDevice)
+{
+#undef FUNC
+#define FUNC           "VfdGetMediaState"
+       DWORD                   result;
+       DWORD                   ret = ERROR_SUCCESS;
+
+       //      Query file information
+
+       if (!DeviceIoControl(
+               hDevice,
+               IOCTL_DISK_IS_WRITABLE,
+               NULL,
+               0,
+               NULL,
+               0,
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               if (ret != ERROR_NOT_READY) {
+                       VFDTRACE(0,
+                               (FUNC ": DeviceIoControl(IOCTL_DISK_IS_WRITABLE) - %s",
+                               SystemMessage(ret)));
+               }
+       }
+
+       return ret;
+}
+
+//
+//     Set or Delete a global drive letter
+//
+DWORD WINAPI VfdSetGlobalLink(
+       HANDLE                  hDevice,
+       CHAR                    cLetter)
+{
+#undef FUNC
+#define FUNC           "VfdSetGlobalLink"
+       CHAR                    letter;
+       ULONG                   number;
+       DWORD                   result;
+       DWORD                   ret;
+
+       if (isalpha(cLetter)) {
+
+               //      make sure the drive does not have a drive letter
+
+               letter = 0;
+
+               VfdGetGlobalLink(hDevice, &letter);
+
+               if (isalpha(letter)) {
+                       VFDTRACE(0,
+                               (FUNC ": Drive already has a drive letter %c\n", letter));
+                       return ERROR_ALREADY_ASSIGNED;
+               }
+
+               VfdGetLocalLink(hDevice, &letter);
+
+               if (isalpha(letter)) {
+                       VFDTRACE(0,
+                               (FUNC ": Drive already has a drive letter %c\n", letter));
+                       return ERROR_ALREADY_ASSIGNED;
+               }
+
+               //      make sure drive letter is not in use
+
+               cLetter = (CHAR)toupper(cLetter);
+
+               if (GetLogicalDrives() & (1 << (cLetter - 'A'))) {
+                       VFDTRACE(0,
+                               (FUNC ": Drive letter %c already used\n", cLetter));
+                       return ERROR_ALREADY_ASSIGNED;
+               }
+
+               //      Assign a new drive letter
+
+               if (!DeviceIoControl(
+                       hDevice,
+                       IOCTL_VFD_SET_LINK,
+                       &cLetter,
+                       sizeof(cLetter),
+                       NULL,
+                       0,
+                       &result,
+                       NULL))
+               {
+                       ret = GetLastError();
+
+                       VFDTRACE(0,
+                               (FUNC ": DeviceIoControl(IOCTL_VFD_SET_LINK) - %s",
+                               SystemMessage(ret)));
+
+                       return ret;
+               }
+
+               //      broadcast system message
+
+               VfdBroadcastLink(cLetter, VFD_LINK_CREATED);
+
+               //      broadcast VFD message
+
+               if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
+                       VfdNotify(VFD_OPERATION_SETLINK, number);
+               }
+
+               return ERROR_SUCCESS;
+       }
+       else if (!cLetter) {
+
+               //      make sure the drive has a global drive letter
+
+               letter = 0;
+
+               VfdGetGlobalLink(hDevice, &letter);
+
+               if (!isalpha(letter)) {
+                       VFDTRACE(0,
+                               (FUNC ": Drive does not have a drive letter\n"));
+                       return ERROR_INVALID_FUNCTION;
+               }
+
+               //      Remove drive letters
+
+               if (!DeviceIoControl(
+                       hDevice,
+                       IOCTL_VFD_SET_LINK,
+                       &cLetter,
+                       sizeof(cLetter),
+                       NULL,
+                       0,
+                       &result,
+                       NULL))
+               {
+                       ret = GetLastError();
+
+                       VFDTRACE(0,
+                               (FUNC ": DeviceIoControl(IOCTL_VFD_SET_LINK) - %s",
+                               SystemMessage(ret)));
+
+                       return ret;
+               }
+
+               //      broadcast system message
+
+               VfdBroadcastLink(letter, VFD_LINK_REMOVED);
+
+               //      broadcast VFD message
+               if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
+                       VfdNotify(VFD_OPERATION_DELLINK, number);
+               }
+
+               return ERROR_SUCCESS;
+       }
+       else {
+               return ERROR_INVALID_PARAMETER;
+       }
+}
+
+//
+//     Get a global drive letter
+//
+DWORD WINAPI VfdGetGlobalLink(
+       HANDLE                  hDevice,
+       PCHAR                   pLetter)
+{
+#undef FUNC
+#define FUNC           "VfdGetGlobalLinks"
+       DWORD                   result;
+       DWORD                   ret;
+
+       if (!pLetter) {
+               return ERROR_INVALID_PARAMETER;
+       }
+
+       *pLetter = 0;
+
+       if (!DeviceIoControl(
+               hDevice,
+               IOCTL_VFD_QUERY_LINK,
+               NULL,
+               0,
+               pLetter,
+               sizeof(*pLetter),
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_LINK) - %s",
+                       SystemMessage(ret)));
+
+               return ret;
+       }
+
+       return ERROR_SUCCESS;
+}
+
+//
+//     Set or remove a local drive letter
+//
+DWORD WINAPI VfdSetLocalLink(
+       HANDLE                  hDevice,
+       CHAR                    cLetter)
+{
+#undef FUNC
+#define FUNC           "VfdSetLocalLink"
+       CHAR                    letter;
+       CHAR                    dos_name[] = "A:";
+       CHAR                    dev_name[MAX_PATH];
+       ULONG                   number;
+       DWORD                   ret;
+
+       if (isalpha(cLetter)) {
+
+               //      make sure the drive does not have a drive letter
+
+               letter = 0;
+
+               VfdGetGlobalLink(hDevice, &letter);
+
+               if (isalpha(letter)) {
+                       VFDTRACE(0,
+                               (FUNC ": Drive already has a drive letter %c\n", letter));
+                       return ERROR_ALREADY_ASSIGNED;
+               }
+
+               VfdGetLocalLink(hDevice, &letter);
+
+               if (isalpha(letter)) {
+                       VFDTRACE(0,
+                               (FUNC ": Drive already has a drive letter %c\n", letter));
+                       return ERROR_ALREADY_ASSIGNED;
+               }
+
+               //      make sure drive letters are not in use
+
+               cLetter = (CHAR)toupper(cLetter);
+
+               if (GetLogicalDrives() & (1 << (cLetter - 'A'))) {
+                       VFDTRACE(0,
+                               (FUNC ": Drive letter already used\n"));
+
+                       return ERROR_ALREADY_ASSIGNED;
+               }
+
+               //      get VFD device name
+
+               ret = VfdGetDeviceName(hDevice, dev_name, sizeof(dev_name));
+
+               if (ret != ERROR_SUCCESS) {
+                       return ret;
+               }
+
+               //      assign a drive letter
+
+               dos_name[0] = cLetter;
+
+               if (!DefineDosDevice(DDD_RAW_TARGET_PATH, dos_name, dev_name)) {
+                       ret = GetLastError();
+
+                       VFDTRACE(0,
+                               (FUNC ": DefineDosDevice(%s,%s) - %s",
+                               dos_name, dev_name, SystemMessage(ret)));
+               }
+
+               if (ret == ERROR_SUCCESS) {
+                       //      broadcast VFD message
+
+                       if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
+                               VfdNotify(VFD_OPERATION_SETLINK, number);
+                       }
+               }
+
+               return ret;
+       }
+       else if (!cLetter) {
+
+               //      make sure the drive has a local drive letter
+
+               letter = 0;
+
+               VfdGetLocalLink(hDevice, &letter);
+
+               if (!isalpha(letter)) {
+                       VFDTRACE(0,
+                               (FUNC ": Drive letter is not assigned to this drive\n"));
+                       return ERROR_INVALID_FUNCTION;
+               }
+
+               //      get VFD device name
+
+               ret = VfdGetDeviceName(hDevice, dev_name, sizeof(dev_name));
+
+               if (ret != ERROR_SUCCESS) {
+                       return ret;
+               }
+
+               //      remove drive letters
+#define DDD_FLAGS      (DDD_REMOVE_DEFINITION | DDD_RAW_TARGET_PATH | DDD_EXACT_MATCH_ON_REMOVE)
+
+               dos_name[0] = (CHAR)toupper(letter);
+
+               if (!DefineDosDevice(DDD_FLAGS, dos_name, dev_name)) {
+                       ret = GetLastError();
+
+                       VFDTRACE(0,
+                               (FUNC ": DefineDosDevice(%s,%s) - %s",
+                               dos_name, dev_name, SystemMessage(ret)));
+               }
+
+               if (ret == ERROR_SUCCESS) {
+                       //      broadcast VFD message
+                       if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
+                               VfdNotify(VFD_OPERATION_DELLINK, number);
+                       }
+               }
+
+               return ret;
+       }
+       else {
+               return ERROR_INVALID_PARAMETER;
+       }
+}
+
+//
+//     Get local drive letters
+//
+DWORD WINAPI VfdGetLocalLink(
+       HANDLE                  hDevice,
+       PCHAR                   pLetter)
+{
+#undef FUNC
+#define FUNC           "VfdGetLocalLinks"
+       CHAR                    global;
+       ULONG                   logical;
+       CHAR                    dos_name[] = "A:";
+       CHAR                    dev_name[MAX_PATH];
+       CHAR                    dos_target[MAX_PATH * 2];
+       DWORD                   ret;
+
+       if (!pLetter) {
+               return ERROR_INVALID_PARAMETER;
+       }
+
+       //      Get the VFD device name
+
+       ret = VfdGetDeviceName(hDevice, dev_name, sizeof(dev_name));
+
+       if (ret != ERROR_SUCCESS) {
+               return ret;
+       }
+
+       //      Get global drive letter
+
+       ret = VfdGetGlobalLink(hDevice, &global);
+
+       if (ret != ERROR_SUCCESS) {
+               return ret;
+       }
+
+       //      Get logical drives
+
+       logical = GetLogicalDrives();
+
+       //      exclude the global drive letter
+
+       if (isalpha(global)) {
+               logical &= ~(1 << (toupper(global) - 'A'));
+       }
+
+       //      start searching from the next drive letter
+
+       if (isalpha(*pLetter)) {
+               dos_name[0] = (CHAR)(toupper(*pLetter) + 1);
+               logical >>= (dos_name[0] - 'A');
+       }
+
+       //      Check dos device targets
+
+       *pLetter = '\0';
+
+       while (logical) {
+               if (logical & 0x01) {
+                       if (QueryDosDevice(dos_name, dos_target, sizeof(dos_target))) {
+                               if (_stricmp(dos_target, dev_name) == 0) {
+                                       *pLetter = dos_name[0];
+                                       break;
+                               }
+                       }
+                       else {
+                               VFDTRACE(0,
+                                       (FUNC ": QueryDosDevice(%s) - %s",
+                                       dos_name, SystemMessage(GetLastError())));
+                       }
+               }
+               logical >>= 1;
+               dos_name[0]++;
+       }
+
+       return ERROR_SUCCESS;
+}
+
+//
+//     Get the Virtual Floppy device number
+//
+DWORD WINAPI VfdGetDeviceNumber(
+       HANDLE                  hDevice,
+       PULONG                  pNumber)
+{
+#undef FUNC
+#define FUNC           "VfdGetDeviceNumber"
+       DWORD                   result;
+       DWORD                   ret = ERROR_SUCCESS;
+
+       if (!pNumber) {
+               return ERROR_INVALID_PARAMETER;
+       }
+
+       *pNumber = 0;
+
+       if (!DeviceIoControl(
+               hDevice,
+               IOCTL_VFD_QUERY_NUMBER,
+               NULL,
+               0,
+               pNumber,
+               sizeof(ULONG),
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_NUMBER) - %s",
+                       SystemMessage(ret)));
+       }
+
+       return ret;
+}
+
+//     Get the Virtual Floppy device name
+
+DWORD WINAPI VfdGetDeviceName(
+       HANDLE                  hDevice,
+       PCHAR                   pName,
+       ULONG                   nLength)
+{
+#undef FUNC
+#define FUNC           "VfdGetDeviceName"
+       DWORD                   result;
+       WCHAR                   wname[MAX_PATH];
+       DWORD                   ret = ERROR_SUCCESS;
+
+       if (!pName || !nLength) {
+               return ERROR_INVALID_PARAMETER;
+       }
+
+       ZeroMemory(pName, nLength);
+
+       if (!DeviceIoControl(
+               hDevice,
+               IOCTL_VFD_QUERY_NAME,
+               NULL,
+               0,
+               wname,
+               sizeof(wname),
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_NUMBER) - %s",
+                       SystemMessage(ret)));
+       }
+
+       if (!WideCharToMultiByte(CP_OEMCP, 0, &wname[1],
+               wname[0] / sizeof(WCHAR), pName, nLength, NULL, NULL)) {
+
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": WideCharToMultiByte - %s",
+                       SystemMessage(ret)));
+       }
+
+       return ret;
+}
+
+//
+//     Get Virtual Floppy driver version
+//
+DWORD WINAPI VfdGetDriverVersion(
+       HANDLE                  hDevice,
+       PULONG                  pVersion)
+{
+#undef FUNC
+#define FUNC           "VfdGetDriverVersion"
+       DWORD                   result;
+       DWORD                   ret = ERROR_SUCCESS;
+
+       if (!pVersion) {
+               return ERROR_INVALID_PARAMETER;
+       }
+
+       *pVersion = '\0';
+
+       if (!DeviceIoControl(
+               hDevice,
+               IOCTL_VFD_QUERY_VERSION,
+               NULL,
+               0,
+               pVersion,
+               sizeof(ULONG),
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(IOCTL_VFD_QUERY_VERSION) - %s",
+                       SystemMessage(ret)));
+       }
+
+       return ret;
+}
+
+//
+//     Change the write protect state of the media
+//
+DWORD WINAPI VfdWriteProtect(
+       HANDLE                  hDevice,
+       BOOL                    bProtect)
+{
+#undef FUNC
+#define FUNC           "VfdWriteProtect"
+       DWORD                   result;
+       DWORD                   ret = ERROR_SUCCESS;
+
+       if (!DeviceIoControl(
+               hDevice,
+               bProtect ? IOCTL_VFD_SET_PROTECT : IOCTL_VFD_CLEAR_PROTECT,
+               NULL,
+               0,
+               NULL,
+               0,
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(IOCTL_VFD_SET_PROTECT) - %s",
+                       SystemMessage(ret)));
+       }
+
+       if (ret == ERROR_SUCCESS) {
+               ULONG number;
+
+               if (VfdGetDeviceNumber(hDevice, &number) == ERROR_SUCCESS) {
+                       VfdNotify(VFD_OPERATION_PROTECT, number);
+               }
+       }
+
+       return ret;
+}
+
+//     Format the current media with FAT12
+
+DWORD WINAPI VfdFormatMedia(
+       HANDLE                  hDevice)
+{
+#undef FUNC
+#define FUNC           "VfdFormatMedia"
+       DWORD                   result;
+       DWORD                   ret = ERROR_SUCCESS;
+       PUCHAR                  buf = NULL;
+       GET_LENGTH_INFORMATION  length;
+
+       //      Get the media size
+
+       if (!DeviceIoControl(
+               hDevice,
+               IOCTL_DISK_GET_LENGTH_INFO,
+               NULL,
+               0,
+               &length,
+               sizeof(length),
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(IOCTL_DISK_GET_LENGTH_INFO) - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       //      Prepare a formatted image buffer
+
+       buf = (PUCHAR)LocalAlloc(LPTR, length.Length.LowPart);
+
+       if (buf == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": LocalAlloc - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       //      format the buffer
+
+       ret = FormatBufferFat(buf,
+               VFD_BYTE_TO_SECTOR(length.Length.LowPart));
+
+       if (ret != ERROR_SUCCESS) {
+               goto exit_func;
+       }
+
+       //      seek the top of the media
+
+       if (SetFilePointer(hDevice, 0, NULL, FILE_BEGIN) != 0) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": SetFilePointer - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       //      write the image into the media
+
+       if (!WriteFile(hDevice, buf, length.Length.LowPart, &result, NULL) ||
+               result != length.Length.LowPart) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": WriteFile - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+exit_func:
+       //      unlock the target volume
+       if (!DeviceIoControl(
+               hDevice,
+               FSCTL_UNLOCK_VOLUME,
+               NULL,
+               0,
+               NULL,
+               0,
+               &result,
+               NULL))
+       {
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(FSCTL_UNLOCK_VOLUME) - %s",
+                       SystemMessage(GetLastError())));
+       }
+
+       //      release the format image buffer
+       if (buf) {
+               LocalFree(buf);
+       }
+
+       return ret;
+}
+
+//     Dismount the volume (should be called before Save, Format)
+
+DWORD WINAPI VfdDismountVolume(
+       HANDLE                  hDevice,
+       BOOL                    bForce)
+{
+#undef FUNC
+#define FUNC           "VfdDismountVolume"
+       DWORD                   result;
+       DWORD                   ret = ERROR_SUCCESS;
+
+       //      Lock the target volume
+
+       if (!DeviceIoControl(
+               hDevice,
+               FSCTL_LOCK_VOLUME,
+               NULL,
+               0,
+               NULL,
+               0,
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(FSCTL_LOCK_VOLUME) - %s",
+                       SystemMessage(ret)));
+
+               if (ret != ERROR_ACCESS_DENIED || !bForce) {
+                       return ret;
+               }
+       }
+
+       //      Dismount the target volume
+
+       if (!DeviceIoControl(
+               hDevice,
+               FSCTL_DISMOUNT_VOLUME,
+               NULL,
+               0,
+               NULL,
+               0,
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(FSCTL_DISMOUNT_VOLUME) - %s",
+                       SystemMessage(ret)));
+       }
+
+       return ret;
+}
+
+//     Save the current image into a file
+
+DWORD WINAPI VfdSaveImage(
+       HANDLE                  hDevice,
+       PCSTR                   sFileName,
+       BOOL                    bOverWrite,
+       BOOL                    bTruncate)
+{
+#undef FUNC
+#define FUNC           "VfdSaveImage"
+       HANDLE                  hFile = INVALID_HANDLE_VALUE;
+       DWORD                   result;
+       DWORD                   ret = ERROR_SUCCESS;
+       PUCHAR                  buf = NULL;
+       GET_LENGTH_INFORMATION  length;
+
+
+       ret = ERROR_SUCCESS;
+
+       //      Get the media size
+
+       if (!DeviceIoControl(
+               hDevice,
+               IOCTL_DISK_GET_LENGTH_INFO,
+               NULL,
+               0,
+               &length,
+               sizeof(length),
+               &result,
+               NULL))
+       {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(IOCTL_DISK_GET_LENGTH_INFO) - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       //      Prepare an intermediate image buffer
+
+       buf = (PUCHAR)LocalAlloc(LPTR, length.Length.LowPart);
+
+       if (buf == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": LocalAlloc - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       //      seek the top of the media
+
+       if (SetFilePointer(hDevice, 0, NULL, FILE_BEGIN) != 0) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": SetFilePointer - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       //      read the image data
+
+       if (!ReadFile(hDevice, buf, length.Length.LowPart, &result, NULL) ||
+               result != length.Length.LowPart) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": ReadFile - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       //      open the destination file
+
+       hFile = CreateFile(sFileName, GENERIC_WRITE, 0, NULL,
+               bOverWrite ? OPEN_ALWAYS : CREATE_NEW, 0, NULL);
+
+       if (hFile == INVALID_HANDLE_VALUE) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": CreateFile - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       //      seek the top of the file
+
+       if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) != 0) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": SetFilePointer - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       //      write the image data
+
+       if (!WriteFile(hFile, buf, length.Length.LowPart, &result, NULL) ||
+               result != length.Length.LowPart) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": WriteFile - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       //      truncate the target file
+
+       if (bTruncate && !SetEndOfFile(hFile)) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": SetEndOfFile - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       //      reset the media modified flag
+
+       if (!DeviceIoControl(
+               hDevice,
+               IOCTL_VFD_RESET_MODIFY,
+               NULL,
+               0,
+               NULL,
+               0,
+               &result,
+               NULL))
+       {
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(IOCTL_VFD_RESET_MODIFY) - %s",
+                       SystemMessage(GetLastError())));
+       }
+
+exit_func:
+       //      unlock the target volume
+
+       if (!DeviceIoControl(
+               hDevice,
+               FSCTL_UNLOCK_VOLUME,
+               NULL,
+               0,
+               NULL,
+               0,
+               &result,
+               NULL))
+       {
+               VFDTRACE(0,
+                       (FUNC ": DeviceIoControl(FSCTL_UNLOCK_VOLUME) - %s",
+                       SystemMessage(GetLastError())));
+       }
+
+       //      release the format image buffer
+
+       if (buf) {
+               LocalFree(buf);
+       }
+
+       //      close the image file
+
+       if (hFile != INVALID_HANDLE_VALUE) {
+               CloseHandle(hFile);
+       }
+
+       return ret;
+}
+
+//
+//     Check if specified file is valid VFD driver
+//
+DWORD WINAPI VfdCheckDriverFile(
+       PCSTR                   sFileName,
+       PULONG                  pFileVersion)
+{
+#undef FUNC
+#define FUNC                   "VfdCheckDriverFile"
+       DWORD                           result;
+       DWORD                           dummy;
+       PVOID                           info;
+       VS_FIXEDFILEINFO        *fixedinfo;
+       DWORD                           ret = ERROR_SUCCESS;
+       PSTR                            str;
+
+       //      Check parameter
+
+       if (!sFileName || !*sFileName) {
+               return ERROR_INVALID_PARAMETER;
+       }
+
+       if (pFileVersion) {
+               *pFileVersion = 0;
+       }
+
+       //      check file existence
+
+       if (GetFileAttributes(sFileName) == INVALID_FILE_ATTRIBUTES) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": GetFileAttributes - %s\n",
+                       SystemMessage(ret)));
+
+               return ret;
+       }
+
+       //      check file version
+
+       result = GetFileVersionInfoSize((PSTR)sFileName, &dummy);
+
+       if (result == 0) {
+               VFDTRACE(0,
+                       (FUNC ": GetFileVersionInfoSize == 0\n"));
+
+               return ERROR_BAD_DRIVER;
+       }
+
+       info = LocalAlloc(LPTR, result);
+
+       if (info == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": LocalAlloc(%lu) - %s\n",
+                       result, SystemMessage(ret)));
+
+               return ret;
+       }
+
+       if (!GetFileVersionInfo((PSTR)sFileName, 0, result, info)) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": GetFileVersionInfo - %s", SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+       result = sizeof(fixedinfo);
+
+       if (!VerQueryValue(info, "\\", (PVOID *)&fixedinfo, (PUINT)&result)) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": VerQueryValue(\"\\\") - %s", SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+       if (fixedinfo->dwFileOS                         != VOS_NT_WINDOWS32 ||
+               fixedinfo->dwFileType                   != VFT_DRV                      ||
+               fixedinfo->dwFileSubtype                != VFT2_DRV_SYSTEM) {
+
+               VFDTRACE(0,
+#ifndef __REACTOS__
+                       (FUNC ": Invalid file type flags\n"));
+#else
+                       (FUNC ": Invalid file type flags. os: %x (%x), type: %x (%x), subtype: %x (%x)\n",
+             fixedinfo->dwFileOS, VOS_NT_WINDOWS32, fixedinfo->dwFileType, VFT_DRV,
+             fixedinfo->dwFileSubtype, VFT2_DRV_SYSTEM));
+#endif
+
+               ret = ERROR_BAD_DRIVER;
+
+               goto cleanup;
+       }
+
+       if (pFileVersion) {
+               *pFileVersion = fixedinfo->dwFileVersionMS;
+
+               if (fixedinfo->dwFileFlags & VS_FF_DEBUG) {
+                       *pFileVersion |= 0x80000000;
+               }
+       }
+
+       if (!VerQueryValue(info,
+               "\\StringFileInfo\\" VFD_VERSIONINFO_LANG "\\OriginalFileName",
+               (PVOID *)&str, (PUINT)&result)) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": VerQueryValue(\"OriginalFileName\") - %s",
+                       SystemMessage(ret)));
+
+               goto cleanup;
+       }
+
+       if (strcmp(str, VFD_DRIVER_FILENAME)) {
+               VFDTRACE(0,
+                       (FUNC ": Invalid original file name\n"));
+
+               ret = ERROR_BAD_DRIVER;
+
+               goto cleanup;
+       }
+
+       if (fixedinfo->dwFileVersionMS          != MAKELONG(VFD_DRIVER_MINOR, VFD_DRIVER_MAJOR) ||
+               fixedinfo->dwProductVersionMS   != MAKELONG(VFD_PRODUCT_MINOR, VFD_PRODUCT_MAJOR)) {
+
+               VFDTRACE(0,
+                       (FUNC ": Invalid version values - file:%08x, prod: %08x\n",
+                       fixedinfo->dwFileVersionMS, fixedinfo->dwProductVersionMS));
+
+               ret = ERROR_BAD_DRIVER;
+
+               goto cleanup;
+       }
+
+       //      Ensure that the driver binary is located on a local drive
+       //      because device driver cannot be started on network drives.
+
+       if (*sFileName == '\\' && *(sFileName + 1) == '\\') {
+               //      full path is a UNC path -- \\server\dir\...
+
+               VFDTRACE(0,
+                       (FUNC ": Driver is located on a network drive\n"));
+
+               return ERROR_NETWORK_ACCESS_DENIED;
+       }
+       else {
+               //      ensure that the drive letter is not a network drive
+
+               CHAR root[] = " :\\";
+
+               root[0] = *sFileName;
+
+               if (GetDriveType(root) == DRIVE_REMOTE) {
+                       // the drive is a network drive
+
+                       VFDTRACE(0,
+                               (FUNC ": Driver is located on a network drive\n"));
+
+                       return ERROR_NETWORK_ACCESS_DENIED;
+               }
+       }
+
+cleanup:
+       LocalFree(info);
+
+       return ret;
+}
+
+//
+//     check an image file
+//
+DWORD WINAPI VfdCheckImageFile(
+       PCSTR                   sFileName,
+       PDWORD                  pAttributes,
+       PVFD_FILETYPE   pFileType,
+       PULONG                  pImageSize)
+{
+#undef FUNC
+#define FUNC           "VfdCheckImageFile"
+       HANDLE                  hFile;
+       DWORD                   ret = ERROR_SUCCESS;
+
+       if (!sFileName || !*sFileName || !pAttributes || !pImageSize || !pFileType) {
+               return ERROR_INVALID_PARAMETER;
+       }
+
+       //      get file attributes
+
+       *pAttributes = GetFileAttributes(sFileName);
+
+       if (*pAttributes == INVALID_FILE_ATTRIBUTES) {
+               ret = GetLastError();
+
+               if (ret != ERROR_FILE_NOT_FOUND) {
+                       VFDTRACE(0,
+                               (FUNC ": GetFileAttributes(%s) - %s\n",
+                               sFileName, SystemMessage(ret)));
+               }
+
+               return ret;
+       }
+
+       //      Open the target file
+
+       hFile = CreateFile(sFileName, GENERIC_READ | GENERIC_WRITE,
+               0, NULL, OPEN_EXISTING, 0, NULL);
+
+       if (hFile == INVALID_HANDLE_VALUE) {
+
+               //      failed to open
+
+               ret = GetLastError();
+
+               if (ret != ERROR_ACCESS_DENIED) {
+                       VFDTRACE(0,
+                               (FUNC ": CreateFile(%s) - %s\n",
+                               sFileName, SystemMessage(ret)));
+
+                       return ret;
+               }
+
+               // try opening it read-only
+
+               hFile = CreateFile(sFileName, GENERIC_READ,
+                       FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+
+               if (hFile == INVALID_HANDLE_VALUE) {
+
+                       // cannot open even read-only
+
+                       ret = GetLastError();
+
+                       VFDTRACE(0,
+                               (FUNC ": CreateFile(%s) - %s\n",
+                               sFileName, SystemMessage(ret)));
+
+                       return ret;
+               }
+
+               // file can be opened read-only
+               *pAttributes |= FILE_ATTRIBUTE_READONLY;
+               ret = ERROR_SUCCESS;
+       }
+
+       //      check if the image is an IMZ file
+
+       if (ExtractZipInfo(hFile, pImageSize) == ERROR_SUCCESS) {
+               *pFileType = VFD_FILETYPE_ZIP;
+       }
+       else {
+               *pImageSize = GetFileSize(hFile, NULL);
+               *pFileType      = VFD_FILETYPE_RAW;
+       }
+
+       CloseHandle(hFile);
+
+       return ret;
+}
+
+//
+//     Create a formatted new image file
+//
+DWORD WINAPI VfdCreateImageFile(
+       PCSTR                   sFileName,
+       VFD_MEDIA               nMediaType,
+       VFD_FILETYPE    nFileType,
+       BOOL                    bOverWrite)
+{
+#undef FUNC
+#define FUNC           "VfdCreateImageFile"
+       HANDLE                  hFile;
+       ULONG                   file_size;
+       PUCHAR                  image_buf = NULL;
+       DWORD                   result;
+       DWORD                   ret = ERROR_SUCCESS;
+
+       if (nFileType != VFD_FILETYPE_RAW) {
+               return ERROR_INVALID_PARAMETER;
+       }
+
+       file_size = VfdGetMediaSize(nMediaType);
+
+       if (file_size == 0) {
+               return ERROR_INVALID_PARAMETER;
+       }
+
+       hFile = CreateFile(sFileName, GENERIC_WRITE, 0, NULL,
+               bOverWrite ? CREATE_ALWAYS : CREATE_NEW, 0, NULL);
+
+       if (hFile == INVALID_HANDLE_VALUE) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": CreateFile - %s",
+                       SystemMessage(ret)));
+
+               return ret;
+       }
+
+       image_buf = (PUCHAR)LocalAlloc(LPTR, file_size);
+
+       if (image_buf == NULL) {
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": LocalAlloc - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       FormatBufferFat(image_buf, VFD_BYTE_TO_SECTOR(file_size));
+
+       if (!WriteFile(hFile, image_buf, file_size, &result, NULL) ||
+               file_size != result) {
+
+               ret = GetLastError();
+
+               VFDTRACE(0,
+                       (FUNC ": WriteFile - %s",
+                       SystemMessage(ret)));
+
+               goto exit_func;
+       }
+
+       SetEndOfFile(hFile);
+
+exit_func:
+       CloseHandle(hFile);
+
+       if (image_buf) {
+               LocalFree(image_buf);
+       }
+
+       return ret;
+}
+
+
+//
+// choose first available drive letter
+//
+CHAR WINAPI VfdChooseLetter()
+{
+       DWORD   logical_drives = GetLogicalDrives();
+       CHAR    drive_letter = 'A';
+
+       if (logical_drives == 0) {
+               return '\0';
+       }
+
+       while (logical_drives & 0x1) {
+               logical_drives >>= 1;
+               drive_letter++;
+       }
+
+       if (drive_letter > 'Z') {
+               return '\0';
+       }
+
+       return drive_letter;
+}
+
+//
+//     media type functions
+//
+static const struct
+{
+       ULONG   Size;
+       PCSTR   Name;
+}
+media_tbl[VFD_MEDIA_MAX] =
+{
+       { 0,                                            "" },                                   //      VFD_MEDIA_NONE,
+       { VFD_SECTOR_TO_BYTE(320),      "5.25\" 160KB" },               //      VFD_MEDIA_F5_160
+       { VFD_SECTOR_TO_BYTE(360),      "5.25\" 180KB" },               //      VFD_MEDIA_F5_180
+       { VFD_SECTOR_TO_BYTE(640),      "5.25\" 320KB" },               //      VFD_MEDIA_F5_320
+       { VFD_SECTOR_TO_BYTE(720),      "5.25\" 360KB" },               //      VFD_MEDIA_F5_360
+       { VFD_SECTOR_TO_BYTE(1280),     "3.5\"  640KB" },               //      VFD_MEDIA_F3_640
+       { VFD_SECTOR_TO_BYTE(1280),     "5.25\" 640KB" },               //      VFD_MEDIA_F5_640
+       { VFD_SECTOR_TO_BYTE(1440),     "3.5\"  720KB" },               //      VFD_MEDIA_F3_720
+       { VFD_SECTOR_TO_BYTE(1440),     "5.25\" 720KB" },               //      VFD_MEDIA_F5_720
+       { VFD_SECTOR_TO_BYTE(1640),     "3.5\"  820KB" },               //      VFD_MEDIA_F3_820
+       { VFD_SECTOR_TO_BYTE(2400),     "3.5\"  1.2MB" },               //      VFD_MEDIA_F3_1P2
+       { VFD_SECTOR_TO_BYTE(2400),     "5.25\" 1.2MB" },               //      VFD_MEDIA_F5_1P2
+       { VFD_SECTOR_TO_BYTE(2880),     "3.5\"  1.44MB" },              //      VFD_MEDIA_F3_1P4
+       { VFD_SECTOR_TO_BYTE(3360),     "3.5\"  1.68MB DMF" },  //      VFD_MEDIA_F3_1P6
+       { VFD_SECTOR_TO_BYTE(3444),     "3.5\"  1.72MB DMF" },  //      VFD_MEDIA_F3_1P7
+       { VFD_SECTOR_TO_BYTE(5760),     "3.5\"  2.88MB"}                //      VFD_MEDIA_F3_2P8
+};
+
+//     Lookup the largest media to fit in a size
+
+VFD_MEDIA WINAPI VfdLookupMedia(
+       ULONG                   nSize)
+{
+       VFD_MEDIA i;
+
+       for (i = 1; i < VFD_MEDIA_MAX; i++) {
+               if (nSize < media_tbl[i].Size) {
+                       break;
+               }
+       }
+
+       return (--i);
+}
+
+//     Get media size (in bytes) of a media type
+
+ULONG WINAPI VfdGetMediaSize(
+       VFD_MEDIA               nMediaType)
+{
+       return nMediaType < VFD_MEDIA_MAX ? media_tbl[nMediaType].Size : 0;
+}
+
+//     Get media type name
+
+PCSTR WINAPI VfdMediaTypeName(
+       VFD_MEDIA               nMediaType)
+{
+       return nMediaType < VFD_MEDIA_MAX ? media_tbl[nMediaType].Name : NULL;
+}