{*******************************************************}
{                                                       }
{       Tocsg.Driver                                    }
{                                                       }
{       Copyright (C) 2022 kku                          }
{                                                       }
{*******************************************************}

unit Tocsg.Driver;

interface

uses
  Winapi.Windows, EM.winioctl, System.Classes, System.SysUtils, Tocsg.Obj;

const
  FAIL_EJECT = '!FAIL';
  METHOD_BUFFERED   = 0;
  METHOD_IN_DIRECT  = 1;
  METHOD_OUT_DIRECT = 2;
  METHOD_NEITHER    = 3;

  IOCTL_DISK_BASE = FILE_DEVICE_DISK;

  IOCTL_VOLUME_BASE = DWORD('V');
  {$EXTERNALSYM IOCTL_VOLUME_BASE}

  IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = (
    (IOCTL_VOLUME_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or
    (0 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS}

/////// GPT를 인식하기 위해 추가 2011-02-10 sunk
  IOCTL_DISK_GET_PARTITION_INFO = (
    (IOCTL_DISK_BASE shl 16) or (FILE_READ_ACCESS shl 14) or
    ($0001 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_DISK_GET_PARTITION_INFO}

  IOCTL_DISK_SET_PARTITION_INFO = (
    (IOCTL_DISK_BASE shl 16) or ((FILE_READ_ACCESS or FILE_WRITE_ACCESS) shl 14) or
    ($0002 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_DISK_SET_PARTITION_INFO}

  IOCTL_DISK_GET_DRIVE_LAYOUT = (
    (IOCTL_DISK_BASE shl 16) or (FILE_READ_ACCESS shl 14) or
    ($0003 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_DISK_GET_DRIVE_LAYOUT}

  IOCTL_DISK_SET_DRIVE_LAYOUT = (
    (IOCTL_DISK_BASE shl 16) or ((FILE_READ_ACCESS or FILE_WRITE_ACCESS) shl 14) or
    ($0004 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_DISK_SET_DRIVE_LAYOUT}

  IOCTL_VOLUME_IS_CLUSTERED = (
    (IOCTL_VOLUME_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or
    (12 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_VOLUME_IS_CLUSTERED}

  IOCTL_DISK_GET_PARTITION_INFO_EX = (
    (IOCTL_DISK_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($0012 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_DISK_GET_PARTITION_INFO_EX}

  IOCTL_DISK_SET_PARTITION_INFO_EX = (
    (IOCTL_DISK_BASE shl 16) or ((FILE_READ_ACCESS or FILE_WRITE_ACCESS) shl 14) or
    ($0013 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_DISK_SET_PARTITION_INFO_EX}

  IOCTL_DISK_GET_DRIVE_LAYOUT_EX = (
    (IOCTL_DISK_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($0014 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_DISK_GET_DRIVE_LAYOUT_EX}

  IOCTL_DISK_SET_DRIVE_LAYOUT_EX = (
    (IOCTL_DISK_BASE shl 16) or ((FILE_READ_ACCESS or FILE_WRITE_ACCESS) shl 14) or
    ($0015 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_DISK_SET_DRIVE_LAYOUT_EX}

  IOCTL_STORAGE_GET_DEVICE_NUMBER = (
    (IOCTL_STORAGE_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($0420 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_STORAGE_GET_DEVICE_NUMBER}

  IOCTL_DISK_GET_LENGTH_INFO = (
    (IOCTL_DISK_BASE shl 16) or (FILE_READ_ACCESS shl 14) or
    ($0017 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_DISK_GET_LENGTH_INFO}

  IOCTL_DISK_GET_DRIVE_GEOMETRY_EX = (
    (IOCTL_DISK_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($0028 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_DISK_GET_DRIVE_GEOMETRY_EX}

  IOCTL_STORAGE_QUERY_PROPERTY = (
    (IOCTL_STORAGE_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($0500 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_STORAGE_QUERY_PROPERTY}

  IOCTL_STORAGE_GET_MEDIA_TYPES_EX = (
    (IOCTL_STORAGE_BASE shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($0301 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_STORAGE_GET_MEDIA_TYPES_EX}

  IOCTL_STORAGE_MEDIA_REMOVAL = (
    (IOCTL_STORAGE_BASE shl 16) or (FILE_READ_ACCESS shl 14) or
    ($0201 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_STORAGE_MEDIA_REMOVAL}

  IOCTL_STORAGE_EJECTION_CONTROL = $2D0940;//CTL_CODE(IOCTL_STORAGE_BASE, $0250, METHOD_BUFFERED, FILE_ANY_ACCESS); //$2D0940
  {$EXTERNALSYM IOCTL_STORAGE_EJECTION_CONTROL}

  IOCTL_STORAGE_EJECT_MEDIA = (
    (IOCTL_STORAGE_BASE shl 16) or (FILE_READ_ACCESS shl 14) or
    ($0202 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM IOCTL_STORAGE_EJECT_MEDIA}

  FSCTL_LOCK_VOLUME = (
    (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($0006 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM FSCTL_LOCK_VOLUME}

  FSCTL_UNLOCK_VOLUME	= (
    (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($0007 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM FSCTL_UNLOCK_VOLUME}

  FSCTL_DISMOUNT_VOLUME	= (
    (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($0008 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM FSCTL_DISMOUNT_VOLUME}

  FSCTL_IS_VOLUME_MOUNTED = (
    (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($000A shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM FSCTL_IS_VOLUME_MOUNTED}

  FSCTL_FILESYSTEM_GET_STATISTICS = (
    (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($0018 shl 2) or METHOD_BUFFERED); // FILESYSTEM_STATISTICS
  {$EXTERNALSYM FSCTL_FILESYSTEM_GET_STATISTICS}

  FSCTL_GET_NTFS_VOLUME_DATA = (
    (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($0019 shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM FSCTL_GET_NTFS_VOLUME_DATA}

  FSCTL_GET_NTFS_FILE_RECORD = (
    (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($001A shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM FSCTL_GET_NTFS_FILE_RECORD}

  FSCTL_GET_VOLUME_BITMAP = (
    (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($001B shl 2) or METHOD_NEITHER);
  {$EXTERNALSYM FSCTL_GET_VOLUME_BITMAP}

  FSCTL_GET_RETRIEVAL_POINTERS = (
    (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($001C shl 2) or METHOD_NEITHER);
  {$EXTERNALSYM FSCTL_GET_RETRIEVAL_POINTERS}

  FSCTL_MOVE_FILE = (
    (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_SPECIAL_ACCESS shl 14) or
    ($001D shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM FSCTL_MOVE_FILE}

  FSCTL_IS_VOLUME_DIRTY = (
    (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($001E shl 2) or METHOD_BUFFERED);
  {$EXTERNALSYM FSCTL_IS_VOLUME_DIRTY}

  FSCTL_ALLOW_EXTENDED_DASD_IO = (
    (FILE_DEVICE_FILE_SYSTEM shl 16) or (FILE_ANY_ACCESS shl 14) or
    ($0020 shl 2) or METHOD_NEITHER);
  {$EXTERNALSYM FSCTL_ALLOW_EXTENDED_DASD_IO}

  GUID_DEVINTERFACE_DISK: TGUID = (
    D1:$53f56307; D2:$b6bf; D3:$11d0; D4:($94, $f2, $00, $a0, $c9, $1e, $fb, $8b));
  {$EXTERNALSYM GUID_DEVINTERFACE_DISK}
  GUID_DEVINTERFACE_CDROM: TGUID = (
    D1:$53f56308; D2:$b6bf; D3:$11d0; D4:($94, $f2, $00, $a0, $c9, $1e, $fb, $8b));
//  GUID_DEVINTERFACE_CDROM: TGUID = '{53f56308-b6bf-11d0-94f2-00a0c91efb8b}';
  {$EXTERNALSYM GUID_DEVINTERFACE_CDROM}
  GUID_DEVINTERFACE_PARTITION: TGUID = (
    D1:$53f5630a; D2:$b6bf; D3:$11d0; D4:($94, $f2, $00, $a0, $c9, $1e, $fb, $8b));
  {$EXTERNALSYM GUID_DEVINTERFACE_PARTITION}
  GUID_DEVINTERFACE_TAPE: TGUID = (
    D1:$53f5630b; D2:$b6bf; D3:$11d0; D4:($94, $f2, $00, $a0, $c9, $1e, $fb, $8b));
  {$EXTERNALSYM GUID_DEVINTERFACE_TAPE}
  GUID_DEVINTERFACE_WRITEONCEDISK: TGUID = (
    D1:$53f5630c; D2:$b6bf; D3:$11d0; D4:($94, $f2, $00, $a0, $c9, $1e, $fb, $8b));
  {$EXTERNALSYM GUID_DEVINTERFACE_WRITEONCEDISK}
  GUID_DEVINTERFACE_VOLUME: TGUID = (
    D1:$53f5630d; D2:$b6bf; D3:$11d0; D4:($94, $f2, $00, $a0, $c9, $1e, $fb, $8b));
  {$EXTERNALSYM GUID_DEVINTERFACE_VOLUME}
  GUID_DEVINTERFACE_MEDIUMCHANGER: TGUID = (
    D1:$53f56310; D2:$b6bf; D3:$11d0; D4:($94, $f2, $00, $a0, $c9, $1e, $fb, $8b));
  {$EXTERNALSYM GUID_DEVINTERFACE_MEDIUMCHANGER}
  GUID_DEVINTERFACE_FLOPPY: TGUID = (
    D1:$53f56311; D2:$b6bf; D3:$11d0; D4:($94, $f2, $00, $a0, $c9, $1e, $fb, $8b));
  {$EXTERNALSYM GUID_DEVINTERFACE_FLOPPY}
  GUID_DEVINTERFACE_CDCHANGER: TGUID = (
    D1:$53f56312; D2:$b6bf; D3:$11d0; D4:($94, $f2, $00, $a0, $c9, $1e, $fb, $8b));
  {$EXTERNALSYM GUID_DEVINTERFACE_CDCHANGER}
  GUID_DEVINTERFACE_STORAGEPORT: TGUID = (
    D1:$2accfe60; D2:$c130; D3:$11d2; D4:($b0, $82, $00, $a0, $c9, $1e, $fb, $8b));
  {$EXTERNALSYM GUID_DEVINTERFACE_STORAGEPORT}
  GUID_DEVINTERFACE_COMPORT: TGUID = (
    D1:$86e0d1e0; D2:$8089; D3:$11d0; D4:($9c, $e4, $08, $00, $3e, $30, $1f, $73));
  {$EXTERNALSYM GUID_DEVINTERFACE_COMPORT}
  GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR: TGUID = (
    D1:$4D36E978; D2:$E325; D3:$11CE; D4:($BF, $C1, $08, $00, $2B, $E1, $03, $18));
  {$EXTERNALSYM GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR}

  GUID_DEVCLASS_NET: TGUID = (
    D1:$4d36e972; D2:$e325; D3:$11ce; D4:($bf, $c1, $08, $00, $2b, $e1, $03, $18));
  {$EXTERNALSYM GUID_DEVCLASS_NET}

  GUID_DEVCLASS_BLUETOOTH: TGUID = (
    D1:$e0cbf06c; D2:$cd8b; D3:$4647; D4:($bb, $8a, $26, $3b, $43, $f0, $f9, $74));
  {$EXTERNALSYM GUID_DEVCLASS_BLUETOOTH}

  GUID_DEVCLASS_USB: TGUID = (
    D1:$36fc9e60; D2:$c465; D3:$11cf; D4:($80, $56, $44, $45, $53, $54, $00, $00));
  {$EXTERNALSYM GUID_DEVCLASS_USB}

  GUID_DEVCLASS_USB_DEVICE: TGUID = (
    D1:$88bae032; D2:$5a81; D3:$49f0; D4:($bc, $3d, $a4, $ff, $13, $82, $16, $d6));
  {$EXTERNALSYM GUID_DEVCLASS_USB}

  GUID_DEVCLASS_WPD: TGUID = (
    D1:$eec5ad98; D2:$8080; D3:$425f; D4:($92, $2a, $da, $bf, $3d, $e3, $f6, $9a));
  {$EXTERNALSYM GUID_DEVCLASS_WPD}

  DIGCF_DEFAULT                       = $00000001;
  DIGCF_PRESENT                       = $00000002;
  DIGCF_ALLCLASSES                    = $00000004;
  DIGCF_PROFILE                       = $00000008;
  DIGCF_DEVICEINTERFACE               = $00000010;

  SPDRP_DEVICEDESC                  = $00000000; // 설명
  SPDRP_HARDWAREID                  = $00000001;
  SPDRP_COMPATIBLEIDS               = $00000002;
  SPDRP_UNUSED0                     = $00000003;
  SPDRP_SERVICE                     = $00000004;
  SPDRP_UNUSED1                     = $00000005;
  SPDRP_UNUSED2                     = $00000006;
  SPDRP_CLASS                       = $00000007;
  SPDRP_CLASSGUID                   = $00000008;
  SPDRP_DRIVER                      = $00000009;
  SPDRP_CONFIGFLAGS                 = $0000000A;
  SPDRP_MFG                         = $0000000B; // 업체명
  SPDRP_FRIENDLYNAME                = $0000000C;
  SPDRP_LOCATION_INFORMATION        = $0000000D;
  SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = $0000000E;
  SPDRP_CAPABILITIES                = $0000000F;
  SPDRP_UI_NUMBER                   = $00000010;
  SPDRP_UPPERFILTERS                = $00000011;
  SPDRP_LOWERFILTERS                = $00000012;
  SPDRP_BUSTYPEGUID                 = $00000013;
  SPDRP_LEGACYBUSTYPE               = $00000014;
  SPDRP_BUSNUMBER                   = $00000015;
  SPDRP_ENUMERATOR_NAME             = $00000016;
  SPDRP_SECURITY                    = $00000017;
  SPDRP_SECURITY_SDS                = $00000018;
  SPDRP_DEVTYPE                     = $00000019;
  SPDRP_EXCLUSIVE                   = $0000001A;
  SPDRP_CHARACTERISTICS             = $0000001B;
  SPDRP_ADDRESS                     = $0000001C;
  SPDRP_UI_NUMBER_DESC_FORMAT       = $0000001D;
  SPDRP_DEVICE_POWER_DATA           = $0000001E;
  SPDRP_REMOVAL_POLICY              = $0000001F;
  SPDRP_LOCATION_PATHS              = $00000023;
  SPDRP_BASE_CONTAINERID            = $00000024;
  SPDRP_MAXIMUM_PROPERTY            = $00000025;

  DN_ROOT_ENUMERATED = $00000001; // Was enumerated by ROOT
  DN_DRIVER_LOADED   = $00000002; // Has Register_Device_Driver
  DN_ENUM_LOADED     = $00000004; // Has Register_Enumerator
  DN_STARTED         = $00000008; // Is currently configured
  DN_MANUAL          = $00000010; // Manually installed
  DN_NEED_TO_ENUM    = $00000020; // May need reenumeration
  DN_NOT_FIRST_TIME  = $00000040; // Has received a config
  DN_HARDWARE_ENUM   = $00000080; // Enum generates hardware ID
  DN_LIAR            = $00000100; // Lied about can reconfig once
  DN_HAS_MARK        = $00000200; // Not CM_Create_DevInst lately
  DN_HAS_PROBLEM     = $00000400; // Need device installer
  DN_FILTERED        = $00000800; // Is filtered
  DN_MOVED           = $00001000; // Has been moved
  DN_DISABLEABLE     = $00002000; // Can be disabled
  DN_REMOVABLE       = $00004000; // Can be removed
  DN_PRIVATE_PROBLEM = $00008000; // Has a private problem
  DN_MF_PARENT       = $00010000; // Multi function parent
  DN_MF_CHILD        = $00020000; // Multi function child
  DN_WILL_BE_REMOVED = $00040000; // DevInst is being removed

  CR_SUCCESS = 0;
  CM_PROB_NOT_CONFIGURED             = $00000001;   // no config for device
  CM_PROB_DEVLOADER_FAILED           = $00000002;   // service load failed
  CM_PROB_OUT_OF_MEMORY              = $00000003;   // out of memory
  CM_PROB_ENTRY_IS_WRONG_TYPE        = $00000004;   //
  CM_PROB_LACKED_ARBITRATOR          = $00000005;   //
  CM_PROB_BOOT_CONFIG_CONFLICT       = $00000006;   // boot config conflict
  CM_PROB_FAILED_FILTER              = $00000007;   //
  CM_PROB_DEVLOADER_NOT_FOUND        = $00000008;   // Devloader not found
  CM_PROB_INVALID_DATA               = $00000009;   // Invalid ID
  CM_PROB_FAILED_START               = $0000000A;   //
  CM_PROB_LIAR                       = $0000000B;   //
  CM_PROB_NORMAL_CONFLICT            = $0000000C;   // config conflict
  CM_PROB_NOT_VERIFIED               = $0000000D;   //
  CM_PROB_NEED_RESTART               = $0000000E;   // requires restart
  CM_PROB_REENUMERATION              = $0000000F;   //
  CM_PROB_PARTIAL_LOG_CONF           = $00000010;   //
  CM_PROB_UNKNOWN_RESOURCE           = $00000011;   // unknown res type
  CM_PROB_REINSTALL                  = $00000012;   //
  CM_PROB_REGISTRY                   = $00000013;   //
  CM_PROB_VXDLDR                     = $00000014;   // WINDOWS 95 ONLY
  CM_PROB_WILL_BE_REMOVED            = $00000015;   // devinst will remove
  CM_PROB_DISABLED                   = $00000016;   // devinst is disabled
  CM_PROB_DEVLOADER_NOT_READY        = $00000017;   // Devloader not ready
  CM_PROB_DEVICE_NOT_THERE           = $00000018;   // device doesn't exist
  CM_PROB_MOVED                      = $00000019;   //
  CM_PROB_TOO_EARLY                  = $0000001A;   //
  CM_PROB_NO_VALID_LOG_CONF          = $0000001B;   // no valid log config
  CM_PROB_FAILED_INSTALL             = $0000001C;   // install failed
  CM_PROB_HARDWARE_DISABLED          = $0000001D;   // device disabled
  CM_PROB_CANT_SHARE_IRQ             = $0000001E;   // can't share IRQ
  CM_PROB_FAILED_ADD                 = $0000001F;   // driver failed add
  CM_PROB_DISABLED_SERVICE           = $00000020;   // service's Start = 4
  CM_PROB_TRANSLATION_FAILED         = $00000021;   // resource translation failed
  CM_PROB_NO_SOFTCONFIG              = $00000022;   // no soft config
  CM_PROB_BIOS_TABLE                 = $00000023;   // device missing in BIOS table
  CM_PROB_IRQ_TRANSLATION_FAILED     = $00000024;   // IRQ translator failed
  CM_PROB_FAILED_DRIVER_ENTRY        = $00000025;   // DriverEntry() failed.
  CM_PROB_DRIVER_FAILED_PRIOR_UNLOAD = $00000026;   // Driver should have unloaded.
  CM_PROB_DRIVER_FAILED_LOAD         = $00000027;   // Driver load unsuccessful.
  CM_PROB_DRIVER_SERVICE_KEY_INVALID = $00000028;   // Error accessing driver's service key
  CM_PROB_LEGACY_SERVICE_NO_DEVICES  = $00000029;   // Loaded legacy service created no devices
  CM_PROB_DUPLICATE_DEVICE           = $0000002A;   // Two devices were discovered with the same name
  CM_PROB_FAILED_POST_START          = $0000002B;   // The drivers set the device state to failed
  CM_PROB_HALTED                     = $0000002C;   // This device was failed post start via usermode
  CM_PROB_PHANTOM                    = $0000002D;   // The devinst currently exists only in the registry
  CM_PROB_SYSTEM_SHUTDOWN            = $0000002E;   // The system is shutting down
  CM_PROB_HELD_FOR_EJECT             = $0000002F;   // The device is offline awaiting removal
  CM_PROB_DRIVER_BLOCKED             = $00000030;   // One or more drivers is blocked from loading
  CM_PROB_REGISTRY_TOO_LARGE         = $00000031;   // System hive has grown too large
  CM_PROB_SETPROPERTIES_FAILED       = $00000032;   // Failed to apply one or more registry properties
  CM_PROB_WAITING_ON_DEPENDENCY      = $00000033;   // Device is stalled waiting on a dependency to start
  CM_PROB_UNSIGNED_DRIVER            = $00000034;   // Failed load driver due to unsigned image.
  CM_PROB_USED_BY_DEBUGGER           = $00000035;   // Device is being used by kernel debugger
  CM_PROB_DEVICE_RESET               = $00000036;   // Device is being reset
  CM_PROB_CONSOLE_LOCKED             = $00000037;   // Device is blocked while console is locked
  CM_PROB_NEED_CLASS_CONFIG          = $00000038;   // Device needs extended class configuration to start
  CM_PROB_GUEST_ASSIGNMENT_FAILED    = $00000039;   // Assignment to guest partition failed

  CM_REMOVE_UI_OK              = $00000000;
  CM_REMOVE_UI_NOT_OK          = $00000001;
  CM_REMOVE_NO_RESTART         = $00000002;
  CM_REMOVE_BITS               = $00000003;

  DIF_SELECTDEVICE                    = $00000001;
  DIF_INSTALLDEVICE                   = $00000002;
  DIF_ASSIGNRESOURCES                 = $00000003;
  DIF_PROPERTIES                      = $00000004;
  DIF_REMOVE                          = $00000005;
  DIF_FIRSTTIMESETUP                  = $00000006;
  DIF_FOUNDDEVICE                     = $00000007;
  DIF_SELECTCLASSDRIVERS              = $00000008;
  DIF_VALIDATECLASSDRIVERS            = $00000009;
  DIF_INSTALLCLASSDRIVERS             = $0000000A;
  DIF_CALCDISKSPACE                   = $0000000B;
  DIF_DESTROYPRIVATEDATA              = $0000000C;
  DIF_VALIDATEDRIVER                  = $0000000D;
  DIF_DETECT                          = $0000000F;
  DIF_INSTALLWIZARD                   = $00000010;
  DIF_DESTROYWIZARDDATA               = $00000011;
  DIF_PROPERTYCHANGE                  = $00000012;
  DIF_ENABLECLASS                     = $00000013;
  DIF_DETECTVERIFY                    = $00000014;
  DIF_INSTALLDEVICEFILES              = $00000015;
  DIF_UNREMOVE                        = $00000016;
  DIF_SELECTBESTCOMPATDRV             = $00000017;
  DIF_ALLOW_INSTALL                   = $00000018;
  DIF_REGISTERDEVICE                  = $00000019;
  DIF_NEWDEVICEWIZARD_PRESELECT       = $0000001A;
  DIF_NEWDEVICEWIZARD_SELECT          = $0000001B;
  DIF_NEWDEVICEWIZARD_PREANALYZE      = $0000001C;
  DIF_NEWDEVICEWIZARD_POSTANALYZE     = $0000001D;
  DIF_NEWDEVICEWIZARD_FINISHINSTALL   = $0000001E;
  DIF_UNUSED1                         = $0000001F;
  DIF_INSTALLINTERFACES               = $00000020;
  DIF_DETECTCANCEL                    = $00000021;
  DIF_REGISTER_COINSTALLERS           = $00000022;
  DIF_ADDPROPERTYPAGE_ADVANCED        = $00000023;
  DIF_ADDPROPERTYPAGE_BASIC           = $00000024;
  DIF_RESERVED1                       = $00000025;
  DIF_TROUBLESHOOTER                  = $00000026;
  DIF_POWERMESSAGEWAKE                = $00000027;
  DIF_ADDREMOTEPROPERTYPAGE_ADVANCED  = $00000028;
  DIF_UPDATEDRIVER_UI                 = $00000029;
  DIF_FINISHINSTALL_ACTION            = $0000002A;
  DIF_RESERVED2                       = $00000030;

  DICS_FLAG_GLOBAL         = $00000001;  // make change in all hardware profiles
  DICS_FLAG_CONFIGSPECIFIC = $00000002;  // make change in specified profile only
  DICS_FLAG_CONFIGGENERAL  = $00000004;  // 1 or more hardware profile-specific

  DICS_ENABLE      = $00000001;
  DICS_DISABLE     = $00000002;
  DICS_PROPCHANGE  = $00000003;
  DICS_START       = $00000004;
  DICS_STOP        = $00000005;
                                               // changes to follow.

//
// Support for GUID Partition Table (GPT) disks.
//

//
// There are currently two ways a disk can be partitioned. With a traditional
// AT-style master boot record (PARTITION_STYLE_MBR) and with a new, GPT
// partition table (PARTITION_STYLE_GPT). RAW is for an unrecognizable
// partition style. There are a very limited number of things you can
// do with a RAW partititon.
//

type
  GUID = TGUID;
  {$NODEFINE GUID}
  LPGUID = ^GUID;
  {$NODEFINE LPGUID}
  CLSID = TGUID;
  {$NODEFINE CLSID}

  ULONG64 = Int64;
  {$EXTERNALSYM ULONG64}
  PULONG64 = ^ULONG64;
  {$EXTERNALSYM PULONG64}
  DWORD64 = Int64;
  {$EXTERNALSYM DWORD64}
  PDWORD64 = ^DWORD64;
  {$EXTERNALSYM PDWORD64}
  UINT64 = Int64;
  {$EXTERNALSYM UINT64}
  PUINT64 = ^UINT64;
  {$EXTERNALSYM PUINT64}

  PPREVENT_MEDIA_REMOVAL = ^PREVENT_MEDIA_REMOVAL;
  {$EXTERNALSYM PPREVENT_MEDIA_REMOVAL}
  _PREVENT_MEDIA_REMOVAL = record
    PreventMediaRemoval: ByteBool;
  end;
  {$EXTERNALSYM _PREVENT_MEDIA_REMOVAL}
  PREVENT_MEDIA_REMOVAL = _PREVENT_MEDIA_REMOVAL;
  {$EXTERNALSYM PREVENT_MEDIA_REMOVAL}
  TPreventMediaRemoval = PREVENT_MEDIA_REMOVAL;
  PPreventMediaRemoval = PPREVENT_MEDIA_REMOVAL;

  PSTORAGE_DEVICE_NUMBER = ^STORAGE_DEVICE_NUMBER;
  {$EXTERNALSYM PSTORAGE_DEVICE_NUMBER}
  _STORAGE_DEVICE_NUMBER = record
    //
    // The FILE_DEVICE_XXX type for this device.
    //
    DeviceType: DEVICE_TYPE;
    //
    // The number of this device
    //
    DeviceNumber: DWORD;
    //
    // If the device is partitionable, the partition number of the device.
    // Otherwise -1
    //
    PartitionNumber: DWORD;
  end;
  {$EXTERNALSYM _STORAGE_DEVICE_NUMBER}
  STORAGE_DEVICE_NUMBER = _STORAGE_DEVICE_NUMBER;
  {$EXTERNALSYM STORAGE_DEVICE_NUMBER}
  TStorageDeviceNumber = STORAGE_DEVICE_NUMBER;
  PStorageDeviceNumber = PSTORAGE_DEVICE_NUMBER;


  _PARTITION_STYLE = (
    PARTITION_STYLE_MBR,
    PARTITION_STYLE_GPT,
    PARTITION_STYLE_RAW);
  {$EXTERNALSYM _PARTITION_STYLE}
  PARTITION_STYLE = _PARTITION_STYLE;
  {$EXTERNALSYM PARTITION_STYLE}
  TPartitionStyle = PARTITION_STYLE;

//
// The following structure defines information in a GPT partition that is
// not common to both GPT and MBR partitions.
//

  PPARTITION_INFORMATION_GPT = ^PARTITION_INFORMATION_GPT;
  {$EXTERNALSYM PPARTITION_INFORMATION_GPT}
  _PARTITION_INFORMATION_GPT = record
    PartitionType: GUID; // Partition type. See table 16-3.
    PartitionId: GUID; // Unique GUID for this partition.
    Attributes: DWORD64; // See table 16-4.
    Name: array [0..35] of WCHAR; // Partition Name in Unicode.
  end;
  {$EXTERNALSYM _PARTITION_INFORMATION_GPT}
  PARTITION_INFORMATION_GPT = _PARTITION_INFORMATION_GPT;
  {$EXTERNALSYM PARTITION_INFORMATION_GPT}
  TPartitionInformationGpt = PARTITION_INFORMATION_GPT;
  PPartitionInformationGpt = PPARTITION_INFORMATION_GPT;


{
  _PREVENT_MEDIA_REMOVAL = record
    PreventMediaRemoval : Boolean;
  end;
  PREVENT_MEDIA_REMOVAL = _PREVENT_MEDIA_REMOVAL;
 }

//
//  The following are GPT partition attributes applicable for any
//  partition type. These attributes are not OS-specific
//

const
  GPT_ATTRIBUTE_PLATFORM_REQUIRED = $0000000000000001;
  {$EXTERNALSYM GPT_ATTRIBUTE_PLATFORM_REQUIRED}

//
// The following are GPT partition attributes applicable when the
// PartitionType is PARTITION_BASIC_DATA_GUID.
//

  GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER = DWORD($8000000000000000);
  {$EXTERNALSYM GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER}
  GPT_BASIC_DATA_ATTRIBUTE_HIDDEN          = $4000000000000000;
  {$EXTERNALSYM GPT_BASIC_DATA_ATTRIBUTE_HIDDEN}
  GPT_BASIC_DATA_ATTRIBUTE_READ_ONLY       = $1000000000000000;
  {$EXTERNALSYM GPT_BASIC_DATA_ATTRIBUTE_READ_ONLY}

//
// The following structure defines information in an MBR partition that is not
// common to both GPT and MBR partitions.
//

type
  PPARTITION_INFORMATION_MBR = ^PARTITION_INFORMATION_MBR;
  {$EXTERNALSYM PPARTITION_INFORMATION_MBR}
  _PARTITION_INFORMATION_MBR = record
    PartitionType: BYTE;
    BootIndicator: BOOLEAN;
    RecognizedPartition: BOOLEAN;
    HiddenSectors: DWORD;
  end;
  {$EXTERNALSYM _PARTITION_INFORMATION_MBR}
  PARTITION_INFORMATION_MBR = _PARTITION_INFORMATION_MBR;
  {$EXTERNALSYM PARTITION_INFORMATION_MBR}
  TPartitionInformationMbr = PARTITION_INFORMATION_MBR;
  PPartitionInformationMbr = PPARTITION_INFORMATION_MBR;

//
// The structure SET_PARTITION_INFO_EX is used with the ioctl
// IOCTL_SET_PARTITION_INFO_EX to set information about a specific
// partition. Note that for MBR partitions, you can only set the partition
// signature, whereas GPT partitions allow setting of all fields that
// you can get.
//

  SET_PARTITION_INFORMATION_MBR = SET_PARTITION_INFORMATION;
  {$EXTERNALSYM SET_PARTITION_INFORMATION_MBR}
  TSetPartitionInformationMbr = SET_PARTITION_INFORMATION_MBR;
  SET_PARTITION_INFORMATION_GPT = PARTITION_INFORMATION_GPT;
  {$EXTERNALSYM SET_PARTITION_INFORMATION_GPT}
  TSetPartitionInformationGpt = SET_PARTITION_INFORMATION_GPT;

  PSET_PARTITION_INFORMATION_EX = ^SET_PARTITION_INFORMATION_EX;
  {$EXTERNALSYM PSET_PARTITION_INFORMATION_EX}
  _SET_PARTITION_INFORMATION_EX = record
    PartitionStyle: PARTITION_STYLE;
    case Integer of
      0: (Mbr: SET_PARTITION_INFORMATION_MBR);
      1: (Gpt: SET_PARTITION_INFORMATION_GPT);
  end;
  {$EXTERNALSYM _SET_PARTITION_INFORMATION_EX}
  SET_PARTITION_INFORMATION_EX = _SET_PARTITION_INFORMATION_EX;
  {$EXTERNALSYM SET_PARTITION_INFORMATION_EX}
  TSetPartitionInformationEx = SET_PARTITION_INFORMATION_EX;
  PSetPartitionInformationEx = PSET_PARTITION_INFORMATION_EX;

//
// The structure CREATE_DISK_GPT with the ioctl IOCTL_DISK_CREATE_DISK
// to initialize an virgin disk with an empty GPT partition table.
//

  PCREATE_DISK_GPT = ^CREATE_DISK_GPT;
  {$EXTERNALSYM PCREATE_DISK_GPT}
  _CREATE_DISK_GPT = record
    DiskId: GUID; // Unique disk id for the disk.
    MaxPartitionCount: DWORD; // Maximim number of partitions allowable.
  end;
  {$EXTERNALSYM _CREATE_DISK_GPT}
  CREATE_DISK_GPT = _CREATE_DISK_GPT;
  {$EXTERNALSYM CREATE_DISK_GPT}
  TCreateDiskGpt = CREATE_DISK_GPT;
  PCreateDiskGpt = PCREATE_DISK_GPT;

//
// The structure CREATE_DISK_MBR with the ioctl IOCTL_DISK_CREATE_DISK
// to initialize an virgin disk with an empty MBR partition table.
//

  PCREATE_DISK_MBR = ^CREATE_DISK_MBR;
  {$EXTERNALSYM PCREATE_DISK_MBR}
  _CREATE_DISK_MBR = record
    Signature: DWORD;
  end;
  {$EXTERNALSYM _CREATE_DISK_MBR}
  CREATE_DISK_MBR = _CREATE_DISK_MBR;
  {$EXTERNALSYM CREATE_DISK_MBR}
  TCreateDiskMbr = CREATE_DISK_MBR;
  PCreateDiskMbr = PCREATE_DISK_MBR;

  PCREATE_DISK = ^CREATE_DISK;
  {$EXTERNALSYM PCREATE_DISK}
  _CREATE_DISK = record
    PartitionStyle: PARTITION_STYLE;
    case Integer of
      0: (Mbr: CREATE_DISK_MBR);
      1: (Gpt: CREATE_DISK_GPT);
  end;
  {$EXTERNALSYM _CREATE_DISK}
  CREATE_DISK = _CREATE_DISK;
  {$EXTERNALSYM CREATE_DISK}
  TCreateDisk = CREATE_DISK;
  PCreateDisk = PCREATE_DISK;

//
// The structure GET_LENGTH_INFORMATION is used with the ioctl
// IOCTL_DISK_GET_LENGTH_INFO to obtain the length, in bytes, of the
// disk, partition, or volume.
//

  PGET_LENGTH_INFORMATION = ^GET_LENGTH_INFORMATION;
  {$EXTERNALSYM PGET_LENGTH_INFORMATION}
  _GET_LENGTH_INFORMATION = record
    Length: LARGE_INTEGER;
  end;
  {$EXTERNALSYM _GET_LENGTH_INFORMATION}
  GET_LENGTH_INFORMATION = _GET_LENGTH_INFORMATION;
  {$EXTERNALSYM GET_LENGTH_INFORMATION}
  TGetLengthInformation = GET_LENGTH_INFORMATION;
  PGetLengthInformation = PGET_LENGTH_INFORMATION;

//
// The PARTITION_INFORMATION_EX structure is used with the
// IOCTL_DISK_GET_DRIVE_LAYOUT_EX, IOCTL_DISK_SET_DRIVE_LAYOUT_EX,
// IOCTL_DISK_GET_PARTITION_INFO_EX and IOCTL_DISK_GET_PARTITION_INFO_EX calls.
//

  PPARTITION_INFORMATION_EX = ^PARTITION_INFORMATION_EX;
  {$EXTERNALSYM PPARTITION_INFORMATION_EX}
  _PARTITION_INFORMATION_EX = record
    PartitionStyle: PARTITION_STYLE;
    StartingOffset: LARGE_INTEGER;
    PartitionLength: LARGE_INTEGER;
    PartitionNumber: DWORD;
    RewritePartition: BOOLEAN;
    case Integer of
      0: (Mbr: PARTITION_INFORMATION_MBR);
      1: (Gpt: PARTITION_INFORMATION_GPT);
  end;
  {$EXTERNALSYM _PARTITION_INFORMATION_EX}
  PARTITION_INFORMATION_EX = _PARTITION_INFORMATION_EX;
  {$EXTERNALSYM PARTITION_INFORMATION_EX}
  TPartitionInformationEx = PARTITION_INFORMATION_EX;
  PPartitionInformationEx = PPARTITION_INFORMATION_EX;

//
// GPT specific drive layout information.
//

  PDRIVE_LAYOUT_INFORMATION_GPT = ^DRIVE_LAYOUT_INFORMATION_GPT;
  {$EXTERNALSYM PDRIVE_LAYOUT_INFORMATION_GPT}
  _DRIVE_LAYOUT_INFORMATION_GPT = record
    DiskId: GUID;
    StartingUsableOffset: LARGE_INTEGER;
    UsableLength: LARGE_INTEGER;
    MaxPartitionCount: DWORD;
  end;
  {$EXTERNALSYM _DRIVE_LAYOUT_INFORMATION_GPT}
  DRIVE_LAYOUT_INFORMATION_GPT = _DRIVE_LAYOUT_INFORMATION_GPT;
  {$EXTERNALSYM DRIVE_LAYOUT_INFORMATION_GPT}
  TDriveLayoutInformationGpt = DRIVE_LAYOUT_INFORMATION_GPT;
  PDriveLayoutInformationGpt = PDRIVE_LAYOUT_INFORMATION_GPT;

//
// MBR specific drive layout information.
//

  PDRIVE_LAYOUT_INFORMATION_MBR = ^DRIVE_LAYOUT_INFORMATION_MBR;
  {$EXTERNALSYM PDRIVE_LAYOUT_INFORMATION_MBR}
  _DRIVE_LAYOUT_INFORMATION_MBR = record
    Signature: DWORD;
  end;
  {$EXTERNALSYM _DRIVE_LAYOUT_INFORMATION_MBR}
  DRIVE_LAYOUT_INFORMATION_MBR = _DRIVE_LAYOUT_INFORMATION_MBR;
  {$EXTERNALSYM DRIVE_LAYOUT_INFORMATION_MBR}
  TDriveLayoutInformationMbr = DRIVE_LAYOUT_INFORMATION_MBR;
  PDriveLayoutInformationMbr = PDRIVE_LAYOUT_INFORMATION_MBR;

//
// The structure DRIVE_LAYOUT_INFORMATION_EX is used with the
// IOCTL_SET_DRIVE_LAYOUT_EX and IOCTL_GET_DRIVE_LAYOUT_EX calls.
//

  PDRIVE_LAYOUT_INFORMATION_EX = ^DRIVE_LAYOUT_INFORMATION_EX;
  {$EXTERNALSYM PDRIVE_LAYOUT_INFORMATION_EX}
  _DRIVE_LAYOUT_INFORMATION_EX = record
    PartitionStyle: DWORD;
    PartitionCount: DWORD;
    Union: record
      case Integer of
        0: (Mbr: DRIVE_LAYOUT_INFORMATION_MBR);
        1: (Gpt: DRIVE_LAYOUT_INFORMATION_GPT);
    end;
    PartitionEntry: array [0..0] of PARTITION_INFORMATION_EX;
  end;
  {$EXTERNALSYM _DRIVE_LAYOUT_INFORMATION_EX}
  DRIVE_LAYOUT_INFORMATION_EX = _DRIVE_LAYOUT_INFORMATION_EX;
  {$EXTERNALSYM DRIVE_LAYOUT_INFORMATION_EX}
  TDriveLayoutInformationEx = DRIVE_LAYOUT_INFORMATION_EX;
  PDriveLayoutInformationEx = PDRIVE_LAYOUT_INFORMATION_EX;
/////// GPT를 인식하기 위해 추가 2011-02-10 sunk

// 파일 클러스터 관련 -----------------------------------------------------
type
//
// Structure for FSCTL_GET_VOLUME_BITMAP
//

  PSTARTING_LCN_INPUT_BUFFER = ^STARTING_LCN_INPUT_BUFFER;
  {$EXTERNALSYM PSTARTING_LCN_INPUT_BUFFER}
  STARTING_LCN_INPUT_BUFFER = record
    StartingLcn: LARGE_INTEGER;
  end;
  {$EXTERNALSYM STARTING_LCN_INPUT_BUFFER}
  TStartingLcnInputBuffer = STARTING_LCN_INPUT_BUFFER;
  PStartingLcnInputBuffer = PSTARTING_LCN_INPUT_BUFFER;

  PVOLUME_BITMAP_BUFFER = ^VOLUME_BITMAP_BUFFER;
  {$EXTERNALSYM PVOLUME_BITMAP_BUFFER}
  VOLUME_BITMAP_BUFFER = record
    StartingLcn: LARGE_INTEGER;
    BitmapSize: LARGE_INTEGER;
    Buffer: array [0..0] of BYTE;
  end;
  {$EXTERNALSYM VOLUME_BITMAP_BUFFER}
  TVolumeBitmapBuffer = VOLUME_BITMAP_BUFFER;
  PVolumeBitmapBuffer = PVOLUME_BITMAP_BUFFER;

//
// Structure for FSCTL_GET_RETRIEVAL_POINTERS
//

  PSTARTING_VCN_INPUT_BUFFER = ^STARTING_VCN_INPUT_BUFFER;
  {$EXTERNALSYM PSTARTING_VCN_INPUT_BUFFER}
  STARTING_VCN_INPUT_BUFFER = record
    StartingVcn: LARGE_INTEGER;
  end;
  {$EXTERNALSYM STARTING_VCN_INPUT_BUFFER}
  TStartingVcnInputBuffer = STARTING_VCN_INPUT_BUFFER;
  PStartingVcnInputBuffer = PSTARTING_VCN_INPUT_BUFFER;

  TRPBExtends = record
    NextVcn: LARGE_INTEGER;
    Lcn: LARGE_INTEGER;
  end;

  PRETRIEVAL_POINTERS_BUFFER = ^RETRIEVAL_POINTERS_BUFFER;
  {$EXTERNALSYM PRETRIEVAL_POINTERS_BUFFER}
  RETRIEVAL_POINTERS_BUFFER = record
    ExtentCount: DWORD;
    StartingVcn: LARGE_INTEGER;
    Extends: array [0..0] of TRPBExtends;
  end;
  {$EXTERNALSYM RETRIEVAL_POINTERS_BUFFER}
  TRetrievalPointersBuffer = RETRIEVAL_POINTERS_BUFFER;
  PRetrievalPointersBuffer = PRETRIEVAL_POINTERS_BUFFER;

//
// Structures for FSCTL_GET_NTFS_FILE_RECORD
//

  PNTFS_FILE_RECORD_INPUT_BUFFER = ^NTFS_FILE_RECORD_INPUT_BUFFER;
  {$EXTERNALSYM PNTFS_FILE_RECORD_INPUT_BUFFER}
  NTFS_FILE_RECORD_INPUT_BUFFER = record
    FileReferenceNumber: LARGE_INTEGER;
  end;
  {$EXTERNALSYM NTFS_FILE_RECORD_INPUT_BUFFER}
  TNtfsFileRecordInputBuffer = NTFS_FILE_RECORD_INPUT_BUFFER;
  PNtfsFileRecordInputBuffer = PNTFS_FILE_RECORD_INPUT_BUFFER;

  PNTFS_FILE_RECORD_OUTPUT_BUFFER = ^NTFS_FILE_RECORD_OUTPUT_BUFFER;
  {$EXTERNALSYM PNTFS_FILE_RECORD_OUTPUT_BUFFER}
  NTFS_FILE_RECORD_OUTPUT_BUFFER = record
    FileReferenceNumber: LARGE_INTEGER;
    FileRecordLength: DWORD;
    FileRecordBuffer: array [0..0] of BYTE;
  end;
  {$EXTERNALSYM NTFS_FILE_RECORD_OUTPUT_BUFFER}
  TNtfsFileRecordOutputBuffer = NTFS_FILE_RECORD_OUTPUT_BUFFER;
  PNtfsFileRecordOutputBuffer = PNTFS_FILE_RECORD_OUTPUT_BUFFER;

//
// Structure for FSCTL_MOVE_FILE
//

  PMOVE_FILE_DATA = ^MOVE_FILE_DATA;
  {$EXTERNALSYM PMOVE_FILE_DATA}
  MOVE_FILE_DATA = record
    FileHandle: THandle;
    StartingVcn: LARGE_INTEGER;
    StartingLcn: LARGE_INTEGER;
    ClusterCount: DWORD;
  end;
  {$EXTERNALSYM MOVE_FILE_DATA}
  TMoveFileData = MOVE_FILE_DATA;
  PMoveFileData = PMOVE_FILE_DATA;


// 추가 22_0616 16:13:02 kku ---------------------------------------------------
  PHDEVINFO           = ^HDEVINFO;
  HDEVINFO            = THandle;
  PCONFIGRET          = ^CONFIGRET;
  CONFIGRET           = DWORD;
  PDEVINST            = ^DEVINST;
  DEVINST             = DWORD;
  PPNP_VETO_TYPE      = ^PNP_VETO_TYPE;
  PNP_VETO_TYPE       = DWORD;
  HMACHINE            = THandle;

  PSPDeviceInterfaceData = ^TSPDeviceInterfaceData;
// 64bit에서는 packed 처리하면 안된다.. 22_0616 23:18:35 kku
  SP_DEVICE_INTERFACE_DATA = {$IFNDEF WIN64} packed {$ENDIF} record
    cbSize              : DWORD;
    InterfaceClassGuid  : TGUID;
    Flags               : DWORD;
    Reserved            : ULONG_PTR;
  end;
  TSPDeviceInterfaceData = SP_DEVICE_INTERFACE_DATA;

  PSPDeviceInterfaceDetailDataA = ^TSPDeviceInterfaceDetailDataA;
  PSPDeviceInterfaceDetailDataW = ^TSPDeviceInterfaceDetailDataW;
  SP_DEVICE_INTERFACE_DETAIL_DATA_A = {$IFNDEF WIN64} packed {$ENDIF} record
    cbSize      : DWORD;
    DevicePath  : array [0..0] of AnsiChar;
  end;
  SP_DEVICE_INTERFACE_DETAIL_DATA_W = {$IFNDEF WIN64} packed {$ENDIF} record
    cbSize      : DWORD;
    DevicePath  : array [0..0] of WideChar;
  end;

  TSPDeviceInterfaceDetailDataA = SP_DEVICE_INTERFACE_DETAIL_DATA_A;
  TSPDeviceInterfaceDetailDataW = SP_DEVICE_INTERFACE_DETAIL_DATA_W;
  TSPDeviceInterfaceDetailData = TSPDeviceInterfaceDetailDataW;
  PSPDeviceInterfaceDetailData = ^TSPDeviceInterfaceDetailDataW;

  PSPDevInfoData = ^TSPDevInfoData;
  SP_DEVINFO_DATA = {$IFNDEF WIN64} packed {$ENDIF} record
    cbSize    : DWORD;
    ClassGuid : TGUID;
    DevInst   : DWORD;
    Reserved  : ULONG_PTR;
  end;
  TSPDevInfoData = SP_DEVINFO_DATA;

  DI_FUNCTION = UINT;
  PSPClassInstallHeader = ^TSPClassInstallHeader;
  SP_CLASSINSTALL_HEADER = {$IFNDEF WIN64} packed {$ENDIF} record
    cbSize: DWORD;
    InstallFunction: DI_FUNCTION;
  end;
  TSPClassInstallHeader = SP_CLASSINSTALL_HEADER;

  PSPPropChangeParams = ^TSPPropChangeParams;
  SP_PROPCHANGE_PARAMS = {$IFNDEF WIN64} packed {$ENDIF} record
    ClassInstallHeader: TSPClassInstallHeader;
    StateChange: DWORD;
    Scope: DWORD;
    HwProfile: DWORD;
  end;
  TSPPropChangeParams = SP_PROPCHANGE_PARAMS;

  function SetupDiGetClassDevsW(ClassGuid: PGUID; const Enumerator: PWideChar; hwndParent: HWND; Flags: DWORD): HDEVINFO; stdcall; external 'SetupApi.dll' name 'SetupDiGetClassDevsW';
  function SetupDiGetClassDevsA(ClassGuid: PGUID; const Enumerator: PAnsiChar; hwndParent:HWND; Flags: DWORD): HDEVINFO; stdcall; external 'SetupApi.dll' name 'SetupDiGetClassDevsA';
  function SetupDiGetClassDevs(ClassGuid: PGUID; const Enumerator: PChar; hwndParent: HWND; Flags: DWORD): HDEVINFO; stdcall; external 'SetupApi.dll' name 'SetupDiGetClassDevsW';

  function SetupDiEnumDeviceInfo(DeviceInfoSet: HDEVINFO;
    MemberIndex: DWORD; var DeviceInfoData: TSPDevInfoData): LongBool; stdcall; external 'SetupApi.dll' name 'SetupDiEnumDeviceInfo';

  function SetupDiGetDeviceRegistryPropertyA(DeviceInfoSet: HDEVINFO;
    const DeviceInfoData: TSPDevInfoData; Property_: DWORD;
    var PropertyRegDataType: DWORD; PropertyBuffer: PBYTE; PropertyBufferSize: DWORD;
    var RequiredSize: DWORD): LongBool; stdcall; external 'SetupApi.dll' name 'SetupDiGetDeviceRegistryPropertyA';
  function SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet: HDEVINFO;
    const DeviceInfoData: TSPDevInfoData; Property_: DWORD;
    var PropertyRegDataType: DWORD; PropertyBuffer: PBYTE; PropertyBufferSize: DWORD;
    var RequiredSize: DWORD): LongBool; stdcall; external 'SetupApi.dll' name 'SetupDiGetDeviceRegistryPropertyW';
  function SetupDiGetDeviceRegistryProperty(DeviceInfoSet: HDEVINFO;
    const DeviceInfoData: TSPDevInfoData; Property_: DWORD;
    var PropertyRegDataType: DWORD; PropertyBuffer: PBYTE; PropertyBufferSize: DWORD;
    var RequiredSize: DWORD): LongBool; stdcall; external 'SetupApi.dll' name 'SetupDiGetDeviceRegistryPropertyW';

  function SetupDiSetClassInstallParamsA(DeviceInfoSet: HDEVINFO;
    DeviceInfoData: PSPDevInfoData; ClassInstallParams: PSPClassInstallHeader;
    ClassInstallParamsSize: DWORD): LongBool; stdcall; external 'SetupApi.dll' name 'SetupDiSetClassInstallParamsA';
  function SetupDiSetClassInstallParamsW(DeviceInfoSet: HDEVINFO;
    DeviceInfoData: PSPDevInfoData; ClassInstallParams: PSPClassInstallHeader;
    ClassInstallParamsSize: DWORD): LongBool; stdcall; external 'SetupApi.dll' name 'SetupDiSetClassInstallParamsW';
  function SetupDiSetClassInstallParams(DeviceInfoSet: HDEVINFO;
    DeviceInfoData: PSPDevInfoData; ClassInstallParams: PSPClassInstallHeader;
    ClassInstallParamsSize: DWORD): LongBool; stdcall; external 'SetupApi.dll' name 'SetupDiSetClassInstallParamsW';

  function SetupDiCallClassInstaller(InstallFunction: DI_FUNCTION;
    DeviceInfoSet: HDEVINFO; DeviceInfoData: PSPDevInfoData): LongBool; stdcall; external 'SetupApi.dll' name 'SetupDiCallClassInstaller';

  function SetupDiEnumDeviceInterfaces(DeviceInfoSet: HDEVINFO; DeviceInfoData: PSPDevInfoData;
      const InterfaceClassGuid: TGUID; MemberIndex: DWORD; var DeviceInterfaceData: TSPDeviceInterfaceData): BOOL; stdcall; external 'SetupApi.dll';

  function SetupDiGetDeviceInterfaceDetailW(DeviceInfoSet: HDEVINFO; DeviceInterfaceData: PSPDeviceInterfaceData;
      DeviceInterfaceDetailData: PSPDeviceInterfaceDetailData; DeviceInterfaceDetailDataSize: DWORD;
      var RequiredSize: DWORD; Device: PSPDevInfoData): BOOL; stdcall; external 'SetupApi.dll' name 'SetupDiGetDeviceInterfaceDetailW';
  function SetupDiGetDeviceInterfaceDetailA(DeviceInfoSet: HDEVINFO; DeviceInterfaceData: PSPDeviceInterfaceData;
      DeviceInterfaceDetailData: PSPDeviceInterfaceDetailDataA; DeviceInterfaceDetailDataSize: DWORD;
      var RequiredSize: DWORD; Device: PSPDevInfoData): BOOL; stdcall; external 'SetupApi.dll' name 'SetupDiGetDeviceInterfaceDetailA';
  function SetupDiGetDeviceInterfaceDetail(DeviceInfoSet: HDEVINFO; DeviceInterfaceData: PSPDeviceInterfaceData;
      DeviceInterfaceDetailData: PSPDeviceInterfaceDetailData; DeviceInterfaceDetailDataSize: DWORD;
      var RequiredSize: DWORD; Device: PSPDevInfoData): BOOL; stdcall; external 'SetupApi.dll' name 'SetupDiGetDeviceInterfaceDetailW';
  function SetupDiDestroyDeviceInfoList(DeviceInfoSet: HDEVINFO): BOOL; stdcall; external 'SetupApi.dll' name 'SetupDiDestroyDeviceInfoList';

  function SetupDiRemoveDevice(DeviceInfoSet: HDEVINFO; var DeviceInfoData: TSPDevInfoData): BOOL; stdcall; external 'SetupApi.dll' name 'SetupDiRemoveDevice';

  function CM_Get_Parent(var dnDevInstParent: DEVINST; dnDevInst: DEVINST; ulFlags: ULONG): CONFIGRET; stdcall; external 'CfgMgr32.dll';
  function CM_Query_And_Remove_SubTreeW(dnAncestor: DEVINST; pVetoType: PPNP_VETO_TYPE; pszVetoName: PWideChar;
      ulNameLength: ULONG; ulFlags: ULONG): CONFIGRET; stdcall; external 'SetupApi.dll' name 'CM_Query_And_Remove_SubTreeW';
  function CM_Query_And_Remove_SubTreeA(dnAncestor: DEVINST;pVetoType: PPNP_VETO_TYPE; pszVetoName: PAnsiChar;
      ulNameLength: ULONG; ulFlags: ULONG): CONFIGRET; stdcall; external 'SetupApi.dll' name 'CM_Query_And_Remove_SubTreeA';
  function CM_Query_And_Remove_SubTree(dnAncestor: DEVINST;pVetoType: PPNP_VETO_TYPE; pszVetoName: PChar;
      ulNameLength: ULONG; ulFlags: ULONG): CONFIGRET; stdcall; external 'SetupApi.dll' name 'CM_Query_And_Remove_SubTreeW';
  function CM_Request_Device_EjectW(dnDevInst: DEVINST; pVetoType: PPNP_VETO_TYPE; pszVetoName: PWideChar;
      ulNameLength: ULONG; ulFlags: ULONG): CONFIGRET; stdcall; external 'SetupApi.dll' name 'CM_Request_Device_EjectW';
  function CM_Request_Device_EjectA(dnDevInst: DEVINST; pVetoType: PPNP_VETO_TYPE; pszVetoName: PAnsiChar;
      ulNameLength: ULONG; ulFlags: ULONG): CONFIGRET; stdcall; external 'SetupApi.dll' name 'CM_Request_Device_EjectA';
  function CM_Request_Device_Eject(dnDevInst: DEVINST; pVetoType: PPNP_VETO_TYPE; pszVetoName: PChar;
      ulNameLength: ULONG; ulFlags: ULONG):CONFIGRET; stdcall; external 'SetupApi.dll' name 'CM_Request_Device_EjectW';

  function CM_Get_Device_IDA(dnDevInst: DEVINST; Buffer: PAnsiChar;
    BufferLen: ULONG; ulFlags: ULONG): CONFIGRET; stdcall; external 'cfgmgr32.dll' name 'CM_Get_Device_IDA';
  function CM_Get_Device_IDW(dnDevInst: DEVINST; Buffer: PWideChar;
    BufferLen: ULONG; ulFlags: ULONG): CONFIGRET; stdcall; external 'cfgmgr32.dll' name 'CM_Get_Device_IDW';
  function CM_Get_Device_ID(dnDevInst: DEVINST; Buffer: PChar;
    BufferLen: ULONG; ulFlags: ULONG): CONFIGRET; stdcall; external 'cfgmgr32.dll' name 'CM_Get_Device_IDW';

  function CM_Get_DevNode_Status(var ulStatus: ULONG; var ulProblemNumber: ULONG;
    dnDevInst: DEVINST; ulFlags: ULONG): CONFIGRET; stdcall; external 'cfgmgr32.dll' name 'CM_Get_DevNode_Status';
  function CM_Get_DevNode_Status_Ex(var ulStatus: ULONG; var ulProblemNumber: ULONG;
    dnDevInst: DEVINST; ulFlags: ULONG; hMachine: HMACHINE): CONFIGRET; stdcall; external 'cfgmgr32.dll' name 'CM_Get_DevNode_Status_Ex';

// 추가 22_0616 16:13:02 kku ---------------------------------------------------

// ------------------------------------------------------------------------
  function CTL_CODE(const dwDeviceType, dwFunc, dwMethod, dwAccess: DWORD): DWORD;
  function ConvSerialToStr(sSerNum: AnsiString): AnsiString;


type
  TTgDriver = class(TTgObject)
  protected
    sDeviceName_: String;
    hDevice_: THandle;

    function GetDeviceHandle: THandle; virtual;
  public
    Constructor Create; virtual;
    Destructor Destroy; override;

    function OpenDevice(const sDeviceName: String;
              dwDesireAccess: DWORD = GENERIC_READ or GENERIC_WRITE;
              dwShareMode: DWORD = FILE_SHARE_READ or FILE_SHARE_WRITE;
              dwFlagsAndAttributes: DWORD = 0): Integer; virtual;
    function CloseDevice: Boolean; virtual;

    function ReadIO(dwCode: DWORD; pBuf: Pointer;
                      dwSize: DWORD; pdwByteReturn: PDWORD = nil): Boolean;
    function WriteIO(dwCode: DWORD; pBuf: Pointer;
                      dwSize: DWORD; pdwByteReturn: PDWORD = nil): Boolean;
    function ReadWriteIO(dwCode: DWORD; pInBuf: Pointer; dwInSize: DWORD;
                           pOutBuf: Pointer; dwOutSize: DWORD;
                           pdwByteReturn: PDWORD = nil): Boolean;

    function IsOpen: Boolean; virtual;

    property Handle: THandle read GetDeviceHandle;
    property DeviceName: String read sDeviceName_;
  end;

{
var
  IOCTL_DISK_GET_LENGTH_INFO: DWORD;
  IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: DWORD;
  IOCTL_STORAGE_QUERY_PROPERTY: DWORD;
  IOCTL_STORAGE_GET_MEDIA_TYPES_EX: DWORD;
  IOCTL_STORAGE_EJECTION_CONTROL: DWORD;
  IOCTL_STORAGE_MEDIA_REMOVAL: DWORD;
  IOCTL_STORAGE_EJECT_MEDIA: DWORD;
  FSCTL_DISMOUNT_VOLUME : DWORD;
  FSCTL_LOCK_VOLUME     : DWORD;
  FSCTL_UNLOCK_VOLUME   : DWORD;
  FSCTL_IS_VOLUME_MOUNTED:DWORD;
  FSCTL_ALLOW_EXTENDED_DASD_IO : DWORD;
  //IOCTL_STORAGE_EJECT_MEDIA : DWORD;

  IOCTL_WDMAUD_OPEN_DEVICE    : DWORD = $1d8014;
  IOCTL_WDMAUD_REMOVE_DEVICE  : DWORD = $1d8008;
  IOCTL_WDMAUD_AUX_GET_VOLUME : DWORD = $1d801c;

  //IOCTL_STORAGE_GET_DEVICE_NUMBER : DWORD = $2D1080;
}

type
  PDriveInfo = ^TDriveInfo;
  TDriveInfo = record
    sDrive,
    sSerial,
    sClass,
    sClassGuid,
    sDesc,
    sFriendlyName: String;
    llSize: LONGLONG;
    nDiskNum: Integer;
  end;

function FlushDriveBuffers(const sDrive: String): Boolean;
function DeviceIoCtrl(hDevice : DWORD; dwCode: DWORD; pdwByteReturn: PDWORD = nil): Boolean;
function GetDriveDetail(sDrive: String; pInfo: PDriveInfo; bAddInfo: Boolean = false): Boolean;
function ForceEjectDrive(sDrive: String): Boolean;
function EjectDrive(sDrive: String; aIgrList: TStringList = nil; bOnlyUsb: Boolean = true; bForceEject: Boolean = false): String;
function EjectDrive2(sDrive: String; aIgrList: TStringList = nil; bOnlyUsb: Boolean = true; bForceEject: Boolean = false; bForceEjectFirst: Boolean = false): String; // 내장 cdrom 제거 23_0223 16:23:32 kku
function SetUsbDevEnableByDevPath(sDevPath: String; bVal: Boolean): Boolean;
function RemoveUsbDevEnableByDevPath(sDevPath: String): Integer;

implementation

uses
  Tocsg.Path,
  Tocsg.Safe, Tocsg.Exception, Tocsg.Convert, Tocsg.Disk, Tocsg.Trace,
  Tocsg.Shell, Tocsg.Process;

//CTL_CODE(IOCTL_STORAGE_BASE, $0250, METHOD_BUFFERED, FILE_READ_ACCESS);
function CTL_CODE(const dwDeviceType, dwFunc, dwMethod, dwAccess: DWORD): DWORD;
begin
  Result := (dwDeviceType shl 16) or
            (dwAccess     shl 14) or
            (dwFunc       shl  2) or
            dwMethod;
end;

function ConvSerialToStr(sSerNum: AnsiString): AnsiString;
var
  i, nStrLen: Integer;
  sPair: AnsiString;
  B: Byte;
  Ch: AnsiChar Absolute B;
begin
  Result := '';
  nStrLen := Length(sSerNum);

  if Odd(nStrLen) then
    exit;

  i := 1;

  while i < nStrLen do
  begin
    sPair := Copy(sSerNum, i, 2);
    HexToBin(PAnsiChar(sPair), PAnsiChar(@B), 1);
    Result := Result + AnsiChar(B);
    Inc(i, 2);
  end;

  i := 1;

  nStrLen := Length(Result);
  while i < nStrLen do
  begin
    Ch := Result[i];
    Result[i] := Result[i + 1];
    Result[i + 1] := Ch;
    Inc(i, 2);
  end;
end;

{ TTgDriver }

Constructor TTgDriver.Create;
begin
  Inherited Create;

  hDevice_ := 0;
end;

Destructor TTgDriver.Destroy;
begin
  CloseDevice;

  Inherited;
end;

function TTgDriver.GetDeviceHandle: THandle;
begin
  Result := hDevice_;
end;

// 성공 - 0, 그외 실패
function TTgDriver.OpenDevice(const sDeviceName: String;
              dwDesireAccess: DWORD = GENERIC_READ or GENERIC_WRITE;
              dwShareMode: DWORD = FILE_SHARE_READ or FILE_SHARE_WRITE;
              dwFlagsAndAttributes: DWORD = 0): Integer;
var
  h: THandle;
begin
  if hDevice_ <> 0 then
    CloseDevice;

  if sDeviceName = '' then
  begin
    Result := ERROR_FILE_NOT_FOUND;
    exit;
  end;

//  if (GetVersion and $ff) >= 5 then
//    sDeviceName := '\\.\Global\' + sDriverName
//  else
//    sDeviceName := '\\.\' + sDriverName;

  h := CreateFile(PChar(sDeviceName),
                  dwDesireAccess,
                  dwShareMode,
                  nil,
                  OPEN_EXISTING,
                  dwFlagsAndAttributes,//FILE_FLAG_NO_BUFFERING or FILE_FLAG_SEQUENTIAL_SCAN,
                  0);

  if h = INVALID_HANDLE_VALUE then
  begin
    Result := GetLastError;
    exit;
  end;

  hDevice_ := h;
  sDeviceName_ := sDeviceName;

// 일단 플러시 실패해도 상관없으니깐 그냥 실행만 시켜준다.
// 09_1111 추가 sunk
//  if FlushFileBuffers(h) then  // 물리 디스크는 안되나부다..
//    _Trace('OpenDevice() - FlushFileBuffers .. success')
//  else
//    _Trace('OpenDevice() - FlushFileBuffers .. fail');

  Result := ERROR_SUCCESS;
end;

function TTgDriver.CloseDevice: Boolean;
begin
  Result := false;

  if hDevice_ <> 0 then
  begin
    CloseHandle(hDevice_);
    hDevice_ := 0;

    Result := true;
  end;
end;

function TTgDriver.ReadIO(dwCode: DWORD; pBuf: Pointer;
                      dwSize: DWORD; pdwByteReturn: PDWORD = nil): Boolean;
var
  dwByteReturned: DWORD;
begin
  Result := false;

  if hDevice_ = 0 then
    exit;

  dwByteReturned := 0;

  Result := DeviceIoControl(hDevice_,
                            dwCode,
                            nil,
                            0,
                            pBuf,
                            dwSize,
                            dwByteReturned,
                            nil);

  if Result and (pdwByteReturn <> nil) then
    pdwByteReturn^ := dwByteReturned;
end;

function TTgDriver.WriteIO(dwCode: DWORD; pBuf: Pointer;
                      dwSize: DWORD; pdwByteReturn: PDWORD = nil): Boolean;
var
  dwByteReturned: DWORD;
begin
  Result := false;

  if hDevice_ = 0 then
    exit;

  Result := DeviceIoControl(hDevice_,
                            dwCode,
                            pBuf,
                            dwSize,
                            nil,
                            0,
                            dwByteReturned,
                            nil);

  if Result and (pdwByteReturn <> nil) then
    pdwByteReturn^ := dwByteReturned;
end;

// 드라이버에 데이터 보내고 받기
// 성공 - bytereturn
// 드라이버가 열러있지 않음, 오류 - -1
function TTgDriver.ReadWriteIO(dwCode: DWORD; pInBuf: Pointer; dwInSize: DWORD;
                                    pOutBuf: Pointer; dwOutSize: DWORD;
                                    pdwByteReturn: PDWORD = nil): Boolean;
var
  dwByteReturned: DWORD;
begin
  Result := false;

  if hDevice_ = 0 then
    exit;

  Result := DeviceIoControl(hDevice_,
                            dwCode,
                            pInBuf,
                            dwInSize,
                            pOutBuf,
                            dwOutSize,
                            dwByteReturned,
                            nil);

  if Result and (pdwByteReturn <> nil) then
    pdwByteReturn^ := dwByteReturned;
end;

function TTgDriver.IsOpen: Boolean;
begin
  Result := (hDevice_ <> 0) and (hDevice_ <> INVALID_HANDLE_VALUE);
end;

function FlushDriveBuffers(const sDrive: String): Boolean;
var
  hVolume: THandle;
  sDrivePath: String;
begin
  Result := false;
  sDrivePath := Format('\\.\%s:', [sDrive[1]]);
  hVolume := CreateFile(PChar(sDrivePath),
                        GENERIC_READ or GENERIC_WRITE,
                        FILE_SHARE_READ or FILE_SHARE_WRITE,
                        nil,
                        OPEN_EXISTING,
                        0,
                        0);

  if hVolume < 1 then
    exit;

  Result := FlushFileBuffers(hVolume);
  CloseHandle(hVolume);
end;

function DeviceIoCtrl(hDevice : DWORD; dwCode: DWORD; pdwByteReturn: PDWORD = nil): Boolean;
var
  dwByteReturned: DWORD;
begin
  Result := false;

  if hDevice = 0 then
    exit;

  Result := DeviceIoControl(hDevice,
                            dwCode,
                            nil,
                            0,
                            nil,
                            0,
                            dwByteReturned,
                            nil);

  if Result and (pdwByteReturn <> nil) then
    pdwByteReturn^ := dwByteReturned;
end;

function GetDevPropertyStr(hDev: HDEVINFO; spdd: TSPDevInfoData; dwProperty: DWORD): String;
var
  c: Integer;
  pBuf: Pointer;
  dwBufSize,
  dwPropertyRegDataType: DWORD;
begin
  Result := '';
  try
    pBuf := nil;
    dwBufSize := 0;
    try
      while not SetupDiGetDeviceRegistryProperty(hDev, spdd,
               dwProperty, dwPropertyRegDataType, pBuf, dwBufSize, dwBufSize) do
      begin
        if GetLastError = ERROR_INSUFFICIENT_BUFFER then
        begin
          if pBuf <> nil then
            FreeMem(pBuf);
          pBuf := AllocMem(dwBufSize);
        end else break;
      end;

      if pBuf <> nil then
        Result := String(PChar(pBuf));
    finally
      if pBuf <> nil then
        FreeMem(pBuf);
    end;
  except
    on E: Exception do
      ETgException.TraceException(E, 'Fail .. GetDevPropertyStr()');
  end;
end;

function GetDriveDetail(sDrive: String; pInfo: PDriveInfo; bAddInfo: Boolean = false): Boolean;
var
  sVol: String;
  Drv: TTgDriver;
  sdn: STORAGE_DEVICE_NUMBER;
  dwIdx,
  dwLen: DWORD;
  DevGuid: TGuid;
  hDev: HDEVINFO;
  sdid: TSPDeviceInterfaceData;
  pBuf: TBytes;
  spdd: TSPDevInfoData;
  DevInstParent: DEVINST;
  sDevName: array [0..255] of Char;
begin
  Result := false;

  ZeroMemory(pInfo, SizeOf(TDriveInfo));
  pInfo.sDrive := sDrive;
  pInfo.nDiskNum := -1;
  pInfo.llSize := GetDriveSize(sDrive);
//  if pInfo.llSize = 0 then
//  begin
//    var DrvEx: TDriveExtent := GetDriveExtent(sDrive);
//    pInfo.llSize := DrvEx.liExtentLength.QuadPart;
//  end;

  if sDrive = '' then
    exit;

  sVol := Format('\\.\%s:', [sDrive[1]]);
  Guard(Drv, TTgDriver.Create);
  if Drv.OpenDevice(sVol, 0) <> ERROR_SUCCESS then
    exit;

  if not Drv.ReadIO(IOCTL_STORAGE_GET_DEVICE_NUMBER, @sdn, SizeOf(sdn), @dwLen) then
    exit;
  Drv.CloseDevice;

  if sdn.DeviceNumber = -1 then
    exit;
  pInfo.nDiskNum := sdn.DeviceNumber;

  case GetDriveType(PChar(sDrive)) of
//    DRIVE_FIXED,
//    DRIVE_REMOVABLE : DevGuid := GUID_DEVINTERFACE_DISK;
    DRIVE_CDROM     : DevGuid := GUID_DEVINTERFACE_CDROM;
    else DevGuid := GUID_DEVINTERFACE_DISK;
  end;

  hDev := SetupDiGetClassDevs(@DevGuid, nil, 0, DIGCF_PRESENT or DIGCF_DEVICEINTERFACE);
  if hDev = INVALID_HANDLE_VALUE then
    exit;

  try
    dwIdx := 0;
    while True do
    begin
      ZeroMemory(@sdid, SizeOf(sdid));
      sdid.cbSize := SizeOf(sdid);
      if not SetupDiEnumDeviceInterfaces(hDev, nil, DevGuid, dwIdx, sdid) then
        break;

      SetupDiGetDeviceInterfaceDetail(hDev, @sdid, nil, 0, dwLen, nil);
      if (dwLen > 0) and (dwLen < 204800) then
      begin
        SetLength(pBuf, dwLen);
        PSPDeviceInterfaceDetailData(@pBuf[0]).cbSize := SizeOf(TSPDeviceInterfaceDetailData);

        ZeroMemory(@spdd, SizeOf(spdd));
        spdd.cbSize := SizeOf(spdd);

        if SetupDiGetDeviceInterfaceDetail(hDev, @sdid,
              PSPDeviceInterfaceDetailData(@pBuf[0]), dwLen, dwLen, @spdd) then
        begin
          if Drv.OpenDevice(PChar(@pBuf[4]), 0) = ERROR_SUCCESS then
          begin
            if not Drv.ReadIO(IOCTL_STORAGE_GET_DEVICE_NUMBER, @sdn, SizeOf(sdn), @dwLen) then
              exit;
            Drv.CloseDevice;

            if sdn.DeviceNumber = pInfo.nDiskNum then
            begin
              pInfo.sFriendlyName := GetDevPropertyStr(hDev, spdd, SPDRP_FRIENDLYNAME);
              if bAddInfo then
              begin
                pInfo.sClass := GetDevPropertyStr(hDev, spdd, SPDRP_CLASS);
                pInfo.sClassGuid := GetDevPropertyStr(hDev, spdd, SPDRP_CLASSGUID);
                pInfo.sDesc := GetDevPropertyStr(hDev, spdd, SPDRP_DEVICEDESC);
              end;

              ZeroMemory(@sDevName, SizeOf(sDevName));
              CM_Get_Parent(DevInstParent, spdd.DevInst, 0);
              CM_Get_Device_ID(DevInstParent, sDevName, 256, 0);

              pInfo.sSerial := sDevName;

              Result := true;
              exit;
            end;
          end;
        end;
      end;
      Inc(dwIdx);
    end;
  finally
    SetupDiDestroyDeviceInfoList(hDev);
  end;
end;

// for BS1
function ForceEjectDrive(sDrive: String): Boolean;
var
  sEjectMd: String;
begin
  TTgTrace.T('ForceEjectDrive() ..', 1);
  Result := false;
  try
//    if not DirectoryExists(sDrive) then
//      exit;

    sEjectMd := GetRunExePathDir + 'Bs1uef.dat';
    if not FileExists(sEjectMd) then
      sEjectMd := GetRunExePathDir + 'conf\Bs1uef.dat';

    if FileExists(sEjectMd) then
    begin
      ExecuteAppWaitUntilTerminate(sEjectMd, Format('%s: -f', [sDrive[1]]), SW_HIDE, 10000);
      if not DirectoryExists(sDrive) then
         Result := true;
    end;
  except
    on E: Exception do
      ETgException.TraceException(E, 'Fail .. ForceEjectDrive()');
  end;
end;

function LockAndDismountVolume(const DriveLetter: Char): Boolean;
var
  h: THandle;
  bytes: DWORD;
  path: string;
  i: Integer;
begin
  path := Format('\\.\%s:', [UpCase(DriveLetter)]);
  h := CreateFile(PChar(path), GENERIC_READ or GENERIC_WRITE,
                  FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
  if h = INVALID_HANDLE_VALUE then Exit(False);
  try
    // 락은 충돌 잦으니 여러 번 재시도
    for i := 1 to 20 do
      if DeviceIoControl(h, FSCTL_LOCK_VOLUME, nil, 0, nil, 0, bytes, nil) then Break
      else Sleep(150); // 잠시 대기 후 재시도

    if not DeviceIoControl(h, FSCTL_LOCK_VOLUME, nil, 0, nil, 0, bytes, nil) then
      Exit(False);

    // 언마운트(파일시스템 분리)
    Result := DeviceIoControl(h, FSCTL_DISMOUNT_VOLUME, nil, 0, nil, 0, bytes, nil);
  finally
    CloseHandle(h);
  end;
end;

function EjectDrive(sDrive: String; aIgrList: TStringList = nil;
      bOnlyUsb: Boolean = true; bForceEject: Boolean = false): String;
var
  sVol: String;
  sDevName: array [0..255] of Char;
  Drv: TTgDriver;
  sdn: STORAGE_DEVICE_NUMBER;
  dwIdx,
  dwLen: DWORD;
  VetoType: PNP_VETO_TYPE;
  sVetoName: PChar;
  nType, nDiskNum: Integer;
  DevGuid: TGuid;
  hDev: HDEVINFO;
  sdid: TSPDeviceInterfaceData;
  pBuf: TBytes;
  spdd: TSPDevInfoData;
  DevInstParent: DEVINST;
  bIsCdRom,
  bSuccess: Boolean;
  DrvEx: TDriveExtent;
  i: Integer;
begin
  Result := FAIL_EJECT;

  if sDrive = '' then
  begin
//    TTgTrace.T('Fail .. EjectDrive() .. 1');
    exit;
  end;

//  bIsCdRom := GetDriveType(PChar(sDrive)) = DRIVE_CDROM;
  sVol := Format('\\.\%s:', [sDrive[1]]);
  Guard(Drv, TTgDriver.Create);
  if Drv.OpenDevice(sVol, 0) <> ERROR_SUCCESS then
  begin
//    TTgTrace.T('Fail .. EjectDrive() .. 2');
    exit;
  end;

  ZeroMemory(@sdn, SizeOf(sdn));
  sdn.DeviceNumber := 999;
  if not Drv.ReadIO(IOCTL_STORAGE_GET_DEVICE_NUMBER, @sdn, SizeOf(sdn), @dwLen) then
    exit;
  Drv.CloseDevice;
  nDiskNum := sdn.DeviceNumber;

//  DrvEx := GetDriveExtent(sDrive);
//  if DrvEx.liExtentLength.QuadPart = 0 then
//  begin
//    TTgTrace.T('Fail .. EjectDrive() .. Size null .. Drive=%s', [sDrive]);
//    exit;
//  end;

//  nDiskNum := DrvEx.dwDiskNumber;
  if (nDiskNum = -1) or (nDiskNum = 999) then
  begin
//    TTgTrace.T('Fail .. EjectDrive() .. Not found disknum .. Drive=%s', [sDrive], 1);
    exit;
  end;

//  TTgTrace.T('EjectDrive() .. Drive=%s, DiskNum=%d', [sDrive, nDiskNum], 1);

  nType := GetDriveType(PChar(sDrive));
  case nType of
    DRIVE_FIXED,
    DRIVE_REMOVABLE : DevGuid := GUID_DEVINTERFACE_DISK;
    DRIVE_CDROM     : DevGuid := GUID_dEVINTERFACE_CDROM;
  end;

  hDev := SetupDiGetClassDevs(@DevGuid, nil, 0, DIGCF_PRESENT or DIGCF_DEVICEINTERFACE);
  if hDev = INVALID_HANDLE_VALUE then
  begin
//    TTgTrace.T('Fail .. EjectDrive() .. 5');
    exit;
  end;

  try
    dwIdx := 0;
    while True do
    begin
      ZeroMemory(@sdid, SizeOf(sdid));
      sdid.cbSize := SizeOf(sdid);
      if not SetupDiEnumDeviceInterfaces(hDev, nil, DevGuid, dwIdx, sdid) then
        break;

//      TTgTrace.T('EjectDrive() .. SetupDiGetDeviceInterfaceDetail .. Init');
      SetupDiGetDeviceInterfaceDetail(hDev, @sdid, nil, 0, dwLen, nil);
      if (dwLen > 0) and (dwLen < 204800) then
      begin
        SetLength(pBuf, dwLen);
        PSPDeviceInterfaceDetailData(@pBuf[0]).cbSize := SizeOf(TSPDeviceInterfaceDetailData);

        ZeroMemory(@spdd, SizeOf(spdd));
        spdd.cbSize := SizeOf(spdd);

        if SetupDiGetDeviceInterfaceDetail(hDev, @sdid,
              PSPDeviceInterfaceDetailData(@pBuf[0]), dwLen, dwLen, @spdd) then
        begin
//          TTgTrace.T('EjectDrive() .. SetupDiGetDeviceInterfaceDetail .. Success');
          if Drv.OpenDevice(PChar(@pBuf[4]), 0) = ERROR_SUCCESS then
          begin
//            TTgTrace.T('EjectDrive() .. OpenDevice .. Success');
            if not Drv.ReadIO(IOCTL_STORAGE_GET_DEVICE_NUMBER, @sdn, SizeOf(sdn), @dwLen) then
            begin
//              TTgTrace.T('EjectDrive() .. ReadIO .. Fail');
              exit;
            end;
//            TTgTrace.T('EjectDrive() .. ReadIO .. Success, DiskNum=%d', [sdn.DeviceNumber]);
            Drv.CloseDevice;

            if sdn.DeviceNumber = nDiskNum then
            begin
              DevInstParent := 0;
              ZeroMemory(@sDevName, SizeOf(sDevName));
              CM_Get_Parent(DevInstParent, spdd.DevInst, 0);
              CM_Get_Device_ID(DevInstParent, sDevName, 256, 0);

//              TTgTrace.T('EjectDrive() .. DevName=%s', [sDevName]);

//              if bIsCdRom and (Pos('CDROM', UpperCase(sDevName)) = 0) then
//              begin
//                TTgTrace.T('EjectDrive() .. No CDROM, DiskNum=%d', [sdn.DeviceNumber]);
//                Inc(dwIdx);
//                continue;
//              end;

//              TTgTrace.T('EjectDrive() .. Found CDROM, DiskNum=%d', [sdn.DeviceNumber]);
//              if bOnlyUsb and (Pos('USB', UpperCase(sDevName)) <> 1) then
//              begin
//                TTgTrace.T('Fail .. EjectDrive() .. 7');
//                exit;
//              end;

              if aIgrList <> nil then
              begin
//                if aIgrList.CaseSensitive then
//                  aIgrList.CaseSensitive := false;
//                if aIgrList.IndexOf(sDevName) <> -1 then
//                begin
//                  Result := '';
//                  exit;
//                end;

              // 비교방식 변경 25_0324 10:12:22 kku
                var sChkDevName: String := UpperCase(sDevName);
                for i := 0 to aIgrList.Count - 1 do
                  if Pos(UpperCase(aIgrList[i]), sDevName) > 0 then
                  begin
                    Result := '';
                    exit;
                  end;
              end;

//              TTgTrace.T('EjectDrive() .. DevName=%s, DiskNum=%d', [sDevName, nDiskNum], 1);

              VetoType := 0;
              sVetoName := nil;

//                var ss: TStringStream;
//                Guard(ss, TStringStream.Create('', TEncoding.ANSI));
//                if GetCmdTextToStream('mountvol.exe', Format('%s: /L', [sDrive[1]]), ss, 5000) then
//                begin
//                  ExecutePath('mountvol.exe', Format('%s: /D', [sDrive[1]]));
//                  Sleep(500);
//                  bSuccess := CM_Request_Device_Eject(DevInstParent, @VetoType, sVetoName, MAX_PATH, 0) = CR_SUCCESS;
//                  ExecutePath('mountvol.exe', Format('%s: %s', [sDrive[1], StringReplace(Trim(ss.DataString), #13#10, '', [rfReplaceAll])]));
////                  if not DirectoryExists(sDrive) then
////                    Result := sDevName;
//                end;

//              LockAndDismountVolume(sDrive[1]);

              bSuccess := CM_Request_Device_Eject(DevInstParent, @VetoType, sVetoName, MAX_PATH, 0) = CR_SUCCESS;
              if bSuccess and (VetoType = 0) then
              begin
//                ShowMessage(Format('%d - %s', [sdn.DeviceNumber, sDevName]));
                Result := sDevName;
              end else begin
                TTgTrace.T('Fail .. EjectDrive() .. VetoType=%d, LastError=%d', [VetoType, GetLastError], 1);

                if bForceEject then
                begin
                  if ForceEjectDrive(sDrive) then
                    Result := sDevName;
                end;

//                ExecutePath('mountvol.exe', Format('%s: /P', [sDrive[1]]));
//                Sleep(500);
//                if not DirectoryExists(sDrive) then
//                begin
//                  Result := sDevName;
//                end;
              end;
              exit;
            end;
          end;
//          else
//            TTgTrace.T('EjectDrive() .. OpenDevice .. Fail');
        end;
//        else
//          TTgTrace.T('EjectDrive() .. SetupDiGetDeviceInterfaceDetail .. Fail');
      end;
      Inc(dwIdx);
    end;
  finally
    SetupDiDestroyDeviceInfoList(hDev);
  end;

//  if Result = FAIL_EJECT then
//    TTgTrace.T('Fail .. EjectDrive() .. end');
end;

function EjectDrive2(sDrive: String; aIgrList: TStringList = nil; bOnlyUsb: Boolean = true; bForceEject: Boolean = false; bForceEjectFirst: Boolean = false): String;
var
  sVol: String;
  sDevName: array [0..255] of Char;
  Drv: TTgDriver;
  sdn: STORAGE_DEVICE_NUMBER;
  dwIdx,
  dwLen: DWORD;
  VetoType: PNP_VETO_TYPE;
  sVetoName: PChar;
  nType, nDiskNum: Integer;
  DevGuid: TGuid;
  hDev: HDEVINFO;
  sdid: TSPDeviceInterfaceData;
  pBuf: TBytes;
  spdd: TSPDevInfoData;
  DevInstParent: DEVINST;
  bSuccess: Boolean;
  DrvEx: TDriveExtent;
  i: Integer;
begin
  Result := FAIL_EJECT;

  if sDrive = '' then
    exit;

  sVol := Format('\\.\%s:', [sDrive[1]]);
  Guard(Drv, TTgDriver.Create);
  if Drv.OpenDevice(sVol, 0) <> ERROR_SUCCESS then
    exit;

  ZeroMemory(@sdn, SizeOf(sdn));
  sdn.DeviceNumber := 999;
  if not Drv.ReadIO(IOCTL_STORAGE_GET_DEVICE_NUMBER, @sdn, SizeOf(sdn), @dwLen) then
    exit;
  Drv.CloseDevice;
//
  nDiskNum := sdn.DeviceNumber;

//  nDiskNum := GetDriveExtent(sDrive).dwDiskNumber;

//  DrvEx := GetDriveExtent(sDrive);
//  if DrvEx.liExtentLength.QuadPart = 0 then
//  begin
//    TTgTrace.T('Fail .. EjectDrive2() .. Size null .. Drive=%s', [sDrive]);
//    exit;
//  end;
//  nDiskNum := DrvEx.dwDiskNumber;

  if (nDiskNum = -1) or (nDiskNum = 999) then
  begin
    TTgTrace.T('Fail .. EjectDrive2() .. Not found disknum .. Drive=%s', [sDrive], 1);
    exit;
  end;

  TTgTrace.T('EjectDrive2() .. Drive=%s, DiskNum=%d', [sDrive, nDiskNum], 1);
//  TTgTrace.T('EjectDrive2() .. Drive=%s, DiskNum=%d', [sDrive, nDiskNum]);

  nType := GetDriveType(PChar(sDrive));
  case nType of
    DRIVE_FIXED,
    DRIVE_REMOVABLE : DevGuid := GUID_DEVINTERFACE_DISK;
    DRIVE_CDROM     : DevGuid := GUID_DEVINTERFACE_CDROM;
  end;

  hDev := SetupDiGetClassDevs(@DevGuid, nil, 0, DIGCF_PRESENT or DIGCF_DEVICEINTERFACE);
  if hDev = INVALID_HANDLE_VALUE then
    exit;

  try
    dwIdx := 0;
    while True do
    begin
      ZeroMemory(@sdid, SizeOf(sdid));
      sdid.cbSize := SizeOf(sdid);
      if not SetupDiEnumDeviceInterfaces(hDev, nil, DevGuid, dwIdx, sdid) then
        break;

      SetupDiGetDeviceInterfaceDetail(hDev, @sdid, nil, 0, dwLen, nil);
      if (dwLen > 0) and (dwLen < 204800) then
      begin
        SetLength(pBuf, dwLen);
        PSPDeviceInterfaceDetailData(@pBuf[0]).cbSize := SizeOf(TSPDeviceInterfaceDetailData);

        ZeroMemory(@spdd, SizeOf(spdd));
        spdd.cbSize := SizeOf(spdd);

        if SetupDiGetDeviceInterfaceDetail(hDev, @sdid,
              PSPDeviceInterfaceDetailData(@pBuf[0]), dwLen, dwLen, @spdd) then
        begin
          if Drv.OpenDevice(PChar(@pBuf[4]), 0) = ERROR_SUCCESS then
          begin
            if not Drv.ReadIO(IOCTL_STORAGE_GET_DEVICE_NUMBER, @sdn, SizeOf(sdn), @dwLen) then
              exit;
            Drv.CloseDevice;

            if sdn.DeviceNumber = nDiskNum then
            begin
              DevInstParent := 0;
              ZeroMemory(@sDevName, SizeOf(sDevName));
              CM_Get_Parent(DevInstParent, spdd.DevInst, 0);
              CM_Get_Device_ID(DevInstParent, sDevName, 256, 0);

//              if Pos('CDROM', UpperCase(sDevName)) = 0 then
//              begin
//                Inc(dwIdx);
//                continue;
//              end;

              if bOnlyUsb and (Pos('USB', UpperCase(sDevName)) <> 1) then
                exit;

              TTgTrace.T('EjectDrive2() .. DevName=%s, DiskNum=%d', [sDevName, nDiskNum], 1);
//              TTgTrace.T('EjectDrive() .. DevName=%s, DiskNum=%d', [sDevName, nDiskNum]);

              if aIgrList <> nil then
              begin
//                if aIgrList.CaseSensitive then
//                  aIgrList.CaseSensitive := false;
//                if aIgrList.IndexOf(sDevName) <> -1 then
//                begin
//                  Result := '';
//                  exit;
//                end;

              // 비교방식 변경 25_0324 10:12:22 kku
                var sChkDevName: String := UpperCase(sDevName);
                for i := 0 to aIgrList.Count - 1 do
                  if Pos(UpperCase(aIgrList[i]), sDevName) > 0 then
                  begin
                    Result := '';
                    exit;
                  end;
              end;

              VetoType := 0;
              sVetoName := nil;
              if bForceEjectFirst then
              begin
                if ForceEjectDrive(sDrive) then
                begin
                  Result := sDevName;
                  exit;
                end else
                  bSuccess := CM_Query_And_Remove_SubTree(DevInstParent, @VetoType, sVetoName, MAX_PATH, CM_REMOVE_NO_RESTART) = CR_SUCCESS;
              end else begin
                bSuccess := CM_Query_And_Remove_SubTree(DevInstParent, @VetoType, sVetoName, MAX_PATH, CM_REMOVE_NO_RESTART) = CR_SUCCESS; // EjectDrive()와 다른점 23_0223 16:24:47 kku
//                bSuccess := CM_Query_And_Remove_SubTree(DevInstParent, nil, nil, 0, CM_REMOVE_NO_RESTART) = CR_SUCCESS; // EjectDrive()와 다른점 23_0223 16:24:47 kku
              end;

              if bSuccess and (VetoType = 0) then
              begin
  //              ShowMessage(Format('%d - %s', [sdn.DeviceNumber, sDevName]));
                Result := sDevName;
              end else begin
                TTgTrace.T('Fail .. EjectDrive2() .. VetoType=%d, LastError=%d', [VetoType, GetLastError], 1);

                if bForceEject then
                begin
                  if ForceEjectDrive(sDrive) then
                    Result := sDevName;
                end;
//                var ss: TStringStream;
//                Guard(ss, TStringStream.Create('', TEncoding.ANSI));
//                if GetCmdTextToStream('mountvol.exe', Format('%s: /P', [sDrive[1]]), ss, 5000) then
//                begin
//                  ExecutePath('mountvol.exe', Format('%s: /P', [sDrive[1]]));
//                  Sleep(500);
//                  if not DirectoryExists(sDrive) then
//                    Result := sDevName;
//                end;
              end;
              exit;
            end;
          end;
        end;
      end;
      Inc(dwIdx);
    end;
  finally
    SetupDiDestroyDeviceInfoList(hDev);
  end;
end;

function SetUsbDevEnableByDevPath(sDevPath: String; bVal: Boolean): Boolean;
var
  hDev: HDEVINFO;
  sdd: TSPDevInfoData;
  i, c: Integer;
  dwBufSize,
  dwStatus, dwProblem,
  dwPropertyRegDataType: DWORD;
  pBuf: Pointer;
  sInfo: String;
  InfoList: TStringList;
begin
  Result := false;

  try
    hDev := SetupDiGetClassDevs(@GUID_DEVCLASS_USB, nil, 0, DIGCF_PRESENT);
    if hDev = INVALID_HANDLE_VALUE then
      exit;

    pBuf := nil;
    try
      ZeroMemory(@sdd, SizeOf(sdd));
      sdd.cbSize := SizeOf(sdd);

      Guard(InfoList, TStringList.Create);

      i := 0;
      while SetupDiEnumDeviceInfo(hDev, i, sdd) do
      begin
        dwBufSize := 0;
        if pBuf <> nil then
        begin
          FreeMem(pBuf);
          pBuf := nil;
        end;

        while not SetupDiGetDeviceRegistryProperty(hDev, sdd,
                 SPDRP_HARDWAREID, dwPropertyRegDataType, pBuf, dwBufSize, dwBufSize) do
        begin
          if GetLastError = ERROR_INSUFFICIENT_BUFFER then
          begin
            if pBuf <> nil then
              FreeMem(pBuf);
            pBuf := AllocMem(dwBufSize);
          end else break;
        end;

        if pBuf <> nil then
        begin
          sInfo := '';
          InfoList.Clear;
          for c := 0 to (dwBufSize div 2) - 1 do
          begin
            if PChar(pBuf)[c] = #0 then
            begin
              if sInfo <> '' then
              begin
                InfoList.Add(sInfo);
                sInfo := '';
              end;
            end else
              sInfo := sInfo + PChar(pBuf)[c];
          end;

          if sInfo <> '' then
            InfoList.Add(sInfo);

          for c := 0 to InfoList.Count - 1 do
          begin
            if Pos(InfoList[c], sDevPath) > 0 then
            begin
              dwStatus := 0;
              dwProblem := 0;

              if CM_Get_DevNode_Status(dwStatus, dwProblem, sdd.DevInst, 0) = CR_SUCCESS then
              begin
                var PropChangeParams: TSPPropChangeParams;
                ZeroMemory(@PropChangeParams, SizeOf(PropChangeParams));
                PropChangeParams.ClassInstallHeader.cbSize := SizeOf(TSPClassInstallHeader);
                PropChangeParams.ClassInstallHeader.InstallFunction := DIF_PROPERTYCHANGE;
                PropChangeParams.Scope := DICS_FLAG_GLOBAL;
                PropChangeParams.StateChange := BooleanToInt(bVal, DICS_ENABLE, DICS_DISABLE);

                if SetupDiSetClassInstallParams(hDev, @sdd, PSPClassInstallHeader(@PropChangeParams), SizeOf(PropChangeParams)) then
                begin
                // 64bit OS 에서는 64bit 프로그램 에서만 실행 가능하다 22_0621 13:59:21 kku
                  Result := SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hDev, @sdd);
                end;
              end;

              exit;
            end;
          end;
        end;

        Inc(i);
      end;

    finally
      SetupDiDestroyDeviceInfoList(hDev);
      if pBuf <> nil then
        FreeMem(pBuf);
    end;
  except
    on E: Exception do
      ETgException.TraceException(E, 'Fail .. SetUsbDevEnableByDevPath()');
  end;
end;

// Result .. -1 = Fail API, 0 = Not found, 1 = Success, 2 = Fail Remove
function _RemoveUsbDevEnableByDevPath(pClassGuid: PGUID; sDevPath: String): Integer;
var
  hDev: HDEVINFO;
  sdd: TSPDevInfoData;
  i, c: Integer;
  dwBufSize,
  dwStatus, dwProblem,
  dwPropertyRegDataType: DWORD;
  pBuf: Pointer;
  sInfo: String;
  InfoList: TStringList;
begin
  Result := -1;

  try
    hDev := SetupDiGetClassDevs(pClassGuid, nil, 0, DIGCF_PRESENT);
    if hDev = INVALID_HANDLE_VALUE then
      exit;

    pBuf := nil;
    try
      ZeroMemory(@sdd, SizeOf(sdd));
      sdd.cbSize := SizeOf(sdd);

      Guard(InfoList, TStringList.Create);

      i := 0;
      while SetupDiEnumDeviceInfo(hDev, i, sdd) do
      begin
        dwBufSize := 0;
        if pBuf <> nil then
        begin
          FreeMem(pBuf);
          pBuf := nil;
        end;

        while not SetupDiGetDeviceRegistryProperty(hDev, sdd,
                 SPDRP_HARDWAREID, dwPropertyRegDataType, pBuf, dwBufSize, dwBufSize) do
        begin
          if GetLastError = ERROR_INSUFFICIENT_BUFFER then
          begin
            if pBuf <> nil then
              FreeMem(pBuf);
            pBuf := AllocMem(dwBufSize);
          end else break;
        end;

        if pBuf <> nil then
        begin
          sInfo := '';
          InfoList.Clear;
          for c := 0 to (dwBufSize div 2) - 1 do
          begin
            if PChar(pBuf)[c] = #0 then
            begin
              if sInfo <> '' then
              begin
                InfoList.Add(sInfo);
                sInfo := '';
              end;
            end else
              sInfo := sInfo + PChar(pBuf)[c];
          end;

          if sInfo <> '' then
            InfoList.Add(sInfo);

          for c := 0 to InfoList.Count - 1 do
          begin
            if Pos(InfoList[c], sDevPath) > 0 then
            begin
              dwStatus := 0;
              dwProblem := 0;

              if CM_Get_DevNode_Status(dwStatus, dwProblem, sdd.DevInst, 0) = CR_SUCCESS then
              begin
                if SetupDiRemoveDevice(hDev, sdd) then
                  Result := 1
                else
                  Result := 2;
              end;

              exit;
            end;
          end;
        end;

        Inc(i);
      end;
      Result := 0;
    finally
      SetupDiDestroyDeviceInfoList(hDev);
      if pBuf <> nil then
        FreeMem(pBuf);
    end;
  except
    on E: Exception do
      ETgException.TraceException(E, 'Fail .. SetUsbDevEnableByDevPath()');
  end;
end;

function RemoveUsbDevEnableByDevPath(sDevPath: String): Integer;
begin
  Result := _RemoveUsbDevEnableByDevPath(@GUID_DEVCLASS_USB, sDevPath);
  if Result = 0 then
  begin
  // 애플 장치는 여기로 (범용 직렬 버스 장치) 따로 잡힌다... 그래서 별도 추가 처리 22_0707 09:23:40 kku
    Result := _RemoveUsbDevEnableByDevPath(@GUID_DEVCLASS_USB_DEVICE, sDevPath);
  end;
  if Result = 0 then
  begin
  // MTP 장치등... 추가 23_0323 13:47:12 kku
    Result := _RemoveUsbDevEnableByDevPath(@GUID_DEVCLASS_WPD, sDevPath);
  end;
end;

{
initialization
  IOCTL_DISK_GET_LENGTH_INFO       := CTL_CODE(IOCTL_DISK_BASE, $0017, METHOD_BUFFERED, FILE_READ_ACCESS);
  IOCTL_DISK_GET_DRIVE_GEOMETRY_EX := CTL_CODE(IOCTL_DISK_BASE, $0028, METHOD_BUFFERED, FILE_ANY_ACCESS);
  IOCTL_STORAGE_QUERY_PROPERTY     := CTL_CODE(IOCTL_STORAGE_BASE, $0500, METHOD_BUFFERED, FILE_ANY_ACCESS);//$2D1400
  IOCTL_STORAGE_GET_MEDIA_TYPES_EX := CTL_CODE(IOCTL_STORAGE_BASE, $0301, METHOD_BUFFERED, FILE_ANY_ACCESS);
  IOCTL_STORAGE_MEDIA_REMOVAL      := CTL_CODE(IOCTL_STORAGE_BASE, $0201, METHOD_BUFFERED, FILE_READ_ACCESS);
  IOCTL_STORAGE_EJECTION_CONTROL   := $2D0940;//CTL_CODE(IOCTL_STORAGE_BASE, $0250, METHOD_BUFFERED, FILE_ANY_ACCESS); //$2D0940
  IOCTL_STORAGE_EJECT_MEDIA        := CTL_CODE(IOCTL_STORAGE_BASE, $0202, METHOD_BUFFERED, FILE_READ_ACCESS);

  FSCTL_DISMOUNT_VOLUME	           := CTL_CODE(FILE_DEVICE_FILE_SYSTEM,8,METHOD_BUFFERED,FILE_ANY_ACCESS);
  FSCTL_LOCK_VOLUME                := CTL_CODE(FILE_DEVICE_FILE_SYSTEM,6,METHOD_BUFFERED,FILE_ANY_ACCESS);
  FSCTL_UNLOCK_VOLUME	             := CTL_CODE(FILE_DEVICE_FILE_SYSTEM,7,METHOD_BUFFERED,FILE_ANY_ACCESS);
  FSCTL_IS_VOLUME_MOUNTED          := CTL_CODE(FILE_DEVICE_FILE_SYSTEM,10,METHOD_BUFFERED,FILE_ANY_ACCESS);
  FSCTL_ALLOW_EXTENDED_DASD_IO     := CTL_CODE(FILE_DEVICE_FILE_SYSTEM,32,METHOD_BUFFERED,FILE_ANY_ACCESS);
  //IOCTL_STORAGE_MEDIA_REMOVAL      := ($2d shl 16) or (1 shl 14) or ($201 shl 2) or 0;
  //IOCTL_STORAGE_EJECT_MEDIA        := ($2d shl 16) or (1 shl 14) or ($202 shl 2) or 0;
}

end.

