{*******************************************************}
{                                                       }
{       nrCommLib Communication Library for Delphi      }
{       Base Library for Win32 platform (VCL)           }
{       Copyright (c) 1997-2011 Roman Novgorodov        }
{       v9.27                                           }
{       last update: 19 May 2009                        }
{                                                       }
{       e-mail:noro@deepsoftware.com                    }
{       DeepSoftware                                    }
{       http://www.deepsoftware.com/                    }
{                                                       }
{*******************************************************}
{$I nrcommlib.inc}
unit nrwin32di;
interface
uses windows,classes, nrwin32;

Const

  CM_DRP_DRIVER             = $0A; // Driver REG_SZ property (RW)
  CM_DRP_DEVICEDESC         = $01;
  FILE_DEVICE_MASS_STORAGE  = $0000002d;
  IOCTL_STORAGE_BASE        = FILE_DEVICE_MASS_STORAGE;

  DIF_PROPERTYCHANGE        = $00000012;

  DICS_ENABLE      = $00000001;
  DICS_DISABLE     = $00000002;
  DICS_PROPCHANGE  = $00000003;
  DICS_START       = $00000004;
  DICS_STOP        = $00000005;

  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

  //
  // Setupapi-specific error codes

  // Inf parse outcomes
  ERROR_EXPECTED_SECTION_NAME  = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or 0;
  ERROR_BAD_SECTION_NAME_LINE  = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or 1;
  ERROR_SECTION_NAME_TOO_LONG  = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or 2;
  ERROR_GENERAL_SYNTAX         = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or 3;

  // Inf runtime errors
  ERROR_WRONG_INF_STYLE        = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $100;
  ERROR_SECTION_NOT_FOUND      = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $101;
  ERROR_LINE_NOT_FOUND         = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $102;
  ERROR_NO_BACKUP              = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $103;

  // Device Installer/other errors
  ERROR_NO_ASSOCIATED_CLASS                = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $200;
  ERROR_CLASS_MISMATCH                     = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $201;
  ERROR_DUPLICATE_FOUND                    = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $202;
  ERROR_NO_DRIVER_SELECTED                 = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $203;
  ERROR_KEY_DOES_NOT_EXIST                 = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $204;
  ERROR_INVALID_DEVINST_NAME               = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $205;
  ERROR_INVALID_CLASS                      = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $206;
  ERROR_DEVINST_ALREADY_EXISTS             = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $207;
  ERROR_DEVINFO_NOT_REGISTERED             = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $208;
  ERROR_INVALID_REG_PROPERTY               = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $209;
  ERROR_NO_INF                             = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $20A;
  ERROR_NO_SUCH_DEVINST                    = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $20B;
  ERROR_CANT_LOAD_CLASS_ICON               = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $20C;
  ERROR_INVALID_CLASS_INSTALLER            = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $20D;
  ERROR_DI_DO_DEFAULT                      = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $20E;
  ERROR_DI_NOFILECOPY                      = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $20F;
  ERROR_INVALID_HWPROFILE                  = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $210;
  ERROR_NO_DEVICE_SELECTED                 = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $211;
  ERROR_DEVINFO_LIST_LOCKED                = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $212;
  ERROR_DEVINFO_DATA_LOCKED                = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $213;
  ERROR_DI_BAD_PATH                        = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $214;
  ERROR_NO_CLASSINSTALL_PARAMS             = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $215;
  ERROR_FILEQUEUE_LOCKED                   = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $216;
  ERROR_BAD_SERVICE_INSTALLSECT            = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $217;
  ERROR_NO_CLASS_DRIVER_LIST               = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $218;
  ERROR_NO_ASSOCIATED_SERVICE              = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $219;
  ERROR_NO_DEFAULT_DEVICE_INTERFACE        = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $21A;
  ERROR_DEVICE_INTERFACE_ACTIVE            = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $21B;
  ERROR_DEVICE_INTERFACE_REMOVED           = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $21C;
  ERROR_BAD_INTERFACE_INSTALLSECT          = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $21D;
  ERROR_NO_SUCH_INTERFACE_CLASS            = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $21E;
  ERROR_INVALID_REFERENCE_STRING           = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $21F;
  ERROR_INVALID_MACHINENAME                = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $220;
  ERROR_REMOTE_COMM_FAILURE                = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $221;
  ERROR_MACHINE_UNAVAILABLE                = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $222;
  ERROR_NO_CONFIGMGR_SERVICES              = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $223;
  ERROR_INVALID_PROPPAGE_PROVIDER          = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $224;
  ERROR_NO_SUCH_DEVICE_INTERFACE           = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $225;
  ERROR_DI_POSTPROCESSING_REQUIRED         = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $226;
  ERROR_INVALID_COINSTALLER                = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $227;
  ERROR_NO_COMPAT_DRIVERS                  = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $228;
  ERROR_NO_DEVICE_ICON                     = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $229;
  ERROR_INVALID_INF_LOGCONFIG              = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $22A;
  ERROR_DI_DONT_INSTALL                    = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $22B;
  ERROR_INVALID_FILTER_DRIVER              = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $22C;
  ERROR_NON_WINDOWS_NT_DRIVER              = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $22D;
  ERROR_NON_WINDOWS_DRIVER                 = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $22E;
  ERROR_NO_CATALOG_FOR_OEM_INF             = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $22F;
  ERROR_DEVINSTALL_QUEUE_NONNATIVE         = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $230;
  ERROR_NOT_DISABLEABLE                    = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $231;
  ERROR_CANT_REMOVE_DEVINST                = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $232;
  ERROR_INVALID_TARGET                     = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $233;
  ERROR_DRIVER_NONNATIVE                   = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $234;
  ERROR_IN_WOW64                           = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $235;
  ERROR_SET_SYSTEM_RESTORE_POINT           = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $236;

  ERROR_SCE_DISABLED                       = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $238;
  ERROR_UNKNOWN_EXCEPTION                  = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $239;
  ERROR_PNP_REGISTRY_ERROR                 = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $23A;
  ERROR_REMOTE_REQUEST_UNSUPPORTED         = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $23B;
  ERROR_NOT_AN_INSTALLED_OEM_INF           = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $23C;
  ERROR_INF_IN_USE_BY_DEVICES              = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $23D;
  ERROR_DI_FUNCTION_OBSOLETE               = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $23E;
  ERROR_NO_AUTHENTICODE_CATALOG            = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $23F;
  ERROR_AUTHENTICODE_DISALLOWED            = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $240;
  ERROR_AUTHENTICODE_TRUSTED_PUBLISHER     = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $241;
  ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $242;
  ERROR_AUTHENTICODE_PUBLISHER_NOT_TRUSTED = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $243;
  ERROR_SIGNATURE_OSATTRIBUTE_MISMATCH     = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $244;
  ERROR_ONLY_VALIDATE_VIA_AUTHENTICODE     = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $245;
  ERROR_DEVICE_INSTALLER_NOT_READY         = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $246;
  ERROR_DRIVER_STORE_ADD_FAILED            = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $247;
  ERROR_DEVICE_INSTALL_BLOCKED             = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $248;
  ERROR_DRIVER_INSTALL_BLOCKED             = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $249;
  ERROR_WRONG_INF_TYPE                     = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $24A;
  ERROR_FILE_HASH_NOT_IN_CATALOG           = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $24B;
  ERROR_DRIVER_STORE_DELETE_FAILED         = APPLICATION_ERROR_MASK or ERROR_SEVERITY_ERROR or $24C;

  FILE_ANY_ACCESS           = 0;
  METHOD_BUFFERED           = 0;

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

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

  CS_x64stub_exe = 'nrX64stub.exe';

  procedure DiEnumInstalledDevices(Guid:PGuid; flPresentOnly, flDevice, flInterface:boolean; slDevices:TStrings);
  function DiGetRelations(devInst, sep:string; const flBus:boolean):string;
  function DiEjectDevice(devInst:dword):boolean;
  function DiResetDevice(devInst:string):boolean;
  function DiRemoveDevice(devInst:string):boolean;
  function DiEnableDevice(devInst:string):boolean;
  function DiDisableDevice(devInst:string):boolean;
  function DiRestartDevice(devInst:string):boolean;
//  function EnumDevicesOpen(Guid:TGuid; flInterface:boolean):dword;
//  function EnumDevicesNext(hEnum:dword;const Index:integer; var sSrv, sReg, sDrv, sAll:string):boolean;
//  procedure EnumDevicesClose(hEnum:dword);

  function InstallDriver(aHid:string; aInf:string):boolean;
  function IniSetupDI:boolean;
  function DiGetVolumes(flRemote, flCDROM, flFixed, flRemovable, flRam:boolean; Sep:string):string;
  function InstallINF(const PathName: String) : boolean;
//  function InstallDriver2(aInf, aAppId, aAppName:string):boolean;

Type

  STORAGE_DEVICE_NUMBER = packed record
    DeviceType :dword;
    DeviceNumber:dword;
    PartitionNumber:dword;
  end;

  STORAGE_PROPERTY_QUERY = packed record
    PropertyId:DWORD;
    QueryType:DWORD;
    AdditionalParameters:byte;
  end;

  STORAGE_DESCRIPTOR_HEADER = packed record
    Version:dword;
    Size:dword;
  end;

  STORAGE_DEVICE_ID_DESCRIPTOR = packed record
    Version :dword;
    Size :dword;
    NumberOfIdentifiers:dword;
    Identifiers:byte;
  end;

  SP_DEVINFO_DATA = packed record
    cbSize    : DWORD;
    ClassGuid : TGUID;
    DevInst   : dword;    // DEVINST handle
    Reserved  : Pointer;
  end;
  PSP_DEVINFO_DATA = ^SP_DEVINFO_DATA;

{  PNP_VETO_TYPE = enum (
    PNP_VetoTypeUnknown,            // Name is unspecified
    PNP_VetoLegacyDevice,           // Name is an Instance Path
    PNP_VetoPendingClose,           // Name is an Instance Path
    PNP_VetoWindowsApp,             // Name is a Module
    PNP_VetoWindowsService,         // Name is a Service
    PNP_VetoOutstandingOpen,        // Name is an Instance Path
    PNP_VetoDevice,                 // Name is an Instance Path
    PNP_VetoDriver,                 // Name is a Driver Service Name
    PNP_VetoIllegalDeviceRequest,   // Name is an Instance Path
    PNP_VetoInsufficientPower,      // Name is unspecified
    PNP_VetoNonDisableable,         // Name is an Instance Path
    PNP_VetoLegacyDriver,           // Name is a Service
    PNP_VetoInsufficientRights      // Name is unspecified
  );}

  SP_CLASSINSTALL_HEADER = packed record
    cbSize : DWORD;
    InstallFunction : DWORD;
  end;

  PSP_CLASSINSTALL_HEADER = ^SP_CLASSINSTALL_HEADER;

  SP_PROPCHANGE_PARAMS  = packed record
    ClassInstallHeader : SP_CLASSINSTALL_HEADER;
    StateChange : DWORD;
    Scope :   DWORD;
    HwProfile : DWORD;
  end;

  PSP_PROPCHANGE_PARAMS = ^SP_PROPCHANGE_PARAMS;

Var
  CmLocate:function (var pdnDevInst:cardinal;
             pDeviceID:PChar;ulFlags:cardinal):cardinal; stdcall;
  CmRegProperty:function (dnDevInst:cardinal;
             ulProperty:cardinal;pulRegDataType:pdword;
             Buffer:PChar;var pulLength:cardinal;
             ulFlags:cardinal):cardinal; stdcall;
  CmGetChild:function (var pdnDevInst:cardinal; dnDevInst,ulFlags:cardinal):cardinal; stdcall;
  CmGetSubling:function (var pdnDevInst:cardinal; dnDevInst,ulFlags:cardinal):cardinal; stdcall;
  CmGetParent:function (var pdnDevInst:cardinal; dnDevInst, ulFlags:cardinal):cardinal; stdcall;
  CM_Get_Device_ID_List_Size: function (var pulLen:dword; pszFilter:PChar; ulFlags:dword):dword; stdcall;
  CM_Get_Device_ID_List: function (pszFilter:PChar; Buffer:PChar; BufferLen:dword; ulFlags:dword):dword; stdcall;
  CM_Request_Device_Eject: function (
    dnDevInst:DWORD;  pVetoType:pointer;  pszVetoName:PChar;
    ulNameLength:DWORD; ulFlags:DWORD):dword; stdcall;

  function GetSetupError(code:integer):string;

implementation
uses sysutils, nrclasses, shellapi, nrsysutils;

{$R ..\Res\nrdix64stub.RES}

Type
   {
   SP_DEVINFO_DATA = packed record
     cbSize:dword;
     ClassGuid:TGUID;
     DevInst:dword;    // DEVINST handle
     Reserved:dword;
   end;
   PSP_DEVINFO_DATA=^SP_DEVINFO_DATA;
   }

   SP_DEVICE_INTERFACE_DATA = SP_DEVINFO_DATA;
   PSP_DEVICE_INTERFACE_DATA = ^SP_DEVICE_INTERFACE_DATA;

   {$IFDEF WIN64}
   SP_DEVICE_INTERFACE_DETAIL_DATA = {packed} record
     cbSize:dword;
     DevicePath: array [0..0] of CHAR;
   end;
   {$ELSE}
   SP_DEVICE_INTERFACE_DETAIL_DATA = packed record
     cbSize:dword;
     DevicePath: array [0..0] of CHAR;
   end;
   {$ENDIF}


   PSP_DEVICE_INTERFACE_DETAIL_DATA = ^SP_DEVICE_INTERFACE_DETAIL_DATA;

Var
  hSetupDll    :THandle    = 0;
  hCfgMgr32DLL :THandle    = 0;
//  hCfgMgrDLL   :dword    = 0;

  SetupDiGetClassDevs:function(pGuid:PGuid; sEnum:PChar; hWnd : THAndle; dwFlags:dword):THandle; stdcall;
  SetupDiDestroyDeviceInfoList:procedure(hPnp:THandle);stdcall;
  SetupDiEnumDeviceInfo:function(hDev:THandle;dwIndex:dword; var DevInf:SP_DEVINFO_DATA):boolean; stdcall;
  SetupDiGetDeviceRegistryProperty:function(hDev:THandle;var pDevInf:SP_DEVINFO_DATA;
               dwProp:dword;pdwPropRegDataType:Pdword;pBuff:pointer;
               dwPropBuffSize:dword;pdwReqSize:Pdword):boolean; stdcall;
  SetupDiOpenDevRegKey:function(hDev:THandle;var pDevInf:SP_DEVINFO_DATA;
                       Scope,HwProfile,KeyType,samDesired:dword):dword;stdcall;
  SetupDiEnumDeviceInterfaces:function(hDev:THandle; DeviceInfoData:PSP_DEVINFO_DATA;
      InterfaceClassGuid:PGuid; MemberIndex: integer; DeviceInterfaceData: PSP_DEVICE_INTERFACE_DATA):boolean; stdcall;

  SetupDiGetDeviceInterfaceDetail:function(hDev:THandle;
     const DeviceInterfaceData: SP_DEVICE_INTERFACE_DATA;
     DeviceInterfaceDetailData : PSP_DEVICE_INTERFACE_DETAIL_DATA;
     DeviceInterfaceDetailDataSize: DWORD;
     RequiredSize:pdword;
     DeviceInfoData : PSP_DEVINFO_DATA):boolean;   stdcall;

  SetupDiCreateDeviceInfoList:function(ClassGuid:PGUID; hwndParent:THandle) :dword; stdcall;

  SetupDiCreateDeviceInfo:function(DeviceInfoSet:THandle; DeviceName:PChar;
    ClassGuid: PGuid; DeviceDescription:PChar; hwndParent:THandle; CreationFlags:dword;
    DeviceInfoData:PSP_DEVINFO_DATA):boolean; stdcall;

  SetupDiGetINFClass:function(InfName:PChar; ClassGuid:PGUID; ClassName:PChar;
    ClassNameSize:dword; RequiredSize:PDword):boolean; stdcall;

  SetupDiSetDeviceRegistryProperty:function(DeviceInfoSet:THandle;
    DeviceInfoData : PSP_DEVINFO_DATA; dwProperty:DWORD; PropertyBuffer:pointer;
    PropertyBufferSize : DWORD):boolean; stdcall;

  SetupDiCallClassInstaller:function (InstallFunction:dword; DeviceInfoSet:THandle;
    DeviceInfoData:PSP_DEVINFO_DATA):boolean; stdcall;

  SetupDiGetDeviceInstanceId:function (DeviceInfoSet :THandle;
                DeviceInfoData:PSP_DEVINFO_DATA; DeviceInstanceId:PChar;
                DeviceInstanceIdSize:dword; RequiredSize:Pdword):boolean; stdcall;

  //SetupDiRestartDevices : function ( DeviceInfoSet:dword; DeviceInfoData:PSP_DEVINFO_DATA):boolean; stdcall;
  SetupDiRemoveDevice : function ( DeviceInfoSet:THandle; DeviceInfoData:PSP_DEVINFO_DATA):boolean; stdcall;
  SetupDiUnremoveDevice : function ( DeviceInfoSet:THandle; DeviceInfoData:PSP_DEVINFO_DATA):boolean; stdcall;


  SetupDiOpenDeviceInfo : function ( DeviceInfoSet:THandle; DeviceInstanceId:PChar; hwndParent:THandle;
                                     OpenFlags : dword; DeviceInfoData:PSP_DEVINFO_DATA):boolean; stdcall;

  SetupDiSetClassInstallParams : function (DeviceInfoSet : THandle; DeviceInfoData: PSP_DEVINFO_DATA;
                                           ClassInstallParams:PSP_CLASSINSTALL_HEADER;
                                           ClassInstallParamsSize:dword) : boolean; stdcall;

Const
  CS_SetupAPI               = 'SetupAPI.dll';
  CS_cfgmgr32               = 'cfgmgr32.dll';

function IniSetupDI:boolean;
var hCM:THandle;
begin
  Result := (hSetupDll <> 0);

  if Result then exit;

  if hSetupDLL = 0 then hSetupDLL := LoadLibrary(CS_SetupAPI);

  if hSetupDLL <> 0 then begin

    @SetupDiGetClassDevs := GetProcAddress(hSetupDLL, GetW32Name('SetupDiGetClassDevsA'));
    @SetupDiDestroyDeviceInfoList :=GetProcAddress(hSetupDLL,'SetupDiDestroyDeviceInfoList');
    @SetupDiEnumDeviceInfo := GetProcAddress(hSetupDLL,'SetupDiEnumDeviceInfo');
    @SetupDiGetDeviceRegistryProperty := GetProcAddress(hSetupDLL,GetW32Name('SetupDiGetDeviceRegistryPropertyA'));
    @SetupDiOpenDevRegKey := GetProcAddress(hSetupDLL,'SetupDiOpenDevRegKey');
    @SetupDiEnumDeviceInterfaces := GetProcAddress(hSetupDLL,'SetupDiEnumDeviceInterfaces');
    @SetupDiGetDeviceInterfaceDetail := GetProcAddress(hSetupDLL,GetW32Name('SetupDiGetDeviceInterfaceDetailA'));
    @SetupDiCreateDeviceInfoList := GetProcAddress(hSetupDLL,'SetupDiCreateDeviceInfoList');
    @SetupDiCreateDeviceInfo := GetProcAddress(hSetupDLL,GetW32Name('SetupDiCreateDeviceInfoA'));
    @SetupDiGetINFClass := GetProcAddress(hSetupDLL,GetW32Name('SetupDiGetINFClassA'));
    @SetupDiSetDeviceRegistryProperty := GetProcAddress(hSetupDLL,GetW32Name('SetupDiSetDeviceRegistryPropertyA'));
    @SetupDiCallClassInstaller  := GetProcAddress(hSetupDLL,'SetupDiCallClassInstaller');
    @SetupDiGetDeviceInstanceId := GetProcAddress(hSetupDLL,GetW32Name('SetupDiGetDeviceInstanceIdA'));
    @SetupDiRemoveDevice := GetProcAddress(hSetupDLL,'SetupDiRemoveDevice');
    @SetupDiUnremoveDevice := GetProcAddress(hSetupDLL,'SetupDiUnremoveDevice');
    @SetupDiOpenDeviceInfo := GetProcAddress(hSetupDLL,GetW32Name('SetupDiOpenDeviceInfoA'));
    @SetupDiSetClassInstallParams := GetProcAddress(hSetupDLL,GetW32Name('SetupDiSetClassInstallParamsA'));

    @CmLocate := GetProcAddress(hSetupDLL, GetW32Name('CM_Locate_DevNodeA'));
    if @CmLocate = nil then begin
      if hCfgMgr32DLL = 0 then hCfgMgr32DLL := LoadLibrary(CS_cfgmgr32);
      hCM := hCfgMgr32DLL;
      @CmLocate := GetProcAddress(hCfgMgr32DLL, GetW32Name('CM_Locate_DevNodeA'));
    end else hCM := hSetupDLL;

    @CmRegProperty := GetProcAddress(hCM, GetW32Name('CM_Get_DevNode_Registry_PropertyA'));
    @CmGetParent := GetProcAddress(hCM, 'CM_Get_Parent');
    @CM_Get_Device_ID_List_Size := GetProcAddress(hCM, GetW32Name('CM_Get_Device_ID_List_SizeA'));
    @CM_Get_Device_ID_List := GetProcAddress(hCM, GetW32Name('CM_Get_Device_ID_ListA'));

    @CmGetChild := GetProcAddress(hCM, 'CM_Get_Child');    // !!!!!!
    @CmGetSubling := GetProcAddress(hCM,'CM_Get_Sibling'); // !!!!!!!!!!

    @CM_Request_Device_Eject := GetProcAddress(hCM, GetW32Name('CM_Request_Device_EjectA')); // !!!

    if (@SetupDiGetClassDevs=nil)or
       (@SetupDiDestroyDeviceInfoList=nil)or
       (@SetupDiEnumDeviceInfo=nil)or
       (@SetupDiGetDeviceRegistryProperty=nil)or
       (@SetupDiOpenDevRegKey=nil) or
       (@SetupDiEnumDeviceInterfaces = nil) or
       (@SetupDiGetDeviceInterfaceDetail = nil ) or
       (@SetupDiCreateDeviceInfoList = nil ) or
       (@SetupDiCreateDeviceInfo = nil ) or
       (@SetupDiGetINFClass = nil) or
       (@SetupDiSetDeviceRegistryProperty = nil) or
       (@SetupDiCallClassInstaller = nil) or
       (@SetupDiGetDeviceInstanceId = nil) or
       (@SetupDiSetClassInstallParams = nil) or
       (@CmLocate = nil) or
       (@CmRegProperty = nil) or
       (@CmGetParent = nil) or
       (@CM_Get_Device_ID_List_Size = nil) or
       (@CM_Get_Device_ID_List = nil) or

       (@CmGetChild = nil) or
       (@CmGetSubling = nil) //or
       //(@CM_Request_Device_Eject2 = nil)

    then begin
      FreeLibrary(hSetupDLL);
      hSetupDLL := 0;
      if hCfgMgr32DLL <> 0 then begin
        FreeLibrary(hCfgMgr32DLL);
        hCfgMgr32DLL := 0;
      end;
    end;
  end;

  Result := (hSetupDll <> 0);// and (hCfgMgrDLL <> 0) and (hCfgMgr32DLL <> 0);
end;

Const
  DIGCF_DEFAULT           = $00000001;  // only valid with DIGCF_DEVICEINTERFACE
  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_REMOVAL_POLICY_HW_DEFAULT   = $00000020;
  SPDRP_REMOVAL_POLICY_OVERRIDE     = $00000021;
  SPDRP_INSTALL_STATE               = $00000022;
  SPDRP_LOCATION_PATHS              = $00000023;

  DIF_REGISTERDEVICE                = $00000019;

  MAX_CLASS_NAME_LEN                = 32;
  DICD_GENERATE_ID                  = 1;
  DICD_INHERIT_CLASSDRVS            = 2;


  INSTALLFLAG_FORCE                 = $00000001;  // Force the installation of the specified driver
  INSTALLFLAG_READONLY              = $00000002;  // Do a read-only install (no file copy)
  INSTALLFLAG_NONINTERACTIVE        = $00000004;  // No UI shown at all. API will fail if any UI must be shown.
  INSTALLFLAG_BITS                  = $00000007;

  CM_GETIDLIST_FILTER_NONE                = $00000000;
  CM_GETIDLIST_FILTER_ENUMERATOR          = $00000001;
  CM_GETIDLIST_FILTER_SERVICE             = $00000002;
  CM_GETIDLIST_FILTER_EJECTRELATIONS      = $00000004;
  CM_GETIDLIST_FILTER_REMOVALRELATIONS    = $00000008;
  CM_GETIDLIST_FILTER_POWERRELATIONS      = $00000010;
  CM_GETIDLIST_FILTER_BUSRELATIONS        = $00000020;
  CM_GETIDLIST_DONOTGENERATE              = $10000040;
  CM_GETIDLIST_FILTER_BITS                = $1000007F;

function DiGetDeviceRegProp(hEnum:THandle; DeviceInfoData:SP_DEVINFO_DATA; dwParam:dword):string;
var len:dword;
begin
  len := 512;
  SetLength(Result, len);
  if SetupDiGetDeviceRegistryProperty(hEnum,DeviceInfoData,dwParam,nil,@Result[1],len,@len)
    then begin
      {$IFDEF UNICODE}
      len := len shr 1;
      {$ENDIF}
      SetLength(Result,len-1);
    end
    else SetLength(Result,0);
end;

procedure DiEnumInstalledDevices(Guid:PGuid; flPresentOnly, flDevice, flInterface :boolean; slDevices:TStrings);
var p, Index:integer;
    hK, hEnum:THandle;
    DeviceInfoData : SP_DEVINFO_DATA;
    DeviceIntfData : SP_DEVINFO_DATA;
    len,  sz:dword;
    sTmp:string;
    dev:TnrDevice;
    IntfDetail:PSP_DEVICE_INTERFACE_DETAIL_DATA;
begin

  if not IniSetupDI then exit;

  if flPresentOnly
    then p := DIGCF_PRESENT
    else p := 0;

  if flInterface then p := p or DIGCF_DEVICEINTERFACE;
  if Guid = nil then p := p or DIGCF_ALLCLASSES;

  hEnum := SetupDiGetClassDevs(Guid, nil, 0, p);

  if hEnum = INVALID_HANDLE_VALUE then begin
    //OutputDebugString(PChar('Z: INVALID hEnum ' + SysErrorMessage(GetLastError())));
    exit;
  end;

  Index := 0;
  DeviceInfoData.cbSize := SizeOf(SP_DEVINFO_DATA);

//  DeviceIntfData.cbSize := SizeOf(SP_DEVINFO_DATA);
//  OutputDebugString(PChar('Sz:  ' + IntToStr(DeviceInfoData.cbSize) + ' / ' + IntToStr(DeviceIntfData.cbSize)));

if flDevice then begin

  while SetupDiEnumDeviceInfo(hEnum, Index, DeviceInfoData) do begin
    dev := TnrDevice.Create;
    dev.Driver := DiGetDeviceRegProp(hEnum, DeviceInfoData, SPDRP_DRIVER);
    dev.Service := DiGetDeviceRegProp(hEnum, DeviceInfoData, SPDRP_SERVICE);
    dev.NamePDO := DiGetDeviceRegProp(hEnum, DeviceInfoData, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME);
    dev.Description := DiGetDeviceRegProp(hEnum, DeviceInfoData, SPDRP_DEVICEDESC);
    dev.Manufacturer := DiGetDeviceRegProp(hEnum, DeviceInfoData, SPDRP_MFG);
    dev.GuidClass := DeviceInfoData.ClassGuid;// Guid^;
    //OutputDebugString(PChar('enum:' + dev.Description + ' svc: ' + dev.Service));

    hK := SetupDiOpenDevRegKey(hEnum,DeviceInfoData,1,0,1,KEY_QUERY_VALUE);
    if hK <> INVALID_HANDLE_VALUE then begin
      dev.RegKey := RegGetKeyFullName(hK, @len);
      RegCloseKey(hK);
      dev.NameSymbolic := RegReadString(HKEY_LOCAL_MACHINE, dev.RegKey, 'SymbolicName');
      dev.PortName := RegReadString(HKEY_LOCAL_MACHINE, dev.RegKey, 'PortName');
    end;

    len := 256;
    SetLength(sTmp,len);
    // get device instance id ...
    if SetupDiGetDeviceInstanceId(hEnum,@DeviceInfoData,@sTmp[1],len,@len) and
      ( len > 0 ) then begin
      SetLength(sTmp,len - 1);
      dev.InstanceId := sTmp;
      dev.DevInst := DeviceInfoData.DevInst;//PSP_DEVINFO_DATA(Pointer(hEnum))^.DevInst;
      // try get bus relations  ...
      dev.BusRelations := DiGetRelations(sTmp,';', True);
    end;

    DeviceIntfData.cbSize := SizeOf(SP_DEVINFO_DATA);

    if flInterface and SetupDiEnumDeviceInterfaces(hEnum, @DeviceInfoData, Guid, 0, @DeviceIntfData) then begin
      sz := 0;
      SetupDiGetDeviceInterfaceDetail(hEnum, DeviceIntfData, nil, 0, @sz, nil);
      if sz > 0 then begin
        IntfDetail := Pointer(LocalAlloc(LPTR, sz));
        IntfDetail^.cbSize := SizeOf(SP_DEVICE_INTERFACE_DETAIL_DATA);
        if SetupDiGetDeviceInterfaceDetail(hEnum, DeviceIntfData, IntfDetail, sz, nil{@sz}, @DeviceInfoData)
          then dev.NameSymbolic := String(PChar(@IntfDetail^.DevicePath[0]));
        LocalFree(DWord(IntfDetail));
      end;
    end;

    slDevices.AddObject(dev.Driver,dev);
    Inc(Index);

  end
end else begin
  //Index := 0;
  while SetupDiEnumDeviceInterfaces(hEnum, nil, Guid, Index, @DeviceIntfData) do begin
    //BytesReturned := 0;
    SetupDiGetDeviceInterfaceDetail(hEnum, DeviceIntfData, nil, 0, @sz, @DeviceInfoData);
    if (sz <> 0) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then begin
      IntfDetail := Pointer(LocalAlloc(LPTR,sz));
      IntfDetail^.cbSize := SizeOf(SP_DEVICE_INTERFACE_DETAIL_DATA);
      if SetupDiGetDeviceInterfaceDetail(hEnum, DeviceIntfData, IntfDetail, sz, @sz, @DeviceInfoData) then begin
        dev := TnrDevice.Create;
        dev.Driver := DiGetDeviceRegProp(hEnum, DeviceInfoData, SPDRP_DRIVER);
        dev.Service := DiGetDeviceRegProp(hEnum, DeviceInfoData, SPDRP_SERVICE);
        dev.NamePDO := DiGetDeviceRegProp(hEnum, DeviceInfoData, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME);
        dev.Description := DiGetDeviceRegProp(hEnum, DeviceInfoData, SPDRP_DEVICEDESC);
        dev.Manufacturer := DiGetDeviceRegProp(hEnum, DeviceInfoData, SPDRP_MFG);
        dev.NameSymbolic := String(PChar(@IntfDetail^.DevicePath[0]));
        dev.GuidClass := DeviceInfoData.ClassGuid;
        //OutputDebugString(PChar(dev.Description + ' - ' + dev.NameSymbolic));
        slDevices.AddObject(dev.Driver,dev);
      end;
      LocalFree(DWord(IntfDetail));
      Inc(Index);
    end;
  end;
END;
  SetupDiDestroyDeviceInfoList(hEnum);

end;

function DiGetRelations(devInst, sep:string; const flBus:boolean):string;
var len:dword;
    pBuffer:PChar;
    id:cardinal;
begin
  if flBus
    then id := CM_GETIDLIST_FILTER_BUSRELATIONS
    else id := CM_GETIDLIST_FILTER_REMOVALRELATIONS;
  Result := '';
  if(CM_Get_Device_ID_List_Size(len, PChar(devInst), id) = 0)
     and (len > 0)
  then begin
    //pBuffer := PChar(LocalAlloc(LPTR,len));
    pBuffer := StrAlloc(len);

    if CM_Get_Device_ID_List(PChar(devInst), pBuffer, len, id) = 0
      then
        // list is retrieved into buffer now parse it ...
        Result := GetParsedWin32StrList(pBuffer, len, sep );

    //LocalFree(Dword(pBuffer));
    StrDispose(pBuffer);
  end;
end;

function UpdateDriverForPlugAndPlayDevices(hwndParent:THandle;
    HardwareId, FullInfPath:PChar; InstallFlags:DWORD;
    bRebootRequired:PBOOL):boolean;
var hDll:THandle;
    fn:function(hwndParent:THandle; HardwareId, FullInfPath:PChar;
                InstallFlags:DWORD; bRebootRequired:PBOOL):boolean; stdcall;
    err:dword;
begin
  Result := False;
  hDll := LoadLibrary('newdev.dll');
  if hDll = 0 then exit;
  @fn := GetProcAddress(hDll, GetW32Name('UpdateDriverForPlugAndPlayDevicesA'));
  if @fn <> nil then begin
    try
      Result := Fn(hwndParent, HardwareId,FullInfPath,InstallFlags, bRebootRequired);
    except
    end;
  end;
  err := 0;
  if not Result then err := GetLastError;
  FreeLibrary(hDll);
  if not Result then SetLastError(err);
end;

(*
function InstallINF(const PathName: string; hParent: HWND) : boolean;
var
  instance: HINST;
begin { InstallINF }
  instance := ShellExecute(
    hParent,
    PChar('open'),
    PChar('rundll32.exe'),
    PChar('setupapi,InstallHinfSection DefaultInstall 132 ' + PathName),
    nil,
    SW_HIDE);

  Result := instance > 32
end; { InstallINF }
*)

function InstallINF(const PathName: String) : boolean;
var
  InstallHinfSection: procedure (hwnd,ModuleHandle :DWORD; CmdLineBuffer:PWideChar; nCmdShow:DWORD); stdcall;
  cmd:WideString;
begin
  Result := false;
  if hSetupDLL = 0 then hSetupDLL := LoadLibrary(CS_SetupAPI);
  @InstallHinfSection := GetProcAddress(hSetupDLL, 'InstallHinfSectionW');
  if @InstallHinfSection = nil then exit;
  cmd := 'USB_Install 132 '+ WideString(PathName) + '';
//  cmd := 'Manufacturer 132 '+ WideString(PathName) + '';
//  cmd := 'MyDevice_WinUSB.NTamd64 132 '+ WideString(PathName) + '';

//  ChDir('C:\temp\tmp\222\');
  //cmd := 'USB_Install 132 C:\temp\tmp\222\nrusb.inf';
  InstallHinfSection(GetMainWindowHandle, hInstance, PWideChar(cmd), SW_HIDE);
//  InstallHinfSection(0, 0, PWideChar(cmd), 0);
//  InstallHinfSection(GetMainWindowHandle, Instance, PAnsiChar(cmd), SW_HIDE);
//  InstallHinfSection(PAnsiChar(cmd));
  Result := true;
end; { InstallINF }


function InstallDriver(aHid:string; aInf:string):boolean;
var //DeviceInfoSet:dword;
    //DeviceInfoData : SP_DEVINFO_DATA;
    //ClassName:string;
    //guidClass: TGUID;
    //hidList:PChar;
    bReboot:bool;
    //errCode:dword;
//label final;
begin
//  Result := false;

//  if not IniSetupDI then exit;

  {
  // Use the INF File to extract the Class GUID.
  SetLength(ClassName,MAX_CLASS_NAME_LEN);
  if not SetupDiGetINFClass(PChar(aInf),@guidClass,PChar(ClassName),MAX_CLASS_NAME_LEN,nil)
    then exit;

  //guidClass := GUID_DEVCLASS_USB;

  //OutputDebugString('begin ....');
  DeviceInfoSet := SetupDiCreateDeviceInfoList(@guidClass,GetMainWindowHandle);
  if(DeviceInfoSet = INVALID_HANDLE_VALUE)
    then exit;

  hidList := PChar(LocalAlloc(LPTR, Length(aHid) + 4));
  CopyMemory(hidList,PChar(aHid),Length(aHid));

  // Now create the element.
  // Use the Class GUID and Name from the INF file.
  //OutputDebugString(' .. 1 ..');
  DeviceInfoData.cbSize := SizeOf(SP_DEVINFO_DATA);
  if not SetupDiCreateDeviceInfo(DeviceInfoSet,
         PChar(ClassName), @guidClass, nil, 0,
        DICD_GENERATE_ID, @DeviceInfoData)
    then goto final;

  //OutputDebugString(' .. 2 ..');
  // Add the HardwareID to the Device's HardwareID property.
  if not SetupDiSetDeviceRegistryProperty(DeviceInfoSet,
         @DeviceInfoData, SPDRP_HARDWAREID, hidList,Length(aHid)+2)
    then goto final;

  //OutputDebugString(' .. 3 ..');
  // Transform the registry element into an actual devnode
  // in the PnP HW tree.
  if not SetupDiCallClassInstaller(DIF_REGISTERDEVICE,
        DeviceInfoSet, @DeviceInfoData)
    then goto final;

  }

  OutputDebugString('nrComm Lib installs driver.');
  Result := UpdateDriverForPlugAndPlayDevices(GetMainWindowHandle,PChar(aHid),PChar(aInf),INSTALLFLAG_FORCE,@bReboot);

  if not Result
    then OutputDebugString(PChar('nrComm Lib Error:' + IntToHex(GetLastError,8)))
    else OutputDebugString('nrComm Lib OK!');

//final:
//  if DeviceInfoSet <> INVALID_HANDLE_VALUE then begin
    //SetupDiDestroyDeviceInfoList(DeviceInfoSet);
//    LocalFree(DWORD(hidList));
//  end;
end;

function DiGetVolumes(flRemote, flCDROM, flFixed, flRemovable, flRam:boolean; Sep:string):string;
var sDrv:string;
    i, typ:dword;
    //pcN:PChar;
    dwDriverMask:dword;
begin

  Result := '';

  dwDriverMask := GetLogicalDrives;

  for i := 0 to 25 do
    if ((1 shl i) and dwDriverMask) <> 0 then begin
      sDrv := Char(Byte('A') + i);
      typ := GetDriveType(PChar(sDrv + ':\'));
      if ((typ = DRIVE_REMOVABLE) and flRemovable) or
         ((typ = DRIVE_REMOTE) and flRemote) or
         ((typ = DRIVE_CDROM) and flCDROM) or
         ((typ = DRIVE_FIXED) and flFixed) or
         ((typ = DRIVE_RAMDISK) and flRam)
        then Result := Result + sDrv + Sep;
    end;

  Result := Trim(Result);
end;

function DiEjectDevice(devInst:dword):boolean;
var sErr:string;
    err,sz:cardinal;
    tries:integer;
begin
  Result := False;
  if (not IniSetupDI) or (@CM_Request_Device_Eject = nil) then exit;
  tries := 0;
  sz := MAX_PATH;
  SetLength(sErr, sz);
  while CM_Request_Device_Eject(devInst, @err, PChar(sErr), sz, 0) <> 0 do begin
    inc(tries);
    if tries > 3 then begin
      SetLength(sErr, lstrlen(PChar(sErr)));
      raise Exception.Create(sErr);
    end;
    sleep(500);
  end;
  SetLength(sErr, 0);
  Result := True;
end;

Const
  // Flags for SetupDiOpenDeviceInfo
  DIOD_INHERIT_CLASSDRVS  = $00000002;
  DIOD_CANCEL_REMOVE      = $00000004;

function DiResetDevice(devInst:string{; guidClass:TGuid}):boolean;
var DeviceInfoSet:dword;
    DeviceInfoData : SP_DEVINFO_DATA;
begin
  Result := False;
  if not IniSetupDI
    then exit;

  DeviceInfoSet := SetupDiCreateDeviceInfoList(nil{@guidClass}, GetMainWindowHandle);
  if(DeviceInfoSet = INVALID_HANDLE_VALUE)
    then exit;

  DeviceInfoData.cbSize := sizeof(SP_DEVINFO_DATA);
  
  if SetupDiOpenDeviceInfo(DeviceInfoSet, PChar(devInst), GetMainWindowHandle,
            0, @DeviceInfoData)
    then begin
      if SetupDiRemoveDevice(DeviceInfoSet, @DeviceInfoData) then begin
        sleep(1000);
        Result := SetupDiUnremoveDevice(DeviceInfoSet, @DeviceInfoData);
      end;
    end;

  SetupDiDestroyDeviceInfoList(DeviceInfoSet);

end;

function DiRemoveDevice(devInst:string{; guidClass:TGuid}):boolean;
var DeviceInfoSet:dword;
    DeviceInfoData : SP_DEVINFO_DATA;
begin
  Result := False;
  if not IniSetupDI
    then exit;

  DeviceInfoSet := SetupDiCreateDeviceInfoList(nil{@guidClass}, GetMainWindowHandle);
  if(DeviceInfoSet = INVALID_HANDLE_VALUE)
    then exit;

  DeviceInfoData.cbSize := sizeof(SP_DEVINFO_DATA);

  if SetupDiOpenDeviceInfo(DeviceInfoSet, PChar(devInst), GetMainWindowHandle,
            0, @DeviceInfoData)
    then begin
      Result := SetupDiRemoveDevice(DeviceInfoSet, @DeviceInfoData);
    end;

  SetupDiDestroyDeviceInfoList(DeviceInfoSet);

end;

function RunX64Stub(arg:string) : boolean;
var sTmpFolder, s_exe:string;
    len : integer;
begin
  Result := False;
  sTmpFolder := '';
  SetLength(sTmpFolder, MAX_PATH);
  len := GetTempPath(MAX_PATH, PChar(sTmpFolder)); // buffer for path  
  if (len > 0) and (len <= MAX_PATH) then begin
    SetLength(sTmpFolder, len);
    s_exe := sTmpFolder + CS_x64stub_exe;
    CreateFileFromRes('nrx64stub', s_exe);
    RunProcess(GetMainWindowHandle, s_exe + ' ' + arg,
               sTmpFolder, True, True);
    Result := GetLastError() = ERROR_SUCCESS;
    sleep(0);
    DeleteFile(s_exe);
  end;
  SetLength(sTmpFolder, 0);
end;

function DiChangeDeviceInstallParams(devInst:string; {guidClass:TGuid;} state:DWORD): boolean;
var DeviceInfoSet:dword;
    DeviceInfoData : SP_DEVINFO_DATA;
    pcp:SP_PROPCHANGE_PARAMS;
//    err:dword;
begin
  Result := False;
  if not IniSetupDI
    then exit;

//  DeviceInfoSet := SetupDiCreateDeviceInfoList(@guidClass, GetMainWindowHandle);
  DeviceInfoSet := SetupDiCreateDeviceInfoList(nil, GetMainWindowHandle);
  if(DeviceInfoSet = INVALID_HANDLE_VALUE) then exit;

  DeviceInfoData.cbSize := sizeof(SP_DEVINFO_DATA);

  if SetupDiOpenDeviceInfo(DeviceInfoSet, PChar(devInst), GetMainWindowHandle,
            0, @DeviceInfoData)
    then begin

      if State = DICS_ENABLE then begin
        pcp.ClassInstallHeader.cbSize := sizeof(SP_CLASSINSTALL_HEADER);
        pcp.ClassInstallHeader.InstallFunction := DIF_PROPERTYCHANGE;
        pcp.StateChange := state;
        pcp.Scope := DICS_FLAG_GLOBAL;
        pcp.HwProfile := 0;
        if SetupDiSetClassInstallParams(DeviceInfoSet, @DeviceInfoData, @pcp.ClassInstallHeader, SizeOf(pcp))
          then SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DeviceInfoSet, @DeviceInfoData);
      end;

      pcp.ClassInstallHeader.cbSize := sizeof(SP_CLASSINSTALL_HEADER);
      pcp.ClassInstallHeader.InstallFunction := DIF_PROPERTYCHANGE;
      pcp.StateChange := state;
      pcp.Scope := DICS_FLAG_CONFIGSPECIFIC;
      pcp.HwProfile := 0;

      Result := SetupDiSetClassInstallParams(DeviceInfoSet, @DeviceInfoData, @pcp.ClassInstallHeader, sizeof(pcp));
      if Result
        then Result := SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DeviceInfoSet, @DeviceInfoData);

      if (not Result) and (ERROR_IN_WOW64 = GetLastError) then begin
//        Result := RunX64Stub('"' + devInst + '" ' + IntToStr(state) + ' "' + GUIDToString(guidClass) + '"');
        Result := RunX64Stub('"' + devInst + '" ' + IntToStr(state) + ' "' + GUIDToString(DeviceInfoData.ClassGuid) + '"');
      end;

    end
    else begin
      raise Exception.Create(Format('%x' ,[GetLastError]));
    end;

  SetupDiDestroyDeviceInfoList(DeviceInfoSet);

end;

function DiEnableDevice(devInst:string):boolean;
begin
  Result := DiChangeDeviceInstallParams(devInst, DICS_ENABLE);
end;

function DiDisableDevice(devInst:string):boolean;
begin
  Result := DiChangeDeviceInstallParams(devInst, DICS_DISABLE);
end;

function DiRestartDevice(devInst:string):boolean;
begin
  Result := DiChangeDeviceInstallParams(devInst, DICS_PROPCHANGE);
end;


function GetSetupError(code:integer):string;
begin
  Result := '#' + IntToHex(code, 8) + '.';
  case DWORD(code) of
    ERROR_NO_SUCH_DEVINST:
      Result := Result + ' Device not found.';
    ERROR_NO_AUTHENTICODE_CATALOG,
    ERROR_AUTHENTICODE_DISALLOWED,
    ERROR_AUTHENTICODE_TRUSTED_PUBLISHER,
    ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED,
    ERROR_AUTHENTICODE_PUBLISHER_NOT_TRUSTED,
    ERROR_SIGNATURE_OSATTRIBUTE_MISMATCH,
    ERROR_ONLY_VALIDATE_VIA_AUTHENTICODE:
      Result := Result + ' Driver authentication error.';
  end;


end;

initialization

finalization
  if hSetupDLL <> 0 then FreeLibrary(hSetupDLL);
  if hCfgMgr32DLL <> 0 then FreeLibrary(hCfgMgr32DLL);

end.

