Jump to content

USB Communication?


Recommended Posts

Would anyone be able to help in converting this to autoit?

This is an API call dump using API Monitor.

and here is my current code

#include <WinAPI.au3>

Local $sFile, $hFile, $sText, $nBytes, $tBuffer


;CreateFileA ( "\\?\usb#vid_0922&pid_0021#13122609380381#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL )
; 1) create file and write data to it
$sFile = "\\?\usb#vid_0922&pid_0021#13122609380381#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}\PIPE00"
$sText = 'abcdefghijklmnopqrstuvwxyz'
$tBuffer = DllStructCreate("byte[" & StringLen($sText) & "]")
DllStructSetData($tBuffer, 1, $sText)
$hFile = _WinAPI_CreateFile($sFile,3,4,4)
_WinAPI_WriteFile($hFile, DllStructGetPtr($tBuffer), StringLen($sText), $nBytes)
_WinAPI_CloseHandle($hFile)
ConsoleWrite('1) ' & FileRead($sFile) & @CRLF)

FileDelete($sFile)

USB2.apmx86(NOT .au3).au3

What is what? What is what.

Link to comment
Share on other sites

Here is the execution process of usb2.exe as shown by API Monitor

#cs
===============================================================================================================================
SetupDiGetClassDevsA({28d78fad-5a12-11d1-ae5b-0000f803a8c2},NULL,NULL,DIGCF_DEVICEINTERFACE|DIGCF_PRESENT)
===============================================================================================================================
#   Type        Name        Pre-Call Value                                      Post-Call Value
1   LPGUID      ClassGuid   0x004020e8={28d78fad-5a12-11d1-ae5b-0000f803a8c2}   0x004020e8={28d78fad-5a12-11d1-ae5b-0000f803a8c2}
2   PCTSTR      Enumerator  NULL                                                NULL
3   HWND        hwndParent  NULL                                                NULL
4   DWORD       Flags       DIGCF_DEVICEINTERFACE|DIGCF_PRESENT               DIGCF_DEVICEINTERFACE|DIGCF_PRESENT
    HDEVINFO    Return      0x005ef130
===============================================================================================================================


===============================================================================================================================
SetupDiEnumDeviceInterfaces(0x005ef130,NULL,{28d78fad-5a12-11d1-ae5b-0000f803a8c2},0,0x0018fce4)
===============================================================================================================================
#   Type                                Name                            Pre-Call Value                                                                                  Post-Call Value
1   HDEVINFO                            DeviceInfoSet                   0x005ef130                                                                                      0x005ef130
2   PSP_DEVICE_INTERFACE_DATA           DeviceInterfaceData             0x0018fce4={cbSize=28,InterfaceClassGuid={28d78fad-5a12-11d1-ae5b-0000f803a8c2},Flags=1  ...}   0x0018fce4={cbSize=28,InterfaceClassGuid={28d78fad-5a12-11d1-ae5b-0000f803a8c2},Flags=1  ...}
3   PSP_DEVICE_INTERFACE_DETAIL_DATA    DeviceInterfaceDetailData       0x005b2a30={cbSize=5,DevicePath=0xc4}                                                           0x005b2a30={cbSize=5,DevicePath="\"}
4   DWORD                               DeviceInterfaceDetailDataSize   84                                                                                              84
5   PDWORD                              RequiredSize                    NULL                                                                                            NULL
6   PSP_DEVINFO_DATA                    DeviceInfoData                  NULL                                                                                            NULL
    BOOL                                Return                                                                                                                          TRUE
===============================================================================================================================


===============================================================================================================================
SetupDiGetDeviceInterfaceDetailA(0x005ef130,0x0018fce4,0x005b2a30,84,NULL,NULL)
===============================================================================================================================
#   Type                                Name                            Pre-Call Value  Post-Call Value
1   HDEVINFO                            DeviceInfoSet                   0x005ef130  0x005ef130
2   PSP_DEVICE_INTERFACE_DATA           DeviceInterfaceData             0x0018fce4={cbSize=28,InterfaceClassGuid={28d78fad-5a12-11d1-ae5b-0000f803a8c2},Flags=1  ...}   0x0018fce4={cbSize=28,InterfaceClassGuid={28d78fad-5a12-11d1-ae5b-0000f803a8c2},Flags=1  ...}
3   PSP_DEVICE_INTERFACE_DETAIL_DATA    DeviceInterfaceDetailData       0x005b2a30={cbSize=5,DevicePath=0xc4}                                                           0x005b2a30={cbSize=5,DevicePath="\"}
4   DWORD                               DeviceInterfaceDetailDataSize   84                                                                                              84
5   PDWORD                              RequiredSize                    NULL                                                                                            NULL
6   PSP_DEVINFO_DATA                    DeviceInfoData                  NULL                                                                                            NULL
    BOOL                                Return                                                                                                                          TRUE
===============================================================================================================================


===============================================================================================================================
CreateFileA("\\?\usb#vid_0922&pid_0021#13122609380381#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL)
===============================================================================================================================
#   Type                    Name                    Pre-Call Value                                                                                  Post-Call Value
1   LPCTSTR                 lpFileName              0x005b2a34 "\\?\usb#vid_0922&pid_0021#13122609380381#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}"    0x005b2a34 "\\?\usb#vid_0922&pid_0021#13122609380381#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}"
2   DWORD                   dwDesiredAccess         GENERIC_READ | GENERIC_WRITE                                                                    GENERIC_READ | GENERIC_WRITE
3   DWORD                   dwShareMode             FILE_SHARE_READ | FILE_SHARE_WRITE                                                              FILE_SHARE_READ | FILE_SHARE_WRITE
4   LPSECURITY_ATTRIBUTES   lpSecurityAttributes    NULL                                                                                            NULL
5   DWORD                   dwCreationDisposition   OPEN_EXISTING                                                                                   OPEN_EXISTING
6   DWORD                   dwFlagsAndAttributes    FILE_ATTRIBUTE_NORMAL                                                                           FILE_ATTRIBUTE_NORMAL
7   HANDLE                  hTemplateFile           NULL                                                                                            NULL
    HANDLE                  Return                                                                                                                  0x000000e8
===============================================================================================================================


===============================================================================================================================
CloseHandle ( 0x000000e8 )
===============================================================================================================================
#   Type    Name    Pre-Call Value  Post-Call Value
1   HANDLE  hObject 0x000000e8      0x000000e8
    BOOL    Return                  TRUE
===============================================================================================================================


===============================================================================================================================
CreateFileA("\\?\usb#vid_0922&pid_0021#13122609380381#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}\PIPE00",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL)
===============================================================================================================================
#   Type                    Name                    Pre-Call Value                                                                                          Post-Call Value
1   LPCTSTR                 lpFileName              0x005b2aa0 "\\?\usb#vid_0922&pid_0021#13122609380381#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}\PIPE00"     0x005b2aa0 "\\?\usb#vid_0922&pid_0021#13122609380381#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}\PIPE00"
2   DWORD                   dwDesiredAccess         GENERIC_READ | GENERIC_WRITE                                                                            GENERIC_READ | GENERIC_WRITE
3   DWORD                   dwShareMode             0                                                                                                       0
4   LPSECURITY_ATTRIBUTES   lpSecurityAttributes    NULL                                                                                                    NULL
5   DWORD                   dwCreationDisposition   OPEN_EXISTING                                                                                           OPEN_EXISTING
6   DWORD                   dwFlagsAndAttributes    FILE_ATTRIBUTE_NORMAL                                                                                   FILE_ATTRIBUTE_NORMAL
7   HANDLE                  hTemplateFile           NULL                                                                                                    NULL
    HANDLE                  Return                                                                                                                          0x000000e8
===============================================================================================================================


===============================================================================================================================
fopen("E:\TechUtils\Initial.Automation\Data\OneButton\Gray.2335dn","r+b")
===============================================================================================================================
#   Type            Name        Pre-Call Value                                                              Post-Call Value
1   const char*     filename    0x005b281c "E:\TechUtils\Initial.Automation\Data\OneButton\Gray.2335dn"     0x005b281c "E:\TechUtils\Initial.Automation\Data\OneButton\Gray.2335dn"
2   const char*     mode        0x004030b8 "r+b"                                                            0x004030b8 "r+b"
    FILE*           Return                                                                                  0x758d2960
===============================================================================================================================


===============================================================================================================================
printf("
Downloading...", ...)
===============================================================================================================================
#   Type            Name    Pre-Call Value      Post-Call Value
1   const char*     format  0x00403090 "        0x00403090 "
                            Downloading..."     Downloading..."
...                 ...
    int             Return                      15
===============================================================================================================================


===============================================================================================================================
fread(0x0018eca8,1,4096,0x758d2960)
===============================================================================================================================
#   Type    Name    Pre-Call Value  Post-Call Value
1   void*   buffer  0x0018eca8      0x0018eca8
2   size_t  size    1               1
3   size_t  count   4096            4096
4   FILE*   stream  0x758d2960      0x758d2960
    size_t  Return                  759
===============================================================================================================================


===============================================================================================================================
WriteFile(0x000000e8,0x0018eca8,759,0x0018eca4,NULL)
===============================================================================================================================
#   Type        Name    Pre-Call Value      Post-Call Value
1   const char* format  0x0040308c "..."    0x0040308c "..."
...             ...
    int         Return                      3
===============================================================================================================================


===============================================================================================================================
printf("...", ...)
===============================================================================================================================
#   Type        Name    Pre-Call Value      Post-Call Value
1   const char* format  0x0040308c "..."    0x0040308c "..."
...             ...
    int         Return                      3
===============================================================================================================================


===============================================================================================================================
fread(0x0018eca8,1,4096,0x758d2960)
===============================================================================================================================
#   Type    Name    Pre-Call Value  Post-Call Value
1   void*   buffer  0x0018eca8      0x0018eca8
2   size_t  size    1               1
3   size_t  count   4096            4096
4   FILE*   stream  0x758d2960      0x758d2960
    size_t  Return                  0
===============================================================================================================================


===============================================================================================================================
printf("

Download complete!! downsize(%d)

", ...)
===============================================================================================================================
#   Type        Name    Pre-Call Value                      Post-Call Value
1   const char* format  0x00403064 "                        0x00403064 "

                        Download complete!! downsize(%d)    Download complete!! downsize(%d)

                        "                                   "
...             ...
    int         Return                                      38
===============================================================================================================================


===============================================================================================================================
fclose(0x758d2960)
===============================================================================================================================
#   Type    Name    Pre-Call Value  Post-Call Value
1   FILE*   stream  0x758d2960      0x758d2960
    int     Return                  0
===============================================================================================================================


===============================================================================================================================
CloseHandle(0x000000e8)
===============================================================================================================================
#   Type    Name    Pre-Call Value  Post-Call Value
1   HANDLE  hObject 0x000000e8      0x000000e8
    BOOL    Return                  TRUE
===============================================================================================================================


===============================================================================================================================
SetupDiEnumDeviceInterfaces(0x005ef130,NULL,{28d78fad-5a12-11d1-ae5b-0000f803a8c2},1,0x0018fce4)
===============================================================================================================================
#   Type                        Name                Pre-Call Value                                                                                  Post-Call Value
1   HDEVINFO                    DeviceInfoSet       0x005ef130                                                                                      0x005ef130
2   PSP_DEVINFO_DATA            DeviceInfoData      NULL                                                                                            NULL
3   LPGUID                      InterfaceClassGuid  0x004020e8                                                                                      0x004020e8
    GUID                                            {28d78fad-5a12-11d1-ae5b-0000f803a8c2}                                                          {28d78fad-5a12-11d1-ae5b-0000f803a8c2}
4   DWORD                       MemberIndex         2                                                                                               2
5   PSP_DEVICE_INTERFACE_DATA   DeviceInterfaceData 0x0018fce4={cbSize=28,InterfaceClassGuid={28d78fad-5a12-11d1-ae5b-0000f803a8c2},Flags=1 ...}    0x0018fce4={cbSize=28,InterfaceClassGuid={28d78fad-5a12-11d1-ae5b-0000f803a8c2},Flags=1 ...}
    BOOL                        Return                                                                                                              FALSE
===============================================================================================================================


===============================================================================================================================
GetLastError()
===============================================================================================================================
#   Type    Name    Pre-Call Value  Post-Call Value
    DWORD   Return                  ERROR_NO_MORE_ITEMS
===============================================================================================================================


===============================================================================================================================
SetupDiDestroyDeviceInfoList(0x005ef130)
===============================================================================================================================
#   Type        Name            Pre-Call Value  Post-Call Value
1   HDEVINFO    DeviceInfoSet   0x005ef130      0x005ef130
    BOOL        Return                          TRUE
===============================================================================================================================


===============================================================================================================================
exit(0)
===============================================================================================================================
#   Type    Name    Pre-Call Value  Post-Call Value
1   int     status  0
===============================================================================================================================
#ce

What is what? What is what.

Link to comment
Share on other sites

Here is a working sketch...

 

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Compression=4
#AutoIt3Wrapper_UseUpx=y
#AutoIt3Wrapper_UseX64=n
#AutoIt3Wrapper_Change2CUI=y
#AutoIt3Wrapper_Run_Au3Stripper=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#Include <Array.au3>
#Include <WInAPIDiag.au3>
#Include <SetupAPI.au3>
$hDevInfo=_SetupDiGetClassDevs(BitOR($DIGCF_DEVICEINTERFACE,$DIGCF_PRESENT),"{28d78fad-5a12-11d1-ae5b-0000f803a8c2}")
Local $tSP_DEVINFO_DATA
Local $tSP_DEVIFINFO_DATA
Local $pSP_DEVINFO_DATA
Local $pSP_DEVIFINFO_DATA
DllStructSetData($pSP_DEVIFINFO_DATA,"cbSize",DllStructGetSize($pSP_DEVIFINFO_DATA))
Local $iDevEnumIfIndex=0
Local $iDelay=0
Local $aDevs[1][7]
$aDevs[0][0]=0
$aDevs[0][1]=0
$aDevs[0][2]=0
$aDevs[0][3]=0
$aDevs[0][4]=0
$aDevs[0][5]=0
$aDevs[0][6]=0
;GUI()
;Get all Device Paths
While True
    $bDevEnumIf=_SetupDiEnumDeviceInterfaces($hDevInfo,0,"{28d78fad-5a12-11d1-ae5b-0000f803a8c2}",$iDevEnumIfIndex,$pSP_DEVIFINFO_DATA)
    If Not $bDevEnumIf Then ExitLoop
    ReDim $aDevs[UBound($aDevs,1)+1][7]
    $aDevs[UBound($aDevs,1)-1][2]=0
    Con()
    $sDevPath=_SetupDiGetDeviceInterfaceDetail($hDevInfo,$pSP_DEVIFINFO_DATA,$tSP_DEVINFO_DATA)
    If Not $sDevPath<>"" Then
        $aDevs[UBound($aDevs,1)-1][2]=-1
            Con()
        ContinueLoop
    EndIf
    $aDevs[UBound($aDevs,1)-1][1]=$sDevPath
    $aDevs[UBound($aDevs,1)-1][2]=1
    Con()
    $iDevEnumIfIndex+=1
WEnd
For $Index=0 To UBound($aDevs,1)-1
    $aDevs[$Index][0]=_WinAPI_CreateFileEx($aDevs[$Index][1], $OPEN_EXISTING,BitOR($GENERIC_READ,$GENERIC_WRITE),BitOR($FILE_SHARE_READ,$FILE_SHARE_WRITE))
    If @error Then
        $aDevs[$Index][1]=""
        $aDevs[$Index][2]=-2
            Con()
        ContinueLoop
    EndIf
    $aDevs[$Index][2]=2
    Con()
    _WinAPI_CloseHandle($aDevs[$Index][0])
    $aDevs[$Index][0]=_WinAPI_CreateFileEx($aDevs[$Index][1]&"\PIPE00",$OPEN_EXISTING,BitOR($GENERIC_READ,$GENERIC_WRITE))
    If @error Then
        $aDevs[$Index][1]=""
        $aDevs[$Index][2]=-3
            Con()
        ContinueLoop
    EndIf
    $aDevs[$Index][2]=3
    Con()
    $aDevs[$Index][3]=DllStructCreate("byte[4096]")
    $aDevs[$Index][4]=0
Next
Local $sFile=@ScriptDir&"\Blank_black.5330dn",$nBytes;"\5330dn_2.70.00.52.hd",$nBytes;",$nBytes;"\Alignment.2335dn"
$iSize=FileGetSize($sFile)
;~ $iBuffSize=4096
;~ $tBuffer=DllStructCreate("byte["&$iBuffSize&"]")
$bFile=FileRead($sFile)
;~ For $a=0 To $iSize Step $iBuffSize
;~     DllStructSetData($tBuffer,1,BinaryMid($bFile,$a,$iBuffSize))
;~     _WinAPI_WriteFile($aDevs[1][0], DllStructGetPtr($tBuffer),$iBuffSize,$nBytes)
;~ Next
$tBuffer=DllStructCreate("byte["&$iSize&"]")
DllStructSetData($tBuffer,1,$bFile)
_WinAPI_WriteFile($aDevs[1][0], DllStructGetPtr($tBuffer),$iSize,$nBytes)

;_WinAPI_CloseHandle($hFile)

;~ Local $bFile=Binary(FileRead($sFile))
;~ ;Local $hFile=_WinAPI_CreateFile($sFile,2,2)

;~ ;Semi-Asynchronous
;~ For $iPos=0 To BinaryLen($bFile) Step 4096
;~     _WinAPI_SetFilePointer($hFile,$iPos)
;~     If @error Then
;~         $aDevs[$Index][2]=-4;Failed to Set Pointer
;~         ;Con()
;~         ExitLoop
;~     EndIf
;~     ;For $Index=1 To 1;UBound($aDevs,1)-1
;~     Local $iNDEX=1
;~     ;Local $Index
;~         If $aDevs[$Index][2]=-4 Then
;~             ;Con()
;~             ExitLoop; Continue with others
;~         EndIf
;~         _WinAPI_ReadFile($hFile,DllStructGetPtr($aDevs[$Index][3]),4096,$aDevs[$Index][4])
;~         If @error Then
;~             $aDevs[$Index][2]=-4; Failed to Read File
;~             ;Con()
;~             ExitLoop; Continue with others
;~         EndIf
;~         ;$aDevs[$Index][3]
;~         MsgBox(64,BinaryLen($bFile),BinaryMid($bFile,0,256))
;~         Exit
;~         ;DllStructSetData(DllStructGetPtr($aDevs[$Index][3]),1,);
;~         _WinAPI_DisplayStruct($aDevs[$Index][3])
;~         _WinAPI_WriteFile($aDevs[$Index][0],DllStructGetPtr($aDevs[$Index][3]),4096,$aDevs[$Index][5])
;~         If @error Then
;~             $aDevs[$Index][2]=-4; Failed writing to device.
;~             ;Con()
;~             ExitLoop; Continue with others
;~         EndIf
;~         $aDevs[$Index][2]=4
;~         ;Con()
;~         $aDevs[$Index][6]=$iPos
;~     Sleep(100)
;~     ;Next
;~ Next
For $Index=0 To UBound($aDevs,1)-1
    _WinAPI_CloseHandle($aDevs[$Index][0])
        If @error Then
            $aDevs[$Index][2]=-5; Failed Closing Device
            Con()
            ContinueLoop
        EndIf
    $aDevs[$Index][2]=5; Completed
    Con()
Next
;_WinAPI_CloseHandle($hFile)
_SetupDiDestroyDeviceInfoList($hDevInfo)
Sleep(5000)
Exit 0
;Console Colums

Func CON()
    ;_WinAPI_ClearConsole()
    Local $sMsg=""
    For $Index=1 To UBound($aDevs,1)-1
        $sMsg&=$Index&". "&_StringCompact($aDevs[$Index][1],16)&"|"
        Switch $aDevs[$Index][2]
            Case 0
                $sStat="Getting Interface Devices"
            Case 1
                $sStat="Getting Device Paths"
            Case 2
                $sStat="Getting Device Handel"
            Case 3
                $sStat="Opening Device Handel"
            Case 4
                $sStat="Sending data"
            Case 5
                $sStat="Done"
            Case -0
                $sStat="Failed to get Device interface"
            Case -1
                $sStat="Failed to get device path"
            Case -2
                $sStat="Failed getting device handel"
            Case -3
                $sStat="Failed opening device handel"
            Case -4
                $sStat="Failed to send data"
            Case -5
                $sStat="Failed to close handel"
        EndSwitch
        $sMsg&=$sStat&"|"
        $sMsg&=$aDevs[$Index][2]&"|"
        $sMsg&=$aDevs[$Index][4]&"|"
        $sMsg&=$aDevs[$Index][5]&"|"
        $sMsg&=$aDevs[$Index][6]&@CRLF
    Next
    ToolTip($sMsg)
    ;ConsoleWrite($sMsg)
    If $iDelay>0 Then Sleep($iDelay)
EndFunc

; #FUNCTION# =======================================================
; Name...........: _WinAPI_ClearConsole
; Description ...: Clears console screen buffer /w six options to clear rows & characters & set cursor position
; Syntax.........: _WinAPI_ClearConsole($hConsole = -1, $iX = Default, $iY = Default)
; Parameters ....: $hConsole - (Optional) Handle to standard output device
; $iX - (Optional) zero based character column position of cursor
; Default or 0 to screen buffer max width (negative values for some modes)
; $iY - (Optional) zero based row position of cursor
; Default or 0 to screen buffer max height (negative values for some modes)
;
;No params ; clear screen (-1, Default, Default)
;(-1, Default, 0) ; clear single row: $iY = row number (0 is row 1)
;(-1, -2, -3) ; clear block of rows: -$iX = number of rows, -$iY = starting row (minimum $iX = -1, $iY = -1)
;(-1, Default, -1) ; clear all rows from start row: -$iY = starting row (minimum $iY = -1)
;(-1, 20, 2) ; clear characters to end of row: $iX = start character column, $iY = row number
;(-1, -20, 2) ; set cursor at coordinates: -$iX = start character column, $iY = row number (minimum $iX = -1)
;(-1, 0, Default) ; set cursor on bottom row of console window: $iX = start character column

; Return values .: Success - return handle to standard output device (does not have to be closed)
; Failure - return 0, set error and extended
; Author ........: rover
; Modified.......:
; Remarks .......:
; Related .......:
;
; Link ..........; @@MsdnLink@@ FillConsoleOutputCharacter
; Example .......; Yes
; ==================================================================
Func _WinAPI_ClearConsole($hConsole = -1, $iX = Default, $iY = Default)
Local $dwCoord, $fFlag = False
Local $bChar = 0x20, $iErr ; fill character: 0x20 (Space)
Local Const $STD_OUTPUT_HANDLE = -11
Local Const $INVALID_HANDLE_VALUE = -1
Local Const $tagCONSOLE_SCREEN_BUFFER_INFO = "short dwSizeX; short dwSizeY;short dwCursorPositionX;" & _
"short dwCursorPositionY; short wAttributes;short Left; short Top; short Right; short Bottom;" & _
"short dwMaximumWindowSizeX; short dwMaximumWindowSizeY"

;// get handle to standard output device (handle does not have to be closed on return)
Local $hDLLK32 = DllOpen("Kernel32.dll"), $aRet
If $hConsole = -1 Then
$aRet = DllCall($hDLLK32, "hwnd", "GetStdHandle", "dword", $STD_OUTPUT_HANDLE)
$iErr = @error
If @error Or UBound($aRet) <> 2 Or $aRet[0] = $INVALID_HANDLE_VALUE Then
Return SetError($iErr, 1, $INVALID_HANDLE_VALUE)
EndIf
$hConsole = $aRet[0]
EndIf

;// create console screen buffer struct, get buffer
Local $tCONSOLE_SCREEN_BUFFER_INFO = DllStructCreate($tagCONSOLE_SCREEN_BUFFER_INFO)
If @error Then Return SetError(@error, 2, 0)
Local $pConsoleScreenBufferInfo = DllStructGetPtr($tCONSOLE_SCREEN_BUFFER_INFO)
If @error Then Return SetError(@error, 3, 0)

$aRet = DllCall($hDLLK32, "int", "GetConsoleScreenBufferInfo", "hwnd", _
$hConsole, "ptr", $pConsoleScreenBufferInfo)
$iErr = @error
If @error Or UBound($aRet) <> 3 Or Not $aRet[0] Then Return SetError($iErr, 4, 0)

;// Get the screen buffer max width (character columns) and height (rows)
Local $dwSizeX = DllStructGetData($tCONSOLE_SCREEN_BUFFER_INFO, "dwSizeX")
Local $dwSizeY = DllStructGetData($tCONSOLE_SCREEN_BUFFER_INFO, "dwSizeY")
Local $dwConSize

;// input coordinates range check
If IsNumber($iX) And (Abs($iX) > ($dwSizeX -1)) Then $iX = $dwSizeX -1
If IsNumber($iY) And (Abs($iY) > ($dwSizeY -1)) Then $iY = $dwSizeY -1

Select
;// clear screen (Default) - max screen buffer width multiplied by height
Case IsNumber($iX) = 0 And IsNumber($iY) = 0
; handles Default keyword and strings in params
$dwConSize = ($dwSizeX * $dwSizeY)
$iX = 0
$iY = 0
;// overwrite or clear any single row - cursor now set to start of that row
Case IsKeyword($iX) = 1 And IsKeyword($iY) = 0 And $iY >= 0
$dwConSize = $dwSizeX
$iX = 0
;// overwrite or clear a number of rows from starting row
;(-$iX parameter is number of rows to overwrite, second row minimum)
Case $iX < 0 And $iY < 0
$iY = Abs($iY)
$dwConSize = ($dwSizeX * Abs($iX))
$iX = 0
;// overwrite or clear all rows from starting row to last row, second row minimum)
Case IsKeyword($iX) = 1 And $iY < 0
$iY = Abs($iY)
$dwConSize = ($dwSizeX * $dwSizeY) - ($dwSizeX * $iY)
$iX = 0
;// overwrite or clear text from character position on row to end of row
Case $iX >= 0 And IsKeyword($iY) = 0 And $iY >= 0
$dwConSize = ($dwSizeX - $iX)
If $iX = 0 And $iY = 0 Then
$fFlag = True
ContinueCase
EndIf
;// place cursor at last row of console window
Case $iX >= 0 And IsKeyword($iY) = 1
If Not $fFlag Then $iY = DllStructGetData($tCONSOLE_SCREEN_BUFFER_INFO, "Bottom")
ContinueCase
;// places cursor at coordinates for overwriting
Case $iX < 0 And $iY >= 0
$dwCoord = BitOR($iY * 0x10000, BitAND(Abs($iX), 0xFFFF))
$aRet = DllCall($hDLLK32, "int", "SetConsoleCursorPosition", "hwnd", _
$hConsole, "dword", $dwCoord)
$iErr = @error
If @error Or UBound($aRet) <> 3 Or Not $aRet[0] Then Return SetError($iErr, 5, 0)
DllClose($hDLLK32)
Return SetError(0, 0, $hConsole)
Case Else
Return SetError(@error, 6, 0)
EndSelect

;// Cursor position: make DWord of X,Y coordinates)
$dwCoord = BitOR($iY * 0x10000, BitAND($iX, 0xFFFF))

;// Fill selected rows with blanks
$aRet = DllCall($hDLLK32, "int", "FillConsoleOutputCharacterW", "hwnd", $hConsole, _
"byte", $bChar, "dword", $dwConSize, "dword", $dwCoord, "int*", 0)
$iErr = @error
If @error Or UBound($aRet) <> 6 Or $aRet[5] <> $dwConSize Then Return SetError($iErr, 7, 0)

;// Get the current text attributes
$aRet = DllCall($hDLLK32, "int", "GetConsoleScreenBufferInfo", "hwnd", _
$hConsole, "dword", $pConsoleScreenBufferInfo)
$iErr = @error
If @error Or UBound($aRet) <> 3 Or Not $aRet[0] Then Return SetError($iErr, 8, 0)
Local $wAttribute = DllStructGetData($tCONSOLE_SCREEN_BUFFER_INFO, "wAttributes")

;// Set the buffer's attributes
$aRet = DllCall($hDLLK32, "int", "FillConsoleOutputAttribute", "hwnd", $hConsole, _
"short", $wAttribute, "dword", $dwConSize, "dword", $dwCoord, "int*", 0)
$iErr = @error
If @error Or UBound($aRet) <> 6 Or $aRet[5] <> $dwConSize Then Return SetError($iErr, 9, 0)

;// Put the cursor at 0,0 or supplied coordinates
$aRet = DllCall($hDLLK32, "int", "SetConsoleCursorPosition", "hwnd", _
$hConsole, "dword", $dwCoord)
$iErr = @error
If @error Or UBound($aRet) <> 3 Or Not $aRet[0] Then Return SetError($iErr, 10, 0)
DllClose($hDLLK32)
Return SetError(@error, 0, $hConsole)
EndFunc ;==>_WinAPI_ClearConsole


; #FUNCTION# ====================================================================================================================
; Name ..........: _StringCompact
; Description ...: Compact a string to a certain number of characters left and right.
; Syntax ........: _StringCompact($sString[, $iChrsLeftAndRight = Default])
; Parameters ....: $sString             - A string to compact. Multi-lines aren't supported.
;                  $iChrsLeftAndRight   - [optional] An integer value. Default is 0, original string.
; Return values .: Compacted string.
; Author ........: guinness. Idea by AZJIO.
; Example .......: Yes
; ===============================================================================================================================
; #FUNCTION# ====================================================================================================================
; Name ..........: _StringCompact
; Description ...: Compact a string to a certain number of characters left and right.
; Syntax ........: _StringCompact($sString[, $iChrsLeftAndRight = Default])
; Parameters ....: $sString             - A string to compact. Multi-lines are supported.
;                  $iChrsLeftAndRight   - [optional] An integer value. Default is 0, original string.
; Return values .: Compacted string.
; Author ........: guinness.
; Example .......: Yes
; ===============================================================================================================================
Func _StringCompact($sString, $iChrsLeftAndRight = Default)
    $iChrsLeftAndRight = Int($iChrsLeftAndRight)
    Local $sStringCompact = $sString
    If StringLen($sString) > ($iChrsLeftAndRight * 2) Then
        $sStringCompact = StringLeft($sString, $iChrsLeftAndRight) & '...' & StringRight($sString, $iChrsLeftAndRight)
    EndIf
    Return $sStringCompact
EndFunc   ;==>_StringCompact

SetupAPI.au3

#include-once
#include <LocalSecurityAuthority.au3>

; #### HEADER INFORMATION ####
; ===============================================================================
; Title : PnP Configuration Manager
; Description   : Plug and Play (PnP) Configuration Manager that are used by class installers, co-installers, or device installation applications.
; Functions : 1 - Scaning device changes on a local or a remote system.
;       : 2 - Disable and enable an existing hardware device, on local or remote system.
;       : 3 - Install and remove device setup classes, devices and device interfaces.
;       : 4 - Create or remove resource descriptor (or get / set information) for a device instance.
;       : 5 - List the devices (or device interfaces) present in local or remote system.
;       : 6 - Read or write device interfaces.
;       : 7 - Else more ...
; Requirements  : AutoIt Version    : AutoIt v3 ++
;       : AutoIt Libraries  : Array.au3, LocalSecurityAuthority.au3
;       : Modules       : Advapi32.dll, Cfgmgr32.dll, Kernel32.dll, Newdev.dll, Setupapi.dll
;       : Minimum client    : Windows 2000 Professional
; Author    : Pusofalse @ 11/20/2009, kanxinqi@yahoo.com.cn
; ==============================================================================

; #### General size definitions ####
; ==============================================================================
Const $MAX_DEVICE_ID_LEN = 200
Const $MAX_DEVNODE_ID_LEN = $MAX_DEVICE_ID_LEN

Const $MAX_GUID_STRING_LEN = 39
Const $MAX_CLASS_NAME_LEN = 32
Const $MAX_PROFILE_LEN = 80

Const $MAX_CONFIG_VALUE = 9999
Const $MAX_INSTANCE_VALUE = 9999

Const $MAX_MEM_REGISTERS = 9 ; Win95 compatibility--not applicable to 32-bit ConfigMgr
Const $MAX_IO_PORTS = 20 ; Win95 compatibility--not applicable to 32-bit ConfigMgr
Const $MAX_IRQS = 7 ; Win95 compatibility--not applicable to 32-bit ConfigMgr
Const $MAX_DMA_CHANNELS = 7 ; Win95 compatibility--not applicable to 32-bit ConfigMgr

Const $CONFIGMG_VERSION = 0x0400
; ==============================================================================

; #### Device Node Status Constants ####
; ==============================================================================
Const $DN_ROOT_ENUMERATED = 0x00000001   ;  Was enumerated by ROOT
Const $DN_DRIVER_LOADED   = 0x00000002   ;  Has Register_Device_Driver
Const $DN_ENUM_LOADED = 0x00000004   ;  Has Register_Enumerator
Const $DN_STARTED = 0x00000008   ;  Is currently configured
Const $DN_MANUAL  = 0x00000010   ;  Manually installed
Const $DN_NEED_TO_ENUM    = 0x00000020   ;  May need reenumeration
Const $DN_NOT_FIRST_TIME  = 0x00000040   ;  Has received a config
Const $DN_HARDWARE_ENUM   = 0x00000080   ;  Enum generates hardware ID
Const $DN_LIAR    = 0x00000100   ;  Lied about can reconfig once
Const $DN_NEED_RESTART = $DN_LIAR   ; Need a system restart
Const $DN_HAS_MARK    = 0x00000200   ;  Not CM_Create_DevInst lately
Const $DN_HAS_PROBLEM = 0x00000400   ;  Need device installer
Const $DN_FILTERED    = 0x00000800   ;  Is filtered
Const $DN_MOVED   = 0x00001000   ;  Has been moved
Const $DN_DISABLEABLE = 0x00002000   ;  Can be rebalanced
Const $DN_REMOVABLE   = 0x00004000   ;  Can be removed
Const $DN_PRIVATE_PROBLEM = 0x00008000   ;  Has a private problem
Const $DN_MF_PARENT   = 0x00010000   ;  Multi function parent
Const $DN_MF_CHILD    = 0x00020000   ;  Multi function child
Const $DN_WILL_BE_REMOVED = 0x00040000   ;  DevInst is being removed
Const $DN_NOT_FIRST_TIMEE  = 0x00080000  ;   Has received a config enumerate
Const $DN_STOP_FREE_RES    = 0x00100000  ;   When child is stopped, free resources
Const $DN_REBAL_CANDIDATE  = 0x00200000  ;   Don't skip during rebalance
Const $DN_BAD_PARTIAL      = 0x00400000  ;   This devnode's log_confs do not have same resources
Const $DN_NT_ENUMERATOR    = 0x00800000  ;   This devnode's is an NT enumerator
Const $DN_NT_DRIVER        = 0x01000000  ;   This devnode's is an NT driver
Const $DN_NEEDS_LOCKING    = 0x02000000  ;   Devnode need lock resume processing
Const $DN_ARM_WAKEUP        = 0x04000000  ;   Devnode can be the wakeup device
Const $DN_APM_ENUMERATOR    = 0x08000000  ;   APM aware enumerator
Const $DN_APM_DRIVER        = 0x10000000  ;   APM aware driver
Const $DN_SILENT_INSTALL    = 0x20000000  ;   Silent install
Const $DN_NO_SHOW_IN_DM    = 0x40000000  ;   No show in device manager
Const $DN_BOOT_LOG_PROB    = 0x80000000  ;   Had a problem during pre
; ==============================================================================

; SP_DEVINSTALL_PARAMS.Flags Constants
; ===============================================================================
Const $DI_SHOWOEM = 0x00000001 ; support Other... button
Const $DI_SHOWCOMPAT = 0x00000002 ; show compatibility list
Const $DI_SHOWCLASS = 0x00000004 ; show class list
Const $DI_SHOWALL = 0x00000007 ; both class & compat list shown
Const $DI_NOVCP = 0x00000008 ; don't create a new copy queue--use caller-supplied FileQueue
Const $DI_DIDCOMPAT = 0x00000010 ; Searched for compatible devices
Const $DI_DIDCLASS = 0x00000020 ; Searched for class devices
Const $DI_AUTOASSIGNRES = 0x00000040 ; No UI for resources if possible

; flags returned by DiInstallDevice to indicate need to reboot/restart
Const $DI_NEEDRESTART = 0x00000080 ; Reboot required to take effect
Const $DI_NEEDREBOOT = 0x00000100 ; Same as DI_NEEDRESTART

; flags for device installation
Const $DI_NOBROWSE = 0x00000200 ; no Browse... in InsertDisk

; Flags set by DiBuildDriverInfoList
Const $DI_MULTMFGS = 0x00000400 ; Set if multiple manufacturers in class driver list. Flag indicates that device is disabled
Const $DI_DISABLED = 0x00000800 ; Set if device disabled

; Flags for Device/Class Properties
Const $DI_GENERALPAGE_ADDED = 0x00001000
Const $DI_RESOURCEPAGE_ADDED = 0x00002000

; Flag to indicate the setting properties for this Device (or class) caused a change so the Dev Mgr UI probably needs to be updatd.
Const $DI_PROPERTIES_CHANGE = 0x00004000

; Flag to indicate that the sorting from the INF file should be used.
Const $DI_INF_IS_SORTED = 0x00008000

; Flag to indicate that only the INF specified by SP_DEVINSTALL_PARAMS.DriverPath should be searched.
Const $DI_ENUMSINGLEINF = 0x00010000

; Flag that prevents ConfigMgr from removing/re-enumerating devices during device registration, installation, and deletion.
Const $DI_DONOTCALLCONFIGMG = 0x00020000

; The following flag can be used to install a device disabled
Const $DI_INSTALLDISABLED = 0x00040000

; Flag that causes SetupDiBuildDriverInfoList to build a device's compatible driver list from its existing class driver list, instead of the normal INF search.
Const $DI_COMPAT_FROM_CLASS = 0x00080000

; This flag is set if the Class Install params should be used.
Const $DI_CLASSINSTALLPARAMS = 0x00100000

; This flag is set if the caller of DiCallClassInstaller does NOT want the internal default action performed if the Class installer returns ERROR_DI_DO_DEFAULT.
Const $DI_NODI_DEFAULTACTION = 0x00200000

; The setupx flag, DI_NOSYNCPROCESSING (0x00400000) is not support in the Setup APIs. Flags for device installation.
Const $DI_QUIETINSTALL = 0x00800000 ; don't confuse the user with questions or excess info
Const $DI_NOFILECOPY = 0x01000000 ; No file Copy necessary
Const $DI_FORCECOPY = 0x02000000 ; Force files to be copied from install path
Const $DI_DRIVERPAGE_ADDED = 0x04000000 ; Prop provider added Driver page.
Const $DI_USECI_SELECTSTRINGS = 0x08000000 ; Use Class Installer Provided strings in the Select Device Dlg.
Const $DI_OVERRIDE_INFFLAGS = 0x10000000 ; Override INF flags
Const $DI_PROPS_NOCHANGEUSAGE = 0x20000000 ; No Enable/Disable in General Props
Const $DI_NOSELECTICONS = 0x40000000 ; No small icons in select device dialogs
Const $DI_NOWRITE_IDS = 0x80000000 ; Don't write HW & Compat IDs on install
; ======================================================================================

; SP_DEVINSTALL_PARAMS.FlagsEx Constants
; ======================================================================================
Const $DI_FLAGSEX_USEOLDINFSEARCH = 0x00000001 ; Inf Search functions should not use Index Search
Const $DI_FLAGSEX_AUTOSELECTRANK0 = 0x00000002 ; SetupDiSelectDevice doesn't prompt user if rank 0 match
Const $DI_FLAGSEX_CI_FAILED = 0x00000004 ; Failed to Load/Call class installer
Const $DI_FLAGSEX_DIDINFOLIST = 0x00000010 ; Did the Class Info List
Const $DI_FLAGSEX_DIDCOMPATINFO = 0x00000020 ; Did the Compat Info List
Const $DI_FLAGSEX_FILTERCLASSES = 0x00000040
Const $DI_FLAGSEX_SETFAILEDINSTALL = 0x00000080
Const $DI_FLAGSEX_DEVICECHANGE = 0x00000100
Const $DI_FLAGSEX_ALWAYSWRITEIDS = 0x00000200
Const $DI_FLAGSEX_ALLOWEXCLUDEDDRVS = 0x00000800
Const $DI_FLAGSEX_NOUIONQUERYREMOVE = 0x00001000
Const $DI_FLAGSEX_USECLASSFORCOMPAT = 0x00002000 ; Use the device's class when building compat drv list.
 ; (Ignored if DI_COMPAT_FROM_CLASS flag is specified.)
Const $DI_FLAGSEX_OLDINF_IN_CLASSLIST = 0x00004000 ; Search legacy INFs when building class driver list.
Const $DI_FLAGSEX_NO_DRVREG_MODIFY = 0x00008000 ; Don't run AddReg and DelReg for device's software (driver) key.
Const $DI_FLAGSEX_IN_SYSTEM_SETUP = 0x00010000 ; Installation is occurring during initial system setup.
Const $DI_FLAGSEX_INET_DRIVER = 0x00020000 ; Driver came from Windows Update
Const $DI_FLAGSEX_APPENDDRIVERLIST = 0x00040000 ; Cause SetupDiBuildDriverInfoList to append
; ===================================================================================

; Values indicating a change in a devices' state
; ====================================================================================
Const $DICS_ENABLE = 0x00000001
Const $DICS_DISABLE = 0x00000002
Const $DICS_PROPCHANGE = 0x00000003
Const $DICS_START = 0x00000004
Const $DICS_STOP = 0x00000005
; =====================================================================================

; Values specifying the scope of a device property change
; ====================================================================================
Const $DICS_FLAG_GLOBAL = 0x00000001
Const $DICS_FLAG_CONFIGSPECIFIC = 0x00000002
Const $DICS_FLAG_CONFIGGENERAL = 0x00000004
; ====================================================================================

; ####  SetupDiGetClassDevs Constants ####
; ==============================================================================
Const $DIGCF_DEFAULT = 1
Const $DIGCF_PRESENT = 2
Const $DIGCF_ALLCLASSES = 4
Const $DIGCF_PROFILE = 8
Const $DIGCF_DEVICEINTERFACE = 16
; ==============================================================================

; #### SetupDiBuildDriverInfoList Constants (internal) ####
; ==============================================================================
Const $SPDIT_NODRIVER = 0x00000000
Const $SPDIT_CLASSDRIVER = 0x00000001
Const $SPDIT_COMPATDRIVER = 0x00000002
; ==============================================================================
Const $DIBCI_NOINSTALLCLASS = 1
Const $DIBCI_NODISPLAYCLASS = 2

 ; #### Value for SP_UNREMOVEDEVICE_PARAMS.Scope ####
; ==============================================================================
Const $DI_UNREMOVEDEVICE_CONFIGSPECIFIC = 2
; ==============================================================================

; #### Device Installation Structures ####
; ==============================================================================
Const $tagSP_DEVICEINFO_DATA = "dword Size;byte Guid[16];dword DevInst;ulong_ptr Reserved"
Const $tagSP_DEVINFO_LIST_DETAIL_DATA = "dword Size;byte ClassGUID[16];hWnd MachineHandle"

Const $tagSP_DEVINSTALL_PARAMS = "dword Size;dword Flags;dword FlagsEx;hWnd hWndParent;ptr InstallMsgHandler;ptr InstallMsgHandlerContext;ptr FileQueue;ulong_ptr ClassInstallReserved;dword Reserved;char DriverPath[260]"
Const $tagSP_DRVINFO_DATA = "dword Size;dword DriverType;ulong_ptr Reserved;char Descr[256];char MfgName[256];char ProviderName[256];dword FileTime[2];int Version"
Const $tagSP_DRVINFO_DETAIL_DATA = "dword Size;dword InfTime[2];dword CompatIDsOffset;dword CompatIDsLength;ulong_ptr Reserved;char SectionName[256];char InfFileName[260];char DrvDescr[256]"
Const $tagSP_CLASSINSTALL_HEADER = "dword Size;dword DIFCode"
Const $tagSP_DETECTDEVICE_PARAMS = $tagSP_CLASSINSTALL_HEADER & ";ptr NotifyCallback;ptr NotifyParam"
Const $tagSP_PROPCHANGE_PARAMS = $tagSP_CLASSINSTALL_HEADER & ";dword State;dword Scope;dword HwProfile"
Const $tagSP_POWERMESSAGEWAKE_PARAMS = $tagSP_CLASSINSTALL_HEADER & ";char Message[256]"
Const $tagSP_UNREMOVEDEVICE_PARAMS = $tagSP_CLASSINSTALL_HEADER & ";dword Scope;dword HWProfile"
Const $tagSP_DEV_INTERFACE_DATA = "dword Size;byte Guid[16];dword Flags;ulong_ptr Reserved"
Const $tagSP_DEV_INTERFACE_DETAIL_DATA = "dword Size;char DevicePath[512]"
Const $tagSP_CLASSIMAGE_DATA = "dword Size;hWnd ImageList;dword Reserved"
Const $tagSP_DRVINSTALL_PARAMS = "dword Size;dword Rank;dword Flags;long_ptr PrivateData;dword Reserved"
Const $tagSP_INF_SIGNER_INFO = "dword Size;wchar CatalogFile[260];wchar DigitalSigner[260];wchar DigitalSignerVersion[260]"
; ==============================================================================

; #### Device Interface Classes ####
; ==============================================================================
; #### Device Interface Classes for Modem Devices ####
; ==============================================================================
Const $GUID_DEVINTERFACE_MODEM = "{2C7089AA-2E0E-11D1-B114-00C04FC2AAE4}"
; ==============================================================================

; #### Device Interface Classes for Network Devices ####
; ==============================================================================
Const $GUID_DEVINTERFACE_NET = "{CAC88484-7515-4C03-82E6-71A87ABAC361}"
; ==============================================================================

; #### Device Interface Classes for Storage Devices ####
; ==============================================================================
Const $GUID_DEVINTERFACE_CDCHANGER = "{53F56312-B6BF-11D0-94F2-00A0C91EFB8B}"
Const $GUID_DEVINTERFACE_CDROM = "{53F56308-B6BF-11D0-94F2-00A0C91EFB8B}"
Const $GUID_DEVINTERFACE_DISK = "{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}"
Const $GUID_DEVINTERFACE_FLOPPY = "{53F56311-B6BF-11D0-94F2-00A0C91EFB8B}"
Const $GUID_DEVINTERFACE_MEDIUMCHANGER = "{53F56310-B6BF-11D0-94F2-00A0C91EFB8B}"
Const $GUID_DEVINTERFACE_PARTITION = "{53F5630A-B6BF-11D0-94F2-00A0C91EFB8B}"
Const $GUID_DEVINTERFACE_STORAGEPORT = "{2ACCFE60-C130-11D2-B082-00A0C91EFB8B}"
Const $GUID_DEVINTERFACE_TAPE = "{53F5630B-B6BF-11D0-94F2-00A0C91EFB8B}"
Const $GUID_DEVINTERFACE_VOLUME = "{53F5630D-B6BF-11D0-94F2-00A0C91EFB8B}"
Const $GUID_DEVINTERFACE_WRITEONCEDISK = "{53F5630C-B6BF-11D0-94F2-00A0C91EFB8B}"
; ==============================================================================

; #### Device Interface Classes for USB Devices ####
; ==============================================================================
Const $GUID_DEVINTERFACE_USB_HUB = "{F18A0E88-C30C-11D0-8815-00A0C906BED8}"
Const $GUID_DEVINTERFACE_USB_HOST_CONTROLLER = "{3ABF6F2D-71C4-462A-8A92-1E6861E6AF27}"
Const $GUID_DEVINTERFACE_USB_DEVICE = "{A5DCBF10-6530-11D2-901F-00C04FB951ED}"
Const $GUID_DEVINTERFACE_USBSTOR = "{A5DCBF10-6530-11D2-901F-00C04FB951ED}"
; ==============================================================================

; #### Device Interface Classes for Display and Image Devices ####
; ==============================================================================
Const $GUID_DEVINTERFACE_BRIGHTNESS = "{FDE5BBA4-B3F9-46FB-BDAA-0728CE3100B4}"
Const $GUID_DEVINTERFACE_DISPLAY_ADAPTER = "{5B45201D-F2F2-4F3B-85BB-30FF1F953599}"
Const $GUID_DEVINTERFACE_I2C = "{2564AA4F-DDDB-4495-B497-6AD4A84163D7}"
Const $GUID_DEVINTERFACE_IMAGE = "{6BDD1FC6-810F-11D0-BEC7-08002BE2092F}"
Const $GUID_DEVINTERFACE_MONITOR = "{E6F07B5F-EE97-4a90-B076-33F57BF4EAA7}"
Const $GUID_DEVINTERFACE_OPM = "{BF4672DE-6B4E-4BE4-A325-68A91EA49C09}"
Const $GUID_DEVINTERFACE_VIDEO_OUTPUT_ARRIVAL = "{1AD9E4F0-F88D-4360-BAB9-4C2D55E564CD}"
; ==============================================================================

; #### Device Interface Classes for Interactive Input Devices ####
; ==============================================================================
Const $GUID_DEVINTERFACE_HID = "{4D1E55B2-F16F-11CF-88CB-001111000030}"
Const $GUID_DEVINTERFACE_KEYBOARD = "{884B96C3-56EF-11D1-BC8C-00A0C91405DD}"
Const $GUID_DEVINTERFACE_MOUSE = "{378DE44C-56EF-11D1-BC8C-00A0C91405DD}"
; ==============================================================================

; #### Device Interface Classes for Bluetooth Devices ####
; ==============================================================================
Const $GUID_BTHPORT_DEVICE_INTERFACE = "{0850302A-B344-4fda-9BE9-90576B8D46F0}"
; ==============================================================================

; #### Device Interface Classes for Battery, Disk Manager, 1394 Host Controller ... #### (??)
; ==============================================================================
Const $GUID_DEVINTERFACE_DVD = "{1186654d-47b8-48b9-beb9-7df113ae3c67}" ; ??
Const $GUID_DEVINTERFACE_FLASHMEDIA = "{2c9f2281-eb3c-11d6-80af-0001020c74d4}"
Const $GUID_DEVINTERFACE_SYSTEM_BUTTON = "{4afa3d53-74a7-11d0-be5e-00a0c9062857}"
Const $GUID_DEVINTERFACE_DISKDRIVE = "{53f5630e-b6bf-11d0-94f2-00a0c91efb8b}"
Const $GUID_DEVINTERFACE_HDAUDIO = "{54c9343c-2a17-42e8-b4fd-9f9da27b94d6}"
Const $GUID_DEVINTERFACE_AUDIO_ADAPTER = "{65E8773D-8F56-11D0-A3B9-00A0C9223196}"
Const $GUID_DEVINTERFACE_IEEE_1394_HOST_CONTROLLER = "{6bdd1fc1-810f-11d0-bec7-08002be2092f}"
Const $GUID_DEVINTERFACE_BATTERY = "{72631e54-78a4-11d0-bcf7-00aa00b7b32a}"
Const $GUID_DEVINTERFACE_STD_MODEM = "{86e0d1e0-8089-11d0-9ce4-08003e301f73}"
Const $GUID_DEVINTERFACE_PROCESSOR = "{97fadb10-4e33-40ae-359c-8bef029dbdd0}"
Const $GUID_DEVINTERFACE_NDIS = "{ad498944-762f-11d0-8dcb-00c04fc3358c}"
Const $GUID_DEVINTERFACE_HDAUDIO_MODEM = "{adb44c00-1b8d-11d4-8d5e-00a0c90d1c42}"

; ==============================================================================

; #### Device Interface Classes for Battery and ACPI Devices
; ==============================================================================
Const $GUID_DEVICE_APPLICATIONLAUNCH_BUTTON = "{629758EE-986E-4D9E-8E47-DE27F8AB054D}"
Const $GUID_DEVICE_BATTERY = "{72631E54-78A4-11D0-BCF7-00AA00B7B32A}"
Const $GUID_DEVICE_LID = "{4AFA3D52-74A7-11d0-be5e-00A0C9062857}"
Const $GUID_DEVICE_MEMORY = "{3FD0F03D-92E0-45FB-B75C-5ED8FFB01021}"
Const $GUID_DEVICE_MESSAGE_INDICATOR = "{CD48A365-FA94-4CE2-A232-A1B764E5D8B4}"
Const $GUID_DEVICE_PROCESSOR = "{97FADB10-4E33-40AE-359C-8BEF029DBDD0}"
Const $GUID_DEVICE_SYS_BUTTON = "{4AFA3D53-74A7-11d0-be5e-00A0C9062857}"
Const $GUID_DEVICE_THERMAL_ZONE = "{4AFA3D51-74A7-11d0-be5e-00A0C9062857}"
; ==============================================================================

; #### Resource Descriptor Structures ####
; ==============================================================================
Const $tagDMA_DES = "dword Count;dword Type;dword Flags;ulong AllocChannel"
Const $tagIO_DES = "dword Count;dword Type;int64 AllocBase;int64 AllocEnd;dword Flags"
Const $tagMEM_DES = $tagIO_DES & ";dword Reserved"
Const $tagIRQ_DES = "dword Count;dword Type;dword Flags;ulong AllocNum;ulong Affinity"
Const $tagBUSNUMBER_DES = "dword Count;dword Type;dword Flags;ulong AllocBase;ulong AllocEnd"
Const $tagMFCARD_DES = "dword Count;dword Type;dword Flags;byte ConfigOptions;byte IoResIndex;byte Reserved[2];dword ConfigRegisterBase"
Const $tagPCCARD_DES = "dword Count;dword Type;dword Flags;byte ConfigIndex;byte Reserved[3];dword MemCardBase1;dword MemCardBase2"
Const $tagCS_DES = "dword SignatureLength;dword LegacyDataOffset;dword LegacyDataSize;dword Flags;byte Guid[16]"
Const $tagCONFLICT_DETAILS = "ulong Size;ulong Mask;dword DevInst;int ResDes;ulong Flags;char Descr[260]"
; ==============================================================================

; #### Device Power State Constants ####
; ==============================================================================
Const $PDCAP_D0_SUPPORTED = 1
Const $PDCAP_D1_SUPPORTED = 2
Const $PDCAP_D2_SUPPORTED = 4
Const $PDCAP_D3_SUPPORTED = 8
Const $PDCAP_S0_SUPPORTED = 0x10000
Const $PDCAP_S1_SUPPORTED = 0x20000
Const $PDCAP_S2_SUPPORTED = 0x40000
Const $PDCAP_S3_SUPPORTED = 0x80000
Const $PDCAP_S4_SUPPORTED = 0x1000000
Const $PDCAP_S5_SUPPORTED = 0x2000000
Const $PDCAP_WAKE_FROM_D0_SUPPORTED = 0x10
Const $PDCAP_WAKE_FROM_D1_SUPPORTED = 0x20
Const $PDCAP_WAKE_FROM_D2_SUPPORTED = 0x40
Const $PDCAP_WAKE_FROM_D3_SUPPORTED = 0x80
Const $PDCAP_WAKE_FROM_S0_SUPPORTED = 0x100000
Const $PDCAP_WAKE_FROM_S1_SUPPORTED = 0x200000
Const $PDCAP_WAKE_FROM_S2_SUPPORTED = 0x400000
Const $PDCAP_WAKE_FROM_S3_SUPPORTED = 0x800000
Const $PDCAP_WARM_EJECT_SUPPORTED = 0x100
; ==============================================================================

; #### Flags for the SPDRP_CONFIGFLAGS in SetupDiGetDeviceRegistryProperty ####
; ==============================================================================
Const $CONFIGFLAG_DISABLED = 0x00000001 ; Set if disabled
Const $CONFIGFLAG_REMOVED = 0x00000002 ; Set if a present hardware enum device deleted
Const $CONFIGFLAG_MANUAL_INSTALL = 0x00000004 ; Set if the devnode was manually installed
Const $CONFIGFLAG_IGNORE_BOOT_LC = 0x00000008 ; Set if skip the boot config
Const $CONFIGFLAG_NET_BOOT = 0x00000010 ; Load this devnode when in net boot
Const $CONFIGFLAG_REINSTALL = 0x00000020 ; Redo install
Const $CONFIGFLAG_FAILEDINSTALL = 0x00000040 ; Failed the install
Const $CONFIGFLAG_CANTSTOPACHILD = 0x00000080 ; Can't stop/remove a single child
Const $CONFIGFLAG_OKREMOVEROM = 0x00000100 ; Can remove even if rom.
Const $CONFIGFLAG_NOREMOVEEXIT = 0x00000200 ; Don't remove at exit.
Const $CONFIGFLAG_FINISH_INSTALL = 0x00000400 ; Complete install for devnode running 'raw'
Const $CONFIGFLAG_NEEDS_FORCED_CONFIG = 0x00000800 ; This devnode requires a forced config

Const $CSCONFIGFLAG_BITS = 0x00000007 ; OR of below bits
Const $CSCONFIGFLAG_NONE = 0
Const $CSCONFIGFLAG_DISABLED = 0x00000001 ; Set if
Const $CSCONFIGFLAG_DO_NOT_CREATE = 0x00000002 ; Set if
Const $CSCONFIGFLAG_DO_NOT_START = 0x00000004 ; Set if
; ==============================================================================

; #### Device Registry Key Constants ####
; ==============================================================================
Const $REGKEY_HARDWARE_CS001_001 = "HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Hardware Profiles\0001\System\CurrentControlSet\Enum\"
Const $REGKEY_HARDWARE_CS002_001 = "HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Hardware Profiles\0001\System\CurrentControlSet\Enum\"
Const $REGKEY_HARDWARE_CS002_CURRENT = "HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Hardware Profiles\Current\System\CurrentControlSet\Enum\"
Const $REGKEY_HARDWARE_CCS_001 = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Hardware Profiles\0001\System\CurrentControlSet\Enum\"
Const $REGKEY_HARDWARE_CCS_CURRENT = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Hardware Profiles\Current\System\CurrentControlSet\Enum\"
Const $REGKEY_HARDWARE_CS002_ENUM = "HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Enum\"

; ==============================================================================

; #### Device Registry Property Structure(s) ####
; ==============================================================================
Const $tagCM_POWER_DATA = "ulong Size;int MostRecentPowerState;ulong Capabilities;ulong D1Latency;ulong D2Latency;ulong D3Latency;int PowerStateMapping[7];int DeepestSystemWake"
Const $tagHWPROFILE_INFO = "ulong HWProfile;char FriendlyName[80];dword Flags"
; ==============================================================================

; #### Configuration Priority Value ####
; ==============================================================================
Const $LCPRI_FORCECONFIG = 0x00000000 ; Coming from a forced config
Const $LCPRI_BOOTCONFIG = 0x00000001  ; Coming from a boot config
Const $LCPRI_DESIRED = 0x00002000  ; Preferable (better performance)
Const $LCPRI_NORMAL = 0x00003000  ; Workable (acceptable performance)
Const $LCPRI_LASTBESTCONFIG = 0x00003FFF  ; CM only--do not use
Const $LCPRI_SUBOPTIMAL = 0x00005000 ;  Not desired, but will work
Const $LCPRI_LASTSOFTCONFIG = 0x00007FFF  ; CM only--do not use
Const $LCPRI_RESTART = 0x00008000 ;  Need to restart
Const $LCPRI_REBOOT = 0x00009000  ; Need to reboot
Const $LCPRI_POWEROFF = 0x0000A000 ;  Need to shutdown/power-off
Const $LCPRI_HARDRECONFIG = 0x0000C000 ;  Need to change a jumper
Const $LCPRI_HARDWIRED = 0x0000E000 ;   Cannot be changed
Const $LCPRI_IMPOSSIBLE = 0x0000F000  ; Impossible configuration
Const $LCPRI_DISABLED = 0x0000FFFF  ; Disabled configuration
Const $MAX_LCPRI = 0x0000FFFF ;  Maximum known LC Priority
; ==============================================================================

; #### Device Manager Error Code ####
; ==============================================================================
Const $CM_PROB_NOT_CONFIGURED = 0x00000001 ; no config for device
Const $CM_PROB_DEVLOADER_FAILED = 0x00000002 ; service load failed
Const $CM_PROB_OUT_OF_MEMORY = 0x00000003 ; out of memory
Const $CM_PROB_ENTRY_IS_WRONG_TYPE = 0x00000004 ;
Const $CM_PROB_LACKED_ARBITRATOR = 0x00000005 ;
Const $CM_PROB_BOOT_CONFIG_CONFLICT = 0x00000006 ; boot config conflict
Const $CM_PROB_FAILED_FILTER = 0x00000007 ;
Const $CM_PROB_DEVLOADER_NOT_FOUND = 0x00000008 ; Devloader not found
Const $CM_PROB_INVALID_DATA = 0x00000009 ;
Const $CM_PROB_FAILED_START = 0x0000000A ;
Const $CM_PROB_LIAR = 0x0000000B ;
Const $CM_PROB_NORMAL_CONFLICT = 0x0000000C ; config conflict
Const $CM_PROB_NOT_VERIFIED = 0x0000000D ;
Const $CM_PROB_NEED_RESTART = 0x0000000E ; requires restart
Const $CM_PROB_REENUMERATION = 0x0000000F ;
Const $CM_PROB_PARTIAL_LOG_CONF = 0x00000010 ;
Const $CM_PROB_UNKNOWN_RESOURCE = 0x00000011 ; unknown res type
Const $CM_PROB_REINSTALL = 0x00000012 ;
Const $CM_PROB_REGISTRY = 0x00000013 ;
Const $CM_PROB_VXDLDR = 0x00000014 ; WINDOWS 95 ONLY
Const $CM_PROB_WILL_BE_REMOVED = 0x00000015 ; devinst will remove
Const $CM_PROB_DISABLED = 0x00000016 ; devinst is disabled
Const $CM_PROB_DEVLOADER_NOT_READY = 0x00000017 ; Devloader not ready
Const $CM_PROB_DEVICE_NOT_THERE = 0x00000018 ; device doesn't exist
Const $CM_PROB_MOVED = 0x00000019 ;
Const $CM_PROB_TOO_EARLY = 0x0000001A ;
Const $CM_PROB_NO_VALID_LOG_CONF = 0x0000001B ; no valid log config
Const $CM_PROB_FAILED_INSTALL = 0x0000001C ; install failed
Const $CM_PROB_HARDWARE_DISABLED = 0x0000001D ; device disabled
Const $CM_PROB_CANT_SHARE_IRQ = 0x0000001E ; can't share IRQ
Const $CM_PROB_FAILED_ADD = 0x0000001F ; driver failed add
Const $CM_PROB_DISABLED_SERVICE = 0x00000020 ; service's Start = 4
Const $CM_PROB_TRANSLATION_FAILED = 0x00000021 ; resource translation failed
Const $CM_PROB_NO_SOFTCONFIG = 0x00000022 ; no soft config
Const $CM_PROB_BIOS_TABLE = 0x00000023 ; device missing in BIOS table
Const $CM_PROB_IRQ_TRANSLATION_FAILED = 0x00000024 ; IRQ translator failed
Const $CM_PROB_FAILED_DRIVER_ENTRY = 0x00000025 ; DriverEntry() failed.
Const $CM_PROB_DRIVER_FAILED_PRIOR_UNLOAD = 0x00000026 ; Driver should have unloaded.
Const $CM_PROB_DRIVER_FAILED_LOAD = 0x00000027 ; Driver load unsuccessful.
Const $CM_PROB_DRIVER_SERVICE_KEY_INVALID = 0x00000028 ; Error accessing driver's service key
Const $CM_PROB_LEGACY_SERVICE_NO_DEVICES = 0x00000029 ; Loaded legacy service created no devices
Const $CM_PROB_DUPLICATE_DEVICE = 0x0000002A ; Two devices were discovered with the same name
Const $CM_PROB_FAILED_POST_START = 0x0000002B ; The drivers set the device state to failed
Const $CM_PROB_HALTED = 0x0000002C ; This device was failed post start via usermode
Const $CM_PROB_PHANTOM = 0x0000002D ; The devinst currently exists only in the registry
Const $CM_PROB_SYSTEM_SHUTDOWN = 0x0000002E ; The system is shutting down
Const $CM_PROB_HELD_FOR_EJECT = 0x0000002F ; The device is offline awaiting removal
Const $CM_PROB_DRIVER_BLOCKED = 0x00000030 ; One or more drivers is blocked from loading
Const $CM_PROB_REGISTRY_TOO_LARGE = 0x00000031 ; System hive has grown too large
Const $NUM_CM_PROB = 0x00000032;
; ==============================================================================

; #### Flags for _CM_Locate_DevNode ####
; ====================================================================================
Const $CM_LOCATE_DEVNODE_NORMAL = 0x00000000
Const $CM_LOCATE_DEVNODE_PHANTOM = 0x00000001
Const $CM_LOCATE_DEVNODE_CANCELREMOVE = 0x00000002
Const $CM_LOCATE_DEVNODE_NOVALIDATION = 0x00000004
Const $CM_LOCATE_DEVNODE_BITS = 0x00000007
Const $CM_LOCATE_DEVINST_NORMAL = $CM_LOCATE_DEVNODE_NORMAL
Const $CM_LOCATE_DEVINST_PHANTOM = $CM_LOCATE_DEVNODE_PHANTOM
Const $CM_LOCATE_DEVINST_CANCELREMOVE = $CM_LOCATE_DEVNODE_CANCELREMOVE
Const $CM_LOCATE_DEVINST_NOVALIDATION = $CM_LOCATE_DEVNODE_NOVALIDATION
Const $CM_LOCATE_DEVINST_BITS = $CM_LOCATE_DEVNODE_BITS
; ====================================================================================

; #### Flags for _CM_Get_Device_ID_List, _CM_Get_Device_ID_List_Size ####
; ====================================================================================
Const $CM_GETIDLIST_FILTER_NONE = 0x00000000
Const $CM_GETIDLIST_FILTER_ENUMERATOR = 0x00000001
Const $CM_GETIDLIST_FILTER_SERVICE = 0x00000002
Const $CM_GETIDLIST_FILTER_EJECTRELATIONS = 0x00000004
Const $CM_GETIDLIST_FILTER_REMOVALRELATIONS = 0x00000008
Const $CM_GETIDLIST_FILTER_POWERRELATIONS = 0x00000010
Const $CM_GETIDLIST_FILTER_BUSRELATIONS = 0x00000020
Const $CM_GETIDLIST_DONOTGENERATE = 0x10000040
Const $CM_GETIDLIST_FILTER_BITS = 0x1000007F
; ====================================================================================

; #### Flags for CM_Get_Device_Interface_List, CM_Get_Device_Interface_List_Size ####
; ====================================================================================
Const $CM_GET_DEVICE_INTERFACE_LIST_PRESENT = 0x00000000 ; only currently 'live' device interfaces
Const $CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES = 0x00000001 ; all registered device interfaces
Const $CM_GET_DEVICE_INTERFACE_LIST_BITS = 0x00000001
; ====================================================================================

; #### Flags for _CM_Reenumerate_DevNode ####
; ====================================================================================
Const $CM_REENUMERATE_NORMAL = 0x00000000
Const $CM_REENUMERATE_SYNCHRONOUS = 0x00000001
Const $CM_REENUMERATE_BITS = 0x00000001
; ====================================================================================

; #### Flags for _CM_Query_And_Remove_SubTree ####
; ====================================================================================
Const $CM_REMOVE_UI_OK = 0x00000000
Const $CM_REMOVE_UI_NOT_OK = 0x00000001
Const $CM_REMOVE_NO_RESTART = 0x00000002
Const $CM_REMOVE_BITS = 0x00000003
; ====================================================================================

; #### Flags for _CM_Add_ID ####
; ====================================================================================
Const $CM_ADD_ID_HARDWARE = 0x00000000
Const $CM_ADD_ID_COMPATIBLE = 0x00000001
Const $CM_ADD_ID_BITS = 0x00000001
; ====================================================================================

; #### Flags for _CM_Get_First_Log_Conf ####
; ====================================================================================
Const $BASIC_LOG_CONF = 0x00000000 ; Specifies the req list.
Const $FILTERED_LOG_CONF = 0x00000001 ; Specifies the filtered req list.
Const $ALLOC_LOG_CONF = 0x00000002 ; Specifies the Alloc Element.
Const $BOOT_LOG_CONF = 0x00000003 ; Specifies the RM Alloc Element.
Const $FORCED_LOG_CONF = 0x00000004 ; Specifies the Forced Log Conf
Const $OVERRIDE_LOG_CONF = 0x00000005 ; Specifies the Override req list.
Const $NUM_LOG_CONF = 0x00000006 ; Number of Log Conf type
Const $LOG_CONF_BITS = 0x00000007 ; The bits of the log conf type.
; =====================================================================================

; #### Property for _SetupDiGetDeviceRegistryProperty ####
; ==============================================================================
Const $SPDRP_DEVICEDESC = 0x00000000 ; DeviceDesc (R/W)
Const $SPDRP_HARDWAREID = 0x00000001 ; HardwareID (R/W)
Const $SPDRP_COMPATIBLEIDS = 0x00000002 ; CompatibleIDs (R/W)
Const $SPDRP_NTDEVICEPATHS = 0x00000003 ; Unsupported, DO NOT USE
Const $SPDRP_SERVICE = 0x00000004 ; Service (R/W)
Const $SPDRP_CONFIGURATION = 0x00000005 ; Configuration (R)
Const $SPDRP_CONFIGURATIONVECTOR = 0x00000006 ; ConfigurationVector (R)
Const $SPDRP_CLASS = 0x00000007 ; Class (R)--tied to ClassGUID
Const $SPDRP_CLASSGUID = 0x00000008 ; ClassGUID (R/W)
Const $SPDRP_DRIVER = 0x00000009 ; Driver (R/W)
Const $SPDRP_CONFIGFLAGS = 0x0000000A ; ConfigFlags (R/W)
Const $SPDRP_MFG = 0x0000000B ; Mfg (R/W)
Const $SPDRP_FRIENDLYNAME = 0x0000000C ; FriendlyName (R/W)
Const $SPDRP_LOCATION_INFORMATION = 0x0000000D ; LocationInformation (R/W)
Const $SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0x0000000E ; PhysicalDeviceObjectName (R)
Const $SPDRP_CAPABILITIES = 0x0000000F ; Capabilities (R)
Const $SPDRP_UI_NUMBER = 0x00000010 ; UiNumber (R)
Const $SPDRP_UPPERFILTERS = 0x00000011 ; UpperFilters (R/W)
Const $SPDRP_LOWERFILTERS = 0x00000012 ; LowerFilters (R/W)
Const $SPDRP_MAXIMUM_PROPERTY = 0x00000013 ; Upper bound on ordinals
Const $SPDRP_REMOVAL_POLICY = 0x1F
Const $SPDRP_REMOVAL_POLICY_HW_DEFAULT = 0x20
; ==============================================================================

; #### Removal policies (retrievable via _SetupDiGetDeviceRegistryProperty with
; the SPDRP_REMOVAL_POLICY, or SPDRP_REMOVAL_POLICY_HW_DFAULT properties) ####
; ====================================================================================
Const $CM_REMOVAL_POLICY_EXPECT_NO_REMOVAL = 1
Const $CM_REMOVAL_POLICY_EXPECT_ORDERLY_REMOVAL = 2
Const $CM_REMOVAL_POLICY_EXPECT_SURPRISE_REMOVAL = 3
; ====================================================================================

; Re-enable and configuration actions (specified in call to _CM_Setup_DevInst)
; ===================================================================================
Const $CM_SETUP_DEVNODE_READY = 0x00000000; Reenable problem devinst
Const $CM_SETUP_DEVINST_READY = $CM_SETUP_DEVNODE_READY
Const $CM_SETUP_DOWNLOAD = 0x00000001; Get info about devinst
Const $CM_SETUP_WRITE_LOG_CONFS = 0x00000002
Const $CM_SETUP_PROP_CHANGE = 0x00000003
Const $CM_SETUP_DEVNODE_RESET = 0x00000004; Reset problem devinst without starting
Const $CM_SETUP_DEVINST_RESET = $CM_SETUP_DEVNODE_RESET
Const $CM_SETUP_BITS = 0x00000007
; ===================================================================================

; #### Flags for _CM_Set_DevNode_Problem ####
; ====================================================================================
Const $CM_SET_DEVNODE_PROBLEM_NORMAL = 0x00000000 ; only set problem if currently no problem
Const $CM_SET_DEVNODE_PROBLEM_OVERRIDE = 0x00000001 ; override current problem with new.
Const $CM_SET_DEVNODE_PROBLEM_BITS = 0x00000001
Const $CM_SET_DEVINST_PROBLEM_NORMAL = $CM_SET_DEVNODE_PROBLEM_NORMAL
Const $CM_SET_DEVINST_PROBLEM_OVERRIDE = $CM_SET_DEVNODE_PROBLEM_OVERRIDE
Const $CM_SET_DEVINST_PROBLEM_BITS = $CM_SET_DEVNODE_PROBLEM_BITS
; ====================================================================================

; #### DIF (device installation function) code ####
; ====================================================================================
Const $DIF_SELECTDEVICE   = 0x00000001
Const $DIF_INSTALLDEVICE           = 0x00000002
Const $DIF_ASSIGNRESOURCES         = 0x00000003
Const $DIF_PROPERTIES     = 0x00000004
Const $DIF_REMOVE         = 0x00000005
Const $DIF_FIRSTTIMESETUP          = 0x00000006
Const $DIF_FOUNDDEVICE    = 0x00000007
Const $DIF_SELECTCLASSDRIVERS      = 0x00000008
Const $DIF_VALIDATECLASSDRIVERS    = 0x00000009
Const $DIF_INSTALLCLASSDRIVERS     = 0x0000000A
Const $DIF_CALCDISKSPACE           = 0x0000000B
Const $DIF_DESTROYPRIVATEDATA      = 0x0000000C
Const $DIF_VALIDATEDRIVER          = 0x0000000D
Const $DIF_MOVEDEVICE     = 0x0000000E
Const $DIF_DETECT         = 0x0000000F
Const $DIF_INSTALLWIZARD           = 0x00000010
Const $DIF_DESTROYWIZARDDATA       = 0x00000011
Const $DIF_PROPERTYCHANGE          = 0x00000012
Const $DIF_ENABLECLASS    = 0x00000013
Const $DIF_DETECTVERIFY   = 0x00000014
Const $DIF_INSTALLDEVICEFILES      = 0x00000015
Const $DIF_UNREMOVE       = 0x00000016
Const $DIF_SELECTBESTCOMPATDRV     = 0x00000017
Const $DIF_ALLOW_INSTALL           = 0x00000018
Const $DIF_REGISTERDEVICE          = 0x00000019
Const $DIF_INSTALLINTERFACES       = 0x00000020
Const $DIF_DETECTCANCEL   = 0x00000021
Const $DIF_REGISTER_COINSTALLERS   = 0x00000022
Const $DIF_POWERMESSAGEWAKE = 0x27
; ====================================================================================

; #### Configuration Manager return status codes ####
; ====================================================================================
Const $CR_SUCCESS = 0x00000000
Const $CR_DEFAULT = 0x00000001
Const $CR_OUT_OF_MEMORY = 0x00000002
Const $CR_INVALID_POINTER = 0x00000003
Const $CR_INVALID_FLAG = 0x00000004
Const $CR_INVALID_DEVNODE = 0x00000005
Const $CR_INVALID_DEVINST = $CR_INVALID_DEVNODE
Const $CR_INVALID_RES_DES = 0x00000006
Const $CR_INVALID_LOG_CONF = 0x00000007
Const $CR_INVALID_ARBITRATOR = 0x00000008
Const $CR_INVALID_NODELIST = 0x00000009
Const $CR_DEVNODE_HAS_REQS = 0x0000000A
Const $CR_DEVINST_HAS_REQS = $CR_DEVNODE_HAS_REQS
Const $CR_INVALID_RESOURCEID = 0x0000000B
Const $CR_DLVXD_NOT_FOUND = 0x0000000C ; WIN 95 ONLY
Const $CR_NO_SUCH_DEVNODE = 0x0000000D
Const $CR_NO_SUCH_DEVINST = $CR_NO_SUCH_DEVNODE
Const $CR_NO_MORE_LOG_CONF = 0x0000000E
Const $CR_NO_MORE_RES_DES = 0x0000000F
Const $CR_ALREADY_SUCH_DEVNODE = 0x00000010
Const $CR_ALREADY_SUCH_DEVINST = $CR_ALREADY_SUCH_DEVNODE
Const $CR_INVALID_RANGE_LIST = 0x00000011
Const $CR_INVALID_RANGE = 0x00000012
Const $CR_FAILURE = 0x00000013
Const $CR_NO_SUCH_LOGICAL_DEV = 0x00000014
Const $CR_CREATE_BLOCKED = 0x00000015
Const $CR_NOT_SYSTEM_VM = 0x00000016 ; WIN 95 ONLY
Const $CR_REMOVE_VETOED = 0x00000017
Const $CR_APM_VETOED = 0x00000018
Const $CR_INVALID_LOAD_TYPE = 0x00000019
Const $CR_BUFFER_SMALL = 0x0000001A
Const $CR_NO_ARBITRATOR = 0x0000001B
Const $CR_NO_REGISTRY_HANDLE = 0x0000001C
Const $CR_REGISTRY_ERROR = 0x0000001D
Const $CR_INVALID_DEVICE_ID = 0x0000001E
Const $CR_INVALID_DATA = 0x0000001F
Const $CR_INVALID_API = 0x00000020
Const $CR_DEVLOADER_NOT_READY = 0x00000021
Const $CR_NEED_RESTART = 0x00000022
Const $CR_NO_MORE_HW_PROFILES = 0x00000023
Const $CR_DEVICE_NOT_THERE = 0x00000024
Const $CR_NO_SUCH_VALUE = 0x00000025
Const $CR_WRONG_TYPE = 0x00000026
Const $CR_INVALID_PRIORITY = 0x00000027
Const $CR_NOT_DISABLEABLE = 0x00000028
Const $CR_FREE_RESOURCES = 0x00000029
Const $CR_QUERY_VETOED = 0x0000002A
Const $CR_CANT_SHARE_IRQ = 0x0000002B
Const $CR_NO_DEPENDENT = 0x0000002C
Const $CR_SAME_RESOURCES = 0x0000002D
Const $CR_NO_SUCH_REGISTRY_KEY = 0x0000002E
Const $CR_INVALID_MACHINENAME = 0x0000002F ; NT ONLY
Const $CR_REMOTE_COMM_FAILURE = 0x00000030 ; NT ONLY
Const $CR_MACHINE_UNAVAILABLE = 0x00000031 ; NT ONLY
Const $CR_NO_CM_SERVICES = 0x00000032 ; NT ONLY
Const $CR_ACCESS_DENIED = 0x00000033 ; NT ONLY
Const $CR_CALL_NOT_IMPLEMENTED = 0x00000034
Const $CR_INVALID_PROPERTY = 0x00000035
Const $CR_DEVICE_INTERFACE_ACTIVE = 0x00000036
Const $CR_NO_SUCH_DEVICE_INTERFACE = 0x00000037
Const $CR_INVALID_REFERENCE_STRING = 0x00000038
Const $CR_INVALID_CONFLICT_LIST = 0x00000039
Const $CR_INVALID_INDEX = 0x0000003A
Const $CR_INVALID_STRUCTURE_SIZE = 0x0000003B
Const $NUM_CR_RESULTS = 0x0000003C
; ====================================================================================

; #### Resource Type for _CM_Get_Next_Res_Des ####
; ====================================================================================
Const $RESTYPE_ALL = 0x00000000 ; Return all resource types
Const $RESTYPE_NONE = 0x00000000 ; Arbitration always succeeded
Const $RESTYPE_MEM = 0x00000001 ; Physical address resource
Const $RESTYPE_IO = 0x00000002 ; Physical I/O address resource
Const $RESTYPE_DMA = 0x00000003 ; DMA channels resource
Const $RESTYPE_IRQ = 0x00000004 ; IRQ resource
Const $RESTYPE_DONOTUSE = 0x00000005 ; Used as spacer to sync subsequent ResTypes w/NT
Const $RESTYPE_BUSNUMBER = 0x00000006 ; bus number resource
Const $RESTYPE_MAX = 0x00000006 ; Maximum known (arbitrated ResType
Const $RESTYPE_IGNORED_BIT = 0x00008000 ; Ignore this resource
Const $RESTYPE_CLASSSPECIFIC = 0x0000FFFF ; class-specific resource
Const $RESTYPE_RESERVED = 0x00008000 ; reserved for internal use
Const $RESTYPE_DEVICEPRIVAT = 0x00008001 ; device private data
Const $RESTYPE_PCCARDCONFIG = 0x00008002 ; PC Card configuration data
Const $RESTYPE_MFCARDCONFIG = 0x00008003 ; MF Card configuration data
; ====================================================================================

; #### Registry properties for device instance ####
; =====================================================================================
Const $CM_DRP_DEVICEDESC = 0x00000001 ; DeviceDesc REG_SZ property (RW)
Const $CM_DRP_HARDWAREID = 0x00000002 ; HardwareID REG_MULTI_SZ property (RW)
Const $CM_DRP_COMPATIBLEIDS = 0x00000003 ; CompatibleIDs REG_MULTI_SZ property (RW)
Const $CM_DRP_UNUSED0 = 0x00000004 ; unused
Const $CM_DRP_SERVICE = 0x00000005 ; Service REG_SZ property (RW)
Const $CM_DRP_UNUSED1 = 0x00000006 ; unused
Const $CM_DRP_UNUSED2 = 0x00000007 ; unused
Const $CM_DRP_CLASS = 0x00000008 ; Class REG_SZ property (RW)
Const $CM_DRP_CLASSGUID = 0x00000009 ; ClassGUID REG_SZ property (RW)
Const $CM_DRP_DRIVER = 0x0000000A ; Driver REG_SZ property (RW)
Const $CM_DRP_CONFIGFLAGS = 0x0000000B ; ConfigFlags REG_DWORD property (RW)
Const $CM_DRP_MFG = 0x0000000C ; Mfg REG_SZ property (RW)
Const $CM_DRP_FRIENDLYNAME = 0x0000000D ; FriendlyName REG_SZ property (RW)
Const $CM_DRP_LOCATION_INFORMATION = 0x0000000E ; LocationInformation REG_SZ property (RW)
Const $CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME = 0x0000000F ; PhysicalDeviceObjectName REG_SZ property (R)
Const $CM_DRP_CAPABILITIES = 0x00000010 ; Capabilities REG_DWORD property (R)
Const $CM_DRP_UI_NUMBER = 0x00000011 ; UiNumber REG_DWORD property (R)
Const $CM_DRP_UPPERFILTERS = 0x00000012 ; UpperFilters REG_MULTI_SZ property (RW)
Const $CM_DRP_LOWERFILTERS = 0x00000013 ; LowerFilters REG_MULTI_SZ property (RW)
Const $CM_DRP_BUSTYPEGUID = 0x00000014 ; Bus Type Guid, GUID, (R)
Const $CM_DRP_LEGACYBUSTYPE = 0x00000015 ; Legacy bus type, INTERFACE_TYPE, (R)
Const $CM_DRP_BUSNUMBER = 0x00000016 ; Bus Number, DWORD, (R)
Const $CM_DRP_ENUMERATOR_NAME = 0x00000017 ; Enumerator Name REG_SZ property (R)
Const $CM_DRP_SECURITY = 0x00000018 ; Security - Device override (RW)
Const $CM_CRP_SECURITY = $CM_DRP_SECURITY ; Class default security (RW)
Const $CM_DRP_SECURITY_SDS = 0x00000019 ; Security - Device override (RW)
Const $CM_CRP_SECURITY_SDS = $CM_DRP_SECURITY_SDS ; Class default security (RW)
Const $CM_DRP_DEVTYPE = 0x0000001A ; Device Type - Device override (RW)
Const $CM_CRP_DEVTYPE = $CM_DRP_DEVTYPE ; Class default Device-type (RW)
Const $CM_DRP_EXCLUSIVE = 0x0000001B ; Exclusivity - Device override (RW)
Const $CM_CRP_EXCLUSIVE = $CM_DRP_EXCLUSIVE ; Class default (RW)
Const $CM_DRP_CHARACTERISTICS = 0x0000001C ; Characteristics - Device Override (RW)
Const $CM_CRP_CHARACTERISTICS = $CM_DRP_CHARACTERISTICS ; Class default (RW)
Const $CM_DRP_ADDRESS = 0x0000001D ; Device Address (R)
Const $CM_DRP_UI_NUMBER_DESC_FORMAT = 0x0000001E ; UINumberDescFormat REG_SZ property (RW)
Const $CM_DRP_DEVICE_POWER_DATA = 0x0000001F ; CM_POWER_DATA REG_BINARY property (R)
Const $CM_DRP_REMOVAL_POLICY = 0x00000020 ; CM_DEVICE_REMOVAL_POLICY REG_DWORD (R)
Const $CM_DRP_REMOVAL_POLICY_HW_DEFAULT = 0x00000021 ; CM_DRP_REMOVAL_POLICY_HW_DEFAULT REG_DWORD (R)
Const $CM_DRP_REMOVAL_POLICY_OVERRIDE = 0x00000022 ; CM_DRP_REMOVAL_POLICY_OVERRIDE REG_DWORD (RW)
Const $CM_DRP_INSTALL_STATE = 0x00000023 ; CM_DRP_INSTALL_STATE REG_DWORD (R)
Const $CM_DRP_MIN = 0x00000001 ; First device register
Const $CM_CRP_MIN = $CM_DRP_MIN ; First class register
Const $CM_DRP_MAX = 0x00000023 ; Last device register
Const $CM_CRP_MAX = $CM_DRP_MAX ; Last class register
; =====================================================================================

; #### Flags for MEM_DES.Flags ####
; =====================================================================================
Const $MMD_MemoryType = 0x1 ; Bitmask, whether memory is writable
Const $FMD_MemoryType = $MMD_MemoryType ; compatibility
Const $FMD_ROM = 0x0 ; Memory range is read-only
Const $FMD_RAM = 0x1 ; Memory range may be written to

Const $MMD_32_24 = 0x2 ; Bitmask, memory is 24 or 32-bit
Const $FMD_32_24 = $MMD_32_24 ; compatibility
Const $FMD_24 = 0x0 ; Memory range is 24-bit
Const $FMD_32 = 0x2 ; Memory range is 32-bit

Const $MMD_Prefetchable = 0x4 ; Bitmask,whether memory prefetchable
Const $FMD_Prefetchable = $MMD_Prefetchable ; compatibility
Const $FMD_Pref = $MMD_Prefetchable ; compatibility
Const $FMD_PrefetchDisallowed = 0x0 ; Memory range is not prefetchable
Const $FMD_PrefetchAllowed = 0x4 ; Memory range is prefetchable

Const $MMD_Readable = 0x8 ; Bitmask,whether memory is readable
Const $FMD_Readable = $MMD_Readable ; compatibility
Const $FMD_ReadAllowed = 0x0 ; Memory range is readable
Const $FMD_ReadDisallowed = 0x8 ; Memory range is write-only

Const $MMD_CombinedWrite = 0x10 ; Bitmask,supports write-behind
Const $FMD_CombinedWrite = $MMD_CombinedWrite ; compatibility
Const $FMD_CombinedWriteDisallowed = 0x0 ; no combined-write caching
Const $FMD_CombinedWriteAllowed = 0x10 ; supports combined-write caching

Const $MMD_Cacheable = 0x20 ; Bitmask,whether memory is cacheable
Const $FMD_NonCacheable = 0x0 ; Memory range is non-cacheable
Const $FMD_Cacheable = 0x20 ; Memory range is cacheable
; =====================================================================================

; #### Flags for CONFLICT_DETAILS.Flags ####
; ====================================================================================
Const $CM_CDFLAGS_DRIVER = 1 ; CONF_DETAILS.Descr reports back legacy driver name
Const $CM_CDFLAGS_ROOT_OWNED = 2 ; CONF_DETAILS.Flags, root owned device
Const $CM_CDFLAGS_RESERVED = 4 ; CONF_DETAILS.Flags specified range is not available for use
; ====================================================================================

; #### Flags for IO_DES.Flags (Port Type Flags) (DWORD) ####
; ====================================================================================
Const $FIOD_MEMORY = 0 ; The device is accessed in memory address space.
Const $FIOD_IO = 1 ; The device is accessed in I/O address space.
; ====================================================================================

; #### Flags for IO_DES.Flags (Decode Flags) (DWORD) ####
; ====================================================================================
Const $FIOD_10_BIT_DECODE = 0x04 ; The device decodes 10 bits of the port address.
Const $FIOD_12_BIT_DECODE = 0x08 ; The device decodes 12 bits of the port address.
Const $FIOD_16_BIT_DECODE = 0x10 ; The device decodes 16 bits of the port address.
Const $FIOD_POSITIVE_DECODE = 0x20 ; The device uses "positive decode" instead of "subtractive decode."
Const $FIOD_DECODE = 0xFC ; Bitmask for the bits within IO_DES.Flags that specify the decode value.
; ====================================================================================

; #### Flags for IRQ_DES.Flags (Sharing Flags) (DWORD) ####
; ====================================================================================
Const $FIRQD_EXCLUSIVE = 0 ; The IRQ line cannot be shared.
Const $FIRQD_SHARE = 1 ; The IRQ line can be shared.
; ====================================================================================

; #### Flags for IRQ_DES.Flags (Triggering Flags) (DWORD) ####
; ====================================================================================
Const $FIRQD_LEVEL = 0 ; The IRQ line is level-triggered.
Const $FIRQD_EDGE = 2 ; The IRQ line is edge-triggered.
; ====================================================================================

; #### Flags for DMA_DES.Flags (DMA Channel Width) ####
; ==============================================================================
Const $MDD_WIDTH = 0x3  ; Bitmask, width of the DMA channel:
Const $FDD_BYTE = 0x0  ; 8-bit DMA channel
Const $FDD_WORD = 0x1  ; 16-bit DMA channel
Const $FDD_DWORD = 0x2  ; 32-bit DMA channel
Const $FDD_BYTE_AND_WORD = 0x3  ; 8-bit and 16-bit DMA channel
; ==============================================================================

; #### Flags for DMA_DES.Flags (Bus Mastering Flags) ####
; ==============================================================================
Const $MDD_BUSMASTER = 0x4  ; Bitmask, whether bus mastering is supported
Const $FDD_NOBUSMASTER = 0x0  ; no bus mastering
Const $FDD_BUSMASTER = 0x4  ; bus mastering
; ==============================================================================

; #### Flags for DMA_DES.Flags (DMA Type Flags) ####
; ==============================================================================
Const $MDD_TYPE = 0x18  ; Bitmask, specifies type of DMA
Const $FDD_TYPESTANDARD = 0x00  ; standard DMA
Const $FDD_TYPEA = 0x08  ; Type-A DMA
Const $FDD_TYPEB = 0x10  ; Type-B DMA
Const $FDD_TYPEF = 0x18  ; Type-F DMA
; ==============================================================================


; #### Return code for _SetupDiVerifyDigitalSinger ####
; ================================================================================
; Indicates that the publisher is trusted because the publisher's certificate is installed in the Trusted Publishers certificate store.
Const $ERROR_AUTHENTICODE_TRUSTED_PUBLISHER = -536870335

; Indicates that trust cannot be automatically established because the publisher's signing certificate is not installed in the trusted publisher certificates store.
; However, this does not necessarily indicate an error. Instead it indicates that the caller must apply a caller-specific policy to establish trust in the publisher.
Const $ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED = -536870334
; ================================================================================

; #### Flags for SP_DRVINSTALL_PARAMS.Flags ####
; =================================================================================
; There are other providers supplying drivers that have the same description as this driver. This flag is READ-ONLY to installers.
Const $DNF_DUPDESC = 1

; This driver currently/previously controlled the associated device. This flag is READ-ONLY to installers.
Const $DNF_OLDDRIVER = 2

; Do not display this driver in any driver-select dialogs.
Const $DNF_EXCLUDEFROMLIST = 4

; Set if no physical driver is to be installed for this logical driver.
Const $DNF_NODRIVER = 8

; This driver comes from a legacy INF file. This flag is valid only for the NT-based operating system. This flag is READ-ONLY to installers.
Const $DNF_LEGACYINF = 0x10

; This driver is a class driver. This flag is read-only to installers.
Const $DNF_CLASS_DRIVER = 0x20

; This driver is a compatible driver. This flag is READ-ONLY to installers.
Const $DNF_COMPATIBLE_DRIVER = 0x40

; This driver came from the Internet or from Windows Update. This flag is READ-ONLY to installers.
Const $DNF_INET_DRIVER = 0x80

Const $DNF_UNUSED1 = 0x100

; This driver node is derived from an INF file that was included with this version of Windows.
Const $DNF_INDEXED_DRIVER = 0x200

; This driver came from the Internet, but Setup does not currently have access to its source files. This flag is READ-ONLY to installers.
; The system will not install a driver marked with this flag because Setup does not have the source files and would end up prompting the user with an invalid,
; path. The INF for such a driver can be used for everything except for installing devices.
Const $DNF_OLD_INET_DRIVER = 0x400

; Do not use this driver. Installers can read and write this flag.
; If this flag is set, SetupDiSelectBestCompatDrv and SetupDiSelectDevice ignore this driver.
; A class installer or co-installer can set this flag to prevent Setup from listing the driver in the Select Driver dialog box.
; An installer might set this flag when it handles a DIF_SELECTDEVICE or DIF_SELECTBESTCOMPATDRV request, for example.
Const $DNF_BAD_DRIVER = 0x800

; There are other providers supplying drivers that have the same description as this driver.
; The only difference between this driver and its match is the driver date. This flag is read-only to installers.
; If this flag is set, Setup displays the driver date and driver version next to the driver so that the user can distinguish it from its match.
Const $DNF_DUPPROVIDER = 0x1000

; (Windows XP and later versions of Windows)
Const $DNF_INF_IS_SIGNED = 0x2000

; Reserved. (Windows XP and later versions of Windows)
Const $DNF_OEM_F6_INF = 0x4000

; (Windows XP and later versions of Windows)
Const $DNF_DUPDRIVERVER = 0x8000

; (Windows XP and later versions of Windows)
Const $DNF_BASIC_DRIVER = 0x10000

; This driver¡¯s INF file is signed by an Authenticode signature. This flag is read-only to installers.
Const $DNF_AUTHENTICODE_SIGNED = 0x20000

; (Windows Vista and later versions of Windows)
; This driver node is currently installed for the device. This flag is read-only to installers.
Const $DNF_INSTALLEDDRIVER = 0x40000

; If set, this flag prevents the driver node from being enumerated, regardless of the client that is performing the enumeration.
Const $DNF_ALWAYSEXCLUEDFROMLIST = 0x80000
; =================================================================================

; #### Flags for SP_DRVINSTALL_PARAMS.Rank ####
; =================================================================================
Const $DRIVER_HARDWAREID_RANK = 0xFFF
Const $DRIVER_COMPATID_RANK = 0x3FFF
Const $DRIVER_UNTRUSTED_RANK = 0x8000
Const $DRIVER_UNTRUSTED_HARDWAREID_RANK = 0x8FFF
Const $DRIVER_UNTRUSTED_COMPATID_RANK = 0xBFFF
Const $DRIVER_W9X_SUSPECT_RANK = 0xC000
Const $DRIVER_W9X_SUSPECT_HARDWAREID_RANK = 0xCFFF
; =================================================================================

; #### Flags for HWPROFILE_INFO.Flags ####
; =================================================================================
Const $CM_HWPI_NOT_DOCKABLE = 0 ; Machine is not dockable
Const $CM_HWPI_UNDOCKED = 1 ; HW Profile for docked config
Const $CM_HWPI_DOCKED = 2 ; HW Profile for undocked config
; =================================================================================

; #### Flags for _CM_Bind_Device ####
; =================================================================================
Const $CM_BIND_UNUSED = 0
Const $CM_BIND_DEVINST_BIND_ID = 1
Const $CM_BIND_DEVINST_BIND_PHYSNAME = 2
Const $CM_BIND_PHYSNAME_BIND_DEVINST = 4
Const $CM_BIND_DEVINST_BIND_CLASS = 8
Const $CM_BIND_CLASS_BIND_DEVINST = 16
Const $CM_BIND_ENUMERATOR_BIND_DEVINST = 32
; =================================================================================

; ####### FUNCTIONS #######
; ==============================================================================
; _CM_Add_Empty_Log_Conf
; _CM_Add_Empty_Log_Conf_Ex
; _CM_Add_ID
; _CM_Add_ID_Ex
; _CM_Add_Res_Des
; _CM_Add_Res_Des_Ex
; _CM_Add_Res_Des_IO_Array_Ex
; _CM_Add_Res_Des_IRQ_Array_Ex
; _CM_Add_Res_Des_Mem_Array_Ex
; _CM_Assign_Var
; _CM_Cdrom_Known_Good_Digital_Playback
; _CM_Close_Handle
; _CM_Connect_Machine
; _CM_Create_Device_Devs
; _CM_Create_Device_Devs_Ex
; _CM_Create_File
; _CM_Device_IO_Control
; _CM_Disable_Cdrom_Digital_Playback
; _CM_Disable_DevNode
; _CM_Disable_DevNode_Ex
; _CM_Disconnect_Machine
; _CM_Duplicate_Buffer
; _CM_Enable_Cdrom_Digital_Playback
; _CM_Enable_DevNode
; _CM_Enable_DevNode_Ex
; _CM_Enable_Privileges
; _CM_Enum_Device_Info
; _CM_Enum_Device_Info_Ex
; _CM_Enum_Device_Info_Notify_Progress
; _CM_Enumerate_Children
; _CM_Enumerate_Children_Ex
; _CM_Enumerate_Classes
; _CM_Enumerate_Classes_Ex
; _CM_Enumerate_Enumerators
; _CM_Enumerate_Enumerators_Ex
; _CM_Enumerate_Physical_Disks
; _CM_Format_Error_Message
; _CM_Format_Problem_Message
; _CM_Free_Log_Conf
; _CM_Free_Log_Conf_Ex
; _CM_Free_Log_Conf_Handle
; _CM_Free_Res_Des
; _CM_Free_Res_Des_Ex
; _CM_Free_Res_Des_Handle
; _CM_Free_Resource_Conflict_Handle
; _CM_Free_Variable
; _CM_Get_Child
; _CM_Get_Child_By_Index
; _CM_Get_Child_By_Index_Ex
; _CM_Get_Child_Ex
; _CM_Get_Children_Count
; _CM_Get_Children_Count_Ex
; _CM_Get_Class_Registry_Property_Ex
; _CM_Get_Depth
; _CM_Get_Depth_Ex
; _CM_Get_Device_Driver_Files
; _CM_Get_Device_ID
; _CM_Get_Device_ID_By_Name_Ex
; _CM_Get_Device_ID_Ex
; _CM_Get_Device_ID_List
; _CM_Get_Device_ID_List_Ex
; _CM_Get_Device_ID_List_Size
; _CM_Get_Device_ID_List_Size_Ex
; _CM_Get_Device_ID_Size
; _CM_Get_Device_ID_Size_Ex
; _CM_Get_Device_Interface_List
; _CM_Get_Device_Interface_List_Ex
; _CM_Get_Device_Interface_List_Size
; _CM_Get_Device_Interface_List_Size_Ex
; _CM_Get_Device_Resources
; _CM_Get_Device_Resources_Ex
; _CM_Get_DevNode_Registry_Property
; _CM_Get_DevNode_Registry_Property_Ex
; _CM_Get_DevNode_Status
; _CM_Get_DevNode_Status_Ex
; _CM_Get_Drive_Disk_Number
; _CM_Get_Drive_Type
; _CM_Get_First_Log_Conf
; _CM_Get_First_Log_Conf_Ex
; _CM_Get_HW_Prof_Flags
; _CM_Get_HW_Prof_Flags_Ex
; _CM_Get_Last_Error
; _CM_Get_Log_Conf_Priority_Ex
; _CM_Get_Next_Log_Conf
; _CM_Get_Next_Log_Conf_Ex
; _CM_Get_Next_Res_Des
; _CM_Get_Next_Res_Des_Ex
; _CM_Get_Parent
; _CM_Get_Parent_Ex
; _CM_Get_Process_Heap
; _CM_Get_Res_Des_By_Index
; _CM_Get_Res_Des_By_Index_Ex
; _CM_Get_Res_Des_Data
; _CM_Get_Res_Des_Data_Ex
; _CM_Get_Res_Des_Data_Size
; _CM_Get_Res_Des_Data_Size_Ex
; _CM_Get_Resource_Conflict_Count
; _CM_Get_Resource_Conflict_Details
; _CM_Get_Sibling
; _CM_Get_Sibling_Ex
; _CM_Get_Version
; _CM_Get_Version_Ex
; _CM_Get_Volume_Name
; _CM_Get_Volume_Path
; _CM_GUID_From_String
; _CM_Heap_Alloc
; _CM_Heap_Free
; _CM_Heap_Size
; _CM_Install_Device_Driver
; _CM_Install_DevInst_Ex
; _CM_Install_New_Device
; _CM_Install_Selected_Device
; _CM_Install_Selected_Driver
; _CM_Is_Cdrom_Digital_Playback_Enabled
; _CM_Is_Device_Disabled
; _CM_Is_Dock_Station_Present
; _CM_Is_Dock_Station_Present_Ex
; _CM_Is_Version_Available
; _CM_Is_Version_Available_Ex
; _CM_Locate_DevNode
; _CM_Locate_DevNode_Ex
; _CM_Lock_Cdrom
; _CM_Modify_Res_Des
; _CM_Modify_Res_Des_Ex
; _CM_Query_And_Remove_SubTree
; _CM_Query_And_Remove_SubTree_Ex
; _CM_Query_Device_Problem
; _CM_Query_Device_Problem_Ex
; _CM_Query_DevNode_Resource_Conflicts_Ex
; _CM_Query_Resource_Conflict_List
; _CM_Reenumerate_DevNode
; _CM_Reenumerate_DevNode_Ex
; _CM_Request_Device_Eject
; _CM_Request_Device_Eject_Ex
; _CM_Request_Eject_PC
; _CM_Request_Eject_PC_Ex
; _CM_Safely_Eject_Disk
; _CM_Scan_Device_Changes
; _CM_Scan_Device_Changes_Ex
; _CM_Set_DevNode_CSConfigFlags
; _CM_Set_DevNode_Problem
; _CM_Set_DevNode_Problem_Ex
; _CM_Setup_DevNode
; _CM_String_From_GUID
; _CM_Uninstall_DevNode
; _CM_Update_PnP_Device
; _CM_Validate_Res_Des_Conflict_Ex
; _InstallNewDevice
; _SetupCloseInfFile
; _SetupDiApiBufferFree
; _SetupDiAskForOEMDisk
; _SetupDiBuildClassInfoList
; _SetupDiBuildDriverInfoList
; _SetupDiCallClassInstaller
; _SetupDiChangeState
; _SetupDiClassGuidsFromName
; _SetupDiClassNameFromGuid
; _SetupDiCreateDeviceDevs
; _SetupDiCreateDeviceDevsEx
; _SetupDiCreateDeviceInfo
; _SetupDiCreateDeviceInfoList
; _SetupDiCreateDeviceInfoListEx
; _SetupDiCreateDeviceInterface
; _SetupDiCreateDeviceInterfaceRegKey
; _SetupDiDeleteDeviceInfo
; _SetupDiDeleteDeviceInterfaceData
; _SetupDiDestroyClassImageList
; _SetupDiDestroyDeviceInfoList
; _SetupDiDestroyDriverInfoList
; _SetupDiDisableDevice
; _SetupDiEnumDeviceInfo
; _SetupDiEnumDeviceInterfaces
; _SetupDiEnumDriverInfo
; _SetupDiGetClassBitmapIndex
; _SetupDiGetClassDescription
; _SetupDiGetClassDescriptionEx
; _SetupDiGetClassDevs
; _SetupDiGetClassDevsEx
; _SetupDiGetClassImageIndex
; _SetupDiGetClassImageList
; _SetupDiGetDeviceInstallParams
; _SetupDiGetDeviceInstanceId
; _SetupDiGetDeviceInterfaceDetail
; _SetupDiGetDeviceInterfaceDevInst
; _SetupDiGetDeviceInterfaceDevs
; _SetupDiGetDeviceInterfaceDevsEx
; _SetupDiGetDevicePowerCapabilities
; _SetupDiGetDevicePowerMapping
; _SetupDiGetDevicePowerState
; _SetupDiGetDeviceRegistryProperty
; _SetupDiGetDevNodeStatus
; _SetupDiGetDriverInfoDetail
; _SetupDiGetDriverInstallParams
; _SetupDiGetDriverSigner
; _SetupDiGetHwProfileFriendlyName
; _SetupDiGetHwProfileList
; _SetupDiGetINFClass
; _SetupDiGetSelectedDriver
; _SetupDiInstallClass
; _SetupDiInstallClassEx
; _SetupDiInstallDevice
; _SetupDiInstallDeviceInterfaces
; _SetupDiLoadClassIcon
; _SetupDiLoadDeviceIcon
; _SetupDiOpenDeviceInfo
; _SetupDiOpenDeviceInterface
; _SetupDiOpenDeviceInterfaceRegKey
; _SetupDiOpenDevRegKey
; _SetupDiRegisterDeviceInfo
; _SetupDiRemoveDevice
; _SetupDiRemoveDeviceInterface
; _SetupDiSelectBestCompatDrv
; _SetupDiSelectDevice
; _SetupDiSelectOEMDrv
; _SetupDiSetClassInstallParams
; _SetupDiSetDeviceInstallParams
; _SetupDiSetDeviceInterfaceDefault
; _SetupDiSetDeviceRegistryProperty
; _SetupDiSetDriverInstallParams
; _SetupDiSetSelectedDevice
; _SetupDiSetSelectedDriver
; _SetupDiUnremoveDevice
; _SetupDiVerifyDigitalSigner
; _SetupGetLineByIndex
; _SetupGetLineCount
; _SetupGetLineText
; _SetupGetTargetPath
; _SetupOpenInfFile
; ==============================================================================

; #### Handle to the modules ####
; ==============================================================================================
Const $SETUPAPI_DllHandle = DllOpen("SetupApi.dll")
Const $KERNEL32_DllHandleA = DllOpen("Kernel32.dll")
Const $NEWDEV_DllHandle = DllOpen("Newdev.dll")
Const $DEVMGR_DllHandle = DllOpen("DevMgr.dll")
Const $STORP_DllHandle = DllOpen("StorProp.dll")
; ==============================================================================================

; #### FUNCTION ####
; ==============================================================================
; Name  : _SetupDiGetClassDevs
; Description   : Retrieves A handle to device information set.
; Parameter(s)  : $iFlags   - A value of type DWORD that specifies control options that filter the device information elements that are added to the device information set. This parameter can be a bitwise OR of zero or more of the following flags, can be a combination of the following values:
;       :   - $DIGCF_ALLCLASSES
;       :   - Return a list of installed devices for all device setup classes or all device interface classes.
;       :   - $DIGCF_DEVICEINTERFACE
;       :   - Return devices that support device interfaces for the specified device interface classes. This flag must be set in the Flags parameter if the Enumerator parameter specifies a device instance ID.
;       :   - $DIGCF_DEFAULT
;       :   - Return only the device that is associated with the system default device interface, if one is set, for the specified device interface classes.
;       :   - $DIGCF_PRESENT
;       :   - Return only devices that are currently present in a system.
;       :   - $DIGCF_PROFILE
;       :   - Return only devices that are a part of the current hardware profile
;       : $vGuid    - An optional variable-type GUID value for a device setup class or a device interface class.
;       : $sEnumerator  - Enumerator for this device information set (PCI/SCSI/PCMCIA...).
; Return values : If succeeds, returns a handle to device information set contains the control options. Otherwise, returns ERROR_INVALID_HANDLE (-1) and sets @error to a system error code.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiGetClassDevs($iFlags, $vGuid = 0, $sEnumerator = "")
    Local $iResult, $tGuid, $pGuid = 0, $sType = "ptr"

    If $sEnumerator <> "" Then $sType = "str"
    If IsString($vGuid) Then
        If StringLeft($vGuid, 1) & StringRight($vGuid, 1) = "{}" Then
            $tGUID = _CM_GUID_From_String($vGuid)
            $pGuid = DllStructGetPtr($tGUID)
        ElseIf $vGuid <> "" Then
            $tGuid = _SetupDiClassGuidsFromName($vGuid)
            $pGuid = DllStructGetPtr($tGuid)
        EndIf
    ElseIf IsDllStruct($vGuid) Then
        $pGuid = DllStructGetPtr($vGuid)
    ElseIf IsPtr($vGuid) Then
        $pGuid = $vGuid
    Else
        $pGuid = 0
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "ptr", "SetupDiGetClassDevs", "ptr", $pGuid, _
            $sType, $sEnumerator, "hWnd", 0, "dword", $iFlags)
    Return SetError(_CM_Get_Last_Error(), _SetupDiApiBufferFree($tGuid), $iResult[0])
EndFunc ;==>_SetupDiGetClassDevs

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiClassGuidsFromName
; Description   : Return the GUID of the specified device class.
; Parameter(s)  : $sClassName   - The name of the device class, (NET/Keyboard...).
; Return values : If succeeds, returns a GUID structure (contains one member - Guid), and @extended is set to non-zero. If fails, returns a GUID structure either, but @extended is set to zero.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiClassGuidsFromName($sClassName)
    Local $tGuid, $pGuid, $iResult

    $tGuid = DllStructCreate("byte Guid[16]")
    $pGuid = DllStructGetPtr($tGuid)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiClassGuidsFromName", _
            "str", $sClassName, "ptr", $pGuid, "dword", 1, "int*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[4], $tGuid)
EndFunc ;==>_SetupDiClassGuidsFromName

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiApiBufferFree
; Description   : Frees a memory buffer, internal used only.
; Parameter(s)  : $vBuffer - Buffer to be freed.
; Return values : None.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiApiBufferFree(ByRef $vBuffer, $vValue = 0, $vReturn = "", _
            $iError = @error, $iExtended = @Extended)
    $vBuffer = $vValue
    Return SetError($iError, $iExtended, $vReturn)
EndFunc ;==>_SetupDiApiBufferFree

Func _SetupDiBuildClassInfoList($iFlags = 0)
    Local $iResult, $tBuffer, $pBuffer, $tagBuffer, $aResult[1][2] = [[0]]

    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiBuildClassInfoList", "dword", $iFlags, _
            "ptr", 0, "dword", 0, "dword*", 0)
    If $iResult[4] = 0 Then Return SetError(_CM_Get_Last_Error(), 0, $aResult)
    $aResult[0][0] = $iResult[4]
    Redim $aResult[$iResult[4] + 1][2]
    For $i = 1 to $iResult[4]
        $tagBuffer &= "ubyte[16];"
    Next
    $tBuffer = DllStructCreate($tagBuffer)
    $pBuffer = DllStructGetPtr($tBuffer)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiBuildClassInfoList", "dword", $iFlags, _
            "ptr", $pBuffer, "dword", $iResult[4], "dword*", 0)
    For $i = 1 to $iResult[4]
        $pBuffer = DllStructGetPtr($tBuffer, $i)
        $aResult[$i][0] = _CM_String_From_GUID($pBuffer)
        $aResult[$i][1] = _SetupDiGetClassDescription($pBuffer)
    Next
    _SetupDiApiBufferFree($tBuffer)
    Return SetExtended($iResult[4], $aResult)
EndFunc ;==>_SetupDiBuildClassInfoList

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiGetClassDescription
; Description   : Retrieves the class description associated with the specified setup class GUID.
; Parameter(s)  : $vGuid    - A variable-type Class-GUID value, can be in one of the following formats:
;       :   - Class name: NET/Display/Mouse...
;       :   - Class GUID string: {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}.
;       :   - Class GUID structure.
;       :   - Class GUID pointer.
; Return values : If succeeds, returns the class description, on failure, @extended is set to zero.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiGetClassDescription($vGuid)
    Local $iResult, $pGuid

    If IsDllStruct($vGuid) Then
        $pGuid = DllStructGetPtr($vGuid)
    ElseIf IsString($vGuid) Then
        Local $tGUID = _CM_GUID_From_String($vGuid)
        $pGuid = DllStructGetPtr($tGUID)
    ElseIf IsPtr($vGuid) Then
        $pGuid = $vGuid
    EndIf

    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetClassDescription", _
            "ptr", $pGuid, "ptr", 0, "dword", 0, "dword*", 0)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetClassDescription", _
            "ptr", $pGuid, "str", "", "dword", $iResult[4], "dword*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[2])
EndFunc ;==>_SetupDiGetClassDescription

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiClassNameFromGuid
; Description   : Retrieves the class name associated with a class GUID.
; Parameter(s)  : $vGuid    - A variable-type Class-GUID value, can be in one of the following formats:
;       :   - Class GUID string: {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}.
;       :   - Class GUID structure.
;       :   - Class GUID pointer.
; Return values : If succeeds, return the class name and sets @extended to non-zero, else return NULL and sets @error.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiClassNameFromGuid($vGuid)
    Local $pGuid, $iResult

    If IsDllStruct($vGuid) Then
        $pGuid = DllStructGetPtr($vGuid)
    ElseIf IsString($vGuid) Then
        Local $tGUID = _CM_GUID_From_String($vGuid)
        $pGuid = DllStructGetPtr($tGUID)
    ElseIf IsPtr($vGuid) Then
        $pGuid = $vGuid
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiClassNameFromGuid", _
            "ptr", $pGuid, "str", "", "dword", 64, "dword*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[2])
EndFunc ;==>_SetupDiClassNameFromGuid

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiEnumDeviceInfo
; Description   : Enumerates a device information element in the device information set.
; Parameter(s)  : $hDevs    - A handle to the device information set.
;       : $iIndex   - The zero-based index of the device information element to retrieve.
;       : $tSP_DEVINFO_DATA - A variable receives a SP_DEVINFO_DATA structure contains the device information element.
; Return values : True indicates success, false to failure, in which case, @error is set to a system error code.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiEnumDeviceInfo($hDevs, $iIndex, ByRef $tSP_DEVINFO_DATA)
    Local $iResult, $pSP_DEVINFO_DATA

    If Not IsDllStruct($tSP_DEVINFO_DATA) Then
        $tSP_DEVINFO_DATA = DllStructCreate($tagSP_DEVICEINFO_DATA)
        DllStructSetData($tSP_DEVINFO_DATA, "Size", 28)
    EndIf
    $pSP_DEVINFO_DATA = DllStructGetPtr($tSP_DEVINFO_DATA)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiEnumDeviceInfo", _
            "ptr", $hDevs, "int", $iIndex, "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiEnumDeviceInfo

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiGetDeviceRegistryProperty
; Description   : Retrieve the device registry property.
; Parameter(s)  : $hDevs    - A handle to a device information set, obtain the handle by using _SetupDiGetClassDevs function.
;       : $pSP_DEVINFO_DATA - A pointer to/or a SP_DEVICEINFO_DATA structure contains the device information from which the property information is retrieved.
;       : $iProperty    - A value indicates the property required, see SPDRP_* constants for a value.
; Return values : If success, returns the requested property. If failure, returns NULL and sets @error to a system error code, pass @error to FormatMessage for an explicit error message.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiGetDeviceRegistryProperty($hDevs, $pSP_DEVINFO_DATA, $iProperty)
    Local $iResult, $pBuffer, $tBuffer, $sVal, $vVar, $iSysError

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetDeviceRegistryProperty", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, "dword", $iProperty, _
            "dword*", 0, "ptr", 0, "dword", 0, "dword*", 0)
    $pBuffer = _CM_Heap_Alloc($iResult[7])
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetDeviceRegistryProperty", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, "dword", $iProperty, _
            "dword*", 0, "ptr", $pBuffer, "dword", $iResult[7], "dword*", 0)
    $iSysError = _CM_Get_Last_Error()
    Switch $iResult[4]
    Case 1, 2, 7
        $tBuffer = DllStructCreate("char Data[" & $iResult[7] & "]", $pBuffer)
    Case 4, 5
        $tBuffer = DllStructCreate("dword Data", $pBuffer)
    Case 6
        $tBuffer = DllStructCreate("wchar Data[" & $iResult[7] / 2 & "]", $pBuffer)
    Case Else
        $tBuffer = DllStructCreate("byte Data[" & $iResult[7] & "]", $pBuffer)
    EndSwitch
    If $iResult[4] <> 7 Then
        $vVar = DllStructGetData($tBuffer, "Data")
        _CM_Heap_Free($pBuffer)
        Return SetError($iSysError, _CM_Free_Variable($tBuffer) + $iResult[4], $vVar)
    EndIf
    For $i = 1 To $iResult[7]
        $sVal = DllStructGetData($tBuffer, "Data", $i)
        If $sVal = Chr(0) Then
            $vVar &= @LF
            ContinueLoop
        EndIf
        $vVar &= $sVal
    Next
    _CM_Heap_Free($pBuffer)
    Return SetError($iSysError, 7 + _CM_Free_Variable($tBuffer), $vVar)
EndFunc ;==>_SetupDiGetDeviceRegistryProperty

; #### FUNCTION #####
; =========================================================================
; Name  : _SetupDiSetDeviceRegistryProperty
; Description   : This function sets property for a device instance.
; Parameter(s)  : $hDevs    - Handle to the device information set.
;       : $pSP_DEVINFO_DATA - A pointer to an SP_DEVINFO_DATA structure contains the device information element.
;       : $iProperty - Property to be set.
;       : $pBuffer - Property value.
;       : $iBufferSize  - Size of $pBuffer.
;       : $sBufferType  - Variable type of $pBuffer parameter, default to recognize as a pointer (PTR).
; Return values : True is returned if succeeds, otherwise returns false and sets @error to a system error code.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiSetDeviceRegistryProperty($hDevs, $pSP_DEVINFO_DATA, $iProperty, $pBuffer, $iBufferSize, $sBufferType = "ptr")
    Local $iResult

    If IsDllStruct($pBuffer) Then $pBuffer = DllStructGetPtr($pBuffer)
    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiSetDeviceRegistryProperty", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, "dword", $iProperty, _
            $sBufferType, $pBuffer, "dword", $iBufferSize)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiSetDeviceRegistryProperty

Func _SetupDiGetDevicePowerCapabilities($hDevs, $tDevInfo)
    Local $bData, $tData, $pData, $tPower, $iCapValue

    $bData = _SetupDiGetDeviceRegistryProperty($hDevs, $tDevInfo, 0x1E);, 0)
    If @error Then Return SetError(@error, 0, 0)

    $tData = DllStructCreate("byte Data[" & BinaryLen($bData) & "]")
    $pData = DllStructGetPtr($tData)
    $tPower = DllStructCreate($tagCM_POWER_DATA, $pData)
    DllStructSetData($tData, "Data", $bData)

    $iCapValue = "0x" & Hex(DllStructGetData($tPower, "Capabilities"))
    _CM_Free_Variable($tPower)
    Return SetExtended(_CM_Free_Variable($tData), $iCapValue)
EndFunc ;==>_SetupDiGetDevicePowerCapabilities

Func _SetupDiGetDevicePowerMapping($hDevs, $tDevInfo)
    Local $aResult[6], $bData, $tData, $pData, $tPower, $iValue

    $bData = _SetupDiGetDeviceRegistryProperty($hDevs, $tDevInfo, 0x1E);, 0)
    If @error Then Return SetError(@error, 0, $aResult)
    $tData = DllStructCreate("byte Data[" & BinaryLen($bData) & "]")
    $pData = DllStructGetPtr($tData)
    $tPower = DllStructCreate($tagCM_POWER_DATA, $pData)
    DllStructSetData($tData, "Data", $bData)

    For $i = 2 to 7
        $iValue = DllStructGetData($tPower, "PowerStateMapping", $i)
        Switch $iValue
        Case 0
            $iValue = "Unspecified"
        Case 1
            $iValue = "D0"
        Case 2
            $iValue = "D1"
        Case 3
            $iValue = "D2"
        Case 4
            $iValue = "D3"
        Case 5
            $iValue = "Maximum"
        Case Else
            $iValue = "Unknown"
        EndSwitch
        $aResult[$i - 2] = "S" & ($i - 2) & " -> " & $iValue
    Next
    _CM_Free_Variable($tPower)
    Return SetExtended(_CM_Free_Variable($tData), $aResult)
EndFunc ;==>_SetupDiGetDevicePowerMapping

Func _SetupDiGetDevicePowerState($hDevs, $tDevInfo)
    Local $bData, $tData, $pData, $tPower, $sValue

    $bData = _SetupDiGetDeviceRegistryProperty($hDevs, $tDevInfo, 0x1E);, 0)
    If @error Then Return SetError(@error, 0, "")

    $tData = DllStructCreate("byte Data[" & BinaryLen($bData) & "]")
    $pData = DllStructGetPtr($tData)
    $tPower = DllStructCreate($tagCM_POWER_DATA, $pData)
    DllStructSetData($tData, "Data", $bData)
    Switch DllStructGetData($tPower, "MostRecentPowerState")
    Case 0
        $sValue = "Unspecified"
    Case 1
        $sValue = "D0"
    Case 2
        $sValue = "D1"
    Case 3
        $sValue = "D2"
    Case 4
        $sValue = "D3"
    Case 5
        $sValue = "Maximum"
    Case Else
        $sValue = "Unknown"
    EndSwitch
    _CM_Free_Variable($tPower)
    Return SetExtended(_CM_Free_Variable($tData), $sValue)
EndFunc ;==>_SetupDiGetDevicePowerState

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiGetDeviceInstanceId
; Description   : Retrieve the instance identifier of the specified device.
; Parameter(s)  : $hDevs    - A handle to a device information set.
;       : $pSP_DEVINFO_DATA - A pointer to/or a SP_DEVICEINFO_DATA structure contains the device information.
; Return values : If succeeds, returns an instance identifier string, else returns NULL and sets @error.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiGetDeviceInstanceId($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult
    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetDeviceInstanceId", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, _
            "ptr", 0, "dword", 0, "dword*", 0)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetDeviceInstanceId", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, _
            "str", "", "dword", $iResult[5], "dword*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[3])
EndFunc ;==>_SetupDiGetDeviceInstanceId

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiCreateDeviceInfoList
; Description   : This function creates an empty device information set.
; Parameter(s)  : $vGuid - GUID for device setup class, can be NULL.
;       : $hWnd - Parent window handle, can be NULL.
; Return values : A valid handle is returned if succeeds, otherwise returns -1 and sets @error.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiCreateDeviceInfoList($vGuid = "", $hWnd = 0)
    Local $iResult, $pGuid
    If IsString($vGuid) Then
        If (StringLeft($vGuid, 1) & StringRight($vGuid, 1) = "{}") Then
            $pGuid = DllStructGetPtr(_CM_GUID_From_String($vGuid))
        ElseIf $vGuid <> "" Then
            $tGuid = _SetupDiClassGuidsFromName($vGuid)
            $pGuid = DllStructGetPtr($tGuid)
        EndIf
    ElseIf IsDllStruct($vGuid) Then
        $pGuid = DllStructGetPtr($vGuid)
    ElseIf IsPtr($vGuid) Then
        $pGuid = $vGuid
    Else
        $pGuid = 0
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "ptr", "SetupDiCreateDeviceInfoList", _
            "ptr", $pGuid, "hWnd", $hWnd)
    Return SetError(_CM_Get_Last_Error(), $iResult[0] <> -1, $iResult[0])
EndFunc ;==>_SetupDiCreateDeviceInfoList

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiSetSelectedDevice
; Description   : This function selectes a device element for a device information set.
; Parameter(s)  : $hDevs    - Handle to the device information set.
;       : $pSP_DEVINFO_DATA - A pointer to SP_DEVINFO_DATA contains the device information element.
; Return values : True indicates success, False indicates failure, sets @error to a system error code.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiSetSelectedDevice($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiSetSelectedDevice", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiSetSelectedDevice

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiSetSelectedDriver
; Description   : The _SetupDiSetSelectedDriver function sets, or resets, the selected driver for a device information element or the selected class driver for a device information set.
; Parameter(s)  : $hDevs - Handle to the device information set.
;       : $pSP_DEVINFO_DATA - A pointer to an SP_DEVINFO_DATA structure contains the device information element.
;       : $tSP_DRVINFO_DATA - An SP_DRVINFO_DATA structure contains the driver to be selected.
; Return values : True is returned if succeeds, otherwise returns False.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiSetSelectedDriver($hDevs, $pSP_DEVINFO_DATA, ByRef $tSP_DRVINFO_DATA)
    Local $iResult, $pSP_DRVINFO_DATA

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $pSP_DRVINFO_DATA = DllStructGetPtr($tSP_DRVINFO_DATA)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiSetSelectedDriver", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, _
            "ptr", $pSP_DRVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiSetSelectedDriver

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiOpenDeviceInfo
; Description   : This function adds a device information element for a device instance to a device information set, if one does not already exist in the device information set, and retrieves information that identifies the device information element for the device instance in the device information set.
; Parameter(s)  : $hDevs    - Supplies a handle to the device information set.
;       : $sInstanceID  - Supplies a device instance identifier, in string format.
;       : $tSP_DEVINFO_DATA - A variable receives a SP_DEVINFO_DATA that contains the specified device information element.
;       : $iFlags - A variable of DWORD type that controls how the device information element is opened, a value of zero is default.
; Return values : If succeeds, returns True. Else returns False and sets @error to a system error code.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiOpenDeviceInfo($hDevs, $sInstanceId, ByRef $tSP_DEVINFO_DATA, $iFlag = 0)
    Local $iResult, $pSP_DEVINFO_DATA

    If Not IsDllStruct($tSP_DEVINFO_DATA) Then
        $tSP_DEVINFO_DATA = DllStructCreate($tagSP_DEVICEINFO_DATA)
        DllStructSetData($tSP_DEVINFO_DATA, "Size", 28)
    EndIf
    $pSP_DEVINFO_DATA = DllStructGetPtr($tSP_DEVINFO_DATA)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiOpenDeviceInfo", _
            "ptr", $hDevs, "str", $sInstanceId, "hWnd", 0, _
            "dword", $iFlag, "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiOpenDeviceInfo

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiGetDeviceInstallParams
; Description   : This function is used to retrieve the device installation parameters for a selected device information element.
; Parameter(s)  : $hDevs    - Supplies a handle to the device information set.
;       : $pSP_DEVINFO_DATA - A pointer to an SP_DEVINFO_DATA structure contains the device information element from which the installation is retrieved.
;       : ByRef $tSP_DEVINST_PARAMS - Supplies a variable that receives an SP_DEVINSTALL_PARAMS structure fills with the requested data.
; Return values : Returns TRUE if succeeds, FALSE otherwise, the logged error is set in @error.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiGetDeviceInstallParams($hDevs, $pSP_DEVINFO_DATA, ByRef $tSP_DEVINST_PARAMS)
    Local $iResult, $pSP_DEVINST_PARAMS

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    If Not IsDllStruct($tSP_DEVINST_PARAMS) Then
        $tSP_DEVINST_PARAMS = DllStructCreate($tagSP_DEVINSTALL_PARAMS)
        DllStructSetData($tSP_DEVINST_PARAMS, 1, DllStructGetSize($tSP_DEVINST_PARAMS))
    EndIf
    $pSP_DEVINST_PARAMS = DllStructGetPtr($tSP_DEVINST_PARAMS)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetDeviceInstallParams", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, _
            "ptr", $pSP_DEVINST_PARAMS)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiGetDeviceInstallParams

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiBuildDriverInfoList
; Description   : This function is used to create the driver information set for a selected device information element.
; Parameter(s)  : $hDevs - Supplies a device information set handle.
;       : $pSP_DEVINFO_DATA - Supplies a pointer to an SP_DEVINFO_DATA contains the device information element.
; Return values : Returns TRUE if succeeds, FALSE otherwise, the logged error is set in @error.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiBuildDriverInfoList($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult, $iType = $SPDIT_CLASSDRIVER

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    If IsPtr($pSP_DEVINFO_DATA) Then $iType = $SPDIT_COMPATDRIVER
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiBuildDriverInfoList", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, "int", $iType)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiBuildDriverInfoList

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiCallClassInstaller
; Description   : The function calls the appropriate class installer, and any registered co-installers, with the specified installation request (DIF code).
; Parameter(s)  : $hDevs    - A handle to the device information set.
;       : $pSP_DEVINFO_DATA - A pointer (or a structure) to an SP_DEVINFO_DATA contains the device information element.
;       : $iDIFCode - The device installation request (DIF request) to pass to the co-installers and class installer.
; Return values : Returns TRUE if succeeds, FALSE otherwise, the error code is set to in @error.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiCallClassInstaller($hDevs, $pSP_DEVINFO_DATA, $iDIFCode)
    Local $iResult

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiCallClassInstaller", _
            "dword", $iDifCode, "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiCallClassInstaller

Func _SetupDiGetSelectedDriver($hDevs, $pSP_DEVINFO_DATA, ByRef $tSP_DRVINFO_DATA)
    Local $iResult, $pSP_DRVINFO_DATA

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    If Not IsDllStruct($tSP_DRVINFO_DATA) Then
        $tSP_DRVINFO_DATA = DllStructCreate($tagSP_DRVINFO_DATA)
        DllStructSetData($tSP_DRVINFO_DATA,1,DllStructGetSize($tSP_DRVINFO_DATA)-12)
    EndIf
    $pSP_DRVINFO_DATA = DllStructGetPtr($tSP_DRVINFO_DATA)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetSelectedDriver", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, _
            "ptr", $pSP_DRVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiGetSelectedDriver

Func _SetupDiGetDriverInfoDetail($hDevs, $pSP_DEVINFO_DATA, $pSP_DRVINFO_DATA, ByRef $tSP_DRVINFO_DETAIL_DATA)
    Local $iResult, $pSP_DRVINFO_DETAIL_DATA, $iLength, $tagBuffer

    Select
    Case IsDllStruct($pSP_DEVINFO_DATA)
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
        ContinueCase
    Case IsDllStruct($pSP_DRVINFO_DATA)
        $pSP_DRVINFO_DATA = DllStructGetPtr($pSP_DRVINFO_DATA)
        ContinueCase
    Case IsDllStruct($tSP_DRVINFO_DETAIL_DATA) = 0
        $tSP_DRVINFO_DETAIL_DATA = DllStructCreate($tagSP_DRVINFO_DETAIL_DATA)
    EndSelect
    $iLength = DllStructGetSize($tSP_DRVINFO_DETAIL_DATA)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetDriverInfoDetail", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, _
            "ptr", $pSP_DRVINFO_DATA, _
            "ptr", 0, "dword", 0, "dword*", 0)
    $iLength = $iResult[6] - $iLength
    If $iLength > 0 Then
        _SetupDiApiBufferFree($tSP_DRVINFO_DETAIL_DATA)
        $tagBuffer = $tagSP_DRVINFO_DETAIL_DATA & ";char HardwareId[" & $iLength & "]"
        $tSP_DRVINFO_DETAIL_DATA = DllStructCreate($tagBuffer)
        DllStructSetData($tSP_DRVINFO_DETAIL_DATA, 1, $iResult[6] - $iLength + 1)
    EndIf
    $pSP_DRVINFO_DETAIL_DATA = DllStructGetPtr($tSP_DRVINFO_DETAIL_DATA)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetDriverInfoDetail", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, _
            "ptr", $pSP_DRVINFO_DATA, _
            "ptr", $pSP_DRVINFO_DETAIL_DATA, _
            "dword", $iResult[6], "dword*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiGetDriverInfoDetail

; #### FUNCTION ####
; =================================================================================
; Name  : _SetupDiSetDriverInstallParams
; Description   : The function sets driver installation parameters for a driver information element.
; Parameter(s)  : $hDevs    - A handle to a device information set that contains a driver information element that represents the driver for which to set installation parameters.
;       : $pSP_DEVINFO_DATA - A pointer to an SP_DEVINFO_DATA structure that specifies a device information element in DeviceInfoSet. This parameter is optional and can be set to NULL. If this parameter is specified, _SetupDiSetDriverInstallParams sets the driver installation parameters for the specified device. If this parameter is NULL, SetupDiSetDriverInstallParams sets driver installation parameters for DeviceInfoSet ($hDevs).
;       : $pSP_DRVINFO_DATA - A pointer to an SP_DRVINFO_DATA structure that specifies the driver for which installation parameters are set. If DeviceInfoData is specified, this driver must be a member of a driver list that is associated with DeviceInfoData. If DeviceInfoData is NULL, this driver must be a member of the global class driver list for DeviceInfoSet ($hDevs).
;       : $pSP_DRVINSTALL_PARAMS - A pointer to an SP_DRVINSTALL_PARAMS structure that specifies the new driver install parameters.
; Return values : The function returns TRUE if it is successful. Otherwise, it returns FALSE and the logged error can be retrieved in @error.
; Author    : Pusofalse
; =================================================================================
Func _SetupDiSetDriverInstallParams($hDevs, $pSP_DEVINFO_DATA, $pSP_DRVINFO_DATA, $pSP_DRVINSTALL_PARAMS)
    Local $iResult

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    If IsDllStruct($pSP_DRVINFO_DATA) Then
        $pSP_DRVINFO_DATA = DllStructGetPtr($pSP_DRVINFO_DATA)
    EndIf
    If IsDllStruct($pSP_DRVINSTALL_PARAMS) Then
        $pSP_DRVINSTALL_PARAMS = DllStructGetPtr($pSP_DRVINSTALL_PARAMS)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiSetDriverInstallParams", "ptr", $hDevs, _
            "ptr", $pSP_DEVINFO_DATA, "ptr", $pSP_DRVINFO_DATA, _
            "ptr", $pSP_DRVINSTALL_PARAMS)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiSetDriverInstallParams

; #### FUNCTION ####
; =================================================================================
; Name  : _SetupDiGetDriverInstallParams
; Description   : The _SetupDiGetDriverInstallParams function retrieves driver installation parameters for a device information set or a particular device information element.
; Parameter(s)  : $hDevs    - A handle to a device information set that contains a driver information element that represents the driver for which to retrieve installation parameters.
;       : $pSP_DEVINFO_DATA - A pointer to an SP_DEVINFO_DATA structure that contains a device information element that represents the device for which to retrieve installation parameters. This parameter is optional and can be NULL. If this parameter is specified, SetupDiGetDriverInstallParams retrieves information about a driver that is a member of a driver list for the specified device. If this parameter is NULL, SetupDiGetDriverInstallParams retrieves information about a driver that is a member of the global class driver list for DeviceInfoSet ($hDevs).
;       : $pSP_DRVINFO_DATA - A pointer to an SP_DRVINFO_DATA structure that specifies the driver information element that represents the driver for which to retrieve installation parameters. If DeviceInfoData is supplied, the driver must be a member of the driver list for the device that is specified by DeviceInfoData. Otherwise, the driver must be a member of the global class driver list for DeviceInfoSet ($hDevs).
;       : $tSP_DRVINSTALL_PARAMS - A variable that receives an SP_DRVINSTALL_PARAMS structure contains the driver installation parameters.
; Return values : TRUE is returned if the function completed without an error. Otherwise, the function returns FALSE, the logged error is set in @error.
; Author    : Pusofalse
; =================================================================================
Func _SetupDiGetDriverInstallParams($hDevs, $pSP_DEVINFO_DATA, $pSP_DRVINFO_DATA, ByRef $tSP_DRVINSTALL_PARAMS)
    Local $iResult, $pSP_DRVINSTALL_PARAMS, $iSizeofParams

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    If IsDllStruct($pSP_DRVINFO_DATA) Then
        $pSP_DRVINFO_DATA = DllStructGetPtr($pSP_DRVINFO_DATA)
    EndIf
    If Not IsDllStruct($tSP_DRVINSTALL_PARAMS) Then
        $tSP_DRVINSTALL_PARAMS = DllStructCreate($tagSP_DRVINSTALL_PARAMS)
        $iSizeofParams = DllStructGetSize($tSP_DRVINSTALL_PARAMS)
        DllStructSetData($tSP_DRVINSTALL_PARAMS, "Size", $iSizeofParams)
    EndIf
    $pSP_DRVINSTALL_PARAMS = DllStructGetPtr($tSP_DRVINSTALL_PARAMS)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetDriverInstallParams", "ptr", $hDevs, _
            "ptr", $pSP_DEVINFO_DATA, "ptr", $pSP_DRVINFO_DATA, _
            "ptr", $pSP_DRVINSTALL_PARAMS)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiGetDriverInstallParams

; #### FUNCTION ####
; ================================================================================
; Name  : _SetupDiGetDriverSigner
; Description   : Retrieves the digital signature for a device instance.
; Parameter(s)  : $hDevs    - Handle to the device information set.
;       : $pSP_DEVINFO_DATA - A pointer to/or an SP_DEVINFO_DATA structure contains the device information element for which to retrieve the digital signature.
; Return values : Returns signature if succeeds, in text format. Else returns NULL and sets @error to a system error code.
; Author    : Pusofalse
; ================================================================================
Func _SetupDiGetDriverSigner($hDevs, $pSP_DEVINFO_DATA)
    Local $aSigner, $tDrvInfo, $tDrvDetail, $sInf

    Select
    Case _SetupDiBuildDriverInfoList($hDevs, $pSP_DEVINFO_DATA) = 0
        Return SetError(@error, 0, "")
    Case _SetupDiSelectBestCompatDrv($hDevs, $pSP_DEVINFO_DATA) = 0
        Return SetError(@error, 0, "")
    Case _SetupDiGetSelectedDriver($hDevs, $pSP_DEVINFO_DATA, $tDrvInfo) = 0
        Return SetError(@error, 0, "")
    Case _SetupDiGetDriverInfoDetail($hDevs, $pSP_DEVINFO_DATA, $tDrvInfo, $tDrvDetail) = 0
        Return SetError(@error, 0, "")
    Case _SetupDiDestroyDriverInfoList($hDevs, $pSP_DEVINFO_DATA) = 0
        Return SetError(@error, 0, "")
    Case Else
        $sInf = DllStructGetData($tDrvDetail, "InfFileName")
        $aSigner = _SetupDiVerifyDigitalSigner($sInf)
        Return SetError(@error, 0, $aSigner[1])
    EndSelect
EndFunc ;==>_SetupDiGetDriverSigner

; #### FUNCTION ####
; ================================================================================
; Name  : _SetupDiVerifyDigitalSigner
; Description   : Verify an INF file and retrieves its digital signer.
; Parameter(s)  : $sInfFile - INF file, may specify the full path.
; Return values : An array in the form:
;       : $aArray[0] - Catalog file.
;       : $aArray[1] - Signature.
;       : $aArray[2] - Signature version.
;       : If fails, all elements of the array are set to NULL, the logged error is set in @error.
; Author    : Pusofalse
; ================================================================================
Func _SetupDiVerifyDigitalSigner($sInfFile)
    Local $iResult, $tBuffer, $aResult[3], $iSysError

    $tBuffer = DllStructCreate($tagSP_INF_SIGNER_INFO)
    DllStructSetData($tBuffer, "Size", DllStructGetSize($tBuffer))

    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupVerifyInfFileW", "wstr", $sInfFile, _
            "ptr", 0, "ptr", DllStructGetPtr($tBuffer))
    If $iResult[0] = 0 Then $iSysError = _CM_Get_Last_Error()
    $aResult[0] = DllStructGetData($tBuffer, "CatalogFile")
    $aResult[1] = DllStructGetData($tBuffer, "DigitalSigner")
    $aResult[2] = DllStructGetData($tBuffer, "DigitalSignerVersion")
    Return SetError($iSysError, _SetupDiApiBufferFree($tBuffer), $aResult)
EndFunc ;==>_SetupDiVerifyDigitalSigner

Func _CM_Update_PnP_Device($sDeviceID, $sInfPath, $iFlags, $hWnd = 0)
    Local $iResult
    $iResult = DllCall($NEWDEV_DllHandle, "int", "UpdateDriverForPlugAndPlayDevices", "hWnd", $hWnd, _
            "str", $sDeviceID, "str", $sInfPath, "dword", $iFlags, "int*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[5], $iResult[0])
EndFunc ;==>_CM_Update_PnP_Device

; #### FUNCTION ####
; =========================================================================
; Name  : _CM_Install_Selected_Driver
; Description   : This function installs a selected driver on a selected device.
; Parameter(s)  : $hDevs    - A handle to a device information set that contains the device information element that represents a selected device and a selected driver for the device.
;       : $fBackup  - A BOOL value indicates the backup option, true indicates backs up the currently installed drivers for the selected device, default to true.
;       : $hWnd - A handle to the top-level window that the this function uses to display user interface components that are associated with installing the driver, can be NULL.
; Return values : Returns true if the driver was installed on the selected device, in this case, @extended is set to a DWORD value indicate whether a system restart is required to complete the installation. If system restart is required, the value is set to DI_NEEDREBOOT, application should prompt user to restart system to finish the installation.
; Author    : Pusofalse
; =========================================================================
Func _CM_Install_Selected_Driver($hDevs, $fBackup = True, $hWnd = 0)
    Local $iResult
    $iResult = DllCall($NEWDEV_DllHandle, "int", "InstallSelectedDriver", "hWnd", $hWnd, _
            "ptr", $hDevs, "str", "", "int", $fBackup, "dword*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[5], $iResult[0])
EndFunc ;==>_CM_Install_Selected_Driver

; #### FUNCTION ####
; =========================================================================
; Name  : _CM_Install_Device_Driver
; Description   : This function updates or installs the device driver for a device instance.
; Parameter(s)  : $sDeviceInstID - Supplies a device instance identifier for which the driver files are installed.
;       : $fBackup - True indicates backup the file installed, False otheriwise, default is True.
;       : $hWnd - Parent window owns the user interface, can be NULL.
; Return values : If succeeds, returns TRUE, if the device installation needs a system reboot, the function sets @extended to non-zero. If an error occured during the installation, function returns FALSE.
; Author    : Pusofalse
; =========================================================================
Func _CM_Install_Device_Driver($sDeviceInstID, $fBackup = True, $hWnd = 0)
    Local $iResult, $hDevs, $tDevInfo

    Select
    Case _SetupDiCreateDeviceDevs($sDeviceInstID, $hDevs, $tDevInfo) = 0
        Return SetError(@error, 0, 0)
    Case _SetupDiBuildDriverInfoList($hDevs, $tDevInfo) = 0
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs) * 0, 0)
    Case _SetupDiSelectBestCompatDrv($hDevs, $tDevInfo) = 0
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs) * 0, 0)
    Case _CM_Install_Selected_Driver($hDevs, $fBackup, $hWnd) = 0
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs) * 0, 0)
    Case Else
        Return SetError(0, @extended + _SetupDiDestroyDeviceInfoList($hDevs) * 0, 1)
    EndSelect
EndFunc ;==>_CM_Install_Device_Driver

; #### FUNCTION ####
; =========================================================================
; Name  : _CM_Install_New_Device
; Description   : This function installs a new device.
; Parameter(s)  : $vGUID - An optional device class GUID.
;       : $hWnd - Window handle that owns the user interface, can be NULL.
; Return values : True if succeeded, otherwise FALSE. If a system reboot is requested to finish the device installation, @extended is set to non-zero.
; Author    : Pusofalse
; =========================================================================
Func _CM_Install_New_Device($vGUID = "", $hWnd = 0)
    Local $pGUID, $iResult, $tGUID

    Select
    Case IsDllStruct($vGUID)
        $pGUID = DllStructGetPtr($vGUID)
    Case IsString($vGUID)
        If StringLeft($vGUID, 1) & StringRight($vGUID, 1) = "{}" Then
            $tGUID = _CM_GUID_From_String($vGUID)
        Else
            $tGUID = _SetupDiClassGuidsFromName($vGUID)
        EndIf
        $pGUID = DllStructGetPtr($tGUID)
    EndSelect
    $iResult = DllCall($NEWDEV_DllHandle, "int", "InstallNewDevice", "hWnd", $hWnd, _
            "ptr", $pGUID, "dword*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[3], $iResult[0])
EndFunc ;==>_CM_Install_New_Device

; #### FUNCTION ####
; =================================================================================
; Name  : _CM_Install_Selected_Device
; Description   : This function installs a device instance selected in device information set.
; Parameter(s)  : $hDevs    - Supplies a handle to the device information set.
;       : $hWnd - Window handle related to device installation, can be NULL.
; Return values : TRUE if succeeds (does not mean the device was installed or updated), FALSE if fails, @error returns the winerror code. If the device installation needs a system reboot, @extended is set to non-zero.
; Author    : Pusofalse
; =================================================================================
Func _CM_Install_Selected_Device($hDevs, $hWnd = 0)
    Local $iResult
    $iResult = DllCall($NEWDEV_DllHandle, "int", "InstallSelectedDevice", "hWnd", $hWnd, _
            "ptr", $hDevs, "dword*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[3], $iResult[0])
EndFunc ;==>_CM_Install_Selected_Device

; #### FUNCION ####
; =================================================================================
; Name  : _CM_Install_DevInst_Ex
; Description   : This function is used to install a device instance.
; Parameter(s)  : $sDeviceID    - Supplies the device instance identifier string.
;       : $fUpdateDriver    - TRUE only newer or higher rank drivers are installed.
;       : $fSilent  - TRUE means "Found new device" dialog will not be displayed, default to display (FALSE).
;       : $hWnd - Window handle of the top-level window to use for any UI related to installing the device, can be NULL specified.
; Return values : TRUE is returned if succeeds (does not mean the device was installed or updated). FALSE otherwise. If the installation needs a system restart, @extended is set to non-zero.
; Author    : Pusofalse
; =================================================================================
Func _CM_Install_DevInst_Ex($sDeviceID, $fUpdateDriver, $fSilent = False, $hWnd = 0)
    Local $iResult
    $iResult = DllCall($NEWDEV_DllHandle, "int", "InstallDevInstEx", "hWnd", $hWnd, _
            "wstr", $sDeviceID, "int", $fUpdateDriver, "dword*", 0, "int", $fSilent)
    Return SetError(_CM_Get_Last_Error(), $iResult[4], $iResult[0])
EndFunc ;==>_CM_Install_DevInst_Ex

; #### FUNCTION ####
; =========================================================================
; Name  : _InstallNewDevice, obsolete, call _CM_Install_New_Device instead.
; Description   : This function installs a new device. The user is prompted to select the device.
; Parameter(s)  : $vGuid    - A vaiable-type value, indicates the GUID of the device class to install. If NULL is specified for this parameter, the user starts at the detection choice page, if non-NULL for this parameter, it must be in one of the following formats:
;       :   - Class name: NET/Display/Mouse...
;       :   - Class GUID string: {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}.
;       :   - Class GUID structure.
;       :   - Class GUID pointer.
;       : $hWndOwner    - A handle to the top-level window to be used for any required user interface, can set to NULL.
; Return values : If no error is occurred, returns true and sets @extended to a DWORD value indicates a system restart whether is requird (DI_NEEDREBOOT indicates the restart behavior is required), else returns false and sets @error to a system code.
; Author    : Pusofalse
; =========================================================================
Func _InstallNewDevice($vGuid = 0, $hWndOwner = 0)
    Local $iResult, $pGuid
    If IsString($vGuid) Then
        If (StringLeft($vGuid, 1) & StringRight($vGuid, 1) = "{}") Then
            $pGuid = DllStructGetPtr(_CM_GUID_From_String($vGuid))
        ElseIf $vGuid <> "" Then
            $tGuid = _SetupDiClassGuidsFromName($vGuid)
            $pGuid = DllStructGetPtr($tGuid)
        EndIf
    ElseIf IsDllStruct($vGuid) Then
        $pGuid = DllStructGetPtr($vGuid)
    ElseIf IsPtr($vGuid) Then
        $pGuid = $vGuid
    Else
        $pGuid = 0
    EndIf

    $iResult = DllCall($NEWDEV_DllHandle, "int", "InstallNewDevice", "hWnd", $hWndOwner, _
            "ptr", $pGuid, "dword*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[3], $iResult[0])
EndFunc ;==>_InstallNewDevice

Func _SetupDiCreateDeviceInfo($hDevs, $sDeviceName, $vClassGuid, $iCreationFlags, ByRef $tSP_DEVINFO_DATA, $sDescr = "", $hWndParent = 0)
    Local $iResult, $tClassGuid, $pClassGuid, $pSP_DEVINFO_DATA

    If IsString($vClassGuid) Then
        If (StringLeft($vClassGuid, 1) & StringRight($vClassGuid, 1) = "{}") Then
            $pClassGuid = DllStructGetPtr(_CM_GUID_From_String($vClassGuid))
        ElseIf ($vClassGuid) <> "" Then
            $tClassGuid = _SetupDiClassGuidsFromName($vClassGuid)
            $pClassGuid = DllStructGetPtr($tClassGuid)
        EndIf
    ElseIf IsDllStruct($vClassGuid) Then
        $pClassGuid = DllStructGetPtr($vClassGuid)
    ElseIf IsPtr($vClassGuid) Then
        $pClassGuid = ($vClassGuid)
    Else
        $pClassGuid = 0
    EndIf
    If $pClassGuid = 0 Then Return SetError(87, 0, 0)

    $tClassGuid = DllStructCreate("byte[16]", $pClassGuid)

    If Not IsDllStruct($tSP_DEVINFO_DATA) Then
        $tSP_DEVINFO_DATA = DllStructCreate($tagSP_DEVICEINFO_DATA)
        DllStructSetData($tSP_DEVINFO_DATA, "Size", 28)
        DllStructSetData($tSP_DEVINFO_DATA, "Guid", DllStructGetData($tClassGuid, 1))
    EndIf
    $pSP_DEVINFO_DATA = DllStructGetPtr($tSP_DEVINFO_DATA)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiCreateDeviceInfo", _
            "ptr", $hDevs, "str", $sDeviceName, "ptr", $pClassGuid, _
            "str", $sDescr, "hWnd", $hWndParent, "dword", $iCreationFlags, _
            "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiCreateDeviceInfo

Func _SetupDiGetDevNodeStatus($hDevInst)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_DevNode_Status", _
            "ulong*", 0, "ulong*", 0, "dword", $hDevInst, "ulong", 0)
    Return SetError($iResult[0], $iResult[2], $iResult[1])
EndFunc ;==>_SetupDiGetDevNodeStatus

; #### FUNCTOIN ####
; =========================================================================
; Name  : _SetupDiChangeState
; Description   : Default handler of DIF_PROPERCHANGE.
; Parameter(s)  : $hDevs    - A pointer to a device information set that contains the device information element.
;       : $pSP_DEVINFO_DATA - A pointer to an SP_DEVINFO_DATA structure contains the device information element for which to change the state.
; Return values : TRUE is returned if succeeds, FALSE otherwise, the logged error can be get in @error macro.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiChangeState($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult
    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiChangeState", "hWnd", $hDevs, _
            "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiChangeState

; ##### FUNCTION ####
; =========================================================================
; Name  : _SetupDiSetClassInstallParams
; Description   : Sets the installation parameters for a device class.
; Parameter(s)  : $hDevs    - Supplies a handle to the device information set.
;       : $pSP_DEVINFO_DATA - A pointer to an SP_DEVINFO_DATA structure contains the device information set.
;       : $pInst    - A pointer to an SP_CLASSINSTALL_PARAMS structure contains the data to be set.
;       : $iSizeInst    - Size of the $pInst.
; Return values : TRUE is returned if succeeds, FALSE otherwise, in which case, @error is set to the reason of the failure.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiSetClassInstallParams($hDevs, $pSP_DEVINFO_DATA, $pInst, $iSizeInst)
    Local $iResult

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    If IsDllStruct($pInst) Then $pInst = DllStructGetPtr($pInst)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiSetClassInstallParams", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, _
            "ptr", $pInst, "dword", $iSizeInst)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiSetClassInstallParams

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiDeviceInstallParams
; Description   : This function sets the installation parameters for a device instance.
; Parameter(s)  : $hDevs    - A handle to the device information set.
;       : $pSP_DEVINFO_DATA - A pointer to an SP_DEVINFO_DATA.
;       : $pSP_DEVINSTALL_PARAMS - A pointer to an SP_DEVINSTALL_PARAMS, contains the installation parameters.
; Return values : True indicates success, False to failure, in this case, @error is set to a system error code.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiSetDeviceInstallParams($hDevs, $pSP_DEVINFO_DATA, $pSP_DEVINSTALL_PARAMS)
    Local $iResult
    Select
    Case IsDllStruct($pSP_DEVINFO_DATA)
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
        ContinueCase
    Case IsDllStruct($pSP_DEVINSTALL_PARAMS)
        $pSP_DEVINSTALL_PARAMS = DllStructGetPtr($pSP_DEVINSTALL_PARAMS)
    EndSelect
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiSetDeviceInstallParams", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, _
            "ptr", $pSP_DEVINSTALL_PARAMS)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiSetDeviceInstallParams

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiEnumDeviceInterfaces
; Description   : This function enumerates the device interfaces for a device instance.
; Parameter(s)  : $hDevs    - A device information set handle.
;       : $pSP_DEVINFO_DATA - A pointer to an SP_DEVINFO_DATA, can be ZERO specified.
;       : $vGUID - GUID for device setup interface.
;       : $iIndex   - Specifies the index to enumerate, zero-based.
;       : $tSP_DEVIFINFO_DATA - Supplies a variable receives a SP_DEVINTERFACE_DATA structure contains the device interface.
; Return values : TRUE is returned if succeeds, FALSE otherwise. If the enumeration is finished, @error is set to 259.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiEnumDeviceInterfaces($hDevs, $pSP_DEVINFO_DATA, $vGuid, $iIndex, ByRef $tSP_DEVIFINFO_DATA)
    Local $iResult, $pSP_DEVIFINFO

    If IsString($vGuid) Then
        If StringLeft($vGuid, 1) & StringRight($vGuid, 1) = "{}" Then
            Local $tGUID = _CM_GUID_From_String($vGuid)
            $pGuid = DllStructGetPtr($tGUID)
        ElseIf $vGuid <> "" Then
            $tGuid = _SetupDiClassGuidsFromName($vGuid)
            $pGuid = DllStructGetPtr($tGuid)
        EndIf
    ElseIf IsDllStruct($vGuid) Then
        $pGuid = DllStructGetPtr($vGuid)
    ElseIf IsPtr($vGuid) Then
        $pGuid = $vGuid
    Else
        $pGuid = 0
    EndIf
    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    If Not IsDllStruct($tSP_DEVIFINFO_DATA) Then
        $tSP_DEVIFINFO_DATA = DllStructCreate($tagSP_DEV_INTERFACE_DATA)
        DllStructSetData($tSP_DEVIFINFO_DATA, "Size", 28)
    EndIf
    $pSP_DEVIFINFO = DllStructGetPtr($tSP_DEVIFINFO_DATA)

    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiEnumDeviceInterfaces", "hWnd", $hDevs, _
            "ptr", $pSP_DEVINFO_DATA, "ptr", $pGuid, "dword", $iIndex, _
            "ptr", $pSP_DEVIFINFO)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiEnumDeviceInterfaces

Func _SetupDiGetDeviceInterfaceDetail($hDevs, $pSP_DEVIFINFO_DATA, ByRef $tSP_DEVINFO_DATA)
    Local $iResult, $pBuffer, $tBuffer, $sDevicePath, $iSysError = 0, $pSP_DEVINFO_DATA

    Select
    Case Not IsDllStruct($tSP_DEVINFO_DATA)
        $tSP_DEVINFO_DATA = DllStructCreate($tagSP_DEVICEINFO_DATA)
        DllStructSetData($tSP_DEVINFO_DATA, "Size", 28)
        ContinueCase
    Case IsDllStruct($pSP_DEVIFINFO_DATA)
        $pSP_DEVIFINFO_DATA = DllStructGetPtr($pSP_DEVIFINFO_DATA)
    EndSelect

    $pSP_DEVINFO_DATA = DllStructGetPtr($tSP_DEVINFO_DATA)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetDeviceInterfaceDetail", _
            "hWnd", $hDevs, "ptr", $pSP_DEVIFINFO_DATA, _
            "ptr", 0, "dword", 0, "dword*", 0, "ptr", $pSP_DEVINFO_DATA)
    $tBuffer = DllStructCreate("dword;char[" & $iResult[5] - 4 & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    DllStructSetData($tBuffer, 1, 5)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetDeviceInterfaceDetail", _
            "hWnd", $hDevs, "ptr", $pSP_DEVIFINFO_DATA, _
            "ptr", $pBuffer, "dword", $iResult[5], "dword*", 0, _
            "ptr", $pSP_DEVINFO_DATA)
    If $iResult[0] = 0 Then
        $iSysError = _CM_Get_Last_Error()
    Else
        $sDevicePath = DllStructGetData($tBuffer, 2)
    EndIf
    Return SetError($iSysError, _SetupDiApiBufferFree($tBuffer) or $iResult[0], $sDevicePath)
EndFunc ;==>_SetupDiGetDeviceInterfaceDetail

; #### FUNCTION ####
; =========================================================================
; Name  : _SetupDiDestroyDeviceInfoList
; Description   : Release a device information set.
; Parameter(s)  : $hDevs    - Supplies a handle to the device information set, typically obtained by the following functions:
;       :       _SetupDiCreateDeviceDevs
;       :       _SetupDiCreateDeviceDevsEx
;       :       _SetupDiCreateDeviceInfo
;       :       _SetupDiCreateDeviceInfoEx
;       :       _SetupDiGetClassDevs
;       :       _SetupDiGetClassDevsEx
; Return values : TRUE indicates success, FALSE indicates failure.
; Author    : Pusofalse
; =========================================================================
Func _SetupDiDestroyDeviceInfoList($hDevs)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiDestroyDeviceInfoList", "hWnd", $hDevs)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiDestroyDeviceInfoList


Func _SetupDiGetHwProfileList()
    Local $iResult, $tBuffer, $pBuffer, $aResult[1][2] = [[0]]

    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetHwProfileList", "ptr", 0, _
            "dword", 0, "dword*", 0, "dword*", 0)
    If $iResult[3] = 0 Then Return SetError(_CM_Get_Last_Error(), -1, $aResult)

    $tBuffer = DllStructCreate("dword ProfileID[" & $iResult[3] & "]")
    $pBuffer = DllStructGetPtr($tBuffer)

    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetHwProfileList", "ptr", $pBuffer, _
            "dword", $iResult[3], "dword*", 0, "dword*", 0)
    If $iResult[0] = 0 Then Return SetError(_CM_Get_Last_Error(), -1, $aResult)
    $aResult[0][0] = $iResult[3]
    Redim $aResult[$iResult[3] + 1][2]
    For $i = 1 to $iResult[3]
        $aResult[$i][0] = DllStructGetData($tBuffer, "ProfileID", $i)
        $aResult[$i][1] = _SetupDiGetHwProfileFriendlyName($aResult[$i][1])
    Next
    Return SetExtended(_SetupDiApiBufferFree($tBuffer) + $iResult[4], $aResult)
EndFunc ;==>_SetupDiGetHwProfileList

Func _SetupDiGetHwProfileFriendlyName($iProfileID = 0)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetHwProfileFriendlyName", _
            "dword", $iProfileID, "ptr", 0, "dword", 0, "dword*", 0)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetHwProfileFriendlyName", _
            "dword", $iProfileID, "str", "", "dword", $iResult[4], "dword*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[2])
EndFunc ;==>_SetupDiGetHwProfileFriendlyName

; #### FUNCTION (internal used only) ####
; =========================================================================
; _CM_Free_Variable is used to free a dll-structure-typed variable.
; =========================================================================
Func _CM_Free_Variable(ByRef $vVariable)
    $vVariable = 0
EndFunc ;==>_CM_Free_Variable

; #### FUNCTION ####
; =========================================================================
; Name  : _CM_Get_Child
; Description   : Retrieves the first child device instance for a specified devnode, on local system.
; Parameter(s)  : $hDevInst - A handle to the device instance.
; Return values : Handle to the first child device instance is returned if success, or zero if failure.
; Related   : _CM_Get_Child_Ex, _CM_Get_Parent, _CM_Get_Sibling
; Author    : Pusofalse
; =========================================================================
Func _CM_Get_Child($hDevInst)
    Local $iResult

    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Child", "dword*", 0, _
            "dword", $hDevInst, "ulong", 0)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[1])
EndFunc ;==>_CM_Get_Child

; #### FUNCTION ####
; =========================================================================
; Name  : _CM_Get_Parent
; Description   : Retrieves the parent node for a device instance in device tree, on local system.
; Parameter(s)  : $hDevInst - A device instance handle for which to retrieve the parent node.
; Return values : Handle to the parent node is returned if success, otherwise returns a value less one.
; Author    : Pusofalse
; =========================================================================
Func _CM_Get_Parent($hDevInst)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Parent", "dword*", 0, _
            "dword", $hDevInst, "ulong", 0)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[1])
EndFunc ;==>_CM_Get_Parent

Func _CM_Get_Sibling($hDevInst)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Sibling", "dword*", 0, _
            "dword", $hDevInst, "ulong", 0)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[1])
EndFunc ;==>_CM_Get_Sibling

Func _CM_Get_Depth($hDevInst)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Depth", "dword*", 0, _
            "dword", $hDevInst, "ulong", 0)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[1])
EndFunc ;==>_CM_Get_Depth

Func _CM_Get_Device_ID($hDevInst)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Device_ID", "dword", $hDevInst, _
            "str", "", "dword", _CM_Get_Device_ID_Size($hDevInst) + 1, "ulong", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[0] = 0, $iResult[2])
EndFunc ;==>_CM_Get_Device_ID

Func _CM_Get_Device_ID_Size($hDevInst)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Device_ID_Size", "ulong*", 0, _
            "dword", $hDevInst, "ulong", 0)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Get_Device_ID_Size

; #### FUNCTION ####
; ===================================================================================
; Name  : _CM_Get_Device_ID_List
; Description   : This function retrieves the device ID strings for the device instance in an array, on the local system.
; Parameter(s)  : See _CM_Get_Device_ID_List_Ex for details.
; Return values : The return value is same to _CM_Get_Device_ID_List_Ex's.
; Author    : Pusofalse
; ===================================================================================
Func _CM_Get_Device_ID_List($sFilter = "", $iFlags = $CM_GETIDLIST_FILTER_NONE)
    Local $iResult, $iLength, $aResult[1] = [0], $pBuffer, $tBuffer, $iPointer, $pBuffer1, $iLength2

    $iLength = _CM_Get_Device_ID_List_Size($sFilter, $iFlags) + 1
    If $iLength < 2 Then Return SetError(@error, 0, $aResult)
    $pBuffer = _CM_Heap_Alloc($iLength)

    Do
        $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Device_ID_List", "str", $sFilter, _
                "ptr", $pBuffer, "ulong", $iLength, "ulong", $iFlags)
        If $iResult[0] = $CR_BUFFER_SMALL Then
            _CM_Heap_Free($pBuffer)
            $iLength += 2
            $pBuffer = _CM_Heap_Alloc($iLength + 128)
        EndIf
    Until   $iResult[0] <> $CR_BUFFER_SMALL
    $pBuffer1 = $pBuffer

    While $iPointer < $iLength
        $tBuffer = DllStructCreate("char DeviceID[512]", $pBuffer)
        $aResult[0] += 1
        Redim $aResult[$aResult[0] + 1]
        $aResult[$aResult[0]] = DllStructGetData($tBuffer, "DeviceID")
        If $aResult[$aResult[0]] = "" Then ExitLoop
        $iLength2 = StringLen($aResult[$aResult[0]]) + 1
        $iPointer += $iLength2
        $pBuffer += $iLength2
    WEnd

    _CM_Heap_Free($pBuffer1)
    If $aResult[0] = 0 Then Return SetError($iResult[0], 0, $aResult)
    $aResult[0] -= 1
    Redim $aResult[$aResult[0] + 1]
    Return SetError($iResult[0], 0, $aResult)
EndFunc ;==>_CM_Get_Device_ID_List

; #### FUNCTION ####
; ===================================================================================
; Name  : _CM_Get_Device_ID_List_Size
; Description   : Return the buffer length required to hold the device ID strings.
; Parameter(s)  : See _CM_Get_Device_ID_List_Size_Ex for details.
; Return values : Length required.
; Author    : Pusofalse
; ===================================================================================
Func _CM_Get_Device_ID_List_Size($sFilter = "", $iFlags = $CM_GETIDLIST_FILTER_NONE)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Device_ID_List_Size", "ulong*", 0, _
            "str", $sFilter, "ulong", $iFlags)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Get_Device_ID_List_Size

; #### FUNCTION ####
; ===================================================================================
; Name  : _CM_Enumerate_Enumerators
; Description   : Lists the device enumerators on the local system.
; Parameter(s)  : This function has no parameters.
; Return values : If succeeds, returns an array with following format:
;       :   - $aArray[0] - The number of returned entries.
;       :   - $aArray[1] - 1st enumerator's name.
;       :   - $aArray[2] - 2nd enumerator's name.
;       :   - ... ...
; Author    : Pusofalse
; ===================================================================================
Func _CM_Enumerate_Enumerators()
    Local $aResult[1] = [0], $iResult

    While 1
        $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Enumerate_Enumerators", _
                "ulong", $aResult[0], "str", "", "ulong*", 32, "ulong", 0)
        If $iResult[0] Then ExitLoop
        $aResult[0] += 1
        Redim $aResult[$aResult[0] + 1]
        $aResult[$aResult[0]] = $iResult[2]
    WEnd
    Return SetError($iResult[0], 0, $aResult)
EndFunc ;==>_CM_Enumerate_Enumerators

; #### FUNCTION ####
; ===================================================================================
; Name  : _CM_Enumerate_Classes
; Description   : This function lists the device setup classes on the local system.
; Parameter(s)  : This function has no parameters.
; Return values : An array in the following form:
;       :   - $aArray[0][0] - The number of returned classes.
;       :   - $aArray[1][0] - The GUID of 1st class, in string format.
;       :   - $aArray[1][1] - Description for 1st class.
;       :   - $aArray[2][0] - The GUID of 2nd class, in string format.
;       :   - $aArray[2][1] - Description for 2nd class.
;       :   - ... ...
; Author    : Pusofalse
; ===================================================================================
Func _CM_Enumerate_Classes()
    Local $iResult, $pGUID, $aResult[1][2] = [[0]]

    $pGUID = _CM_Heap_Alloc(32)
    While 1
        $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Enumerate_Classes", _
                "ulong", $aResult[0][0], "ptr", $pGUID, "ulong", 0)
        If $iResult[0] = $CR_NO_SUCH_VALUE Then ExitLoop
        $aResult[0][0] += 1
        Redim $aResult[$aResult[0][0] + 1][2]
        $aResult[$aResult[0][0]][0] = _CM_String_From_GUID($pGUID)
        $aResult[$aResult[0][0]][1] = _SetupDiGetClassDescription($pGUID)
    WEnd
    Return SetError($iResult[0], _CM_Heap_Free($pGUID), $aResult)
EndFunc ;==>_CM_Enumerate_Classes

Func _CM_Get_DevNode_Status($hDevInst)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_DevNode_Status", _
            "ulong*", 0, "ulong*", 0, "dword", $hDevInst, "ulong", 0)
    Return SetError($iResult[0], $iResult[2], $iResult[1])
EndFunc ;==>_CM_Get_DevNode_Status


Func _CM_Request_Device_Eject($hDevInst, ByRef $sVetoName)
    Local $iResult, $hToken
    Local $aPriv[2][2] = [[$SE_UNDOCK_NAME, 2], [$SE_LOAD_DRIVER_NAME, 2]]

    $hToken = _OpenProcessToken(-1)
    _AdjustTokenPrivileges($hToken, $aPriv)
    _LsaCloseHandle($hToken)

    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Request_Device_Eject", "dword", $hDevInst, _
            "int*", 0, "str", "", "ulong", 260, "ulong", 0)
    $sVetoName = $iResult[3]
    Return SetError($iResult[0], $iResult[2], $iResult[0] = 0)
EndFunc ;==>_CM_Request_Device_Eject

Func _CM_Query_And_Remove_SubTree($hDevInst, $iFlags, ByRef $sVeto)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Query_And_Remove_SubTree", _
            "dword", $hDevInst, "int*", 0, "str", "", "ulong", 260, "ulong", $iFlags)
    $sVeto = $iResult[3]
    Return SetError($iResult[0], $iResult[2], $iResult[0] = 0)
EndFunc ;==>_CM_Query_And_Remove_SubTree


Func _CM_Get_Device_Interface_List_Size($pInterfaceClassGuid, $sDevId = "", $iFlags = 0)
    Local $iResult

    If IsDllStruct($pInterfaceClassGuid) Then
        $pInterfaceClassGuid = DllStructGetPtr($pInterfaceClassGuid)
    ElseIf IsString($pInterfaceClassGuid) Then
        Local $tGUID = _CM_GUID_From_String($pInterfaceClassGuid)
        $pInterfaceClassGuid = DllStructGetPtr($tGUID)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Device_Interface_List_Size", _
            "ulong*", 0, "ptr", $pInterfaceClassGuid, "str", $sDevId, "ulong", $iFlags)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[1])
EndFunc ;==>_CM_Get_Device_Interface_List_Size

Func _CM_Get_Device_Interface_List($pInterfaceClassGuid, $sDevId = "", $iFlags = 0)
    Local $iResult, $iLength, $tBuffer, $pBuffer, $pBuffer1, $aResult[1] = [0], $iLen

    If IsDllStruct($pInterfaceClassGuid) Then
        $pInterfaceClassGuid = DllStructGetPtr($pInterfaceClassGuid)
    ElseIf IsString($pInterfaceClassGuid) Then
        Local $tGUID = _CM_GUID_From_String($pInterfaceClassGuid)
        $pInterfaceClassGuid = DllStructGetPtr($tGUID)
    EndIf
    $iLength = _CM_Get_Device_Interface_List_Size($pInterfaceClassGuid, $sDevId, $iFlags)
    $tBuffer = DllStructCreate("char ID[" & $iLength & "]") - 1
    $pBuffer = DllStructGetPtr($tBuffer)
    $pBuffer1 = $pBuffer + $iLength

    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Device_Interface_List", _
            "ptr", $pInterfaceClassGuid, "str", $sDevId, "ptr", $pBuffer, _
            "ulong", $iLength, "ulong", $iFlags)

    While Number($pBuffer) < Number($pBuffer1) - 1
        $aResult[0] += 1
        Redim $aResult[$aResult[0] + 1]
        $aResult[$aResult[0]] = DllStructGetData($tBuffer, "ID")
        $pBuffer += StringLen($aResult[$aResult[0]]) + 1
        $iLength -= StringLen($aResult[$aResult[0]]) + 1
        $tBuffer = 0
        $tBuffer = DllStructCreate("char ID[" & $iLength & "]", $pBuffer)
    WEnd
    Return SetError($iResult[0], _CM_Free_Variable($tBuffer), $aResult)
EndFunc ;==>_CM_Get_Device_Interface_List


Func _CM_Get_Next_Res_Des(ByRef $iResDes, $iResourceID)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Next_Res_Des", _
            "long*", 0, "long", $iResDes, "long", $iResourceID, _
            "long*", 0, "ulong", 0)
    If $iResult[0] Then Return SetError($iResult[0], 0, 0)
    _CM_Free_Res_Des_Handle($iResDes)
    $iResDes = $iResult[1]
    Return SetExtended($iResult[4], 1)
EndFunc ;==>_CM_Get_Next_Res_Des

Func _CM_Get_Res_Des_Data_Size(ByRef $iResDes)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Res_Des_Data_Size", _
            "ulong*", 0, "long", $iResDes, "ulong", 0)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[1])
EndFunc ;==>_CM_Get_Res_Des_Data_Size

Func _CM_Get_Res_Des_Data(ByRef $iResDes)
    Local $pBuffer, $iLength, $iResult

    $iLength = _CM_Get_Res_Des_Data_Size($iResDes)
    $pBuffer = _CM_Heap_Alloc($iLength)

    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Res_Des_Data", _
            "long", $iResDes, "ptr", $pBuffer, "ulong", $iLength, "ulong", 0)
    Return SetError($iResult[0], ($iResult[0] = 0), $pBuffer)
EndFunc ;==>_CM_Get_Res_Des_Data

; #### FUNCTION ####
; ================================================================================
; Function      : Obsolete, do not use. Call _CM_Get_Device_Resources_Ex instead.
; ================================================================================
Func _CM_Get_Device_Resources($hDevInst, $iResourceType)
    Local $hConf, $aResult[1] = [0], $pBuffer, $tagStructure, $tBuffer

    Switch $iResourceType
    Case $RESTYPE_IO
        $tagStructure = $tagIO_DES
    Case $RESTYPE_MEM
        $tagStructure = $tagMEM_DES
    Case $RESTYPE_IRQ
        $tagStructure = $tagIRQ_DES
    Case $RESTYPE_DMA
        $tagStructure = $tagDMA_DES
    Case $RESTYPE_BUSNUMBER
        $tagStructure = $tagBUSNUMBER_DES
    Case Else
        Return SetError(50, 0, $aResult)
    EndSwitch

    $hConf = _CM_Get_First_Log_Conf($hDevInst, 2)
    If @error or $hConf = 0 Then Return SetError(@error, 0, $aResult)

    While _CM_Get_Next_Res_Des($hConf, $iResourceType)
        $aResult[0] += 1
        Redim $aResult[$aResult[0] + 1]
        $pBuffer = _CM_Get_Res_Des_Data($hConf)
        $tBuffer = DllStructCreate($tagStructure, $pBuffer)
        Switch $iResourceType
        Case $RESTYPE_MEM, $RESTYPE_IO, $RESTYPE_BUSNUMBER
            $aResult[$aResult[0]] &= "0x" & Hex(DllStructGetData($tBuffer, "AllocBase"))
            $aResult[$aResult[0]] &= " - 0x" & Hex(DllStructGetData($tBuffer, "AllocEnd"))
        Case $RESTYPE_IRQ
            $aResult[$aResult[0]] = DllStructGetData($tBuffer, "AllocNum")
        Case $RESTYPE_DMA
            $aResult[$aResult[0]] = DllStructGetData($tBuffer, "AllocChannel")
        EndSwitch
        _CM_Heap_Free($pBuffer)
        _CM_Free_Variable($tBuffer)
    WEnd
    Return $aResult
EndFunc ;==>_CM_Get_Device_Resources

Func _CM_Get_First_Log_Conf($hDevInst, $iFlags)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_First_Log_Conf", "long*", 0, _
            "dword", $hDevInst, "ulong", $iFlags)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[1])
EndFunc ;==>_CM_Get_First_Log_Conf

Func _CM_Get_Process_Heap()
    Local $iResult
    $iResult = DllCall($Kernel32_DllHandleA, "long", "GetProcessHeap")
    Return $iResult[0]
EndFunc ;==>_CM_Get_Process_Heap

Func _CM_Heap_Alloc($iLength, $iFlags = 8)
    If $iLength < 1 Then Return 0

    Local $pMem, $hHeap = _CM_Get_Process_Heap()
    $pMem = DllCall($Kernel32_DllHandleA, "ptr", "HeapAlloc", "hWnd", $hHeap, _
            "dword", $iFlags, "dword", $iLength)
    Return $pMem[0]
EndFunc ;==>_CM_Heap_Alloc

Func _CM_Heap_Free(ByRef $pMem)
    If $pMem < 1 Then Return SetError(87, 0, False)

    Local $iResult, $hHeap = _CM_Get_Process_Heap()
    $iResult = DllCall($Kernel32_DllHandleA, "int", "HeapFree", "hWnd", $hHeap, _
            "dword", 0, "ptr", $pMem)
    If $iResult[0] Then $pMem = Ptr(0)
    Return $iResult[0] <> 0
EndFunc ;==>_CM_Heap_Free

Func _CM_Heap_Size($pMem)
    If $pMem < 1 Then Return SetError(87, 0, 0)

    Local $iResult, $hHeap = _CM_Get_Process_Heap()
    $iResult = DllCall($Kernel32_DllHandleA, "long", "HeapSize", "hWnd", $hHeap, _
            "dword", 0, "ptr", $pMem)
    Return $iResult[0]
EndFunc ;==>_CM_Heap_Size

Func _CM_Modify_Res_Des($iResDes, $iResourceID, $pData, $iLength)
    Local $iResult

    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Modify_Res_Des", "long*", 0, _
            "long", $iResDes, "long", $iResourceID, "ptr", $pData, _
            "ulong", $iLength, "ulong", 0)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[0] = 0)
EndFunc ;==>_CM_Modify_Res_Des

Func _CM_Free_Res_Des(ByRef $iResDes)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Free_Res_Des", "long*", 0, _
            "ulong", $iResDes, "ulong", 0)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[1])
EndFunc ;==>_CM_Free_Res_Des

Func _CM_Free_Res_Des_Handle(ByRef $iResDes)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Free_Res_Des_Handle", "long", $iResDes)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[0] = 0)
EndFunc ;==>_CM_Free_Res_Des_Handle

Func _CM_Free_Log_Conf(ByRef $hLogConf)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Free_Log_Conf", "long", $hLogConf, "long", 0)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[0] = 0)
EndFunc ;==>_CM_Free_Log_Conf

Func _CM_Free_Log_Conf_Handle(ByRef $hLogConf)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Free_Log_Conf_Handle", "long", $hLogConf)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[0] = 0)
EndFunc ;==>_CM_Free_Log_Conf_Handle

Func _CM_Add_Res_Des(ByRef $hLogConf, $iResourceID, $pData, $iLength)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Add_Res_Des", "long*", 0, _
            "long", $hLogConf, "long", $iResourceID, "ptr", $pData, _
            "ulong", $iLength, "ulong", 0)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[1])
EndFunc ;==>_CM_Add_Res_Des

Func _CM_Get_Next_Log_Conf(ByRef $hLogConf)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Next_Log_Conf", "long*", 0, _
            "long", $hLogConf, "ulong", 0)
    If $iResult[0] Then Return SetError($iResult[0], 0, 0)
    _CM_Free_Log_Conf_Handle($hLogConf)
    $hLogConf = $iResult[1]
    Return SetExtended(1, 1)
EndFunc ;==>_CM_Get_Next_Log_Conf

Func _CM_Get_Last_Error()
    Local $iResult
    $iResult = DllCall($Kernel32_DllHandleA, "long", "GetLastError")
    Return $iResult[0]
EndFunc ;==>_CM_Get_Last_Error

Func _CM_GUID_From_String($sGUID)
    Local $tGUID = DllStructCreate("byte Guid[16]"), $iResult

    $iResult = DllCall("Ole32.dll", "int", "CLSIDFromString", "wstr", $sGUID, "ptr", DllStructGetPtr($tGUID))
    Return SetError($iResult[0], 0, $tGUID)
EndFunc ;==>_CM_GUID_From_String

Func _CM_String_From_GUID($pGUID)
    Local $iResult

    If IsDllStruct($pGUID) Then $pGUID = DllStructGetPtr($pGUID)
    $iResult = DllCall("Ole32.dll", "int", "StringFromGUID2", _
            "ptr", $pGUID, "wstr", "", "int", 128)
    Return $iResult[2]
EndFunc ;==>_CM_String_From_GUID

; #### FUNCTION ####
; ===================================================================================
; Name  : _CM_Device_IO_Control
; Description   : Control a device.
; Parameter(s)  : $hDevice  - Handle to the device, returned by _CM_Create_File.
;       : $iCtrlCode    - Control code.
;       : $pIn  - A pointer to a buffer as a IN parameter.
;       : $iSizeIn  - Size of $pIn.
;       : $pOut - A pointer to a buffer as a OUT parameter.
;       : $iSizeOut - Size of $pOut.
;       : $pOverlapped  - A pointer to a OVERLAPPED structure, if $hDevice is created without OVERLAPPED flag, this parameter can be NULL.
; Return values : Returns TRUE if succeeds, otherwise returns FALSE.
; Author    : Pusofalse
; ===================================================================================
Func _CM_Device_IO_Control($hDevice, $iCtrlCode, $pIn, $iSizeIn, $pOut, $iSizeOut, $pOverlapped = 0)
    Local $iResult

    If IsDllStruct($pIn) Then $pIn = DllStructGetPtr($pIn)
    If IsDllStruct($pOut) Then $pOut = DllStructGetPtr($pOut)
    If IsDllStruct($pOverlapped) Then $pOverlapped = DllStructGetPtr($pOverLapped)
    $iResult = DllCall($Kernel32_DllHandleA, "int", "DeviceIoControl", "ptr", $hDevice, _
            "dword", $iCtrlCode, "ptr", $pIn, "dword", $iSizeIn, _
            "ptr", $pOut, "dword", $iSizeOut, "int*", 0, "ptr", $pOverlapped)
    Return SetError(_CM_Get_Last_Error(), $iResult[7], $iResult[0])
EndFunc ;==>_CM_Device_IO_Control

Func _CM_Write_Device($hFile, $pBuffer, $iSizeofBuffer, $pOverlap = 0, $sBufferType = "ptr")
    Local $iResult

    If IsDllStruct($pBuffer) Then $pBuffer = DllStructGetPtr($pBuffer)
    $iResult = DllCall($Kernel32_DllHandleA, "int", "WriteFile", "hWnd", $hFile, _
            $sBufferType, $pBuffer, "dword", $iSizeofBuffer, _
            "dword*", 0, "ptr", $pOverlap)
    If @error Then Return SetError(1, 0, 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[4], $iResult[0])
EndFunc ;==>_CM_Write_Device

Func _CM_Read_Device($hFile, $pBuffer, $iSizeofBuffer, $pOverlap = 0)
    Local $iResult

    $iResult = DllCall($Kernel32_DllHandleA, "int", "ReadFile", "hWnd", $hFile, _
            "ptr", $pBuffer, "dword", $iSizeofBuffer, "dword*", 0, "ptr", $pOverlap)
    Return SetError(_CM_Get_Last_Error(), $iResult[4], $iResult[0])
EndFunc ;==>_CM_Read_Device

Func _CM_Create_File($sFile, $iAccess, $iShare, $pSecurAttr, $iCreation, $iFlags, $hTemplate = 0)
    Local $iResult

    $iResult = DllCall($Kernel32_DllHandleA, "long", "CreateFile", "str", $sFile, _
            "dword", $iAccess, "dword", $iShare, "ptr", $pSecurAttr, _
            "dword", $iCreation, "dword", $iFlags, "long", $hTemplate)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_CM_Create_File

Func _CM_Open_File(Byref $hFile, $iDesiredAccess, $pObject, $iShareAccess, $iFlags)
    Local $iResult, $pIO = _CM_Heap_Alloc(16)

    $iResult = DllCall("Ntdll.dll", "dword", "NtOpenFile", "hWnd*", 0, "dword", $iDesiredAccess, _
            "ptr", $pObject, "ptr", $pIO, "ulong", $iShareAccess, "ulong", $iFlags)
    $hFile = $iResult[1]
    _CM_Heap_Free($pIO)
    Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $iResult[0] = 0)
EndFunc ;==>_CM_Open_File

Func _CM_Open_Device(ByRef $hDevice, $hDevInst, $iDesiredAccess, $iShareAccess, $iFlags)
    Local $sPhysName, $pObject, $tBuffer, $iSysError, $fResult, $pBufferW

    $sPhysName = _CM_Get_DevNode_Registry_Property($hDevInst, 0xF)
    If $sPhysName = "" Then Return SetError(@error, 0, 0)

    $pObject = _LsaInitializeObjectAttributes($sPhysName)
    $fResult = _CM_Open_File($hDevice, $iDesiredAccess, $pObject, $iShareAccess, $iFlags)
    $iSysError = @error
    $tBuffer = DllStructCreate($tagOBJECT_ATTRIBUTES, $pObject)
    $pBufferW = DllStructGetData($tBuffer, "BufferW")
    _CM_Heap_Free($pBufferW)
    _CM_Heap_Free($pObject)
    _CM_Free_Variable($tBuffer)
    Return SetError($iSysError, 0, $fResult)
EndFunc ;==>_CM_Open_Device

Func _CM_Close_Handle($hHandle)
    Local $iResult
    $iResult = DllCall($Kernel32_DllHandleA, "int", "CloseHandle", "long", $hHandle)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_CM_Close_Handle

Func _CM_Assign_Var(ByRef $vVariable, $vValue = 0, $vReturn = "", $iError = 0, $iExtended = 0)
    $vVariable = $vValue
    Return SetError($iError, $iExtended, $vReturn)
EndFunc ;==>_CM_Assign_Var

Func _SetupDiOpenDeviceInterface($hDevs, $sDevicePath, ByRef $tSP_DEVIFINFO_DATA, $iFlags = 0)
    Local $iResult, $pSP_DEVIFINFO_DATA

    If Not IsDllStruct($tSP_DEVIFINFO_DATA) Then
        $tSP_DEVIFINFO_DATA = DllStructCreate($tagSP_DEV_INTERFACE_DATA)
        DllStructSetData($tSP_DEVIFINFO_DATA, "Size", 28)
    EndIf
    $pSP_DEVIFINFO_DATA = DllStructGetPtr($tSP_DEVIFINFO_DATA)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiOpenDeviceInterface", _
            "hWnd", $hDevs, "str", $sDevicePath, "dword", $iFlags, _
            "ptr", $pSP_DEVIFINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiOpenDeviceInterface


; #### FUNCTION ####
; ===================================================================================
; Name  : _SetupDiGetDeviceInterfaceDevInst
; Description   : Specifies the device path, retrieves the device instance handle.
; Parameter(s)  : $sDevicePath  - Device path, in string format.
; Return values : A value of non-zero indicates the handle to the device instance if succeeds.
; Author    : Pusofalse
; ===================================================================================
Func _SetupDiGetDeviceInterfaceDevInst($sDevicePath)
    Local $iResult, $hDevs, $tDevIfInfo, $tDevInfo, $hDevInst

    $hDevs = _SetupDiGetClassDevs($DIGCF_ALLCLASSES)
    Select
    Case _SetupDiOpenDeviceInterface($hDevs, $sDevicePath, $tDevIfInfo) = 0
        Return SetError(@error, 0, _SetupDiDestroyDeviceInfoList($hDevs) * 0)
    Case _SetupDiGetDeviceInterfaceDetail($hDevs, $tDevIfInfo, $tDevInfo) <> $sDevicePath
        Return SetError(@error, 0, _SetupDiDestroyDeviceInfoList($hDevs) * 0)
    EndSelect
    $hDevInst = DllStructGetData($tDevInfo, "DevInst")
    _CM_Assign_Var($tDevInfo)
    _CM_Assign_Var($tDevIfInfo)
    _SetupDiDestroyDeviceInfoList($hDevs)
    Return $hDevInst
EndFunc ;==>_SetupDiGetDeviceInterfaceDevInst

; #### FUNCTION ####
; ===================================================================================
; Name  : _SetupDiRemoveDevice
; Description   : This function removes a device.
; Parameter(s)  : $hDevs    - Supplies a device information set handle.
;       : $pSP_DEVINFO_DATA - A pointer to a SP_DEVINFO_DATA structure contains the device information element to be removed.
;       : $fForce   - A BOOL value. If device information element specified in $pSP_DEVINFO_DATA is a non-removable device and this value is set  to FALSE, the function failed with ERROR_INVALID_PARAMETER. Otherwise, this parameter is ignored.
; Return values : True indicates success, False indicates failure.
; Author    : Pusofalse
; ===================================================================================
Func _SetupDiRemoveDevice($hDevs, $pSP_DEVINFO_DATA, $fForce = False)
    Local $iResult, $hDevInst, $iNodeStat, $tDevInfo

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    If Not IsPtr($pSP_DEVINFO_DATA) Then Return SetError(87, 0, 0)

    $tDevInfo = DllStructCreate($tagSP_DEVICEINFO_DATA, $pSP_DEVINFO_DATA)
    $hDevInst = DllStructGetData($tDevInfo, "DevInst")
    If $hDevInst = 0 Then Return SetError(87, 0, 0)
    $iNodeStat = _SetupDiGetDevNodeStatus($hDevInst)
    If $fForce = False AND bitAND($iNodeStat, $DN_REMOVABLE) <> $DN_REMOVABLE Then
        Return SetError(87, 0, 0)
    EndIf

    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiRemoveDevice", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiRemoveDevice

Func _SetupDiUnremoveDevice($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiUnremoveDevice", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiUnremoveDevice

; #### FUNCTION ####
; ===================================================================================
; Name  : _SetupDiCreateDeviceDevs
; Description   : Creates a device information set for a device instance.
; Parameter(s)  : $sDeviceID    - Device instance identifier, in string format.
;       : $hDevs    - A variable receives the handle the device information set.
;       : $tDevInfo - A variable receives an SP_DEVINFO_DATA structure that contains the device information element.
;       : $vGUID    - A device class GUID, can be NULL.
;       : $hWnd - A window handle owns the handle the device information set, can be NULL.
; Return values : True indicates success, False indicates failure, in which case, @error is set to a system error code.
; Author    : Pusofalse
; ===================================================================================
Func _SetupDiCreateDeviceDevs($sDeviceID, ByRef $hDevs, ByRef $tDevInfo, $vGuid = "", $hWnd = 0)
    $hDevs = _SetupDiCreateDeviceInfoList($vGuid, $hWnd)
    If Number($hDevs) < 1 Then Return SetError(@error, 0, 0)
    If _SetupDiOpenDeviceInfo($hDevs, $sDeviceID, $tDevInfo) = 0 Then
        Return SetError(@error, 0, _SetupDiDestroyDeviceInfoList($hDevs) * 0)
    ElseIf _SetupDiSetSelectedDevice($hDevs, $tDevInfo) = 0 Then
        Return SetError(@error, 0, _SetupDiDestroyDeviceInfoList($hDevs) * 0)
    EndIf
    Return 1
EndFunc ;==>_SetupDiCreateDeviceDevs

; #### FUNCTION ####
; ===================================================================================
; Name  : _SetupDiDisableDevice
; Description   : Disables or enables a device instance.
; Parameter(s)  : $hDevs    - Handle to the device information set.
;       : $pSP_DEVINFO_DATA - A pointer to a SP_DEVINFO_DATA structure contains the device information element to be disabled.
;       : $fDisable - True to disable, False to enable, default to True.
; Return values : If succeeds, returns true. Otherwise returns False and sets @error to a system error code.
; Author    : Pusofalse
; ===================================================================================
Func _SetupDiDisableDevice($hDevs, $pSP_DEVINFO_DATA, $fDisable = True)
    Local $hDevInst, $iDevNode, $tInstParam, $tDevInfo

    If $hDevs = 0 Then Return SetError(87, 0, 0)
    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf

    $tDevInfo = DllStructCreate($tagSP_DEVICEINFO_DATA, $pSP_DEVINFO_DATA)
    $hDevInst = DllStructGetData($tDevInfo, "DevInst")
    If $hDevInst = 0 Then Return SetError(87, 0, 0)

    $iDevNode = _CM_Get_DevNode_Status($hDevInst)
    If ($fDisable = (@extended = 22)) Then Return SetError(85, 0, 1)
    If bitAND($iDevNode, $DN_DISABLEABLE) <> $DN_DISABLEABLE Then
        Return SetError(50, 0, 0)
    EndIf

    $tInstParam = DllStructCreate($tagSP_PROPCHANGE_PARAMS)
    DllStructSetData($tInstParam, "Size", 8)
    DllStructSetData($tInstParam, "DIFCode", $DIF_PROPERTYCHANGE)
    DllStructSetData($tInstParam, "Scope", $DICS_FLAG_GLOBAL)
    DllStructSetData($tInstParam, "HWProfile", 0)
    If $fDisable = True Then
        DllStructSetData($tInstParam, "State", $DICS_DISABLE)
        _CM_Set_DevNode_CSConfigFlags($hDevInst, $CSCONFIGFLAG_DISABLED)
    Else
        DllStructSetData($tInstParam, "State", $DICS_ENABLE)
        _CM_Set_DevNode_CSConfigFlags($hDevInst, $CSCONFIGFLAG_NONE)
    EndIf

    Select
    Case _SetupDiSetClassInstallParams($hDevs, $pSP_DEVINFO_DATA, $tInstParam, 20) = 0
        Return SetError(@error, _CM_Free_Variable($tInstParam), 0)
    Case _SetupDiChangeState($hDevs, $pSP_DEVINFO_DATA) = 0
        Return SetError(@error, _CM_Free_Variable($tInstParam), 0)
    Case Else
        Return SetError(@error, _CM_Free_Variable($tInstParam), 1)
    EndSelect
EndFunc ;==>_SetupDiDisableDevice

; #### FUNCTION ####
; ===================================================================================
; Name  : _CM_Format_Problem_Message
; Description   : Retrieves the problem message text for a problem code.
; Parameter(s)  : $iProblemCode - A value of problem code.
; Return values : Error message, or NULL if failed.
; Author    : Pusofalse
; ===================================================================================
Func _CM_Format_Problem_Message($iProblemCode)
    Local $sErrMsgs, $aErrMsgs, $sMsg

    If $iProblemCode = 0 Then Return "This deivce is working properly."
    If $iProblemCode < 1 OR $iProblemCode > 50 Then Return ""
    $sErrMsgs = "This device is not configured correctly. (Code 1)" & @LF & _
"Windows could not load the driver for this device because the computer is reporting two <bustype> bus types. (Code 2)" & @LF & _
"The driver for this device might be corrupted, or your system may be running low on memory or other resources. (Code 3)" & @LF & _
"This device is not working properly because one of its drivers may be bad, or your registry may be bad. (Code 4)" & @LF & _
"The driver for this device requested a resource that Windows does not know how to handle. (Code 5)" & @LF & _
"Another device is using the resources this device needs. (Code 6)" & @LF & _
"The drivers for this device need to be reinstalled. (Code 7)" & @LF & _
"Device failure: Try changing the driver for this device. If that doesn't work, see your hardware documentation. (Code 8)" & @LF & _
"Windows cannot identify this hardware because it does not have a valid hardware identification number. (Code 9)" & @LF & _
"This device cannot start. (Code 10)" & @LF & _
"Windows stopped responding while attempting to start this device, and therefore will never attempt to start this device again. (Code 11)" & @LF & _
"This device cannot find enough free resources that it can use. (Code 12)" & @LF & _
"This device is either not present, not working properly, or does not have all the drivers installed. (Code 13)" & @LF & _
"This device cannot work properly until you restart your computer. (Code 14)" & @LF & _
"This device is causing a resource conflict. (Code 15)" & @LF & _
"Windows cannot identify all the resources this device uses. (Code 16)" & @LF & _
"The driver information file (INF-file-name) is telling this child device to use a resource that the parent device does not have or recognize. (Code 17)" & @LF & _
"Reinstall the drivers for this device. (Code 18)" & @LF & _
"Windows cannot start this hardware device because its configuration information (in the registry) is incomplete or damaged. (Code 19)" & @LF & _
"Windows could not load one of the drivers for this device. (Code 20)" & @LF & _
"Windows is removing this device. (Code 21)" & @LF & _
"This device is disabled. (Code 22)" & @LF & _
"This display adapter is functioning correctly. (Code 23)" & @LF & _
"This device is not present, is not working properly, or does not have all its drivers installed. (Code 24)" & @LF & _
"Windows is in the process of setting up this device. (Code 25)" & @LF & _
"Windows is in the process of setting up this device. (Code 26)" & @LF & _
"Windows can't specify the resources for this device. (Code 27)" & @LF & _
"The drivers for this device are not installed. (Code 28)" & @LF & _
"This device is disabled because the firmware of the device did not give it the required resources. (Code 29)" & @LF & _
"This device is using an Interrupt Request (IRQ) resource that is in use by another device and cannot be shared. You must change the conflicting setting or remove the real-mode driver causing the conflict. (Code 30)" & @LF & _
"This device is not working properly because Windows cannot load the drivers required for this device. (Code 31)" & @LF & _
"A driver (service) for this device has been disabled. An alternate driver may be providing this functionality. (Code 32)" & @LF & _
"Windows cannot determine which resources are required for this device. (Code 33)" & @LF & _
"Windows cannot determine the settings for this device. Consult the documentation that came with this device and use the Resource tab to set the configuration. (Code 34)" & @LF & _
"Your computer's system firmware does not include enough information to properly configure and use this device. To use this device, contact your computer manufacturer to obtain a firmware or BIOS update. (Code 35)" & @LF & _
"This device is requesting a PCI interrupt but is configured for an ISA interrupt (or vice versa). Please use the computer's system setup program to reconfigure the interrupt for this device. (Code 36)" & @LF & _
"Windows cannot initialize the device driver for this hardware. (Code 37)"

$sErrMsgs &= @LF &"Windows cannot load the device driver for this hardware because a previous instance of the device driver is still in memory. (Code 38)" & @LF & _
"Windows cannot load the device driver for this hardware. The driver may be corrupted or missing. (Code 39)" & @LF & _
"Windows cannot access this hardware because its service key information in the registry is missing or recorded incorrectly (Code 40)" & @LF & _
"Windows successfully loaded the device driver for this hardware but cannot find the hardware device. (Code 41)" & @LF & _
"Windows cannot load the device driver for this hardware becuse there is a duplicate device already running in the system. (Code 42)" & @LF & _
"Windows has stopped this device because it has reported problems. (Code 43)" & @LF & _
"An application or service has shut down this hardware device. (Code 44)" & @LF & _
"Currently, this hardware device is not connected to the computer. (Code 45)" & @LF & _
"Windows cannot gain access to this hardware device because the operating system is in the process of shutting down. (Code 46)" & @LF & _
"Windows cannot use this hardware device because it has been prepared for 'safe removal', but it has not been removed from the computer. (Code 47)" & @LF & _
"The software for this device has been blocked from starting because it is known to have problems with Windows. Contact the hardware vendor for a new driver. (Code 48)" & @LF & _
"Windows cannot start new hardware devices because the system hive is too large (exceeds the Registry Size Limit). (Code 49)" & @LF & _
"Windows cannot apply all of the properties for this device. Device properties may include information that describes the device's capabilities and settings (such as security settings for example). (Code 50)"
    $aErrMsgs = StringSplit($sErrMsgs, @LF)
    $sMsg = $aErrMsgs[$iProblemCode]
    _CM_Free_Variable($aErrMsgs)
    Return SetError(_CM_Assign_Var($sErrMsgs, ""), "", $sMsg)
EndFunc ;==>_CM_Format_Problem_Message

Func _CM_Get_Version()
    Local $iResult = DllCall($SETUPAPI_DllHandle, "ushort", "CM_Get_Version")
    Return $iResult[0]
EndFunc ;==>_CM_Get_Version

Func _CM_Is_Version_Available($wVersion)
    Local $iResult = DllCall($SETUPAPI_DllHandle, "int", "CM_Is_Version_Available", "short", $wVersion)
    Return $iResult[0] <> 0
EndFunc ;==>CM_Is_Version_Available

Func _CM_Locate_DevNode($sDeviceID, $iFlags = $CM_LOCATE_DEVNODE_NORMAL)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Locate_DevNode", "dword*", 0, _
            "str", $sDeviceID, "ulong", $iFlags)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[1])
EndFunc ;==>_CM_Locate_DevNode

Func _CM_Reenumerate_DevNode($hDevInst, $iFlags = $CM_REENUMERATE_NORMAL)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Reenumerate_DevNode", _
            "dword", $hDevInst, "ulong", $iFlags)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[0] = 0)
EndFunc ;==>_CM_Reenumerate_DevNode

Func _SetupDiSelectBestCompatDrv($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult
    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiSelectBestCompatDrv", _
            "long", $hDevs, "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiSelectBestCompatDrv

Func _SetupDiSelectDevice($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult
    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiSelectDevice", _
            "long", $hDevs, "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>SetupDiSelectDevice

Func _SetupDiInstallDevice($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult
    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiInstallDevice", _
            "long", $hDevs, "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiInstallDevice

Func _SetupDiRegisterDeviceInfo($hDevs, $pSP_DEVINFO_DATA, $iFlags, ByRef $tSP_DEVINFO_DATA_DUP)
    Local $iResult, $pSP_DEVINFO_DATA_DUP

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    If Not IsDllStruct($tSP_DEVINFO_DATA_DUP) Then
        $tSP_DEVINFO_DATA_DUP = DllStructCreate($tagSP_DEVICEINFO_DATA)
        DllStructSetData($tSP_DEVINFO_DATA_DUP, "Size", 28)
    EndIf
    $pSP_DEVINFO_DATA_DUP = DllStructGetPtr($tSP_DEVINFO_DATA_DUP)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiRegisterDeviceInfo", "long", $hDevs, _
            "ptr", $pSP_DEVINFO_DATA, "dword", $iFlags, "ptr", 0, "ptr", 0, _
            "ptr", $pSP_DEVINFO_DATA_DUP)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiRegisterDeviceInfo

Func _CM_Set_DevNode_Problem($hDevInst, $iProblemCode)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Set_DevNode_Problem", _
            "dword", $hDevInst, "ulong", $iProblemCode, "ulong", 0)
    Return SetError($iResult[0], $iResult[0] = 0, $iResult[0] = 0)
EndFunc ;==>_CM_Set_DevNode_Problem

Func _SetupDiCreateDeviceInterface($hDevs, $pSP_DEVINFO_DATA, $vGUID, ByRef $tSP_DEVIFINFO_DATA, $sReference = "")
    Local $iResult, $pSP_DEVIFINFO_DATA, $tGUID, $pGUID
    If IsString($vGUID) Then
        If StringLeft($vGUID, 1) & StringRight($vGUID, 1) = "{}" Then
            $tGUID = _CM_GUID_From_String($vGUID)
            $pGUID = DllStructGetPtr($tGUID)
        ElseIf $vGuid <> "" Then
            $tGUID = _SetupDiClassGuidsFromName($vGUID)
            $pGUID = DllStructGetPtr($tGUID)
        EndIf
    ElseIf IsDllStruct($vGUID) Then
        $pGUID = DllStructGetPtr($vGUID)
    ElseIf IsPtr($vGUID) Then
        $pGUID = $vGUID
    Else
        $pGUID = 0
    EndIf
    If $pGUID = 0 Then Return SetError(87, 0, 0)

    Select
    Case IsDllStruct($pSP_DEVINFO_DATA)
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
        ContinueCase
    Case IsDllStruct($tSP_DEVIFINFO_DATA) = 0
        $tSP_DEVIFINFO_DATA = DllStructCreate($tagSP_DEV_INTERFACE_DATA)
        DllStructSetData($tSP_DEVIFINFO_DATA, "Size", 28)
    EndSelect
    If Not IsPtr($pSP_DEVINFO_DATA) Then Return SetError(87, 0, 0)
    $pSP_DEVIFINFO_DATA = DllStructGetPtr($tSP_DEVIFINFO_DATA)

    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiCreateDeviceInterface", _
            "long", $hDevs, "ptr", $pSP_DEVINFO_DATA, _
            "ptr", $pGUID, "str", $sReference, "dword", 0, _
            "ptr", $pSP_DEVIFINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), _CM_Free_Variable($tGUID), $iResult[0])
EndFunc ;==>_SetupDiCreateDeviceInterface

Func _SetupDiEnumDriverInfo($hDevs, $pSP_DEVINFO_DATA, $iIndex, ByRef $tSP_DRVINFO_DATA)
    Local $iResult, $pSP_DRVINFO_DATA, $iType = $SPDIT_CLASSDRIVER

    Select
    Case IsDllStruct($pSP_DEVINFO_DATA)
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
        ContinueCase
    Case IsDllStruct($tSP_DRVINFO_DATA) = 0
        $tSP_DRVINFO_DATA = DllStructCreate($tagSP_DRVINFO_DATA)
        DllStructSetData($tSP_DRVINFO_DATA, 1, DllStructGetSize($tSP_DRVINFO_DATA) - 12)
    EndSelect
    $pSP_DRVINFO_DATA = DllStructGetPtr($tSP_DRVINFO_DATA)
    If IsPtr($pSP_DEVINFO_DATA) Then $iType = $SPDIT_COMPATDRIVER

    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiEnumDriverInfo", "long", $hDevs, _
            "ptr", $pSP_DEVINFO_DATA, "dword", $iType, _
            "dword", $iIndex, "ptr", $pSP_DRVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiEnumDriverInfo

Func _SetupDiDestroyDriverInfoList($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult, $iType = $SPDIT_CLASSDRIVER

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    If IsPtr($pSP_DEVINFO_DATA) Then $iType = $SPDIT_COMPATDRIVER
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiDestroyDriverInfoList", _
            "long", $hDevs, "ptr", $pSP_DEVINFO_DATA, "dword", $iType)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiDestroyDriverInfoList


Func _SetupDiGetClassBitmapIndex($vGUID)
    Local $iResult, $pGUID

    If IsString($vGUID) Then
        If StringLeft($vGUID, 1) & StringRight($vGUID, 1) = "{}" Then
            $tGUID = _CM_GUID_From_String($vGUID)
            $pGUID = DllStructGetPtr($tGUID)
        ElseIf $vGuid <> "" Then
            $tGUID = _SetupDiClassGuidsFromName($vGUID)
            $pGUID = DllStructGetPtr($tGUID)
        EndIf
    ElseIf IsDllStruct($vGUID) Then
        $pGUID = DllStructGetPtr($vGUID)
    ElseIf IsPtr($vGUID) Then
        $pGUID = $vGUID
    Else
        $pGUID = 0
    EndIf

    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetClassBitmapIndex", _
            "ptr", $pGUID, "uint*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[2])
EndFunc ;==>_SetupDiGetClassBitmapIndex

Func _SetupDiGetClassImageList(ByRef $tSP_CLASSIMAGE_DATA)
    Local $iResult, $pSP_CLASSIMAGE_DATA

    If Not IsDllStruct($tSP_CLASSIMAGE_DATA) Then
        $tSP_CLASSIMAGE_DATA = DllStructCreate($tagSP_CLASSIMAGE_DATA)
        DllStructSetData($tSP_CLASSIMAGE_DATA, "Size", 12)
    EndIf
    $pSP_CLASSIMAGE_DATA = DllStructGetPtr($tSP_CLASSIMAGE_DATA)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetClassImageList", _
            "ptr", $pSP_CLASSIMAGE_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], DllStructGetData($tSP_CLASSIMAGE_DATA, 2))
EndFunc ;==>_SetupDiGetClassImageList

Func _SetupDiDestroyClassImageList($pSP_CLASSIMAGELIST_DATA)
    Local $iResult
    If IsDllStruct($pSP_CLASSIMAGELIST_DATA) Then
        $pSP_CLASSIMAGELIST_DATA = DllStructGetPtr($pSP_CLASSIMAGELIST_DATA)
    EndIf
    If Not IsPtr($pSP_CLASSIMAGELIST_DATA) Then Return SetError(87, 0, 0)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiDestroyClassImageList", _
            "ptr", $pSP_CLASSIMAGELIST_DATA)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[0])
EndFunc ;==>_SetupDiDestroyClassImageList

Func _SetupDiGetClassImageIndex($vGUID, $pSP_CLASSIMAGE_DATA = 0)
    Local $iResult, $tSP_CLASSIMAGE_DATA, $pGUID, $iError, $tGUID

    If $pSP_CLASSIMAGE_DATA = 0 Then
        _SetupDiGetClassImageList($tSP_CLASSIMAGE_DATA)
        $pSP_CLASSIMAGE_DATA = DllStructGetPtr($tSP_CLASSIMAGE_DATA)
    ElseIf IsDllStruct($pSP_CLASSIMAGE_DATA) Then
        $pSP_CLASSIMAGE_DATA = DllStructGetPtr($pSP_CLASSIMAGE_DATA)
    EndIf
    If Not IsPtr($pSP_CLASSIMAGE_DATA) Then Return SetError(87, 0, -1)

    If IsString($vGUID) Then
        If StringLeft($vGUID, 1) & StringRight($vGUID, 1) = "{}" Then
            $tGUID = _CM_GUID_From_String($vGUID)
            $pGUID = DllStructGetPtr($tGUID)
        ElseIf $vGuid <> "" Then
            $tGUID = _SetupDiClassGuidsFromName($vGUID)
            $pGUID = DllStructGetPtr($tGUID)
        EndIf
    ElseIf IsDllStruct($vGUID) Then
        $pGUID = DllStructGetPtr($vGUID)
    ElseIf IsPtr($vGUID) Then
        $pGUID = $vGUID
    Else
        $pGUID = 0
    EndIf
    If $pGUID = 0 Then Return SetError(87, 0, -1)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetClassImageIndex", _
            "ptr", $pSP_CLASSIMAGE_DATA, "ptr", $pGUID, "int*", -1)
    $iError = _CM_Get_Last_Error()
    If IsDllStruct($tSP_CLASSIMAGE_DATA) Then
        _SetupDiDestroyClassImageList($pSP_CLASSIMAGE_DATA)
    EndIf
    $tGUID = 0
    Return SetError($iError, $iResult[0], $iResult[3])
EndFunc ;==>_SetupDiGetClassImageIndex

Func _CM_Get_Volume_Path($sVolumeGUID)
    Local $iResult
    $iResult = DllCall($Kernel32_DllHandleA, "int", "GetVolumePathNamesForVolumeName", _
            "str", $sVolumeGUID, "str", "", "dword", 32, "dword*", 0)
    If StringRight($iResult[2], 1) = "\" Then $iResult[2] = StringTrimRight($iResult[2], 1)
    Return $iResult[2]
EndFunc ;==>_CM_Get_Volume_Path

Func _CM_Get_Volume_Name($sVolume)
    Local $iResult
    If StringRight($sVolume, 1) <> "\" Then $sVolume &= "\"
    $iResult = DllCall($Kernel32_DllHandleA, "int", "GetVolumeNameForVolumeMountPoint", _
            "str", $sVolume, "str", "", "dword", 260)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[2])
EndFunc ;==>_CM_Get_Volume_Name

Func _CM_Scan_Device_Changes($sDevNode = "")
    Local $hDevInst, $fResult

    $hDevInst = _CM_Locate_DevNode($sDevNode)
    If $hDevInst < 1 Then Return SetError(@error, 0, 0)
    $fResult = _CM_Reenumerate_DevNode($hDevInst)
    Return SetError(@error, 0, $fResult)
EndFunc ;==>_CM_Scan_Device_Changes

Func _CM_Scan_Device_Changes_Ex($hMachine, $sDevNode = "")
    Local $hDevIntst, $iResult

    $hDevInst = _CM_Locate_DevNode_Ex($sDevNode, $hMachine)
    If $hDevInst < 1 Then Return SetError(@error, 0, 0)
    $iResult = _CM_Reenumerate_DevNode_Ex($hDevInst, $hMachine)
    Return SetError(@error, 0, $iResult)
EndFunc ;==>_CM_Scan_Device_Changes_Ex


Func _CM_Query_Device_Problem($hDevInst)
    Return _CM_Query_Device_Problem_Ex($hDevInst, 0)
EndFunc ;==>_CM_Query_Device_Problem

Func _CM_Query_Device_Problem_Ex($hDevInst, $hMachine)
    Local $iDevNodeStat, $iProblemCode

    $iDevNodeStat = _CM_Get_DevNode_Status_Ex($hDevInst, $hMachine)
    $iProblemCode = @extended
    If bitAND($iDevNodeStat, $DN_HAS_PROBLEM) = $DN_HAS_PROBLEM Then
        Return _CM_Format_Problem_Message($iProblemCode)
    ElseIf bitAND($iDevNodeStat, $DN_NEED_RESTART) = $DN_NEED_RESTART Then
        Return "A system reboot is requested to finish with the device installation."
    EndIf
    Return "This device is working properly."
EndFunc ;==>_CM_Query_Device_Problem_Ex

Func _SetupDiGetINFClass($sInfFileName, ByRef $sGUID)
    Local $iResult, $pGUID = _CM_Heap_Alloc(16)

    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetINFClass", "str", $sInfFileName, _
            "ptr", $pGUID, "str", "", "dword", 0, "dword*", 0)
    If $iResult[5] = 0 Then Return SetError(_CM_Get_Last_Error(), 0, "")
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetINFClass", "str", $sInfFileName, _
            "ptr", $pGUID, "str", "", "dword", $iResult[5], "dword*", 0)
    $sGUID = _CM_String_From_GUID($pGUID)
    Return SetExtended(_CM_Heap_Free($pGuid), $iResult[3])
EndFunc ;==>_SetupDiGetINFClass

Func _SetupDiLoadDeviceIcon($hDevs, $pSP_DEVINFO_DATA, $iWidth = 32, $iHeight = 32)
    Local $iResult
    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiLoadDeviceIcon", "ptr", $hDevs, _
            "ptr", $pSP_DEVINFO_DATA, "uint", $iWidth, "uint", $iHeight, _
            "dword", 0, "hWnd*", 0)
    If @error Then Return SetError(1, 0, -1)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[6])
EndFunc ;==>_SetupDiLoadDeviceIcon

Func _SetupDiLoadClassIcon($vGUID)
    Local $iResult, $tGUID, $pGUID

    If IsString($vGUID) Then
        If StringLeft($vGUID, 1) & StringRight($vGUID, 1) = "{}" Then
            $tGUID = _CM_GUID_From_String($vGUID)
        Else
            $tGUID = _SetupDiClassGuidsFromName($vGUID)
        EndIf
        $pGUID = DllStructGetPtr($tGUID)
    ElseIf IsDllStruct($vGUID) Then
        $pGUID = DllStructGetPtr($vGUID)
    ElseIf IsPtr($vGUID) Then
        $pGUID = $vGUID
    Else
        Return SetError(87, 0, -1)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiLoadClassIcon", "ptr", $pGUID, _
            "hWnd*", 0, "int*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[3], $iResult[2])
EndFunc ;==>_SetupDiLoadClassIcon


Func _SetupDiOpenDevRegKey($hDevs, $pSP_DEVINFO_DATA, $iAccess, $iScope = 1, $iHwProfile = 0, $iKeyType = 1)
    Local $iResult
    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "hWnd", "SetupDiOpenDevRegKey", "ptr", $hDevs, _
            "ptr", $pSP_DEVINFO_DATA, "dword", $iScope, "dword", $iHwProfile, _
            "dword", $iKeyType, "dword", $iAccess)
    If $iResult[0] = 0 Then Return SetError(_CM_Get_Last_Error(), 0, 0)
    Return $iResult[0]
EndFunc ;==>_SetupDiOpenDevRegKey

Func _CM_Create_Device_Devs($hDevInst, ByRef $hDevs, ByRef $tSP_DEVINFO_DATA)
    Local $sDevInstID = _CM_Get_Device_ID($hDevInst)
    If _SetupDiCreateDeviceDevs($sDevInstID, $hDevs, $tSP_DEVINFO_DATA) = 0 Then
        Return SetError(@error, 0, 0)
    EndIf
    Return 1
EndFunc ;==>_CM_Create_Device_Devs

Func _CM_Create_Device_Devs_Ex($hDevInst, ByRef $hDevs, ByRef $tSP_DEVINFO_DATA, $sMachine)
    Local $hMachine, $sDeviceID, $fResult

    $hMachine = _CM_Connect_Machine($sMachine)
    If @error Then Return SetError(@error, 0, 0)
    $sDeviceID = _CM_Get_Device_ID_Ex($hDevInst, $hMachine)
    If @error Then Return SetError(@error, _CM_Disconnect_Machine($hMachine), 0)
    _CM_Disconnect_Machine($hMachine)

    If _SetupDiCreateDeviceDevsEx($sDeviceID, $hDevs, $tSP_DEVINFO_DATA, $sMachine) Then
        Return 1
    Else
        Return SetError(@error, 0, _SetupDiApiBufferFree($tSP_DEVINFO_DATA) * 0)
    EndIf
EndFunc ;==>_CM_Create_Device_Devs_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _SetupDiGetClassDevsEx
; Description   : This function obtains a device information set on a local or a remote system.
; Parameter(s)  : $iFlags   - One or more of the following values:
;       : - DIGCF_ALLCLASSES: Return a list of installed devices for the specified device setup classes or device interface classes.
;       : - DIGCF_DEVICEINTERFACE: Return devices that support device interfaces for the specified device interface classes. This flag must be set in the $iFlags parameter if the $sEnumerator parameter specifies a device instance ID.
;       : - DIGCF_DEFAULT: Return only the device that is associated with the system default device interface, if one is set, for the specified device interface classes.
;       : - DIGCF_PRESENT: Return only devices that are currently present.
;       : - DIGCF_PROFILE: Return only devices that are a part of the current hardware profile.
;       : $vGUID    - Device class GUID, for examples, specifiy 'NET' for this parameter to retrieve the net adapters device information set.
;       : $sEnumerator -  A pointer to a NULL-terminated string that specifies: An identifier (ID) of a Plug and Play (PnP) enumerator. This ID can either be the enumerator¡¯s globally unique identifier (GUID) or symbolic name. For example, ¡°PCI¡± can be used to specify the PCI PnP enumerator. Other examples of symbolic names for PnP enumerators include ¡°USB¡±, ¡°PCMCIA¡±, and ¡°SCSI¡±. A PnP device instance IDs. When specifying a PnP device instance ID, DIGCF_DEVICEINTERFACE must be set in the $Flags parameter. This parameter is optional and can be set to NULL.
;       : $sMachine - Specifies the system on which the function executes, default to local.
;       : $hDevs    - The handle to an existing device information set to which SetupDiGetClassDevsEx adds the requested device information elements. This parameter is optional and can be set to NULL.
; Return values : Returns a handle to the device information set contains the device information elements, if succeeds; otherwise returns INVALID_HANDLE_VALUE (-1), in which case, @error is set to a system error code.
; Author    : Pusofalse
; ====================================================================================
Func _SetupDiGetClassDevsEx($iFlags, $vGUID = "", $sEnumerator = "", Const $sMachine = "", $hDevs = 0)
    Local $iResult, $tGUID, $pGUID, $sType = "ptr"

    If $sEnumerator <> "" Then $sType = "str"
    If IsString($vGUID) Then
        If StringLeft($vGUID, 1) & StringRight($vGUID, 1) = "{}" Then
            $tGUID = _CM_GUID_From_String($vGUID)
            $pGUID = DllStructGetPtr($tGUID)
        ElseIf $vGUID <> "" Then
            $tGUID = _SetupDiClassGUIDsFromName($vGUID)
            $pGUID = DllStructGetPtr($tGUID)
        EndIf
    ElseIf IsDllStruct($vGUID) Then
        $pGUID = DllStructGetPtr($vGUID)
    ElseIf IsPtr($vGUID) Then
        $pGUID = $vGUID
    Else
        $pGUID = 0
    EndIf

    $iResult = DllCall($SETUPAPI_DllHandle, "ptr", "SetupDiGetClassDevsEx", "ptr", $pGUID, _
            $sType, $sEnumerator, "hWnd", 0, "dword", $iFlags, _
            "ptr", $hDevs, "str", $sMachine, "ptr", 0)
    If $iResult[0] = 0xFFFFFFFF Then Return SetError(_CM_Get_Last_Error(), 0, -1)
    Return $iResult[0]
EndFunc ;==>_SetupDiGetClassDevsEx

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Connect_Machine
; Description   : The _CM_Connect_Machine function creates a connection to a remote machine.
; Parameter(s)  : $sUncSystem   - System name, in UNC format.
; Return values : Returns a machine handle if success, else returns INVALID_HANDLE_VALUE (0xFFFFFFFF).
; Author    : Pusofalse
; ====================================================================================
Func _CM_Connect_Machine($sUncSystem)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Connect_Machine", "str", $sUncSystem, "ptr*", 0)
    Return SetError($iResult[0], 0, $iResult[2])
EndFunc ;==>_CM_Connect_Machine

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Disconnect_Machine
; Description   : The _CM_Disconnect_Machine function removes a connection to a remote machine.
; Parameter(s)  : $hMachine - A machine handle, returned by _CM_Connect_Machine.
; Return values : True indicates success, False otherwise.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Disconnect_Machine($hMachine)
    Local $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Disconnect_Machine", "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Disconnect_Machine

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Child_Ex
; Description   : The _CM_Get_Child_Ex function is used to retrieve a device instance handle to the first child node of a specified device node (devnode) in a local or a remote machine's device tree.
; Parameter(s)  : $hDevInst - A device instance handle under which the child node is retrieved.
;       : $hMachine - A machine handle.
; Return values : Returns the device node handle if succeeds, else returns zero and sets @error to a configuration manager error code.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Child_Ex($hDevInst, $hMachine)
    Local $iResult
    $iResult  = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Child_Ex", "dword*", 0, _
            "dword", $hDevInst, "ulong", 0, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Get_Child_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Depth_Ex
; Description   : The function is used to obtain the depth of a specified device node (devnode) within a local or a remote machine's device tree.
; Parameter(s)  : $hDevInst - A handle to a device instance.
;       : $hMachine - A handle to a machine.
; Return values : If success, returns the depth value, else @error is set to non-zero.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Depth_Ex($hDevInst, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Depth_Ex", "ulong*", 0, _
            "dword", $hDevInst, "ulong", 0, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Get_Depth_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Parent_Ex
; Description   : This function obtains a device instance handle to the parent node of a specified device node (devnode) in a local or a remote machine's device tree.
; Parameter(s)  : $hDevInst - A device instance handle.
;       : $hMachine - A machine handle.
; Return values : Retrieves the parent device node handle if success, otherwise returns zero and sets @error to a configuration manager error code.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Parent_Ex($hDevInst, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Parent_Ex", "dword*", 0, _
            "dword", $hDevInst, "ulong", 0, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Get_Parent_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Sibling_Ex
; Description   : This function obtains a device instance handle to the sibling node of a specified device node (devnode) in a local or a remote machine's device tree.
; Parameter(s)  : $hDevInst - A handle to a device instance.
;       : $hMachine - A handle to a machine.
; Return values : A device instance handle indicates success, else returns zero and sets @error to a CR_* value.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Sibling_Ex($hDevInst, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Sibling_Ex", "dword*", 0, _
            "dword", $hDevInst, "ulong", 0, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Get_Sibling_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Version_Ex
; Description   : The _CM_Get_Version_Ex function returns version 4.0 of the Plug and Play (PNP) Configuration Manager DLL (Cfgmgr32.dll) for a local or a remote machine.
; Parameter(s)  : $hMachine - Supplies a machine handle, returned by _CM_Connect_Machine.
; Return values : If the function succeeds, it returns the major revision number in the high-order byte and the minor revision number in the low-order byte. Version 4.0 is returned as 0x0400. Version 4.0 is supported by default by Microsoft Windows NT 4.0 and later versions of Windows. If an internal error occurs, the function returns 0x0000. Call GetLastError to obtain the error code for the failure.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Version_Ex($hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "short", "CM_Get_Version_Ex", "ptr", $hMachine)
    Return $iResult[0]
EndFunc ;==>_CM_Get_Version_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Locate_DevNode_Ex
; Description   : Obtains a device instance handle to the device node that is associated with a specified device instance identifier, on a local machine or a remote machine.
; Parameter(s)  : $sDevInstID - A device instance ID string on which the device instance handle is returned, a value of NULL indicates the root of the device tree.
;       : $hMachine - A machine handle.
;       : $iFlags   - One of the following values:
;       :   - CM_LOCATE_DEVNODE_NORMAL: The function retrieves the device instance handle for the specified device only if the device is currently configured in the device tree.
;       :   - CM_LOCATE_DEVNODE_PHANTOM: The function retrieves a device instance handle for the specified device if the device is currently configured in the device tree or the device is a nonpresent device that is not currently configured in the device tree.
;       :   - CM_LOCATE_DEVNODE_CANCELREMOVE: The function retrieves a device instance handle for the specified device if the device is currently configured in the device tree or in the process of being removed for the device tree. If the device is in the process of being removed, the function cancels the removal of the device.
; Return values : If succeeds, returns the specified device instance handle, else returns zero.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Locate_DevNode_Ex($sDevInstID, $hMachine, $iFlags = $CM_LOCATE_DEVNODE_NORMAL)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Locate_DevNode_Ex", "dword*", 0, _
            "str", $sDevInstID, "ulong", $iFlags, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Locate_DevNode_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Reenumerate_DevNode_Ex
; Description   :  The _CM_Reenumerate_DevNode_Ex function enumerates the devices identified by a specified device node and all of its children.
; Parameter(s)  : $hDevInst - A device instance handle.
;       : $hMachine - A machine handle.
;       : $iFlags - Can be a combination of the following values:
;       :   - CM_REENUMERATE_ASYNCHRONOUS: Reenumeration should occur asynchronously. The call to this function returns immediately after the PnP manager receives the reenumeration request. If this flag is set, the CM_REENUMERATE_SYNCHRONOUS flag should not also be set.
;       :   - CM_REENUMERATE_NORMAL: Specifies default reenumeration behavior, in which reenumeration occurs synchronously. This flag is currently equivalent to CM_REENUMERATE_SYNCHRONOUS.
;       :   - CM_REENUMERATE_RETRY_INSTALLATION: Specifies that Plug and Play should make another attempt to install any devices in the specified subtree that have been detected but are not yet configured, or are marked as needing reinstallation, or for which installation must be completed. This flag can be set along with either the CM_REENUMERATE_SYNCHRONOUS flag or the CM_REENUMERATE_ASYNCHRONOUS flag. This flag must be used with extreme caution, because it can cause the PnP manager to prompt the user to perform installation of any such devices. Currently, only components such as Device Manager and Hardware Wizard use this flag, to allow the user to retry installation of devices that might already have been detected but are not currently installed.
; Return values : Returns True if success, else returns false, @error is set to the reason of the failure.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Reenumerate_DevNode_Ex($hDevInst, $hMachine, $iFlags = $CM_REENUMERATE_NORMAL)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Reenumerate_DevNode_Ex", _
            "dword", $hDevInst, "ulong", $iFlags, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[0] = 0)
EndFunc ;==>_CM_Reenumerate_DevNode_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Is_Version_Available_Ex
; Description   : The function indicates whether a specified version of the Plug and Play (PNP) Configuration Manager DLL (Cfgmgr32.dll) is supported by a local or a remote machine.
; Parameter(s)  : $wVersion - Identifies a version of the configuration manager. The supported version of the configuration manager corresponds directly to the operating system version. The major version is specified by the high-order byte and the minor version is specified by the low-order byte. For example, 0x0400 specifies version 4.0, which is supported by default by Microsoft Windows NT 4.0 and later versions of Windows. Version 0x0501 specifies version 5.1, which is supported by Windows XP and later versions of Windows.
;       : $hMachine - A machine handle.
; Return values : The function returns TRUE if the function can connect to the specified machine and if the machine supports the specified version. Otherwise, the function returns FALSE.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Is_Version_Available_Ex($wVersion, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Is_Version_Available_Ex", _
            "short", $wVersion, "ptr", $hMachine)
    Return $iResult[0] <> 0
EndFunc ;==>_CM_Is_Version_Available_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Device_ID_Ex
; Description   : The function retrieves the device instance ID for a specified device instance, on a local or a remote machine.
; Parameter(s)  : $hDevInst - A handle to a device instance.
;       : $hMachine - A handle to a machine.
; Return values : A device instance ID string is returned if success, else returns NULL and sets @error to a CR_* error code.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Device_ID_Ex($hDevInst, $hMachine)
    Local $iResult, $tBuffer, $pBuffer, $iLength

    $iLength = _CM_Get_Device_ID_Size_Ex($hDevInst, $hMachine) + 1
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Device_ID_Ex", "dword", $hDevInst, _
            "str", "", "ulong", $iLength, "ulong", 0, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[2])
EndFunc ;==>_CM_Get_Device_ID_Ex

; #### FUNCTION ####
; ================================================================================
; Name  : _CM_Get_Device_ID_By_Name_Ex
; Description   : Specifies a device friendly name (or device description), retrieves the device instance ID binds to this name.
; Parameter(s)  : $sFriendlyName    - Specifies a device friendly name, or description.
;       : $fMatchAll    - A value of TRUE indicates the full words is matched, default to TRUE.
;       : $sDeviceID    - Device identifier string of the device from which the match is started, can be NULL.
;       : $hMachine - Supplies a machine handle, on which the function to execute, default to local.
; Return values : If a device instance is matched, the return value is set to the device instance identifier, otherwise returns NULL.
; Author    : Pusofalse
; ================================================================================
Func _CM_Get_Device_ID_By_Name_Ex($sFriendlyName, $fMatchAll = True, $sDeviceID = "", $hMachine = 0)
    Local $aChild, $sDescr, $sName, $hDevInst, $sVal

    If IsString($sDeviceID) = 0 Then
        $hDevInst = $sDeviceID
    Else
        $hDevInst = _CM_Locate_DevNode_Ex($sDeviceID, $hMachine)
    EndIf
    $aChild = _CM_Enumerate_Children_Ex($hDevInst, $hMachine)

    For $i = 1 To $aChild[0]
        $hDevInst = _CM_Locate_DevNode_Ex($aChild[$i], $hMachine)
        $sDescr = _CM_Get_DevNode_Registry_Property_Ex($hDevInst, 1, $hMachine)
        $sName = _CM_Get_DevNode_Registry_Property_Ex($hDevInst, 0xD, $hMachine)
        If $sName <> "" Then $sDescr = $sName
        If $sDescr = "" Then $sDescr = "[Unknown device]"
        If $fMatchAll Then
            If $sDescr = $sFriendlyName Then Return $aChild[$i]
        Else
            If StringInStr($sDescr, $sFriendlyName) Then Return $aChild[$i]
        EndIf
        $sVal = _CM_Get_Device_ID_By_Name_Ex($sFriendlyName, $fMatchAll, $hDevInst, $hMachine)
        If $sVal <> "" Then Return $sVal
    Next
    Return ""
EndFunc ;==>_CM_Get_Device_ID_By_Name_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Device_ID_Size_Ex
; Description   : The function retrieves the buffer size required to hold a device instance identifier for a device instance on a local or a remote machine.
; Parameter(s)  : $hDevInst - A device instance handle.
;       : $hMachine - A machine handle.
; Return values : Returns the buffer length required if success, else returns zero and sets @error to a CR_* error code.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Device_ID_Size_Ex($hDevInst, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Device_ID_Size_Ex", "ulong*", 0, _
            "dword", $hDevInst, "ulong", 0, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Get_Device_ID_Size_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Device_ID_List_Ex
; Description   : The function retrieves a list of device instance identifiers for the device instances on a local or a remote machine.
; Parameter(s)  : $hMachine - Supplies a machine handle that is returned by _CM_Connect_Machine function.
;       : $sFilter  - See the following description of the $iFlags.
;       : $iFlags   - One of the following caller-supplied bit flags that specifies search filters:
;       : - CM_GETIDLIST_FILTER_BUSRELATIONS: If this flag is set, $sFilter must specify a device instance identifier. The function returns device instance IDs for the bus relations of the specified device instance.
;       : - CM_GETIDLIST_FILTER_CLASS (Windows 7 and later versions of Windows): If this flag is set, $sFilter contains a string that specifies a device setup class GUID. The returned list contains device instances for which the property (referenced by the CM_DRP_CLASSGUID constant) matches the specified device setup class GUID.
;       : - CM_GETIDLIST_FILTER_PRESENT (Windows 7 and later versions of Windows): If this flag is set, the returned list contains only device instances that are currently present on the system. This value can be combined with other ulFlags values, such as CM_GETIDLIST_FILTER_CLASS.
;       : - CM_GETIDLIST_FILTER_TRANSPORTRELATIONS (Windows 7 and later versions of Windows): If this flag is set, $sFilter must specify the device instance identifier of a composite devnode. The function returns the device instance identifiers of the devnodes that represent the transport relations of the specified composite devnode.
;       : - CM_GETIDLIST_DONOTGENERATE: Used only with CM_GETIDLIST_FILTER_SERVICE. If set, and if the device tree does not contain a devnode for the specified service, this flag prevents the function from creating a devnode for the service.
;       : - CM_GETIDLIST_FILTER_EJECTRELATIONS: If this flag is set, $sFilter must specify a device instance identifier. The function returns device instance IDs for the ejection relations of the specified device instance.
;       : - CM_GETIDLIST_FILTER_ENUMERATOR: If this flag is set, $sFilter must specify the name of a device enumerator, optionally followed by a device identifier (ID). The string format is EnumeratorName\<DeviceID>, such as ROOT or ROOT\*PNP0500. If $sFilter supplies only an enumerator name, the function returns device Instance IDs for the instances of each device associated with the enumerator. Enumerator names can be obtained by calling _CM_Enumerate_Enumerators. If $sFilter supplies both an enumerator and a device ID, the function returns device instance IDs only for the instances of the specified device that is associated with the enumerator.
;       : - CM_GETIDLIST_FILTER_NONE: If this flag is set, $sFilter is ignored, and a list of all devices on the system is returned.
;       : - CM_GETIDLIST_FILTER_POWERRELATIONS: If this flag is set, $sFilter must specify a device instance identifier. The function returns device instance IDs for the power relations of the specified device instance.
;       : - CM_GETIDLIST_FILTER_REMOVALRELATIONS: If this flag is set, $sFilter must specify a device instance identifier. The function returns device instance IDs for the removal relations of the specified device instance.
;       : - CM_GETIDLIST_FILTER_SERVICE: If this flag is set, $sFilter must specify the name of a Microsoft Windows service (typically a driver). The function returns device instance IDs for the device instances controlled by the specified service.
;       : - If no search filter flag is specified, the function returns all device instance IDs for all device instances, default is CM_GETIDLIST_FILTER_NONE.
; Return values : A list contains the device instance IDs string, value of $aArray[0] is set to the number of entries.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Device_ID_List_Ex($hMachine, $sFilter = "", $iFlags = $CM_GETIDLIST_FILTER_NONE)
    Local $iResult, $iLength, $tBuffer, $pBuffer, $iPointer, $aResult[1] = [0], $iLength2, $pBuffer1

    $iLength = _CM_Get_Device_ID_List_Size_Ex($hMachine, $sFilter, $iFlags) + 1
    $pBuffer = _CM_Heap_Alloc($iLength)

    Do
        $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Device_ID_List_Ex", "str", $sFilter, _
            "ptr", $pBuffer, "ulong", $iLength, "ulong", $iFlags, "ptr", $hMachine)
        If $iResult[0] = $CR_BUFFER_SMALL Then
            _CM_Heap_Free($pBuffer)
            $iLength += 2
            $pBuffer = _CM_Heap_Alloc($iLength + 128)
        EndIf
    Until   $iResult[0] <> $CR_BUFFER_SMALL
    $pBuffer1 = $pBuffer

    While $iPointer < $iLength
        $tBuffer = DllStructCreate("char DeviceID[512]", $pBuffer)
        $aResult[0] += 1
        Redim $aResult[$aResult[0] + 1]
        $aResult[$aResult[0]] = DllStructGetData($tBuffer, "DeviceID")
        If $aResult[$aResult[0]] = "" Then ExitLoop
        $iLength2 = StringLen($aResult[$aResult[0]]) + 1
        $pBuffer += $iLength2
        $iPointer += $iLength2
        $tBuffer = 0
    WEnd
    _CM_Heap_Free($pBuffer1)
    If $aResult[0] = 0 Then Return SetError($iResult[0], 0, $aResult)
    $aResult[0] -= 1
    Redim $aResult[$aResult[0] + 1]
    Return SetError($iResult[0], 0, $aResult)
EndFunc ;==>_CM_Get_Device_ID_List_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Device_ID_List_Size_Ex
; Description   : Retrieves the buffer length required to hold a list of device instance identifiers for a local or remote system's device tree.
; Parameter(s)  : $hMachine - A value of machine handle.
;       : $sFilter, $iFlags - See _CM_Get_Device_ID_List_Ex function for details of these 2 parameters.
; Return values : Returns the length required if success, else returns zero, @error is set to the reason of the failure.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Device_ID_List_Size_Ex($hMachine, $sFilter = "", $iFlags = $CM_GETIDLIST_FILTER_NONE)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Device_ID_List_Size_Ex", "ulong*", 0, _
            "str", $sFilter, "ulong", $iFlags, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Get_Device_ID_List_Size_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_DevNode_Status_Ex
; Description   : The function obtains the status of a device instance from its device node (devnode) on a local or a remote machine's device tree.
; Parameter(s)  : $hDevInst - Supplies a device instance handle.
;       : $hMachine - Supplies a machine handle, that is returned by _CM_Connect_Machine.
; Return values : A bitwise OR of DN_* constants is returned if success.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_DevNode_Status_Ex($hDevInst, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_DevNode_Status_Ex", "ulong*", 0, _
            "ulong*", 0, "dword", $hDevInst, "ulong", 0, "ptr", $hMachine)
    Return SetError($iResult[0], $iResult[2], $iResult[1])
EndFunc ;==>_CM_Get_DevNode_Status_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Enumerate_Classes_Ex
; Description   : This function lists a local or a remote machine's installed device classes.
; Parameter(s)  : $sMachine - A system name, in UNC format.
; Return values : An array in the following form is returned if succeeds:
;       : $aArray[0][0] - The number of returned classes.
;       : $aArray[1][0] - GUID of 1st class entry, in string format.
;       : $aArray[1][1] - Description of 1st class entry.
;       : $aArray[2][0] - GUID of 2nd class entry.
;       : ... ...
; Author    : Pusofalse
; ====================================================================================
Func _CM_Enumerate_Classes_Ex($sMachine)
    Local $iResult, $aResult[1][2] = [[0]], $hMachine, $pGUID

    $hMachine = _CM_Connect_Machine($sMachine)
    If $hMachine = 0 Then Return SetError(@error, 0, $aResult)
    $pGUID = _CM_Heap_Alloc(16)
    While 1
        $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Enumerate_Classes_Ex", _
            "ulong", $aResult[0][0], "ptr", $pGUID, "ulong", 0, "ptr", $hMachine)
        If $iResult[0] Then ExitLoop
        $aResult[0][0] += 1
        Redim $aResult[$aResult[0][0] + 1][2]
        $aResult[$aResult[0][0]][0] = _CM_String_From_GUID($pGUID)
        $aResult[$aResult[0][0]][1] = _SetupDiGetClassDescriptionEx($pGUID, $sMachine)
    WEnd
    _CM_Disconnect_Machine($hMachine)
    Return SetError($iResult[0], _CM_Heap_Free($pGUID), $aResult)
EndFunc ;==>_CM_Enumerate_Classes_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Enumerate_Enumerators_Ex
; Description   : This function lists the device enumerators, by supplying each enumerator's name.
; Parameter(s)  : $hMachine - Supplies a handle to a machine.
; Return values : An array in the following form is returned if succeeds:
;       :   - $aArray[0] - The number of entries returned.
;       :   - $aArray[1] - 1st enumerator's name.
;       :   - $aArray[2] - 2nd enumerator's name.
;       :   - ... ...
; Author    : Pusofalse
; ====================================================================================
Func _CM_Enumerate_Enumerators_Ex($hMachine)
    Local $iResult, $aResult[1] = [0]

    While True
        $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Enumerate_Enumerators_Ex", _
            "ulong", $aResult[0], "str", "", "ulong*", 32, "ulong", 0, "ptr", $hMachine)
        If $iResult[0] Then ExitLoop
        $aResult[0] += 1
        Redim $aResult[$aResult[0] + 1]
        $aResult[$aResult[0]] = $iResult[2]
    WEnd
    Return SetError($iResult[0], 0, $aResult)
EndFunc ;==>_CM_Enumerate_Enumerators_Ex

; #### FUNCTION ####
; =====================================================================================
; Name  : _CM_Is_Device_Disabled
; Description   : Determines the specified devnode is whether disabled.
; Parameter(s)  : $hDevInst - Supplies a device instance handle.
; Return values : Returns True if the device is disabled; Returns False if not disabled or an error occured.
; Author    : Pusofalse
; =====================================================================================

Func _CM_Is_Device_Disabled($hDevInst)
    Local $iStatus, $iFlags, $sDeviceID, $sKey

    $iStatus = _CM_Get_DevNode_Status($hDevInst)
    $iFlags = @Extended
    If bitAND($iStatus, $DN_DISABLEABLE) <> $DN_DISABLEABLE Then
        Return SetError(50, 0, 0)
    ElseIf ($iFlags = 0) And (BitAnd($iStatus, $DN_NEED_RESTART) = 0 Or _
        BitAnd($iStatus, $DN_HAS_PROBLEM) = 0) Then
        Return SetError(0, 0, 0)
    ElseIf $iFlags = 22 Then
        Return SetError(0, 0, 1)
    EndIf

    $sDeviceID = _CM_Get_Device_ID($hDevInst)
    $sKey = $REGKEY_HARDWARE_CS001_001 & $sDeviceID
    $iFlags = RegRead($sKey, "CSConfigFlags")
    If bitAND($iFlags, $CSCONFIGFLAG_DISABLED) = $CSCONFIGFLAG_DISABLED Then
        Return 1
    EndIf
    $iFlags = _CM_Get_DevNode_Registry_Property($hDevInst, $CM_DRP_CONFIGFLAGS)
    If bitAND($iFlags, $CONFIGFLAG_DISABLED) = $CONFIGFLAG_DISABLED Then
        Return 1
    EndIf
    Return 0
EndFunc ;==>_CM_Is_Device_Disabled

; #### FUNCTION ####
; =====================================================================================
; Name  : _CM_Enum_Device_Info
; Description   : Supplies a device class, enumerates the devices present in the device class.
; Parameter(s)  : $vClassGuid   - Device setup class GUID.
;       : $sEnumerator  - Enumerator, can be NULL specified for this parameter.
; Return values : An array in the following format:
;       : $aArray[0][0] - The number of devices returned.
;       : $aArray[1][0] - Description of 1st device.
;       : $aArray[1][1] - Device instance ID of 1st device.
;       : $aArray[1][2] - Device instance handle to 1st device.
;       : ... ...
; Author    : Pusofalse
; =====================================================================================
Func _CM_Enum_Device_Info($vClassGuid, $sEnumerator = "", $iMask = $DIGCF_PRESENT)
    Local $aResult[1][3], $sDescr, $sName, $tDevInfo, $hDevs

    $hDevs = _SetupDiGetClassDevs($iMask, $vClassGuid, $sEnumerator)
    While _SetupDiEnumDeviceInfo($hDevs, $aResult[0][0], $tDevInfo)
        $sName = _SetupDiGetDeviceRegistryProperty($hDevs, $tDevInfo, 0xC)
        If $sName = "" Then
            $sName = _SetupDiGetDeviceRegistryProperty($hDevs, $tDevInfo, 0)
            If $sName = "" Then $sName = "[Unknown device]"
        EndIf
        $aResult[0][0] += 1
        Redim $aResult[$aResult[0][0] + 1][3]
        $aResult[$aResult[0][0]][0] = $sName
        $aResult[$aResult[0][0]][1] = _SetupDiGetDeviceInstanceID($hDevs, $tDevInfo)
        $aResult[$aResult[0][0]][2] = DllStructGetData($tDevInfo, "DevInst")
    WEnd
    _CM_Assign_Var($tDevInfo, 0, @error)
    Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), $aResult)
EndFunc ;==>_CM_Enum_Device_Info

; #### FUNCTION ####
; =================================================================================
; Name  : _CM_Enum_Device_Info_Ex
; Description   : This function lists a set of specified class device, presents on local or remote system.
; Parameter(s)  : $sMachine - Machine name, in UNC format.
;       : $vClassGuid   - Class setup device GUID.
;       : $sEnumerator  - Enumerator, can be NULL.
; Return values : Returns an array in the following format:
;       : $aArray[0][0] - The number of enumerated devices.
;       : $aArray[1][0] - Description of 1st device instance.
;       : $aArray[1][1] - Device intance identifer for 1st entry.
;       : $aArray[1][2] - Handle to the device instance for 1st entry.
;       : $aArray[2][0] - Description of 2nd device instance.
;       :   ... ...
;       : If no error occurs during the call, @error is set to 259 which indicates the enumeration is finished normally. Otherwise, @error is set to a system error code.
; Author    : Pusofalse
; =================================================================================
Func _CM_Enum_Device_Info_Ex($sMachine, $vClassGuid, $sEnumerator = "")
    Local $aResult[1][3], $sDescr, $sName, $tDevInfo, $hDevs

    $hDevs = _SetupDiGetClassDevsEx($DIGCF_PRESENT, $vClassGuid, $sEnumerator, $sMachine)
    While _SetupDiEnumDeviceInfo($hDevs, $aResult[0][0], $tDevInfo)
        $sDescr = _SetupDiGetDeviceRegistryProperty($hDevs, $tDevInfo, 0)
        $sName = _SetupDiGetDeviceRegistryProperty($hDevs, $tDevInfo, 0xC)
        If $sName <> "" Then $sDescr = $sName
        If $sDescr = "" Then $sDescr = "[Unknown device]"
        $aResult[0][0] += 1
        Redim $aResult[$aResult[0][0] + 1][3]
        $aResult[$aResult[0][0]][0] = $sDescr
        $aResult[$aResult[0][0]][1] = _SetupDiGetDeviceInstanceID($hDevs, $tDevInfo)
        $aResult[$aResult[0][0]][2] = DllStructGetData($tDevInfo, "DevInst")
    WEnd
    _CM_Assign_Var($tDevInfo, 0, @error)
    Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), $aResult)
EndFunc ;==>_CM_Enum_Device_Info_Ex

; #### FUNCTION ####
; =================================================================================
; Name  : _CM_Enum_Device_Info_Notify_Progress
; Description   : Enumerate a device instance set, and notify user the enumeration progress.
; Parameter(s)  : $vClassGuid   - Device setup class GUID, can not be NULL.
;       : $sEnumerator  - Enumerator for enumeration, can be NULL specified.
;       : $sFunction    - Name of the callback function, if the progress is not required, this parameter can be NULL. Otherwise, defines a function by the following:
;       :   Func _EnumDeviceNotifyProgressCallback($iIndex, $sDeviceID)
;       :   EndFunc ;==>_EnumDeviceNotifyProgressCallback
;       : $iIndex parameter is the index of the device in device set, one based. $sDeviceID parameter in the callback function indicates the identifier string of the enumerated device instance, in text format. The callback function can return a value.
;       : Return values : If succeeds, returns a value in the following format:
;       :   $aArray[0][0] - The number of returned entries.
;       :   $aArray[1][0] - Description for 1st entry.
;       :   $aArray[1][1] - Device instance identifier string for 1st entry.
;       :   $aArray[1][2] - Device instance handle for 1st entry.
;       :   $aArray[1][3] - Return value of the user-defined callback function. If $sFunction is NULL, the value is NULL.
;       :   $aArray[2][0] - Description for 2nd entry.
;       : If the enumeration is finished without an error, @error is set to 259. Otherwise, is set to a system error code.
; Author    : Pusofalse
; Example   : _CM_Enum_Device_Info_Notify_Progress("Mouse")
; =================================================================================
Func _CM_Enum_Device_Info_Notify_Progress($vClassGuid, $sEnumerator = "", $sFunction = "")
    Local $aResult[1][4], $sDescr, $sName, $tDevInfo, $hDevs

    $hDevs = _SetupDiGetClassDevs($DIGCF_PRESENT, $vClassGuid, $sEnumerator)
    While _SetupDiEnumDeviceInfo($hDevs, $aResult[0][0], $tDevInfo)
        $sDescr = _SetupDiGetDeviceRegistryProperty($hDevs, $tDevInfo, 0)
        $sName = _SetupDiGetDeviceRegistryProperty($hDevs, $tDevInfo, 0xC)
        If $sName <> "" Then $sDescr = $sName
        If $sDescr = "" Then $sDescr = "[Unknown device]"
        $aResult[0][0] += 1
        Redim $aResult[$aResult[0][0] + 1][4]
        $aResult[$aResult[0][0]][0] = $sDescr
        $aResult[$aResult[0][0]][1] = _SetupDiGetDeviceInstanceID($hDevs, $tDevInfo)
        $aResult[$aResult[0][0]][2] = DllStructGetData($tDevInfo, "DevInst")
        If $sFunction <> "" Then
            $aResult[$aResult[0][0]][3] = Call($sFunction, $aResult[0][0], $aResult[$aResult[0][0]][1])
        EndIf
    WEnd
    _CM_Assign_Var($tDevInfo, 0, @error)
    Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), $aResult)
EndFunc ;==>_CM_Enum_Device_Info_Notify_Progress

; #### FUNCTION ####
; ====================================================================================
; Name  : _SetupDiGetClassDescriptionEx
; Description   : Retrieves the description of a device class from the specified remote system.
; Parameter(s)  : $vGUID    - Class GUID, or class name.
;       : $sMachine - Machine name, in UNC format.
; Return values : Returns the description string if succeeds, else returns NULL, and sets @error to a system error code.
; Author    : Pusofalse
; ====================================================================================
Func _SetupDiGetClassDescriptionEx($vGUID, $sMachine)
    Local $iResult, $tGUID, $pGUID
    If IsString($vGUID) Then
        If StringLeft($vGUID, 1) & StringRight($vGUID, 1) = "{}" Then
            $tGUID = _CM_GUID_From_String($vGUID)
            $pGUID = DllStructGetPtr($tGUID)
        ElseIf $vGUID <> "" Then
            $tGUID = _SetupDiClassGUIDsFromName($vGUID)
            $pGUID = DllStructGetPtr($tGUID)
        EndIf
    ElseIf IsDllStruct($vGUID) Then
        $pGUID = DllStructGetPtr($vGUID)
    ElseIf IsPtr($vGUID) Then
        $pGUID = $vGUID
    Else
        $pGUID = 0
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetClassDescriptionEx", "ptr", $pGUID, _
            "ptr", 0, "dword", 0, "dword*", 0, "str", $sMachine, "ptr", 0)
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiGetClassDescriptionEx", "ptr", $pGUID, _
            "str", "", "dword", $iResult[4], "dword*", 0, "str", $sMachine, "ptr", 0)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[2])
EndFunc ;==>_SetupDiGetClassDescriptionEx

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Request_Device_Eject_Ex
; Description   : This function prepares a local or a remote device instance for safe removal, if the device is removable. If the device can be physically ejected, it will be.
; Parameter(s)  : $hDevInst - Supplies a handle to a device instance.
;       : $hMachine - Supplies a machine handle, obtained by _CM_Connect_Machine.
;       : $sVetoName    - A variable receives a text string, dependent on the value of @extended.
; Return values : Returns True if succeeds, else returns false. If the removal fails, @extended is set to the reason for the failure.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Request_Device_Eject_Ex($hDevInst, $hMachine, ByRef $sVetoName)
    Local $iResult, $hToken
    Local $aPriv[2][2] = [[$SE_UNDOCK_NAME, 2], [$SE_LOAD_DRIVER_NAME, 2]]

    $hToken = _OpenProcessToken(-1)
    _AdjustTokenPrivileges($hToken, $aPriv)
    _LsaCloseHandle($hToken)

    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Request_Device_Eject_Ex", "dword", $hDevInst, _
            "ulong*", 0, "str", "", "ulong", 260, "ulong", 0, "ptr", $hMachine)
    $sVetoName = $iResult[3]
    Return SetError($iResult[0], $iResult[2], $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Request_Device_Eject_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Query_And_Remove_SubTree_Ex
; Description   : This function checks whether a device instance and its children can be removed and, if so, it removes them.
; Parameter(s)  : $hDevInst - A handle to the device instance handle.
;       : $hMachine - A handle to a machine on which the function executes.
;       : $sVetoName - A variable receives a text string, the type of information this string provides is dependent on the value of @extended.
;       : $iFlags - A bitwise OR of the caller-supplied flag constants, can be a combination of the following values:
;       :   - CM_REMOVE_UI_OK : The function allows a user dialog box to be displayed to indicate the reason for the veto (Default).
;       :   - CM_REMOVE_UI_NOT_OK: The function suppresses the display of a user dialog box that indicates the reason for the veto.
;       :   - CM_REMOVE_NO_RESTART: If this flag is set, the function configures the device status such that the device cannot be restarted until after the device status is reset.
; Return values : True indicates success, false to failure. If the removal request fails, @extended is set to the reason for the failure.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Query_And_Remove_SubTree_Ex($hDevInst, $hMachine, ByRef $sVetoName, $iFlags = $CM_REMOVE_UI_OK)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Query_And_Remove_SubTree_Ex", _
            "dword", $hDevInst, "ulong*", 0, "str", "", "ulong", 260, _
            "ulong", $iFlags, "ptr", $hMachine)
    $sVetoName = $iResult[3]
    Return SetError($iResult[0], $iResult[2], $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Query_And_Remove_SubTree_Ex

; #### FUNCTION ####
; =================================================================================
; Name  : _CM_Is_Dock_Station_Present
; Description   : This function identifies whether a docking station is present in a local machine.
; Parameter(s)  : This function has no parameters.
; Return values : If an error occured during the call or the docking station is not present in local system, the function returns FALSE and sets @error to a configuration manager error code. Otherwise, the function returns TRUE indicates the docking station is present in the local machine.
; Author    : Pusofalse
; =================================================================================
Func _CM_Is_Dock_Station_Present()
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Is_Dock_Station_Present", "int*", 0)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Is_Dock_Station_Present

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Is_Dock_Station_Present_Ex
; Description   : This function identifies whether a docking station is present in a local or a remote machine.
; Parameter(s)  : $hMachine - Supplies a machine handle, that is returned by _CM_Connect_Machine.
; Return values : Returns True if a docking station is present, False otherwise, or the function cannot connect to the specified machine.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Is_Dock_Station_Present_Ex($hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Is_Dock_Station_Present_Ex", _
            "int*", 0, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[1] <> 0)
EndFunc ;==>_CM_Is_Dock_Station_Present_Ex

; #### FUNCTION ####
; =================================================================================
; Name  : _CM_Request_Eject_PC
; Description   : This function requests a portable PC, which is inserted in local docking station, be ejected.
; Parameter(s)  : This function has no parameters.
; Return values : If the operation succeeds, the function returns TRUE. Otherwise, returns FALSE and sets @error to a CR_* error code.
; Author    : Pusofalse
; =================================================================================
Func _CM_Request_Eject_PC()
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Request_Eject_PC")
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Request_Eject_PC

; #### FUNCTION ####
; =================================================================================
; Name  : _CM_Request_Eject_PC_Ex
; Description   : The _CM_Request_Eject_PC_Ex function requests that a portable PC, which is inserted in a local or a remote docking station, be ejected.
; Parameter(s)  : $hMachine - Supplies a handle that is returned by _CM_Connect_Machine.
; Return values : TRUE is returned if succeeds. Otherwise, the function returns FALSE and sets @error to one of the CR_-prefixed error codes.
; Author    : Pusofalse
; =================================================================================
Func _CM_Request_Eject_PC_Ex($hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Request_Eject_PC_Ex", "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Request_Eject_PC_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Setup_DevNode
; Description   : This function restarts a device instance that is not running, because of a problem with device configuration.
; Parameter(s)  : $hDevInst - A handle to the device instance.
;       : $iFlags   - One of the following values:
;       : CM_SETUP_DEVNODE_READY: Restarts a device instance that is not running because of a problem with the device configuration.
;       : CM_SETUP_DEVNODE_RESET: Resets a device instance that has the no restart device status flag set. The no restart device status flag is set if a device is removed by calling _CM_Query_And_Remove_SubTree or _CM_Query_And_Remove_SubTree_Ex and specifying the CM_REMOVE_NO_RESTART flag.
; Return values : True indicates success, False otherwise, in which case, @error is set to the reason of the failure, see CR_* error constants.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Setup_DevNode($hDevInst, $iFlags)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Setup_DevNode", _
            "dword", $hDevInst, "ulong", $iFlags)
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Setup_DevNode

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Add_ID
; Description   : The CM_Add_ID function appends a specified device ID (if not already present) to a device instance's hardware ID list or compatible ID list.
; Parameter(s)  : $hDevInst - A handle to a device instance.
;       : $sDeviceID    - A string contains the device ID to be appended to the list.
;       : $iFlags   - One of the following values:
;       :   - CM_ADD_ID_HARDWARE: The specified device ID should be appended to the specific device instance's hardware ID list.
;       :   - CM_ADD_ID_COMPATIBLE: The specified device ID should be appended to the specific device instance's compatible ID list.
; Return values : True indicates success, false otherwise.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Add_ID($hDevInst, $sDeviceID, $iFlags = $CM_ADD_ID_HARDWARE)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Add_ID", "dword", $hDevInst, _
            "str", $sDeviceID, "ulong", $iFlags)
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Add_ID

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Add_ID_Ex
; Description   :  The function appends a device ID (if not already present) to a device instance's hardware ID list or compatible ID list, on either the local or a remote machine.
; Parameter(s)  : $hDevInst - Supplies a device instance handle.
;       : $sDeviceID    - A device ID string to be appended to the list.
;       : $hMachine - A machine handle.
;       : $iFlags   - One of the following values:
;       :   - CM_ADD_ID_HARDWARE: The specified device ID should be appended to the specific device instance's hardware ID list.
;       :   - CM_ADD_ID_COMPATIBLE: The specified device ID should be appended to the specific device instance's compatible ID list.
; Return values : Returns True if success, otherwise returns False.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Add_ID_Ex($hDevInst, $sDeviceID, $hMachine, $iFlags = $CM_ADD_ID_HARDWARE)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Add_ID_Ex", "dword", $hDevInst, _
            "str", $sDeviceID, "ulong", $iFlags, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Add_ID_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Enumerate_Children_Ex
; Description   : This function lists all child device nodes (devnode) of the specified device instance on a local or a remote machine.
; Parameter(s)  : $hDevInst - A handle to a device instance.
;       : $hMachine - A handle to a machine.
; Return values : Returns an array in the following format if succeess:
;       :   - $aArray[0] - The number of returned entries.
;       :   - $aArray[1] - 1st device instance ID, in string format.
;       :   - $aArray[2] - 2nd device instance ID.
;       :   - ... ...
; Author    : Pusofalse
; ====================================================================================
Func _CM_Enumerate_Children_Ex($hDevInst, $hMachine = 0)
    Local $aResult[1] = [0], $hChild, $hSibling, $sDevInst

    $hChild = _CM_Get_Child_Ex($hDevInst, $hMachine)
    If ($hChild = 0) Then Return SetError(@error, 0, $aResult)

    $hSibling = $hChild
    While 1
        $sDevInst &= _CM_Get_Device_ID($hSibling) & ","
        $hSibling = _CM_Get_Sibling_Ex($hSibling, $hMachine)
        If (@error) Or ($hSibling = 0) Then ExitLoop
    WEnd
    Return SetError(@error, 0, StringSplit(StringTrimRight($sDevInst, 1), ","))
EndFunc ;==>_CM_Enumerate_Children_Ex


; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Enumerate_Children
; Description   : This function lists the sub device nodes (devnode) for a device instance on local system.
; Parameter(s)  : $hDevInst - A handle to a device instance.
; Return values : Returns an array in the following format if succeess:
;       :   - $aArray[0] - The number of returned entries.
;       :   - $aArray[1] - 1st device instance ID, in string format.
;       :   - $aArray[2] - 2nd device instance ID.
;       :   - ... ...
; Author    : Pusofalse
; ====================================================================================
Func _CM_Enumerate_Children($hDevInst)
    Return _CM_Enumerate_Children_Ex($hDevInst, 0)
EndFunc ;==>_CM_Enumerate_Children

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Children_Count_Ex
; Description   : Return the number of the sub device nodes (devnode) for a device instance on a local or remote system.
; Parameter(s)  : $hDevInst - Supplies a device instance handle.
;       : $hMachine - Supplies a machine handle.
; Return values : The number of sub device nodes.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Children_Count_Ex($hDevInst, $hMachine)
    Local $aChildren = _CM_Enumerate_Children_Ex($hDevInst, $hMachine)
    Return $aChildren[0]
EndFunc ;==>_CM_Get_Children_Count_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Children_Count
; Description   : Return the number of the sub device nodes (devnode) for a device instance on local system.
; Parameter(s)  : $hDevInst - A device instance handle.
; Return values : The number of the sub device nodes (devnode) of the $hDevInst.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Children_Count($hDevInst)
    Return _CM_Get_Children_Count_Ex($hDevInst, 0)
EndFunc ;==>_CM_Get_Children_Count

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Child_By_Index_Ex
; Description   : Returns the device instance ID on specified position by using the index caller specified, on local or a remote system.
; Parameter(s)  : $hDevInst - A device instance handle under which the specified sub device instance is retrieved.
;       : $hMachine - Supplies a machine, on which the function executes.
;       : $iIndex   - Zero-based index of device instants to retrieve, default to the first.
; Return values : Returns the device instance handle if succeeds, else returns zero.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Child_By_Index_Ex($hDevInst, $hMachine, $iIndex = 0)
    Local $aChildren = _CM_Enumerate_Children_Ex($hDevInst, $hMachine)

    If @error OR $aChildren[0] < 1 Then Return SetError(@error, 0, 0)
    If $iIndex < 0 Then Return SetError($CR_INVALID_DATA, 0, 0)
    If $iIndex > $aChildren[0] - 1 Then Return SetError($CR_NO_SUCH_DEVINST, 0, 0)
    Return _CM_Locate_DevNode_Ex($aChildren[$iIndex + 1], $hMachine)
EndFunc ;==>_CM_Get_Child_By_Index_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Child_By_Index
; Description   : Returns the device instance ID on specified position by using the index caller specified, on local system.
; Parameter(s)  : $hDevInst - A device instance handle under which the specified sub device instance is retrieved.
;       : $iIndex   - Zero-based index of device instants to retrieve, default to the first.
; Return values : Returns the device instance handle if succeeds, else returns zero.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Child_By_Index($hDevInst, $iIndex = 0)
    Local $hDevInst1
    $hDevInst1 = _CM_Get_Child_By_Index_Ex($hDevInst, 0, $iIndex)
    Return SetError(@error, 0, $hDevInst1)
EndFunc ;==>_CM_Get_Child_By_Index

; #### FUNCTION ####
; ==============================================================================
; Name  : _CM_Disable_DevNode
; Description   : This function disables a device node (devnode), on local system.
; Parameter(s)  : $hDevInst - Supplies a device instance handle.
; Return values : Returns True if succeeds; False otherwise, @error is set to a CR_* error code.
; Author    : Pusofalse
; ==============================================================================
Func _CM_Disable_DevNode($hDevInst)
    Local $iResult
    _CM_Set_DevNode_CSConfigFlags($hDevInst, $CSCONFIGFLAG_DISABLED)
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Disable_DevNode", _
            "dword", $hDevInst, "ulong", 0)
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Disable_DevNode

; #### FUNCTION ####
; ==============================================================================
; Name  : _CM_Enable_DevNode
; Description   : This function enables a device node (devnode) that has been disabled.
; Parameter(s)  : $hDevInst - Handle to the device instance to enable.
; Return values : Returns True if succeeds; False otherwise, @error is set to a CR_* error code.
; Author    : Pusofalse
; ==============================================================================
Func _CM_Enable_DevNode($hDevInst)
    Local $iResult
    _CM_Set_DevNode_CSConfigFlags($hDevInst, $CSCONFIGFLAG_NONE)
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Enable_DevNode", _
            "dword", $hDevInst, "ulong", 0)
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Enable_DevNode

; #### FUNCTION ####
; ==============================================================================
; Name  : _CM_Disable_DevNode_Ex
; Description   : Disables a device instance, on local or remote machine.
; Parameter(s)  : $hDevInst - Handle to the device instance.
;       : $hMachine - Handle to the machine, the function executes on this system.
; Return values : True if succeeds, else False, extended error is stored in @error.
; Author    : Pusofalse
; ==============================================================================
Func _CM_Disable_DevNode_Ex($hDevInst, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Disable_DevNode_Ex", _
            "dword", $hDevInst, "ulong", 0, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Disable_DevNode_Ex

; #### FUNCTION ####
; ==============================================================================
; Name  : _CM_Enable_DevNode_Ex
; Description   : Enables a device instance that has been disabled, on local or remote system.
; Parameter(s)  : $hDevInst - Handle to the device instance.
;       : $hMachine - Handle to the machine, typically returned by _CM_Connect_Machine function.
; Return values : True is returned if succeeds. Otherwise, False is returned, with that @error is set to the reason of the failure.
; Author    : Pusofalse
; ==============================================================================
Func _CM_Enable_DevNode_Ex($hDevInst, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Enable_DevNode_Ex", _
            "dword", $hDevInst, "ulong", 0, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Enable_DevNode_Ex

; #### FUNCTION ####
; ==============================================================================
; Name  : _CM_Uninstall_DevNode
; Description   : This function uninstalls a device node (devnode), on local system.
; Parameter(s)  : $hDevInst - Device instance to be uninstalled.
; Return values : True indicates success, False indicates failure, in which case, @error is set to a CR_* error code.
; Author    : Pusofalse
; Remarks   : If no error occur, the function returns True, but the device instance still works well, until the next system reboot.
; ==============================================================================
Func _CM_Uninstall_DevNode($hDevInst)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Uninstall_DevNode", _
            "dword", $hDevInst, "ulong", 0)
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Uninstall_DevNode

; #### FUNCTION ####
; ================================================================================
; Name  : _CM_Restart_Device
; Description   : This function restarts a device instance, on local system.
; Parameter(s)  : $hDevInst - Handle to the device instance.
; Return values : True is returned if succeeds, else False, the logged error is set in @error.
; Author    : Pusofalse
; ================================================================================
Func _CM_Restart_Device($hDevInst)
    Local $hDevs, $tDevInfo

    Select
    Case _CM_Create_Device_Devs($hDevInst, $hDevs, $tDevInfo) = 0
        Return SetError(@error, 0, 0)
    Case _SetupDiDisableDevice($hDevs, $tDevInfo, True) = 0
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), 0)
    Case _SetupDiDisableDevice($hDevs, $tDevInfo, False) = 0
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), 0)
    Case Else
        _CM_Assign_Var($tDevInfo, 0, @error)
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), 1)
    EndSelect
EndFunc ;==>_CM_Restart_Device

; #### FUNCTION ####
; ================================================================================
; Name  : _CM_Restart_Device_Ex
; Description   : This function restarts a device instance, on local or remote system.
; Parameter(s)  : $hDevInst - Supplies a device instance handle, binding to the configuration manager on $sMachine system.
;       : $sMachine - Supplies a string contains the system name on which the function to execute, in UNC format. If specifies NULL, local system is used. Note that this parameter is a string but not a handle.
; Return values : Returns True if the function completes without an error, otherwise returns False, and the logged error can be retrieved from macro @error.
; Author    : Pusofalse
; ================================================================================
Func _CM_Restart_Device_Ex($hDevInst, $sMachine = "")
    Local $hDevs, $tDevInfo

    Select
    Case _CM_Create_Device_Devs_Ex($hDevInst, $hDevs, $tDevInfo, $sMachine) = 0
        Return SetError(@error, 0, 0)
    Case _SetupDiDisableDevice($hDevs, $tDevInfo, True) = 0
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), 0)
    Case _SetupDiDisableDevice($hDevs, $tDevInfo, False) = 0
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), 0)
    Case Else
        _CM_Assign_Var($tDevInfo, 0, @error)
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), 1)
    EndSelect
EndFunc ;==>_CM_Restart_Device_Ex


Func _CM_Set_DevNode_CSConfigFlags($hDevInst, $iFlags = 0)
    Local $fResult = 1, $sKey, $sDeviceID, $aKey[5]

    $sDeviceID = _CM_Get_Device_ID($hDevInst)
    If $sDeviceID = "" OR @error Then Return SetError(@error, 0, 0)

    $aKey[0] = $REGKEY_HARDWARE_CS001_001 & $sDeviceID
    $aKey[1] = $REGKEY_HARDWARE_CS002_001 & $sDeviceID
    $aKey[2] = $REGKEY_HARDWARE_CS002_CURRENT & $sDeviceID
    $aKey[3] = $REGKEY_HARDWARE_CCS_001 & $sDeviceID
    $aKey[4] = $REGKEY_HARDWARE_CCS_CURRENT & $sDeviceID

    For $i = 0 To 4
        $fResult = $fResult And RegWrite($aKey[$i], "CSConfigFlags", "REG_DWORD", $iFlags)
    Next
    Return SetError(@error, 0, $fResult)
EndFunc ;==>_CM_Set_DevNode_CSConfigFlags

; #### FUNCTION ####
; ====================================================================================
; Name  : _SetupDiCreateDeviceInfoListEx
; Description   : Creates an empty device information set on a remote or a local computer and optionally associates the set with a device setup class.
; Parameter(s)  : $sMachine - Machine name, in UNC format, empty indicates local.
;       : $vGUID    - Class GUID.
;       : $hWnd - A handle to the top-level window to use for any user interface that is related to non-device-specific actions (such as a select-device dialog box that uses the global class driver list). This handle is optional and can be NULL. If a specific top-level window is not required, set to NULL.
; Return values : A handle to the empty device information set is returned if succeeds, 0 or -1 otherwise.
; Author    : Pusofalse
; Remarks   : When finished with the device information set handle, call _SetupDiDestroyDeviceInfoList function to free it.
; ====================================================================================
Func _SetupDiCreateDeviceInfoListEx($sMachine, $vGUID = "", $hWnd = 0)
    Local $iResult, $pGuid
    If IsString($vGuid) Then
        If (StringLeft($vGuid, 1) & StringRight($vGuid, 1) = "{}") Then
            $pGuid = DllStructGetPtr(_CM_GUID_From_String($vGuid))
        ElseIf $vGuid <> "" Then
            $tGuid = _SetupDiClassGuidsFromName($vGuid)
            $pGuid = DllStructGetPtr($tGuid)
        EndIf
    ElseIf IsDllStruct($vGuid) Then
        $pGuid = DllStructGetPtr($vGuid)
    ElseIf IsPtr($vGuid) Then
        $pGuid = $vGuid
    Else
        $pGuid = 0
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "ptr", "SetupDiCreateDeviceInfoListEx", "ptr", $pGUID, _
            "hWnd", $hWnd, "str", $sMachine, "ptr", 0)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiCreateDeviceInfoListEx

; #### FUNCTION ####
; ====================================================================================
; Name  : _SetupDiCreateDeviceDevsEx
; Description   : Creates a device information set for a specified device instance on a local or a remote computer.
; Parameter(s)  : $sDevInstID   - Device instance identifier for which creates device information set.
;       : $hDevs    - A variable receives the handle to the device information set.
;       : $tSP_DEVINFO_DATA - A variable receives a SP_DEVICEINFO_DATA structure.
;       : $sMachine - System on which the function to execute.
;       : $vGUID    - Class GUID.
;       : $hWnd - An optional window handle.
; Author    : Pusofalse
; ====================================================================================
Func _SetupDiCreateDeviceDevsEx($sDevInstID, ByRef $hDevs, ByRef $tSP_DEVINFO_DATA, $sMachine, $vGUID = "", $hWnd = 0)
    Local $iResult

    $hDevs = _SetupDiCreateDeviceInfoListEx($sMachine, $vGUID, $hWnd)
    If $hDevs < 1 Then Return SetError(@error, 0, 0)

    Select
    Case _SetupDiOpenDeviceInfo($hDevs, $sDevInstID, $tSP_DEVINFO_DATA) = 0
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), 0)
    Case _SetupDiSetSelectedDevice($hDevs, $tSP_DEVINFO_DATA) = 0
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), 0)
    Case Else
        Return 1
    EndSelect
EndFunc ;==>_SetupDiCreateDeviceDevsEx

; #### FUNCTION ####
; ====================================================================================
; Name  : _SetupDiSelectOEMDrv
; Description   : This function selects a driver for a device information set or a particular device information element that uses an OEM path supplied by the user.
; Parameter(s)  : $hDevs    - A device information set handle.
;       : $pSP_DEVINFO_DATA - A pointer to an SP_DEVINFO_DATA structure that specifies a device information element in $hDevs.
;       : $hWnd - Window owner, optional.
; Return values : True indicates success, false to failure.
; Author    : Pusofalse
; Remarks   : _SetupDiSelectOEMDrv is primarily designed to select an OEM driver for a device on a local computer before installing the device on that computer. Although _SetupDiSelectOEMDrv will not fail if the device information set is for a remote computer, the result is of limited use because the device information set cannot subsequently be used with DIF_Xxx installation requests or _SetupDiXxx functions that do not support operations on a remote computer. In particular, the device information set cannot be used as input with a DIF_INSTALLDEVICE installation request to install a device on a remote computer. The selected driver can be retrieved in a call to _SetupDiGetDeviceInstallParams.
; ====================================================================================
Func _SetupDiSelectOEMDrv($hDevs, $pSP_DEVINFO_DATA, $hWnd = 0)
    Local $iResult
    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf

    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiSelectOEMDrv", "hWnd", $hWnd, _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiSelectOEMDrv

; #### FUNCTION ####
; ====================================================================================
; Name  : _SetupDiAskForOEMDisk
; Description   : The function displays a dialog that asks the user for the path of an OEM installation disk.
; Parameter(s)  : $hDevs    - A handle to a device information set for the local computer. This set contains a device information element that represents the device that is being installed.
;       : $pSP_DEVINFO_DATA - A pointer to an SP_DEVINFO_DATA structure that specifies the device information element in $hDevs.
; Return values : True indicates success, false indicates failure.
; Author    : Pusofalse
; ====================================================================================
Func _SetupDiAskForOEMDisk($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult
    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiAskForOEMDisk", "ptr", $hDevs, _
            "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiAskForOEMDisk

; #### FUNCTION ####
; ====================================================================================
; Name  : _SetupDiSetDeviceInterfaceDefault
; Description   : Sets a device interface as the default interface for a device interface class.
; Parameter(s)  : $hDevs    - Supplies a handle to a device information set.
;       : $pSP_DEVIFINFO_DATA - A pointer to a SP_DEVICE_INTERFACE_DATA structure that specifies the device interface in $hDevs.
; Return values : True indicate success, false indicates failure.
; Author    : Pusofalse
; ====================================================================================
Func _SetupDiSetDeviceInterfaceDefault($hDevs, $pSP_DEVIFINFO_DATA)
    Local $iResult
    If IsDllStruct($pSP_DEVIFINFO_DATA) Then
        $pSP_DEVIFINFO_DATA = DllStructGetPtr($pSP_DEVIFINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiSetDeviceInterfaceDefault", "ptr", $hDevs, _
            "ptr", $pSP_DEVIFINFO_DATA, "dword", 0, "ptr", 0)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiSetDeviceInterfaceDefault

; #### FUNCTION ####
; ====================================================================================
; Name  : _SetupDiRemoveDeviceInterface
; Description   : This function removes a device interface for a device interface class.
; Parameter(s)  : $hDevs    - A handle to the device information set.
;       : $pSP_DEVIFINFO_DATA - A pointer to a SP_DEVICE_INTERFACE_DATA that specifies the device interface in $hDevs.
; Return values : True indicate success, False to failure, in which case, @error is set to a system error code.
; Author    : Pusofalse
; ====================================================================================
Func _SetupDiRemoveDeviceInterface($hDevs, $pSP_DEVIFINFO_DATA)

    Local $iResult
    If IsDllStruct($pSP_DEVIFINFO_DATA) Then
        $pSP_DEVIFINFO_DATA = DllStructGetPtr($pSP_DEVIFINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiRemoveDeviceInterface", "ptr", $hDevs, _
            "ptr", $pSP_DEVIFINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiRemoveDeviceInterface

; #### FUNCTION ####
; ====================================================================================
; Name  : _SetupDiInstallDeviceInterfaces
; Description   : The function is the default handler for the DIF_INSTALLINTERFACES installation request.
; Parameter(s)  : $hDevs    - A handle to a device information set.
;       : $pSP_DEVINFO_DATA - A pointer to an SP_DEVINFO_DATA structure that specifies the device information element in $hDevs.
; Return values : Returns True if the function completed without error, else False is returned, and the error code for the failure is set in @error.
; Author    : Pusofalse
; ====================================================================================
Func _SetupDiInstallDeviceInterfaces($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult
    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiInstallDeviceInterfaces", "ptr", $hDevs, _
            "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiInstallDeviceInterfaces

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_First_Log_Conf_Ex
; Description   : This function retrieves handle to the first logical configuration for a device instance, on a local or remote system.
; Parameter(s)  : $hDevInst - A device instance handle.
;       : $hMachine - A machine handle.
;       : $iFlags   - See #### Flags for _CM_Get_First_Log_Conf #### for a value, default retrieves a handle that contains the resource allocation.
; Return values : A handle to the first logical configuration is returned if succeeds, otherwise returned a value less one.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_First_Log_Conf_Ex($hDevInst, $hMachine, $iFlags = $ALLOC_LOG_CONF)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_First_Log_Conf_Ex", "long*", 0, _
            "dword", $hDevInst, "ulong", $iFlags, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Get_First_Log_Conf_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Add_Empty_Log_Conf_Ex
; Description   : This function adds an empty logical configuration for a device instance, on a local or remote system.
; Parameter(s)  : $hDevInst - A handle to the device instance.
;       : $iPriority    - Priority for the new logical configuration, see LCPRI_* constants for a value.
;       : $iFlags   - Specify the type of the logical configuration, see #### Flags for _CM_Get_First_Log_Conf #### for a value.
;       : $hMachine - System on which the function executes, a value of zero indicates local.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Add_Empty_Log_Conf_Ex($hDevInst, $iPriority, $iFlags, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Add_Empty_Log_Conf_Ex", "long*", 0, _
            "dword", $hDevInst, "ulong", $iPriority, "ulong", $iFlags, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Add_Empty_Log_Conf_Ex

; #### FUNCTION ####
; ================================================================================
; Name  : _CM_Add_Empty_Log_Conf
; Description   :  The _CM_Add_Empty_Log_Conf function creates an empty logical configuration, for a specified configuration type and a specified device instance, on the local machine.
; Parameter(s)  : $hDevInst - Supplies a device instance handle that is bound to the local machine.
;       : $iPriority - Configuration priority value, see LCPRI_* constants for a possible value.
;       : $iFlags   - Flags for the logical configuration, see #### Flags for _CM_Get_First_Log_Conf #### for a possible value.
; Return values : If succeeds, returns a handle the newly created logical configuration. Otherwise, returns a value of zero, and @error is set to a cofiguration management error code.
; Author    : Pusofalse
; ================================================================================
Func _CM_Add_Empty_Log_Conf($hDevInst, $iPriority, $iFlags)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Add_Empty_Log_Conf", "long*", 0, _
            "dword", $hDevInst, "dword", $iPriority, "ulong", $iFlags)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Add_Empty_Log_Conf

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Next_Log_Conf_Ex
; Description   : This function continues to enumerate the logical configurations for a device instance, on a local or remote system.
; Parameter(s)  : $hLogConf - Handle to the logical configuration, typically returned by the following functions:
;       :   - _CM_Get_First_Log_Conf
;       :   - _CM_Get_First_Log_Conf_Ex
;       :   - _CM_Get_Next_Log_Conf
;       :   - _CM_Get_Next_Log_Conf_Ex
;       :   - _CM_Add_Empty_Log_Conf
;       :   - _CM_Add_Empty_Log_Conf_Ex
;       : $hMachine - Handle to the machine.
; Return values : True indicates success, false to failure, in which case, @error is set to a system error code.
; Author    : Pusofalse
; Remarks   : $hLogConf is an IN/OUT parameter in this function, because this function changes its value by using the handle to the new enumerated logical configuration, a value of zero indicates the enumeration is finished for the device instance.
; ====================================================================================
Func _CM_Get_Next_Log_Conf_Ex(ByRef $hLogConf, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Next_Log_Conf_Ex", "long*", 0, _
            "long", $hLogConf, "ulong", 0, "ptr", $hMachine)
    _CM_Free_Log_Conf_Handle($hLogConf)
    $hLogConf = $iResult[1]
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Get_Next_Log_Conf_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Log_Conf_Priority_Ex
; Description   : Retrieve the priority for the logical configuration of the device instance, on local or remote system.
; Parameter(s)  : $hLogConf - Handle to the logical configuration, typically returned by the following function:
;       :   _CM_Add_Empty_Log_Conf
;       :   _CM_Add_Empty_Log_Conf_Ex
;       :   _CM_Get_First_Log_Conf
;       :   _CM_Get_First_Log_Conf_Ex
;       :   _CM_Get_Next_Log_Conf
;       :   _CM_Get_Next_Log_Conf_Ex
;       : $hMachine - Handle to the machine.
; Return values : A priority value indicates success, or @error if non-zero, the function failed.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Log_Conf_Priority_Ex($hLogConf, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Log_Conf_Priority_Ex", "long", $hLogConf, _
            "ulong*", 0, "ulong", 0, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[2])
EndFunc ;==>_CM_Get_Log_Conf_Priority_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Free_Log_Conf_Ex
; Description   : This function frees a logical configuraton for a device instance, on local or remote system.
; Parameter(s)  : $hLogConf - Handle to the logical configuration, typically returned by the following function:
;       :   _CM_Add_Empty_Log_Conf
;       :   _CM_Add_Empty_Log_Conf_Ex
;       :   _CM_Get_First_Log_Conf
;       :   _CM_Get_First_Log_Conf_Ex
;       :   _CM_Get_Next_Log_Conf
;       :   _CM_Get_Next_Log_Conf_Ex
;       : $hMachine - Handle to the machine.
; Return values : True indicates success, False indicates failure.
; Author    : Pusofalse
; Remarks   : This function frees a logical configuraton, but not a handle to the logical configuration, to free a handle, call _CM_Free_Log_Conf_Handle function. $hLogConf is an IN/OUT parameter, because that if the function executes successfully, its value will be set to zero.
; ====================================================================================
Func _CM_Free_Log_Conf_Ex(ByRef $hLogConf, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Free_Log_Conf_Ex", "long", $hLogConf, _
            "ulong", 0, "ptr", $hMachine)
    If $iResult[0] = $CR_SUCCESS Then $hLogConf = 0
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Free_Log_Conf_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Next_Res_Des_Ex
; Description   : This function continues to enumerate the resource descriptor for a device instance.
; Parameter(s)  : $hResDes  - Handle to the resource descriptor, see Remarks section for detail.
;       : $iResourceID  - Resource type.
;       : $hMachine - Handle to the machine.
; Return values : True indicates success, False indicates failure, in this case, @error is set to a CR_* error code.
; Author    : Pusofalse
; Remarks   : The $hResDes parameter is an IN/OUT parameter. On the first call to this function, $hResDes is returned by _CM_Get_First_Log_Conf or _CM_Get_First_Log_Conf_Ex, then during the subsequent calls to the function, $hResDes is obtained by the previous call. On each call, $hResDes will be updated by using the handle to the new enumerated resource descriptor, if the value of $hResDes is set to zero, then the enumeration is finished.
; ====================================================================================
Func _CM_Get_Next_Res_Des_Ex(ByRef $hResDes, $iResourceID, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Next_Res_Des_Ex", "long*", 0, _
            "long", $hResDes, "int", $iResourceID, _
            "long*", 0, "ulong", 0, "ptr", $hMachine)
    If $iResult[0] Then Return SetError($iResult[0], 0, 0)
    _CM_Free_Res_Des_Handle($hResDes)
    $hResDes = $iResult[1]
    Return SetExtended($iResult[4], $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Get_Next_Res_Des_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Res_Des_By_Index
; Description   : Retrieves the specified resource descriptor in the logical configuration.
; Parameter(s)  : $hLogConf - Handle to the logical configuration.
;       : $iIndex   - A DWORD value indicates the resource descriptor's position, zero based.
;       : $iResourceID  - Resource type.
; Return values : Returns the handle to the specified resource descriptor if succeeds, or zero otherwise.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Res_Des_By_Index($hLogConf, $iIndex, $iResourceID = $RESTYPE_ALL)
    Local $hResDes

    $hResDes = _CM_Get_Res_Des_By_Index_Ex($hLogConf, $iIndex, 0, $iResourceID)
    Return SetError(@error, @extended, $hResDes)
EndFunc ;==>_CM_Get_Res_Des_By_Index

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Res_Des_By_Index_Ex
; Description   : Retrieves the specified resource descriptor in the logical configuration, on local or remote system.
; Parameter(s)  : $hLogConf - Handle to the logical configuration.
;       : $iIndex   - A DWORD value indicates the resource descriptor's position, zero based.
;       : $hMachine - A machine handle.
;       : $iResourceID  - Resource type.
; Return values : Returns the handle to the specified resource descriptor if succeeds, or zero otherwise.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Res_Des_By_Index_Ex($hLogConf, $iIndex, $hMachine = 0, $iResourceID = $RESTYPE_ALL)
    Local $iResult, $hResDes = $hLogConf, $iPointer = -1

    While _CM_Get_Next_Res_Des_Ex($hResDes, $iResourceID, $hMachine)
        $iPointer += 1
        If $iPointer = $iIndex Then Return SetExtended(@extended, $hResDes)
    WEnd
    Return SetError(@error, 0, 0)
EndFunc ;==>_CM_Get_Res_Des_By_Index_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Modify_Res_Des_Ex
; Description   : This function modifies a resource descriptor in a logical configuration for a device instance.
; Parameter(s)  : $hResDes  - Handle to the resource descriptor to modify.
;       : $iResourceID  - Resource type of the $hResDes.
;       : $pData    - A pointer to a buffer that contains the resource data.
;       : $iDataSize    - Specifies the size of the buffer.
;       : $hMachine - A machine handle.
; Return values : True indicates success, False indicates failure. If failure, @error is set to a CR_* error code.
; Author    : Pusofalse
; Remarks   : $hResDes is an IN/OUT parameter, because that the function will change its value by using the handle to the modified resource descriptor.
; ====================================================================================
Func _CM_Modify_Res_Des_Ex(ByRef $hResDes, $iResourceID, $pData, $iDataSize, $hMachine)
    Local $iResult

    If IsDllStruct($pData) Then $pData = DllStructGetPtr($pData)
    If Not IsPtr($pData) Then Return SetError($CR_INVALID_DATA, 0, 0)

    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Modify_Res_Des_Ex", "long*", 0, _
            "long", $hResDes, "int", $iResourceID, "ptr", $pData, _
            "ulong", $iDataSize, "ulong", 0, "ptr", $hMachine)
    _CM_Free_Res_Des_Handle($hResDes)
    $hResDes = $iResult[1]
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Modify_Res_Des_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Add_Res_Des_Ex
; Description   : This function creates a new resource descriptor for a logical configuration.
; Parameter(s)  : $hLogConf - Handle to the logical configuration at which the new resource descriptor is created.
;       : $iResourceID  - Specifies the type of the new resource descriptor.
;       : $pData    - A pointer to a buffer contains the resource descriptor data.
;       : $iDataSize    - Size of the buffer.
;       : $hMachine - Supplies a machine handle.
; Return values : The handle to the newly created resource is returned if the function completed without error. Else, the reason code of the failure can be obtained in @error.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Add_Res_Des_Ex($hLogConf, $iResourceID, $pData, $iDataSize, $hMachine)
    Local $iResult
    If IsDllStruct($pData) Then $pData = DllStructGetPtr($pData)
    If Not IsPtr($pData) Then Return SetError($CR_INVALID_DATA, 0, -1)

    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Add_Res_Des_Ex", "long*", 0, _
            "long", $hLogConf, "int", $iResourceID, "ptr", $pData, _
            "ulong", $iDataSize, "ulong", 0, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Add_Res_Des_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Free_Res_Des_Ex
; Description   : This function frees a resource for a device instance, on local or remote system.
; Parameter(s)  : $hResDes  - Handle to the resource, this is an IN/OUT parameter, see remarks section.
;       : $hMachine - Handle to the machine.
; Return values : True indicates success, False indicates failure.
; Author    : Pusofalse
; Remarks   : This function frees a resource descriptor, but not a descriptor's handle, to free the handle, call _CM_Free_Res_Des_Handle function. $hResRes should be updated after the call, the new value of $hResRes is the handle to a resource descriptor that was previous. If $hResDes represented the resource descriptor located first in the logical configuration, $hResDes is set to the handle of the logical configuration.
; ====================================================================================
Func _CM_Free_Res_Des_Ex(ByRef $hResDes, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Free_Res_Des_Ex", "long*", 0, _
            "long", $hResDes, "ulong", 0, "ptr", $hMachine)
    _CM_Free_Res_Des_Handle($hResDes)
    $hResDes = $iResult[1]
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Free_Res_Des_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Duplicate_Buffer
; Description   : This function duplicates a memory block.
; Parameter(s)  : $pBuffer - A pointer to a memory block, typically returned by _CM_Heap_Alloc function.
; Return values : A pointer to a duplicated buffer.
; Author    : Pusofalse
; Remarks   : If $pBuffer is not returned by _CM_Heap_Alloc, the function fails.
; ====================================================================================
Func _CM_Duplicate_Buffer($pBuffer)
    Local $pBuffer2, $tBuffer2, $iLength, $bData

    $iLength = _CM_Heap_Size($pBuffer)
    If $iLength < 1 Then Return SetError(@error, 0, 0)

    $tBuffer = DllStructCreate("byte Data[" & $iLength & "]", $pBuffer)
    $bData = DllStructGetData($tBuffer, "Data")
    $pBuffer2 = _CM_Heap_Alloc($iLength)
    $tBuffer2 = DllStructCreate("byte Data[" & $iLength & "]", $pBuffer2)
    DllStructSetData($tBuffer2, "Data", $bData)
    Return $pBuffer2
EndFunc ;==>_CM_Duplicate_Buffer

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Device_Resources_Ex
; Description   : This function lists the device instance resources, on a local or a remote system.
; Parameter(s)  : $hDevInst - A device instance handle.
;       : $hMachine - A machine handle.
;       : $iResourceID  - Specifies the type of the resources to be retrieved, default to retrieve all.
; Return values : An array in the following format:
;       :   - $aArray[0][0] - The number of resource entries returned.
;       :   - $aArray[1][0] - The type of the resource of 1st resource, for example, 'MEM' indicates a memory resource.
;       :   - $aArray[1][1] - The configuration of the 1st resource.
;       :   - $aArray[2][0] - 2nd resource type.
;       :   - $aArray[2][1] - 2nd resource configuration.
;       :   ... ...
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Device_Resources_Ex($hDevInst, $hMachine = 0, $iResourceID = $RESTYPE_ALL, $hLogConf = -1)
    Local $iResult, $aResult[1][3] = [[0]], $hResDes, $iResType, $tagStruct, $tBuffer, $iIndex = -1

    If $hLogConf <> -1 Then
        $hResDes = $hLogConf
    Else
        $hResDes = _CM_Get_First_Log_Conf_Ex($hDevInst, $hMachine, $ALLOC_LOG_CONF)
        If @error Then $hResDes = _CM_Get_First_Log_Conf_Ex($hDevInst, $hMachine, $BOOT_LOG_CONF)
    EndIf
    If $hResDes < 1 Then Return SetError(@error, 0, $aResult)
    While _CM_Get_Next_Res_Des_Ex($hResDes, $iResourceID, $hMachine)
        $iIndex += 1
        If $iResourceID = $RESTYPE_ALL Then
            $iResType = @extended
        Else
            $iResType = $iResourceID
        EndIf

        Switch $iResType
        Case $RESTYPE_MEM
            $tagStruct = $tagMEM_DES
        Case $RESTYPE_IO
            $tagStruct = $tagIO_DES
        Case $RESTYPE_DMA
            $tagStruct = $tagDMA_DES
        Case $RESTYPE_IRQ
            $tagStruct = $tagIRQ_DES
        Case $RESTYPE_BUSNUMBER
            $tagStruct = $tagBUSNUMBER_DES
        Case $RESTYPE_PCCARDCONFIG
            $tagStruct = $tagPCCARD_DES
        Case $RESTYPE_MFCARDCONFIG
            $tagStruct = $tagMFCARD_DES
        Case $RESTYPE_CLASSSPECIFIC
            $tagStruct = $tagCS_DES
        Case Else
            ContinueLoop
        EndSwitch

        $aResult[0][0] += 1
        Redim $aResult[$aResult[0][0] + 1][3]
        $aResult[$aResult[0][0]][2] = $iIndex
        $pBuffer = _CM_Get_Res_Des_Data_Ex($hResDes, $hMachine)
        $tBuffer = DllStructCreate($tagStruct, $pBuffer)
        Switch $iResType
        Case $RESTYPE_MEM, $RESTYPE_IO
            $aResult[$aResult[0][0]][0] = "MEM"
            If $iResType = $RESTYPE_IO Then $aResult[$aResult[0][0]][0] = "IO"
            Local $tBinary = DllStructCreate("byte AllocBase[8];byte AllocEnd[8]", DllStructGetPtr($tBuffer, "AllocBase"))
            Local $bBase = String(DllStructGetData($tBinary, "AllocBase"))
            Local $bEnd = String(DllStructGetData($tBinary, "AllocEnd"))
            Local $sBase = "", $sEnd = ""
            For $i = 3 To 17 Step 2
                $sBase = StringMid($bBase, $i, 2) & $sBase
                $sEnd = StringMid($bEnd, $i, 2) & $sEnd
            Next
            $aResult[$aResult[0][0]][1] = "0x" & $sBase & " - 0x" & $sEnd
        Case $RESTYPE_BUSNUMBER
            $aResult[$aResult[0][0]][0] = "BUSNUMBER"
    $aResult[$aResult[0][0]][1] = "0x" & Hex(DllStructGetData($tBuffer, "AllocBase")) & " - 0x" & Hex(DllStructGetData($tBuffer, "AllocEnd"))
        Case $RESTYPE_IRQ
            $aResult[$aResult[0][0]][0] = "IRQ"
            $aResult[$aResult[0][0]][1] = "0x" & Hex(DllStructGetData($tBuffer, "AllocNum"))
        Case $RESTYPE_DMA
            $aResult[$aResult[0][0]][0] = "DMA"
            $aResult[$aResult[0][0]][1] = "0x" & Hex(DllStructGetData($tBuffer, "AllocChannel"))
        Case $RESTYPE_MFCARDCONFIG
            $aResult[$aResult[0][0]][0] = "MFCARD"
            $aResult[$aResult[0][0]][1] = "0x" & Hex(DllStructGetData($tBuffer, "ConfigRegisterBase"))
        Case $RESTYPE_PCCARDCONFIG
            $aResult[$aResult[0][0]][0] = "PCCARD"
$aResult[$aResult[0][0]][1] = "0x" & Hex(DllStructGetData($tBuffer, "MemCardBase1")) & " / 0x" & Hex(DllStructGetData($tBuffer, "MemCardBase2"))
        Case $RESTYPE_CLASSSPECIFIC
            $aResult[$aResult[0][0]][0] = "CS"
            Local $iSignatureLength, $tData, $pData, $iOffset, $iSizeofBuffer, $iLegacyLength
            $iOffset = DllStructGetData($tBuffer, "LegacyDataOffset")
            $iSignatureLength = DllStructGetData($tBuffer, "SignatureLength")
            $iLegacyLength = DllStructGetData($tBuffer, "LegacyDataSize")
            $tData = DllStructCreate($tagCS_DES & ";byte Signature[" & $iSignatureLength & "]", $pBuffer)
            $iSizeofBuffer = DllStructGetSize($tData)
            $pData = $pBuffer + $iSizeofBuffer + $iOffset
            $tData = DllStructCreate("byte LegacyData[" & $iLegacyLength & "]", $pData)
            $aResult[$aResult[0][0]][1] = DllStructGetData($tData, "LegacyData")
            _CM_Free_Variable($tData)
        EndSwitch
        _CM_Heap_Free($pBuffer)
        _CM_Free_Variable($tBuffer)
    WEnd
    Return SetError(@error, 0, $aResult)
EndFunc ;==>_CM_Get_Device_Resources_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Res_Des_Data_Size_Ex
; Description   : Retrieves the buffer length requested to hold the resource data on a local or remote system.
; Parameter(s)  : $hResDes  - Handle to the resource.
;       : $hMachine - Handle to the machine.
; Return values : Length requested, or zero otherwise.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Res_Des_Data_Size_Ex($hResDes, $hMachine)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Res_Des_Data_Size_Ex", _
            "ulong*", 0, "long", $hResDes, "ulong", 0, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Get_Res_Des_Data_Size_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Res_Des_Data_Ex
; Description   : Retrieves the resource data for a device instance on a local or remote system.
; Parameter(s)  : $hResDes  - Handle to the device instance resource.
;       : $hMachine - Handle to the machine, typically returned by _CM_Connect_Machine.
; Return values : If succeeds, returns the pointer to the data of the device instance resource, or zero otherwise, @error is set to a CR_* error code. Must call _CM_Heap_Free function to free the pointer when finished with it.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Res_Des_Data_Ex($hResDes, $hMachine)
    Local $iLength, $iResult, $pBuffer

    $iLength = _CM_Get_Res_Des_Data_Size_Ex($hResDes, $hMachine)
    If $iLength < 1 Then Return SetError(@error, 0, 0)

    $pBuffer = _CM_Heap_Alloc($iLength)
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Res_Des_Data_Ex", "long", $hResDes, _
            "ptr", $pBuffer, "ulong", $iLength, "ulong", 0, "ptr", $hMachine)
    If $iResult[0] Then Return SetError($iResult[0], 0 * _CM_Heap_Free($pBuffer), 0)
    Return $pBuffer
EndFunc ;==>_CM_Get_Res_Des_Data_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Device_Interface_List_Ex
; Description   : This function lists the device interfaces for a device interface class.
; Parameter(s)  : $hMachine - Supplies a machine handle.
;       : $vClassGuid   - Device interface class GUID.
;       : $sDeviceInstID    - An optional device instance ID string, if non-null, the function retrieves the device path associated with this device instance. If NULL, the function retrieves the device paths associcated the GUID specified in $vClassGUID.
;       : $iFlags   - CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES: The function provides a list containing device interfaces associated with all devices that match the specified GUID and device instance ID, if any.
;       :   - CM_GET_DEVICE_INTERFACE_LIST_PRESENT: The function provides a list containing device interfaces associated with devices that are currently active, and which match the specified GUID and device instance ID, if any.
; Return values : An array in the following format:
;       :   - $aArray[0]    - The number of returned entries.
;       :   - $aArray[1]    - 1st device path.
;       :   - $aArray[2]    - 2nd device path.
;       :   ... ...
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Device_Interface_List_Ex($hMachine, $vClassGuid, $sDeviceInstID = "", $iFlags = 0)
    Local $iResult, $iLength, $iPointer, $pBuffer, $tBuffer, $tClassGuid, $pClassGuid, $aResult[1] = [0]

    If IsDllStruct($vClassGuid) Then
        $vClassGuid = DllStructGetPtr($vClassGuid)
    ElseIf IsString($vClassGuid) Then
        $tClassGuid = _CM_GUID_From_String($vClassGuid)
        $pClassGuid = DllStructGetPtr($tClassGuid)
    Else
        $pClassGuid = $vClassGuid
    EndIf
    If Not IsPtr($pClassGuid) Then Return SetError($CR_INVALID_DATA, 0, 0)

    $iLength = _CM_Get_Device_Interface_List_Size_Ex($hMachine,$pClassGuid,$sDeviceInstID,$iFlags)
    If $iLength < 1 Then Return SetError(@error, 0, $aResult)
    $pBuffer = _CM_Heap_Alloc($iLength)
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Device_Interface_List_Ex", _
            "ptr", $pClassGuid, "str", $sDeviceInstID, "ptr", $pBuffer, _
            "ulong", $iLength, "ulong", $iFlags, "ptr", $hMachine)
    If $iResult[0] Then Return SetError($iResult[0], 0, $aResult)

    While $iPointer < $iLength - 1
        $tBuffer = DllStructCreate("char DevicePath[260]", $pBuffer)
        $aResult[0] += 1
        Redim $aResult[$aResult[0] + 1]
        $aResult[$aResult[0]] = DllStructGetData($tBuffer, "DevicePath")
        If $aResult[$aResult[0]] = "" Then ExitLoop
        $iResult = StringLen($aResult[$aResult[0]]) + 1
        $iPointer += $iResult
        $pBuffer += $iResult
    WEnd
    _CM_Heap_Free($pBuffer)
    _CM_Free_Variable($tBuffer)
    Return $aResult
EndFunc ;==>_CM_Get_Device_Interface_List_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_Device_Interface_List_Size_Ex
; Description   : Retrieves the buffer length requested to hold the device interfaces, on a local or remote system.
; Parameter(s)  : $hMachine - Handle to the machine on which the function executes.
;       : $vClassGuid   - Device interface class GUID, must same as $vClassGuid parameter in _CM_Get_Device_Interface_List_Ex.
;       : $sDeviceInstID    - String contains a device instance ID, can be NULL.
;       : $iFlags   - See _CM_Get_Device_Interface_List_Ex.
; Return values : Returns a value of non-zero indicates the buffer length requested, or zero otherwise.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_Device_Interface_List_Size_Ex($hMachine, $vClassGuid, $sDeviceInstID = "", $iFlags = 0)
    Local $iResult, $pClassGuid, $tClassGuid

    If IsDllStruct($vClassGuid) Then
        $vClassGuid = DllStructGetPtr($vClassGuid)
    ElseIf IsString($vClassGuid) Then
        $tClassGuid = _CM_GUID_From_String($vClassGuid)
        $pClassGuid = DllStructGetPtr($tClassGuid)
    Else
        $pClassGuid = $vClassGuid
    EndIf
    If Not IsPtr($pClassGuid) Then Return SetError($CR_INVALID_DATA, 0, 0)

    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Device_Interface_List_Size_Ex", "ulong*", 0, _
            "ptr", $pClassGuid, "str", $sDeviceInstID, "ulong", $iFlags, "ptr", $hMachine)
    Return SetError($iResult[0], _CM_Free_Variable($tClassGuid), $iResult[1])
EndFunc ;==>_CM_Get_Device_Interface_List_Size_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_DevNode_Registry_Property_Ex
; Description   : Retrieves the property of the specified device instance on a local or a remote system.
; Parameter(s)  : $hDevInst - A handle to the device information set.
;       : $iProperty    - Property to be retrieved, see CM_DRP* constants for a value.
; Return values : Requested property if succeeds, otherwise, @error is set to a configuration manager error code.
; Author: Pusofalse
; ====================================================================================
Func _CM_Get_DevNode_Registry_Property_Ex($hDevInst, $iProperty, $hMachine)
    Local $iResult, $tBuffer, $pBuffer, $vResult

    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_DevNode_Registry_Property_Ex", _
            "dword", $hDevInst, "ulong", $iProperty, "ulong*", 0, "ptr", 0, _
            "ulong*", 0, "ulong", 0, "ptr", $hMachine)
    If $iResult[5] = 0 Then Return SetError($iResult[0], 0, "")
    $pBuffer = _CM_Heap_Alloc($iResult[5])
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_DevNode_Registry_Property_Ex", _
            "dword", $hDevInst, "ulong", $iProperty, "ulong*", 0, "ptr", $pBuffer, _
            "ulong*", $iResult[5], "ulong", 0, "ptr", $hMachine)
    If $iResult[0] Then Return SetError($iResult[0], 0, "")

    Switch $iResult[3]
    Case 1, 2 ; REG_SZ, REG_EXPAND_SZ
        $tBuffer = DllStructCreate("char Data[" & $iResult[5] & "]", $pBuffer)
        $vResult = DllStructGetData($tBuffer, "Data")
    Case 3 ; REG_BINARY
        $tBuffer = DllStructCreate("byte Data[" & $iResult[5] & "]", $pBuffer)
        $vResult = DllStructGetData($tBuffer, "Data")
    Case 4, 5 ; REG_DWORD, REG_DWORD_BIG_ENDIAN
        $tBuffer = DllStructCreate("dword Data", $pBuffer)
        $vResult = DllStructGetData($tBuffer, "Data")
    Case 6 ; REG_LINK
        $tBuffer = DllStructCreate("wchar Data[" & $iResult[5] / 2 & "]", $pBuffer)
        $vResult = DllStructGetData($tBuffer, "Data")
    Case 7 ; REG_MULTI_SZ
        Local $sChr
        $tBuffer = DllStructCreate("char Data[" & $iResult[5] & "]", $pBuffer)
        For $i = 1 To $iResult[5]
            $sChr = DllStructGetData($tBuffer, "Data", $i)
            If $sChr = Chr(0) Then
                $vResult &= @LF
            Else
                $vResult &= $sChr
            EndIf
        Next
        If StringRight($vResult, 1) = Chr(0) Then $vResult = StringTrimRight($vResult, 1)
    Case Else
        $tBuffer = DllStructCreate("byte Data[" & $iResult[5] & "]", $pBuffer)
        $vResult = DllStructGetData($tBuffer, "Data")
    EndSwitch
    _CM_Heap_Free($pBuffer)
    Return SetError(0, _CM_Free_Variable($tBuffer), $vResult)
EndFunc ;==>_CM_Get_DevNode_Registry_Property_Ex

; #### FUNCTION ####
; =================================================================================
; Name  : _CM_Get_Class_Registry_Property_Ex
; Description   : This function retrieves the device setup class property.
; Parameter(s)  : $vClassGuid   - Device setup class GUID.
;       : $iProperty    - Property requested, see CM_CRP_* contants for a value.
;       : $hMachine - Supplies a machine handle, zero indicates on local system.
; Return values : Property value is returned if succeeds, @extended is set to the type of the return value. Otherwise, returns NULL and sets @error to a configuration manager error code.
; Author    : Pusofalse
; Remarks   : _CM_Get_Class_Registry_Property_Ex function should only be used on Windows 2000 system.
; =================================================================================
Func _CM_Get_Class_Registry_Property_Ex($vClassGuid, $iProperty, $hMachine = 0)
    Local $pBuffer, $tBuffer, $iResult, $tClassGuid, $pClassGuid, $vResult

    If IsPtr($vClassGuid) Then
        $pClassGuid = $vClassGuid
    ElseIf IsDllStruct($vClassGuid) Then
        $pClassGuid = DllStructGetPtr($vClassGuid)
    Else
        If IsString($vClassGuid) Then
            If StringLeft($vClassGuid, 1) & StringRight($vClassGuid, 1) = "{}" Then
                $tClassGuid = _CM_GUID_From_String($vClassGuid)
            Else
                $tClassGuid = _SetupDiClassGuidsFromName($vClassGuid)
            EndIf
            $pClassGuid = DllStructGetPtr($tClassGuid)
        EndIf
    EndIf

    If Not IsPtr($pClassGuid) Then Return SetError($CR_INVALID_DATA, 0, "")
    $iResult = DllCall("Cfgmgr32.dll", "long", "CM_Get_Class_Registry_Property", _
            "ptr", $pClassGuid, "ulong", $iProperty, "ulong*", 0, "ptr", 0, _
            "ulong*", 0, "ulong", 0, "ptr", $hMachine)
    $pBuffer = _CM_Heap_Alloc($iResult[5])
    $iResult = DllCall("Cfgmgr32.dll", "long", "CM_Get_Class_Registry_Property", _
            "ptr", $pClassGuid, "ulong", $iProperty, "ulong*", 0, "ptr", $pBuffer, _
            "ulong*", $iResult[5], "ulong", 0, "ptr", $hMachine)
    _CM_Free_Variable($tClassGuid)
    Switch $iResult[3]
    Case 1, 2, 7
        $tBuffer = DllStructCreate("char Data[" & $iResult[5] & "]", $pBuffer)
    Case 4, 5
        $tBuffer = DllStructCreate("dword Data", $pBuffer)
    Case 6
        $tBuffer = DllStructCreate("wchar Data[" & Int($iResult[5] / 2) + 2 & "]", $pBuffer)
    Case Else
        $tBuffer = DllStructCreate("byte Data[" & $iResult[5] & "]", $pBuffer)
    EndSwitch
    If $iResult[3] <> 7 Then
        $vResult = DllStructGetData($tBuffer, "Data")
    Else
        Local $sChr
        For $i = 1 To $iResult[5]
            $sChr = DllStructGetData($tBuffer, "Data", $i)
            If $sChr = Chr(0) Then
                $vResult &= @LF
            Else
                $vResult &= $sChr
            EndIf
        Next
        While StringRight($vResult, 1) = @LF
            $vResult = StringTrimRight($vResult, 1)
        WEnd
    EndIf
    _CM_Heap_Free($pBuffer)
    Return SetError($iResult[0], $iResult[3] + _CM_Free_Variable($tBuffer), $vResult)
EndFunc ;==>_CM_Get_Class_Registry_Property_Ex

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Get_DevNode_Registry_Property
; Description   : Retrieves the device instance property on a local system.
; Parameter(s)  : $hDevInst - Supplies a device instance handle.
;       : $iProperty    - Specifies a property value to be retrieved, see CM_DRP* constants for a value.
; Return values : Requested property is returned if succeeds, otherwise, @error is set to a configuration manager error code.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Get_DevNode_Registry_Property($hDevInst, $iProperty)
    Local $vResult
    $vResult = _CM_Get_DevNode_Registry_Property_Ex($hDevInst, $iProperty, 0)
    Return SetError(@error, 0, $vResult)
EndFunc ;==>_CM_Get_DevNode_Registry_Property

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Advanced_Properties
; Description   : This function displays the advanced properties for a specified device.
; Parameter(s)  : $sDeviceID    - Device instance identifier.
;       : $hWnd - Handle the window own the properties window.
;       : $sMachine - Machine on which the function executes, default is local.
; Return values : True indicates success, otherwise the @error is set to a system error.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Advanced_Properties($sDeviceID, $hWnd = 0, $sMachine = "")
    Local $iResult

    $iResult = DllCall($DEVMGR_DllHandle, "int_ptr", "DeviceAdvancedPropertiesW", "hWnd", $hWnd, _
            "wstr", $sMachine, "wstr", $sDeviceID)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_CM_Advanced_Properties

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Format_Problem_Text_Ex
; Description   : This function formats a problem text.
; Parameter(s)  : $hDevInst - Supplies the handle to a device instance.
;       : $iProblemID   - Problem ID.
;       : $hMachine - Supplies the handle to a machine, default is local.
; Return values : Problem text corresponding to the $iProblemID if succeeds.
; Author    : Pusofalse
; ====================================================================================
Func _CM_Format_Problem_Text_Ex($hDevInst, $iProblemID, $hMachine = 0)
    Local $iResult
    $iResult = DllCall($DEVMGR_DllHandle, "uint", "DeviceProblemTextW", "ptr", $hMachine, _
            "dword", $hDevInst, "ulong", $iProblemID, "wstr", "", "uint", 1024)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[4])
EndFunc ;==>_CM_Format_Problem_Text_Ex

Func _CM_Device_Properties_Ex($sDeviceID, $hWnd = 0, $fShowDevMgr = 0, $iFlags = 0, $sMachine = "")
    Local $iResult
    $iResult = DllCall($DEVMGR_DllHandle, "int_ptr", "DevicePropertiesExW", "hWnd", $hWnd, _
            "wstr", $sMachine, "wstr", $sDeviceID, "dword", $iFlags, "int", $fShowDevMgr)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_CM_Device_Properties_Ex

Func _CM_Device_Problem_Wizard_Ex($sDeviceID, $hWnd = 0, $sMachine = "")
    Local $iResult
    $iResult = DllCall($DEVMGR_DllHandle, "int", "DeviceProblemWizardW", "hWnd", $hWnd, _
            "wstr", $sMachine, "wstr", $sDeviceID)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_CM_Device_Problem_Wizard_Ex

; #### Reserved, only used for debug ####
; =====================================================================================
; Name  : _CM_Format_Error_Message
; Parameter(s)  : $iErrorCode   - Error code, returned by _CM_*.
; Remarks   : $iErrorCode is a configuration manager error code, but not a system error code.
; Return values : Variable identifier corresponds to the error code.
; =====================================================================================
Func _CM_Format_Error_Message($iErrorCode, $fShow = False, $sFunction = "", $vParams = "")
    Local $iFlag = 64, $sMsg, $sVal

    $sMsg &= "SUCCESS, DEFAULT, OUT OF MEMORY, INVALID POINTER, "
    $sMsg &= "INVALID FLAG, INVALID DEVNODE, INVALID RES DES, "
    $sMsg &= "INVALID LOG CONF, INVALID ARBITRATOR, NODELIST, DEVNODE HAS REQS, "
    $sMsg &= "INVALID RESOURCEID, DLVXD NOT FOUND, NO SUCH DEVNODE, "
    $sMsg &= "NO MORE LOG CONF, NO MORE RES DES, ALREADY SUCH DEVNODE, "
    $sMsg &= "INVALID RANGE LIST, INVALID RANGE, FAILURE, NO SUCH LOGICAL DEV, "
    $sMsg &= "CREATE BLOCKED, NOT SYSTEM VM, EMOVE VETOED, APM VETOED, "
    $sMsg &= "INVALID LOAD TYPE, BUFFER SMALL, NO ARBITRATOR, "
    $sMsg &= "NO REGISTRY HANDLE, REGISTRY ERROR, INVALID DEVICE ID, "
    $sMsg &= "INVALID DATA, INVALID API, DEVLOADER NOT READY, "
    $sMsg &= "NEED RESTART, NO MORE HW PROFILES, DEVICE NOT THERE, "
    $sMsg &= "NO SUCH VALUE, WRONG TYPE, INVALID PRIORITY, NOT DISABLEABLE, "
    $sMsg &= "FREE RESOURCES, QUERY VETOED, CANT SHARE IRQ, NO DEPENDENT, "
    $sMsg &= "SAME RESOURCES, NO SUCH REGISTRY KEY, INVALID MACHINENAME, "
    $sMsg &= "REMOTE COMM FAILURE, MACHINE UNAVAILABLE, NO CM SERVICES, "
    $sMsg &= "ACCESS DENIED, CALL NOT IMPLEMENTED, INVALID PROPERTY, "
    $sMsg &= "DEVICE INTERFACE ACTIVE, NO SUCH DEVICE INTERFACE, "
    $sMsg &= "INVALID REFERENCE STRING, INVALID CONFLICT LIST, "
    $sMsg &= "INVALID INDEX, INVALID STRUCTURE SIZE, INTERNAL UNKNOWN ERROR"

    $aMsg = StringSplit($sMsg, ", ", 1)
    If $iErrorCode < 0 OR $iErrorCode > $aMsg[0] Then $iErrorCode = $aMsg[0] - 1
    $sVal = "$CR_" & StringReplace($aMsg[$iErrorCode + 1], " ", "_")
    $sMsg = ""
    If $fShow Then
        If $iErrorCode Then $iFlag = 48
        $sMsg &= "Function: " & "CM\" & $sFunction & @CRLF & @CRLF
        $sMsg &= "Error code: " & $sVal & " (" & $iErrorCode & ")           "
        If $vParams Then $sMsg &= @CRLF & @CRLF & "Parameter(s): " & $vParams
        Msgbox($iFlag, "Configuration Manager", $sMsg)
    EndIf
    Return $sVal
EndFunc ;==>_CM_Format_Error_Message

Func _CM_Get_HW_Prof_Flags_Ex($sDeviceID, $hMachine, $iHardwareProfile = 0)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_HW_Prof_Flags_Ex", "str", $sDeviceID, _
            "ulong", $iHardwareProfile, "ulong*", 0, "ulong", 0, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[3])
EndFunc ;==>_CM_Get_HW_Profile_Flags_Ex

Func _CM_Get_HW_Prof_Flags($sDeviceID, $iHardwareProfile = 0)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_HW_Prof_Flags", "str", $sDeviceID, _
            "ulong", $iHardwareProfile, "ulong*", 0, "ulong", 0)
    Return SetError($iResult[0], 0, $iResult[3])
EndFunc ;==>_CM_Get_HW_Prof_Flags

Func _SetupDiInstallClass($sInfFileName, $iFlags, $hFileQueue = 0, $hWndOwner = 0)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiInstallClass", "hWnd", $hWndOwner, _
            "str", $sInfFileName, "dword", $iFlags, "hWnd", $hFileQueue)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiInstallClass

Func _SetupDiInstallClassEx($sInfFileName, $iFlags, $vGUID = "", $hFileQueue = 0, $hWndOwner = 0)
    Local $iResult, $tGUID, $pGUID

    If IsString($vGUID) Then
        If StringLeft($vGUID, 1) & StringRight($vGUID, 1) = "{}" Then
            $tGUID = _CM_GUID_From_String($vGUID)
            $pGUID = DllStructGetPtr($tGUID)
        EndIf
    ElseIf IsDllStruct($vGUID) Then
        $pGUID = DllStructGetPtr($vGUID)
    ElseIf IsPtr($vGUID) Then
        $pGUID = $vGUID
    Else
        $pGUID = 0
    EndIf

    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiInstallClassEx", "hWnd", $hWndOwner, _
            "str", $sInfFileName, "dword", $iFlags, "hWnd", $hFileQueue, _
            "ptr", $pGUID, "ptr", 0, "ptr", 0)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiInstallClassEx

; #### FUNCTION ####
; ====================================================================================
; Name  : _SetupDiDeleteDeviceInfo
; Description   : This function deletes a device information element from a device information set.
; Parameter(s)  : $hDevs    - Handle to a device information set.
;       : $pSP_DEVINFO_DATA - A pointer to a SP_DEVICEINFO_DATA.
; Return values : True indicates success, False indicates failure.
; Author    : Pusofalse
; Remarks   : This function does not delete an actual device.
; ====================================================================================
Func _SetupDiDeleteDeviceInfo($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult
    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiDeleteDeviceInfo", "ptr", $hDevs, _
            "ptr", $pSP_DEVINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiDeleteDeviceInfo

Func _SetupDiCreateDeviceInterfaceRegKey($hDevs, $pSP_DEVIFINFO_DATA, $iAccess, $hInfFile = 0, $sInfSection = 0)
    Local $iResult

    If IsDllStruct($pSP_DEVIFINFO_DATA) Then
        $pSP_DEVIFINFO_DATA = DllStructGetPtr($pSP_DEVIFINFO_DATA)
    EndIf

    ; Returns the handle to the registry key.
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "SetupDiCreateDeviceInterfaceRegKey", _
            "ptr", $hDevs, "ptr", $pSP_DEVIFINFO_DATA, "dword", 0, _
            "dword", $iAccess, "hWnd", $hInfFile, "str", $sInfSection)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiCreateDeviceInterfaceRegKey

Func _SetupDiDeleteDeviceInterfaceRegKey($hDevs, $pSP_DEVIFINFO_DATA)
    Local $iResult

    If IsDllStruct($pSP_DEVIFINFO_DATA) Then
        $pSP_DEVIFINFO_DATA = DllStructGetPtr($pSP_DEVIFINFO_DATA)
    EndIf

    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiDeleteDeviceInterfaceRegKey", _
            "ptr", $hDevs, "ptr", $pSP_DEVIFINFO_DATA, "dword", 0)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiDeleteDeviceInterfaceRegKey

; #### FUNCTION ####
; ====================================================================================
; Name  : _SetupDiGetDeviceInterfaceDevs
; Description   : Specifies the device path, returns the device information set associated with this device on a local system.
; Parameter(s)  : $sDevicePath  - A string contains the device path.
;       : $hDevs    - A variable receives the handle to the device information set.
;       : $tSP_DEVINFO_DATA - A variable receives the SP_DEVICEINFO_DATA structure.
; Return values : Returns True if succeeds, else returns False.
; Author    : Pusofalse
; Remarks   : This function can only execute on the local system, to retrieve the device information from a remote system, call _SetupDiGetDeviceInterfaceDevsEx function.
; ====================================================================================
Func _SetupDiGetDeviceInterfaceDevs($sDevicePath, ByRef $hDevs, ByRef $tSP_DEVINFO_DATA)
    Return _SetupDiGetDeviceInterfaceDevsEx($sDevicePath, $hDevs, $tSP_DEVINFO_DATA)
EndFunc ;==>_SetupDiGetDeviceInterfaceDevs

; #### FUNCTION ####
; ====================================================================================
; Name  : _SetupDiGetDeviceInterfaceDevsEx
; Description   : Specifies the device path, returns the device information set associated with this device on a local or remote system.
; Parameter(s)  : $sDevicePath  - A string contains the device path.
;       : $hDevs    - A variable receives the handle to the device information set.
;       : $tSP_DEVINFO_DATA - A variable receives the SP_DEVICEINFO_DATA structure.
;       : $sMachine - Specify the system on which the function executes, in UNC format, default to local system.
; Return values : True indicates success, false to failure.
; Author    : Pusofalse
; ====================================================================================
Func _SetupDiGetDeviceInterfaceDevsEx($sDevicePath, ByRef $hDevs, ByRef $tSP_DEVINFO_DATA,$sMachine="")
    Local $tDevIfInfo

    $hDevs = _SetupDiGetClassDevsEx($DIGCF_ALLCLASSES, "", "", $sMachine)
    If $hDevs < 1 Then Return SetError(@error, 0, 0)

    Select
    Case _SetupDiOpenDeviceInterface($hDevs, $sDevicePath, $tDevIfInfo) = 0
        Return SetError(@error, 0 * _SetupDiDestroyDeviceInfoList($hDevs), 0)
    Case _SetupDiGetDeviceInterfaceDetail($hDevs, $tDevIfInfo,$tSP_DEVINFO_DATA)<>$sDevicePath
        Return SetError(@error, 0 * _SetupDiDestroyDeviceInfoList($hDevs), 0)
    Case _SetupDiSetSelectedDevice($hDevs, $tSP_DEVINFO_DATA) = 0
        Return SetError(@error, 0 * _SetupDiDestroyDeviceInfoList($hDevs), 0)
    Case Else
        Return SetError(0, _CM_Free_Variable($tDevIfInfo), 1)
    EndSelect
EndFunc ;==>_SetupDiGetDeviceInterfaceDevsEx

; #### FUNCTION ####
; ====================================================================================
; Name  : _SetupDiOpenDeviceInterfaceRegKey
; Description   : This function returns the handle to the registry key that is specific to a device interface.
; Parameter(s)  : $hDevs    - A handle to the device information set.
;       : $pSP_DEVIFINFO_DATA - A pointer to a SP_DEVIFINFO_DATA structure that contains the device interface data.
;       : $iAccess  - Access mask to open the registry key.
; Return values : If succeeds, returns the handle to the device interface registry key, otherwise, returns a value less than a value of one, and sets @error to a system error code.
; Author    : Pusofalse
; ====================================================================================
Func _SetupDiOpenDeviceInterfaceRegKey($hDevs, $pSP_DEVIFINFO_DATA, $iAccess)
    Local $iResult
    If IsDllStruct($pSP_DEVIFINFO_DATA) Then
        $pSP_DEVIFINFO_DATA = DllStructGetPtr($pSP_DEVIFINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "SetupDiOpenDeviceInterfaceRegKey", _
            "ptr", $hDevs, "ptr", $pSP_DEVIFINFO_DATA, "dword", 0, "dword", $iAccess)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiOpenDeviceInterfaceRegKey

; #### FUNCTION ####
; ====================================================================================
; Name  : _SetupDiDeleteDeviceInterfaceData
; Description   : Deletes a device interface from a device information set.
; Parameter(s)  : $hDevs    - A handle to device information set.
;       : $pSP_DEVIFINFO_DATA   - A pointer to a SP_DEV_INTERFACE_DATA structure, typically returned by _SetupDiEnumDeviceInterfaces function or _SetupDiOpenDeviceInterface.
; Return values : True indicates success, False indicates failure, in this case, @error is set to a system error code.
; Author    : Pusofalse
; ====================================================================================
Func _SetupDiDeleteDeviceInterfaceData($hDevs, $pSP_DEVIFINFO_DATA)
    Local $iResult
    If IsDllStruct($pSP_DEVIFINFO_DATA) Then
        $pSP_DEVIFINFO_DATA = DllStructGetPtr($pSP_DEVIFINFO_DATA)
    EndIf
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupDiDeleteDeviceInterfaceData", _
            "ptr", $hDevs, "ptr", $pSP_DEVIFINFO_DATA)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupDiDeleteDeviceInterfaceData

Func _CM_Set_DevNode_Problem_Ex($hDevInst, $iProblem, $hMachine, $iFlags = $CM_SET_DEVNODE_PROBLEM_NORMAL)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Set_DevNode_Problem_Ex", "dword", $hDevInst, _
            "ulong", $iProblem, "ulong", $iFlags, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Set_DevNode_Problem_Ex

; #### FUNCTION ####
; =====================================================================================
; Name  : _CM_Get_Drive_Disk_Number
; Description   : Retrieves the physical disk number for the specified logical partition.
; Parameter(s)  : $sDrive   - Logical partition (for example, C: or C:\), the name string must not begin with "\\.\" or "\\?\".
; Return values : Physical disk index, zero based, or -1 if failure. @extended is set to the disk type.
; Author    : Pusofalse
; =====================================================================================
Func _CM_Get_Drive_Disk_Number($sDrive)
    Local $iNum, $iType, $tBuffer, $hDrive, $iAccessMask

    While StringRight($sDrive, 1) = "\"
        $sDrive = StringTrimRight($sDrive, 1)
    WEnd

    $iAccessMask = bitOR($GENERIC_READ, $GENERIC_WRITE)
    $hDrive = _CM_Create_File("\\.\" & $sDrive, $iAccessMask, 3, 0, 3, 0)
    If $hDrive < 1 Then Return SetError(@error, 0, -1)

    $tBuffer = DllStructCreate("int Type;ulong Number;ulong PartNumber")
    If _CM_Device_IO_Control($hDrive, 0x2D1080, 0, 0, $tBuffer, 12) = 0 Then
        Return SetError(@error, _CM_Close_Handle($hDrive) * 0 - 1, -1)
    Else
        _CM_Close_Handle($hDrive)
        $iType = DllStructGetData($tBuffer, "Type")
        $iNum = DllStructGetData($tBuffer, "Number")
        Return SetError(_CM_Free_Variable($tBuffer), $iType, $iNum)
    EndIf
EndFunc ;==>_CM_Get_Drive_Disk_Number

; #### FUNCTION ####
; =====================================================================================
; Name  : _CM_Get_Drive_Type
; Description   : Retrieves the type for a drive.
; Parameter(s)  : $sDrive   - Logical partition (for example, C: or C:\), the name string must not begin with "\\.\" or "\\?\".
; Return values : One of the following strings:
;       :   - UNKNOWN
;       :   - NO_ROOT_DIR
;       :   - REMOVABLE
;       :   - FIXED
;       :   - REMOTE
;       :   - CDROM
;       :   - RAMDISK
;       :   - REMOVABLE_DISK
; Author    : Pusofalse
; =====================================================================================
Func _CM_Get_Drive_Type($sDrive)
    Local $iResult, $hDevs, $tDevInfo, $tDevIfInfo, $iFlags, $iDiskNumber, $sDevicePath, $aClass
    Local $aType[7] = ["UNKNOWN", "NO_ROOT_DIR", "REMOVABLE", "FIXED", "REMOTE", "CDROM", "RAMDISK"]

    $iResult = DllCall($Kernel32_DllHandleA, "uint", "GetDriveType", "str", $sDrive)
    If $iResult[0] <> 3 Then Return $aType[$iResult[0]]

    $iDiskNumber = _CM_Get_Drive_Disk_Number($sDrive)
    If $iDiskNumber = -1 Then Return SetError(@error, 0, "UNKNOWN")

    $aClass = _CM_Get_Device_Interface_List_Ex(0, $GUID_DEVINTERFACE_DISK)
    If $aClass[0] = 0 Then Return SetError(@error, 0, "UNKNOWN")

    For $i = 1 To $aClass[0]
        $sDevicePath = StringTrimLeft($aClass[$i], 4)
        If _CM_Get_Drive_Disk_Number($sDevicePath) <> $iDiskNumber Then ContinueLoop
        $hDevs = _SetupDiCreateDeviceInfoList()
        Select
        Case _SetupDiOpenDeviceInterface($hDevs, $aClass[$i], $tDevIfInfo) = 0
            Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), "UNKNOWN")
        Case _SetupDiGetDeviceInterfaceDetail($hDevs, $tDevIfInfo, $tDevInfo) <> $aClass[$i]
            Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), "UNKNOWN")
        Case Else
            $iFlags = _SetupDiGetDeviceRegistryProperty($hDevs, $tDevInfo, 0x20);, 0)
            _SetupDiDestroyDeviceInfoList($hDevs)
            If bitAND($iFlags, 2) = 2 Then
                Return SetError(_CM_Free_Variable($tDevInfo), 0, "REMOVABLE_DISK")
            Else
                Return SetError(_CM_Free_Variable($tDevInfo), 0, "FIXED")
            EndIf
        EndSelect
    Next
    Return "UNKNOWN"
EndFunc ;==>_CM_Get_Drive_Type

; #### FUNCTION ####
; =====================================================================================
; Name  : _CM_Safely_Eject_Disk
; Description   : This function safely ejects an U-styled (or a CD-ROM) disk.
; Parameter(s)  : $sDrive   - Logical partition (for example, C: or C:\), the name string must not begin with "\\.\" or "\\?\".
;       : $fSafely  - If TRUE, the function ejects the disk with removing its subtree first. If FALSE, the function attempts to forcibly remove the disk. Default to TRUE.
; Return values : True indicates success, False indicates failure, in which case, @error is set to a system error code (or a configuration manager error code).
; Author    : Pusofalse
; Remarks   : This function does not support to eject a floppy disk (only CD-ROM and U-styled disk are supported).
; =====================================================================================
Func _CM_Safely_Eject_Disk($sDrive, $fSafely = True)
    Local $hDevs, $tDevInfo, $hDisk, $aInterface, $hDevInst, $iDiskNum
    Local $iAccessMask, $sDevicePath, $fResult, $sDeviceType, $iStatus, $sVeto

    While StringRight($sDrive, 1) = "\"
        $sDrive = StringTrimRight($sDrive, 1)
    WEnd

    $iDiskNum = _CM_Get_Drive_Disk_Number($sDrive)
    $iAccessMask = bitOR($GENERIC_READ, $GENERIC_WRITE)
    $sDeviceType = _CM_Get_Drive_Type($sDrive)
    If $sDeviceType = "CDROM" Then
        $hDisk = _CM_Create_File("\\.\Cdrom" & $iDiskNum, $iAccessMask, 3, 0, 3, 0)
        If $hDisk < 1 Then Return SetError(@error, 0, 0)
        $fResult = _CM_Device_IO_Control($hDisk, 0x2D4808, 0, 0, 0, 0)
        Return SetError(@error, _CM_Close_Handle($hDisk), $fResult)
    ElseIf $sDeviceType <> "REMOVABLE" AND $sDeviceType <> "REMOVABLE_DISK" Then
        Return SetError(50, 0, 0)
    EndIf
    $aInterface = _CM_Get_Device_Interface_List($GUID_DEVINTERFACE_DISK)
    For $i = 1 To $aInterface[0]
        $sDevicePath = StringTrimLeft($aInterface[$i], 4)
        If _CM_Get_Drive_Disk_Number($sDevicePath) = $iDiskNum Then
            ExitLoop _CM_Assign_Var($sDevicePath, $aInterface[$i]) * 0 + 1
        EndIf
    Next
    If StringLeft($sDevicePath, 4) <> "\\?\" Then Return SetError(2, 0, 0)
    If _SetupDiGetDeviceInterfaceDevsEx($sDevicePath, $hDevs, $tDevInfo) = 0 Then
        Return SetError(@error, 0, 0)
    EndIf
    $hDevInst = DllStructGetData($tDevInfo, "DevInst")
    $iStatus = _CM_Get_DevNode_Status($hDevInst)
    If bitAND($iStatus, $DN_REMOVABLE) <> $DN_REMOVABLE Then
        _SetupDiDestroyDeviceInfoList($hDevs)
        $hDevInst =_CM_Get_Parent($hDevInst)
        $iStatus = _CM_Get_DevNode_Status($hDevInst)
        If bitAND($iStatus, $DN_REMOVABLE) <> $DN_REMOVABLE Then
            Return SetError(50, 0, 0)
        EndIf
        If _CM_Create_Device_Devs($hDevInst, $hDevs, $tDevInfo) = 0 Then
            Return SetError(@error, 0, 0)
        EndIf
    EndIf

    If $fSafely = True Then
        _SetupDiApiBufferFree($tDevInfo)
        _SetupDiDestroyDeviceInfoList($hDevs)
        Select
        Case _CM_Query_And_Remove_SubTree($hDevInst, 0, $sVeto) = 0
            Return SetError(@error, 0, 0)
        Case _CM_Request_Device_Eject($hDevInst, $sVeto) = 0
            Return SetError(@error, 0, 0)
        Case FileExists($sDrive) = 1
            Return SetError(997, 0, 0)
        Case Else
            Return SetError(0, 0, 1)
        EndSelect
    ElseIf _SetupDiRemoveDevice($hDevs, $tDevInfo) = 0 Then
        _CM_Assign_Var($tDevInfo, 0, @error, @extended)
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), 0)
    ElseIf FileExists($sDrive) = 1 Then
        _CM_Free_Variable($tDevInfo)
        Return SetError(997, _SetupDiDestroyDeviceInfoList($hDevs), 0)
    Else
        _CM_Free_Variable($tDevInfo)
        Return SetError(0, _SetupDiDestroyDeviceInfoList($hDevs), 1)
    EndIf
EndFunc ;==>_CM_Safely_Eject_Disk
; #### FUNCTION ####
; ================================================================================
; Name  : _CM_Safely_Eject_Device_Ex
; Description   : This function safely ejects a device instance, on local or remote machine.
; Parameter(s)  : $sDeviceID    - Device instance ID string.
;       : $hMachine - A machine handle, zero indicates on local.
; Return values : TRUE is returned if succeeds, FALSE otherwise. @error is set to a CR_* error code for the failure. If $sDeviceID device instance does not support a safe ejection, @error is set to $CR_INVALID_DEVINST. If the device is pending, @error is set to a system error code 997.
; Author    : Pusofalse
; ================================================================================
Func _CM_Safely_Eject_Device_Ex($sDeviceID, $hMachine = 0)
    Local $iStatus, $hDevInst, $sVeto

    $hDevInst = _CM_Locate_DevNode_Ex($sDeviceID, $hMachine)
    If $hDevInst < 1 OR @error Then Return SetError(@error, 0, 0)

    $iStatus = _CM_Get_DevNode_Status_Ex($hDevInst, $hMachine)
    If bitAND($iStatus, $DN_REMOVABLE) <> $DN_REMOVABLE Then
        Return SetError($CR_INVALID_DEVINST, 0, 0)
    EndIf
    _CM_Query_And_Remove_SubTree_Ex($hDevInst, $hMachine, $sVeto)
    _CM_Request_Device_Eject_Ex($hDevInst, $hMachine, $sVeto)
    _CM_Get_DevNode_Status_Ex($hDevInst, $hMachine)
    If @Extended <> 47 Then Return SetError(997, 0, 0)
    Return SetError(0, 0, 1)
EndFunc ;==>_CM_Safely_Eject_Device_Ex

; #### FUNCTION ####
; =================================================================================
; Name  : _CM_Enumerate_Physical_Disks
; Description   : This function lists the physical disks present on the local system.
; Parameter(s)  : This function has no parameters.
; Return values : An array in the form:
;       :   - $aArray[0][0] - The number of returned disks.
;       :   - $aArray[1][0] - 1st disk's path.
;       :   - $aArray[1][1] - 1st disk's description, or friendly name.
;       :   - $aArray[1][2] - 1st physical disk's device instance ID.
;       :   - $aArray[1][3] - Logical drives in 1st physical disk.
;       :   - $aArray[1][4] - U-styled whether.
;       :   - $aArray[2][0] - 2nd disk's path.
; Author    : Pusofalse
; =================================================================================
Func _CM_Enumerate_Physical_Disks()
    Local $iFlags, $aDrive, $hDevs, $tDevInfo, $sDescr, $sName, $aDisk, $aCdrom
    Local $sDevicePath, $sDiskType, $iDiskNum, $sVolume, $sDeviceID, $aResult[1][5]


    $aDrive = DriveGetDrive("ALL")
    For $i = 1 To $aDrive[0]
        $iDiskNum = _CM_Get_Drive_Disk_Number($aDrive[$i])
        If $iDiskNum < 0 Then ContinueLoop
        $sDeviceType = "\\.\PhysicalDrive" & $iDiskNum
        If @Extended = 2 Then $sDeviceType = "\\.\Cdrom" & $iDiskNum
        Assign($sDeviceType, Eval($sDeviceType) & $aDrive[$i] & ", ")
    Next

    $aDisk = _CM_Get_Device_Interface_List($GUID_DEVINTERFACE_DISK)
    $aCdrom = _CM_Get_Device_Interface_List($GUID_DEVINTERFACE_CDROM)
    Redim $aDisk[$aDisk[0] + $aCdrom[0] + 1]
    For $i = 1 To $aCdrom[0]
        $aDisk[$aDisk[0] + $i] = $aCdrom[$i]
    Next
    $aDisk[0] = Ubound($aDisk) - 1
    $aResult[0][0] = $aDisk[0]
    Redim $aResult[$aDisk[0] + 1][5]

    For $i = 1 To $aResult[0][0]
        $sDevicePath = StringTrimLeft($aDisk[$i], 4)
        $iDiskNum = _CM_Get_Drive_Disk_Number($sDevicePath)
        If $iDiskNum < 0 Then ContinueLoop
        $sDeviceType = "\\.\PhysicalDrive" & $iDiskNum
        If @Extended = 2 Then $sDeviceType = "\\.\Cdrom" & $iDiskNum
        If _SetupDiGetDeviceInterfaceDevs($aDisk[$i], $hDevs, $tDevInfo) = 0 Then
            ContinueLoop
        EndIf
        $sDeviceID = _SetupDiGetDeviceInstanceID($hDevs, $tDevInfo)
        $sDescr = _SetupDiGetDeviceRegistryProperty($hDevs, $tDevInfo, 1)
        $sName = _SetupDiGetDeviceRegistryProperty($hDevs, $tDevInfo, 0xC)
        $iFlags = _SetupDiGetDeviceRegistryProperty($hDevs, $tDevInfo, 0x20);, 0)
        _SetupDiDestroyDeviceInfoList($hDevs)
        If $sName <> "" Then $sDescr = $sName
        If $sDescr = "" Then $sDescr = "(Unknown)"
        $aResult[$i][0] = $sDeviceType  ; disk type
        $aResult[$i][1] = $sDescr   ; description
        $aResult[$i][2] = $sDeviceID    ; device instance indenfier string
        $aResult[$i][3] = StringTrimRight(Eval($sDeviceType), 2)    ; logical drives
        $aResult[$i][4] = bitAND($iFlags, 2) = 2    ; U-styled
    Next
    Return SetExtended(_SetupDiApiBufferFree($tDevInfo), $aResult)
EndFunc ;==>_CM_Enumerate_Physical_Disks

; #### FUNCTION ####
; ============================================================================
; Name  : _CM_Enable_Privileges
; Description   : Enable required privileges for calling process, for sub-sequent calls.
; Parameter(s)  : This function has no parameters.
; Return values : Returns TRUE if succeeds, FALSE otherwise. @error is set to system error code for failure and success.
; Author    : Pusofalse
; ============================================================================
Func _CM_Enable_Privileges()
    Local $hToken, $fResult, $aPriv[7][2] = [[$SE_DEBUG_NAME, 2], _
            [$SE_RESTORE_NAME, 2], _
            [$SE_BACKUP_NAME, 2], _
            [$SE_LOAD_DRIVER_NAME, 2], _
            [$SE_UNDOCK_NAME, 2], _
            [$SE_SECURITY_NAME, 2], _
            [$SE_TAKE_OWNERSHIP_NAME, 2]]
    $hToken = _OpenProcessToken(-1)
    $fResult = _AdjustTokenPrivileges($hToken, $aPriv)
    Return SetError(@error, _LsaCloseHandle($hToken), $fResult)
EndFunc ;==>_CM_Enable_Privileges

Func _CM_Lock_Cdrom($sDrive, $fLock = True)
    Local $fResult, $hCdrom, $iMask, $tLock

    $iDiskNum = _CM_Get_Drive_Disk_Number($sDrive)
    If @extended <> 2 OR $iDiskNum < 0 Then Return SetError(87, 0, 0)

    $iMask = bitOR($GENERIC_READ, $GENERIC_WRITE)
    $hCdrom = _CM_Create_File("\\.\Cdrom" & $iDiskNum, $iMask, 3, 0, 3, 0)
    If $hCdrom < 1 Then Return SetError(@error, 0, 0)

    $tLock = DllStructCreate("int fLock")
    DllStructSetData($tLock, "fLock", Number(Not (Not $fLock)))

    $fResult = _CM_Device_IO_Control($hCdrom, 0x2D4804, $tLock, 4, 0, 0)
    Return SetError(@error, _CM_Close_Handle($hCdrom), $fResult)
EndFunc ;==>_CM_Lock_Cdrom

; #### FUNCTION ####
; =====================================================================================
; Name  : _CM_Cdrom_Known_Good_Digital_Playback
; Description   : Determines whether the specified CD-ROM or DVD drive has digital playback that is known to be good.
; Parameter(s)  : $hDevs    - Handle to the device information set.
;       : $pSP_DEVINFO_DATA - A pointer to an SP_DEVINFO_DATA structure defines the device information element.
; Return values : If digital playback is good, the return value is TRUE. Otherwise, the return value is FALSE.
; Author    : Pusofalse
; =====================================================================================
Func _CM_Cdrom_Known_Good_Digital_Playback($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($STORP_DllHandle, "int", "CdromKnownGoodDigitalPlayback", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA)
    Return $iResult[0]
EndFunc ;==>_CM_Cdrom_Known_Good_Digital_Playback

Func _CM_Enable_Cdrom_Digital_Playback($hDevs, $pSP_DEVINFO_DATA, $fForce = True)
    Local $iResult

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($STORP_DllHandle, "long", "CdromEnableDigitalPlayback", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, "int", $fForce)
    Return SetError($iResult[0], 0, $iResult[0] = $ERROR_SUCCESS)
EndFunc ;==>_CM_Enable_Cdrom_Digital_Playback

Func _CM_Disable_Cdrom_Digital_Playback($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($STORP_DllHandle, "long", "CdromDisableDigitalPlayback", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA)
    Return SetError($iResult[0], 0, $iResult[0] = $ERROR_SUCCESS)
EndFunc ;==>_CM_Disbable_Cdrom_Digital_Playback

Func _CM_Is_Cdrom_Digital_Playback_Enabled($hDevs, $pSP_DEVINFO_DATA)
    Local $iResult

    If IsDllStruct($pSP_DEVINFO_DATA) Then
        $pSP_DEVINFO_DATA = DllStructGetPtr($pSP_DEVINFO_DATA)
    EndIf
    $iResult = DllCall($STORP_DllHandle, "long", "CdromIsDigitalPlaybackEnabled", _
            "ptr", $hDevs, "ptr", $pSP_DEVINFO_DATA, "int*", 0)
    Return SetError($iResult[0], 0, $iResult[3])
EndFunc ;==>_CM_Is_Cdrom_Digital_Playback_Enabled

Func _CM_Free_Resource_Conflict_Handle($hConflict)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Free_Resource_Conflict_Handle", "ptr", $hConflict)
    Return SetError($iResult[0], 0, $iResult[0] = $CR_SUCCESS)
EndFunc ;==>_CM_Free_Resource_Conflict_Handle

Func _CM_Get_Resource_Conflict_Count($hConflict)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Resource_Conflict_Count", _
            "ptr", $hConflict, "ulong*", 0)
    Return SetError($iResult[0], 0, $iResult[2])
EndFunc ;==>_CM_Get_Resource_Conflict_Count

Func _CM_Get_Resource_Conflict_Details($hConflict, $iIndex = 0)
    Local $iResult, $tDetail
    $tDetail = DllStructCreate($tagCONFLICT_DETAILS)
    DllStructSetData($tDetail, "Mask", 13)
    DllStructSetData($tDetail, "Size", DllStructGetSize($tDetail))
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Resource_Conflict_Details", _
            "ptr", $hConflict, "ulong", $iIndex, "ptr", DllStructGetPtr($tDetail))
    Return SetError($iResult[0], 0, $tDetail)
EndFunc ;==>_CM_Get_Resource_Conflict_Details

Func _CM_Query_Resource_Conflict_List($hDevInst, $iResourceID, $pData, $iSizeData, $hMachine = 0)
    Local $iResult

    If IsDllStruct($pData) Then $pData = DllStructGetPtr($pData)
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Query_Resource_Conflict_List", "ptr*", 0, _
            "dword", $hDevInst, "int", $iResourceID, "ptr", $pData, _
            "ulong", $iSizeData, "ulong", 0, "ptr", $hMachine)
    Return SetError($iResult[0], 0, $iResult[1])
EndFunc ;==>_CM_Query_Resource_Conflict_List

Func _CM_Validate_Res_Des_Conflict_Ex($hDevInst, $iResourceID, $pBuffer, $iSizeofBuffer, $hMachine = 0)
    Local $hConflict, $aConflict[1], $iCount, $tDetail, $sDescr, $sName, $iFlags, $hDevInst1

    $hConflict = _CM_Query_Resource_Conflict_List($hDevInst, $iResourceID, $pBuffer, $iSizeofBuffer, $hMachine)
    $iCount = _CM_Get_Resource_Conflict_Count($hConflict)
    $aConflict[0] = $iCount
    Redim $aConflict[$iCount + 1]
    For $i = 1 To $iCount
        $tDetail = _CM_Get_Resource_Conflict_Details($hConflict, $i - 1)
        $iFlags = DllStructGetData($tDetail, "Flags")
        $hDevInst1 = DllStructGetData($tDetail, "DevInst")

        If $iFlags = 0 Then
            $sDescr = _CM_Get_DevNode_Registry_Property($hDevInst1, 1)
            $sName = _CM_Get_DevNode_Registry_Property($hDevInst1, 0xD)
            If $sName <> "" Then $sDescr = $sName
            If $sDescr = "" Then $sDescr = "[Unknown device]"
            $aConflict[$i] = $sDescr & " , " & _CM_Get_Device_ID_Ex($hDevInst1, $hMachine)
            If $aConflict[$i] = "[Unknown device] , " Then
                $aConflict[$i] = DllStructGetData($tDetail, "Descr")
                If $aConflict[$i] = "" Then $aConflict[$i] = "The resource is unavailable"
            EndIf
        ElseIf bitAND($iFlags, $CM_CDFLAGS_DRIVER) = $CM_CDFLAGS_DRIVER Then
            $aConflict[$i] = DllStructGetData($tDetail, "Descr")
        ElseIf bitAND($iFlags, $CM_CDFLAGS_RESERVED) = $CM_CDFLAGS_RESERVED Then
            $aConflict[$i] = "Unknown"
        ElseIf bitAND($iFlags, $CM_CDFLAGS_ROOT_OWNED) = 2 Then
            $aConflict[$i] = "Owned by the root device"
        Else
            $aConflict[$i] = "The resource is unavailable"
        EndIf
        If $aConflict[$i] = "" Then $aConflict[$i] = "Unknown"
        _CM_Free_Variable($tDetail)
    Next
    _CM_Free_Resource_Conflict_Handle($hConflict)
    Return $aConflict
EndFunc ;==>_CM_Validate_Res_Des_Conflict_Ex

; #### FUNCTION ####
; =====================================================================================
; Name  : _CM_Add_Res_Des_IRQ_Array_Ex
; Description   : This function newly adds one or more IRQ resource descriptors to a device instance.
; Parameter(s)  : $hDevInst - Handle to the device instance.
;       : $hLogConf - Handle to the logical configuration.
;       : $aIRQDes  - An array in the form:
;       :   $aIRQDes[0][0] - 1st interrupt request line (IRQ) number.
;       :   $aIRQDes[0][1] - Flags for 1st IRQ, see Flags for IRQ_DES.Flags for correct values.
;       :   $aIRQDes[0][2] - A bitmask representing the processor affinity of the IRQ line that is allocated to the device. Bit zero represents the first processor, bit two the second, and so on. Set this value to -1 to represent all processors.
;       :   $aIRQDes[0][3] - On input, this value specifies a BOOL value indicates that whether the function creates the resource descriptor forcibly by ignoring the conflict list, TRUE indicates to ignore. On output, this value receives the conflict list string text, splitted by @CRLF, if no conflicts against with the other device instance, this value is set to NULL.
;       :   $aIRQDes[0][4] - A value receives the handle to the newly created resource descriptor, when you finished with this handle, call _CM_Free_Res_Des_Handle (Not _CM_Free_Res_Des) function to free it.
;       :   $aIRQDes[1][0] - 2nd interrupt request line (IRQ) number.
;       :   - ... ...
;       : $hMachine - Supplies a handle to the machine on where the function to execute, typically returned by _CM_Connect_Machine. A value of ZERO indicates on local system.
; Return values : If $aIRQDes parameter is incorrect, @error is set to CR_INVALID_DATA. Caller can determine the result with $aIRQDes[n][3].
; Author    : Pusofalse
; =====================================================================================
Func _CM_Add_Res_Des_IRQ_Array_Ex($hDevInst, $hLogConf, ByRef $aIRQDes, $hMachine = 0)
    Local $iFlags, $pBuffer, $tBuffer, $aConflict

    If Ubound($aIRQDes, 0) <> 2 Then Return SetError($CR_INVALID_DATA, 0, 0)
    If Ubound($aIRQDes, 2) <> 5 Then Return SetError($CR_INVALID_DATA, 0, 0)

    $pBuffer = _CM_Heap_Alloc(36)
    $tBuffer = DllStructCreate($tagIRQ_DES, $pBuffer)
    DllStructSetData($tBuffer, "Count", 0)
    DllStructSetData($tBuffer, "Type", 12)

    For $i = 0 To Ubound($aIRQDes) - 1
        $iFlags = $aIRQDes[$i][3]
        $aIRQDes[$i][3] = ""
        If $aIRQDes[$i][1] = -1 Then $aIRQDes[$i][1] = 1
        DllStructSetData($tBuffer, "AllocNum", $aIRQDes[$i][0])
        DllStructSetData($tBuffer, "Flags", $aIRQDes[$i][1])
        DllStructSetData($tBuffer, "Affinity", $aIRQDes[$i][2])

        $aConflict = _CM_Validate_Res_Des_Conflict_Ex($hDevInst, $RESTYPE_IRQ, $pBuffer, 36, $hMachine)
        If $aConflict[0] Then
            $aIRQDes[$i][4] = 0
            For $j = 1 To $aConflict[0]
                $aIRQDes[$i][3] &= $aConflict[$j] & @CRLF
            Next
            If $iFlags = False Then ContinueLoop
        EndIf
        $aIRQDes[$i][4] = _CM_Add_Res_Des_Ex($hLogConf, $RESTYPE_IRQ, $pBuffer, 36, $hMachine)
    Next
    _CM_Heap_Free($pBuffer)
    Return _CM_Free_Variable($tBuffer)
EndFunc ;==>_CM_Add_Res_Des_IRQ_Array_Ex

; #### FUNCTION ####
; =====================================================================================
; Name  : _CM_Add_Res_Des_IO_Array_Ex
; Description   : This function newly creates one or more I/O resource descriptors for a device instance.
; Parameter(s)  : $hDevInst - Supplies a handle to the device instance.
;       : $hLogConf - Supplies a handle to the logical configuration.
;       : $aIORes       - An array in the form:
;       :   - $aIORes[0][0] - Allocation base address for 1st IO resource.
;       :   - $aIORes[0][1] - Allocation end address for 1st IO resource.
;       :   - $aIORes[0][2] - Allocation flags for 1st IO resource, see Flags for IO_DES.Flags for correct values.
;       :   - $aIORes[0][3] - On input, this value specifies a BOOL value indicates that whether ignores the conflict, TRUE indicates forcibly to create the IO resource, ignoring the conflict. On output, this value receives a conflict list string text splitted by @CRLF, if no conflicts, this value is set to NULL.
;       :   - $aIORes[0][4] - OUT-typed, receives the handle to the newly created resource descriptor. When you finished with this handle, call _CM_Free_Res_Des_Handle function to close it. If the resource conflicts to other device instance, and $aIORes[0][3] specifies False, this value is set to zero.
;       :   - $aIORes[1][0] - Allocation base address for 2nd IO resource.
;       :   - ... ...
;       : $hMachine - A handle to a machine indicates on where the function executes, a value of zero indicates local.
; Author    : Pusofalse
; =====================================================================================
Func _CM_Add_Res_Des_IO_Array_Ex($hDevInst, $hLogConf, ByRef $aIORes, $hMachine = 0)
    Local $iFlags, $pBuffer, $tBuffer, $aConflict

    If Ubound($aIORes, 0) <> 2 Then Return SetError($CR_INVALID_DATA, 0, 0)
    If Ubound($aIORes, 2) <> 5 Then Return SetError($CR_INVALID_DATA, 0, 0)

    $pBuffer = _CM_Heap_Alloc(68)
    $tBuffer = DllStructCreate($tagIO_DES, $pBuffer)
    DllStructSetData($tBuffer, "Count", 0)
    DllStructSetData($tBuffer, "Type", 40)

    For $i = 0 To Ubound($aIORes) - 1
        $iFlags = $aIORes[$i][3]
        $aIORes[$i][3] = ""
        If $aIORes[$i][2] = -1 Then $aIORes[$i][2] = 33
        DllStructSetData($tBuffer, "AllocBase", $aIORes[$i][0])
        DllStructSetData($tBuffer, "AllocEnd", $aIORes[$i][1])
        DllStructSetData($tBuffer, "Flags", $aIORes[$i][2])

        $aConflict = _CM_Validate_Res_Des_Conflict_Ex($hDevInst, $RESTYPE_IO, $pBuffer, 68, $hMachine)
        If $aConflict[0] Then
            $aIORes[$i][4] = 0
            For $j = 1 To $aConflict[0]
                $aIORes[$i][3] &= $aConflict[$j] & @CRLF
            Next
            If $iFlags = False Then ContinueLoop
        EndIf
        $aIORes[$i][4] = _CM_Add_Res_Des_Ex($hLogConf, $RESTYPE_IO, $pBuffer, 68, $hMachine)
    Next
    _CM_Heap_Free($pBuffer)
    Return _CM_Free_Variable($tBuffer)
EndFunc ;==>_CM_Add_Res_Des_IO_Array_Ex

; #### FUNCTION ####
; ================================================================================
; Name  : _CM_Add_Res_Des_Mem_Array_Ex
; Description   : This function newly creates one or more memory resource descriptor for a device instance.
; Parameter(s)  : $hDevInst - Supplies a device instance handle.
;       : $hLogConf - Supplies a logical configuration handle.
;       : ByRef $aMemRes    - An array in the following format:
;       :   - $aMemRes[0][0] - Allocation base address for 1st memory resource.
;       :   - $aMemRes[0][1] - Allocation end address for 1st memory resource.
;       :   - $aMemRes[0][2] - Flags for 1st memory resource allocation.
;       :   - $aMemRes[0][3] - On INPUT, specifies a BOOL value indicates that whether forcibly creates the resource descriptor by ignoring the conflicts with other device instances, A value of TRUE indicates ignores the conflict to create forcibly. On OUTPUT, this value receives the conflict list (in string format, splitted by @CRLF), if the value is set to NULL, no conflicts are present.
;       :   - $aMemRes[0][4] - Receives the handle to the newly created resource descriptor. When you no longer use this returned handle, call _CM_Free_Res_Des_Handle (but not _CM_Free_Res_Des_Ex) function to release it. If the resource specified in $aMemRes (elements 0 to 2) is conflicted to other device instance's logical configuration, and $aMemRes[0][3] specifies FALSE, or an error occured during the call, this value is set to zero.
;       :   - $aMemRes[1][0] - Allocation base address for 2nd memory resource.
;       :   - ... ...
;       : $hMachine - A handle to a machine on which the function to execute, zero indicates local system.
; Return values : $aMemRes[x][4] receives the handle to the newly created resource descriptor.
; Author    : Pusofalse
; ================================================================================
Func _CM_Add_Res_Des_Mem_Array_Ex($hDevInst, $hLogConf, ByRef $aMemRes, $hMachine = 0)
    Local $iResult, $aConflict, $iFlags, $tBuffer, $pBuffer

    If Ubound($aMemRes, 0) <> 2 Then Return SetError($CR_INVALID_DATA, 0, 0)
    If Ubound($aMemRes, 2) <> 5 Then Return SetError($CR_INVALID_DATA, 0, 0)

    $pBuffer = _CM_Heap_Alloc(68)
    $tBuffer = DllStructCreate($tagMEM_DES, $pBuffer)
    DllStructSetData($tBuffer, "Count", 0)
    DllStructSetData($tBuffer, "Reserved", 0)
    DllStructSetData($tBuffer, "Type", 36)

    For $i = 0 To Ubound($aMemRes) - 1
        $iFlags = $aMemRes[$i][3]
        $aMemRes[$i][3] = ""
        If $aMemRes[$i][2] = -1 Then $aMemRes[$i][2] = bitOR($FMD_RAM, $FMD_32)
        DllStructSetData($tBuffer, "AllocBase", $aMemRes[$i][0])
        DllStructSetData($tBuffer, "AllocEnd", $aMemRes[$i][1])
        DllStructSetData($tBuffer, "Flags", $aMemRes[$i][2])
        $aConflict = _CM_Validate_Res_Des_Conflict_Ex($hDevInst, $RESTYPE_MEM, $pBuffer, 68, $hMachine)
        If $aConflict[0] Then
            $aMemRes[$i][4] = 0
            For $j = 1 To $aConflict[0]
                $aMemRes[$i][3] &= $aConflict[$j] & @CRLF
            Next
            If $iFlags = False Then ContinueLoop
        EndIf
        $aMemRes[$i][4] = _CM_Add_Res_Des_Ex($hLogConf, $RESTYPE_MEM, $pBuffer, 68, $hMachine)
    Next
    _CM_Heap_Free($pBuffer)
    Return _CM_Free_Variable($tBuffer) + 1
EndFunc ;==>_CM_Add_Res_Des_Mem_Array_Ex

; #### FUNCTION ####
; ================================================================================
; Name  : _CM_Add_Res_Des_DMA_Array_Ex
; ================================================================================
Func _CM_Add_Res_Des_DMA_Array_Ex($hDevInst, $hLogConf, ByRef $aDmaRes, $hMachine = 0)
    Local $iResult, $tBuffer, $iFlags, $pBuffer

    If Ubound($aDmaRes, 0) <> 2 Then Return SetError($CR_INVALID_DATA, 0, 0)
    If Ubound($aDmaRes, 2) <> 5 Then Return SetError($CR_INVALID_DATA, 0, 0)

    $pBuffer = _CM_Heap_Alloc(28)
    $tBuffer = DllStructCreate($tagDMA_DES, $pBuffer)
    DllStructSetData($tBuffer, "Count", 0)
    DllStructSetData($tBuffer, "Type", 12)

    For $i = 0 To Ubound($aDmaRes) - 1
        $iFlags = $aDmaRes[$i][3]
        $aDmaRes[$i][3] = ""
        If $aDmaRes[$i][2] = -1 Then $aDmaRes[$i][2] = $FDD_BYTE_AND_WORD
        DllStructSetData($tBuffer, "Flags", $aDmaRes[$i][2])
        DllStructSetData($tBuffer, "AllocChannel", $aDmaRes[$i][1])

        $aConflict = _CM_Validate_Res_Des_Conflict_Ex($hDevInst, $RESTYPE_DMA, $pBuffer, 28, $hMachine)
        If $aConflict[0] Then
            $aDmaRes[$i][4] = 0
            For $j = 1 To $aConflict[0]
                $aDmaRes[$i][3] &= $aConflict[$j] & @CRLF
            Next
            If $iFlags = False Then ContinueLoop
        EndIf
        $aDmaRes[$i][4] = _CM_Add_Res_Des_Ex($hLogConf, $RESTYPE_DMA, $pBuffer, 28, $hMachine)
    Next
    _CM_Heap_Free($pBuffer)
    Return _CM_Free_Variable($tBuffer)
EndFunc ;==>_CM_Add_Res_Des_DMA_Array_Ex

; #### FUNCTION ####
; ================================================================================
; Name  : _CM_Query_DevNode_Resource_Conflicts_Ex
; Description   : Retrieves the conflict list for a device instance logical configuration.
; Parameter(s)  : $hDevInst - A handle to the device instance, typically returned by _CM_Locate_DevNode function.
;       : $hMachine - A handle to the machine, returned by _CM_Connect_Machine, can be a value of zero, which indicates to execute on local system.
; Return values : If succeeds, returns an array in the following form:
;       : $aArray[0] - The number of conflicts.
;       : $aArray[1] - 1st conflict detail.
;       : $aArray[2] - 2nd conflict detail.
;       : $aArray[n] - nth conflict detail.
;       : ... ...
;       : If no conflicts, $aArray[0] and @error are both set to zero.
;       : If fails, @error is set to a CR_* error code, and $aArray[0] is set to 0.
; Author    : Pusofalse
; ================================================================================
Func _CM_Query_DevNode_Resource_Conflicts_Ex($hDevInst, $hMachine = 0)
    Local $hLogConf, $hResDes, $aResDes, $iResourceID
    Local $aConflict, $pBuffer, $iSizeofBuffer, $aResult[1] = [0]

    $aResDes = _CM_Get_Device_Resources_Ex($hDevInst, $hMachine, $RESTYPE_ALL)
    If $aResDes[0][0] = 0 Then Return SetError(@error, 0, $aResult)
    $hLogConf = _CM_Get_First_Log_Conf_Ex($hDevInst, $hMachine, $ALLOC_LOG_CONF)
    If @error OR $hLogConf < 1 Then
        $hLogConf = _CM_Get_First_Log_Conf_Ex($hDevInst, $hMachine, $BOOT_LOG_CONF)
        If @error OR $hLogConf < 1 Then Return SetError(@error, 0, $aResult)
    EndIf
    For $i = 1 To $aResDes[0][0]
        $hResDes = _CM_Get_Res_Des_By_Index_Ex($hLogConf, $aResDes[$i][2], $hMachine)
        $iResourceID = @Extended
        $pBuffer = _CM_Get_Res_Des_Data_Ex($hResDes, $hMachine)
        $iSizeofBuffer = _CM_Heap_Size($pBuffer)
        _CM_Free_Res_Des_Handle($hResDes)
        $aConflict = _CM_Validate_Res_Des_Conflict_Ex($hDevInst, $iResourceID, $pBuffer, $iSizeofBuffer, $hMachine)
        If $aConflict[0] = 0 Then ContinueLoop
        For $j = 1 To $aConflict[0]
            $aResult[0] += 1
            Redim $aResult[$aResult[0] + 1]
            $aResult[$aResult[0]] = $aConflict[$j]
        Next
    Next
    Return SetError(@error, _CM_Free_Log_Conf_Handle($hLogConf), $aResult)
EndFunc ;==>_CM_Query_DevNode_Resource_Conflicts_Ex

; #### FUNCTION ####
; ================================================================================
; Name  : _CM_Get_Device_ID_By_IRQ_Ex
; Description   : Specifies an IRQ number, retrieves the device instances that deploy the given IRQ.
; Parameter(s)  : $aDeviceIDs   - A variable receives the device instances identifier strings.
;       : $iIRQ - IRQ Number.
;       : $sDeviceID    - Specifies an existing device instance identifier string from which the function begins to check, can be NULL specified.
;       : $hMachine - Supplies a machine handle.
; Return values : The number of device instances that deploy the specified IRQ. $aDeviceIDs is set to an array in the form:
;       : $aDeviceIDs[0] - Number of device instances.
;       : $aDeviceIDs[1] - 1st device instance.
;       : ......
; Author    : Pusofalse
; ================================================================================
Func _CM_Get_Device_ID_By_IRQ_Ex(ByRef $aDeviceIDs, $iIRQ, $sDeviceID = "", $hMachine = 0)
    Local $aDeviceID[1], $hDevInst

    $hDevInst = _CM_Locate_DevNode_Ex($sDeviceID, $hMachine)
    _CM_Get_Device_ID_By_IRQ_Ex_($iIRQ, $aDeviceID, $hDevInst, $hMachine)
    $aDeviceIDs = $aDeviceID
    Return $aDeviceIDs[0]
EndFunc ;==>_CM_Get_Device_ID_By_IRQ_Ex

; #### FUNCTION ####
; ================================================================================
; The _CM_Get_Device_ID_By_IRQ_Ex_ function is only internal used by _CM_Get_Device_ID_By_IRQ_Ex. Do not use it.
; ================================================================================
Func _CM_Get_Device_ID_By_IRQ_Ex_($iIRQ, ByRef $aDeviceID, $hDevInst, $hMachine)
    Local $aChild, $aResDes

    $aChild = _CM_Enumerate_Children_Ex($hDevInst, $hMachine)
    For $i = 1 To $aChild[0]
        $hDevInst = _CM_Locate_DevNode_Ex($aChild[$i], $hMachine)
        $aResDes = _CM_Get_Device_Resources_Ex($hDevInst, $hMachine)
        For $j = 1 To $aResDes[0][0]
            If $aResDes[$j][0] <> "IRQ" Then ContinueLoop
            If Number($aResDes[$j][1]) <> $iIRQ Then ContinueLoop
            $aDeviceID[0] += 1
            Redim $aDeviceID[$aDeviceID[0] + 1]
            $aDeviceID[$aDeviceID[0]] = $aChild[$i]
            ExitLoop
        Next
        _CM_Get_Device_ID_By_IRQ_Ex_($iIRQ, $aDeviceID, $hDevInst, $hMachine)
    Next
EndFunc ;==>_CM_Get_Device_ID_By_IRQ_Ex_

Func _CM_Get_Device_Driver_Files($sDeviceID)
    Local $tDevInfo, $hDevs, $tDrvInfo, $tDrvDetail, $sInf, $sSection, $aDriver[1]
    Local $sClass, $aVal, $iVar, $aSection, $aSect, $hDevInst

    Select
    Case _SetupDiCreateDeviceDevs($sDeviceID, $hDevs, $tDevInfo) = 0
        Return SetError(@error, 0, $aDriver)
    Case _SetupDiBuildDriverInfoList($hDevs, $tDevInfo) = 0
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), $aDriver)
    Case _SetupDiSelectBestCompatDrv($hDevs, $tDevInfo) = 0
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), $aDriver)
    Case _SetupDiGetSelectedDriver($hDevs, $tDevInfo, $tDrvInfo) = 0
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), $aDriver)
    Case _SetupDiGetDriverInfoDetail($hDevs, $tDevInfo, $tDrvInfo, $tDrvDetail) = 0
        Return SetError(@error, _SetupDiDestroyDeviceInfoList($hDevs), $aDriver)
    EndSelect

    $sInf = DllStructGetData($tDrvDetail, "InfFileName")
    $sSection = DllStructGetData($tDrvDetail, "SectionName")

    $sGuid = _SetupDiGetDeviceRegistryProperty($hDevs, $tDevInfo, 8)
    _SetupDiApiBufferFree($tDrvInfo)
    _SetupDiApiBufferFree($tDevInfo)
    _SetupDiApiBufferFree($tDrvDetail)
    _SetupDiDestroyDeviceInfoList($hDevs)
    $sClass = _SetupDiClassNameFromGuid($sGuid)
    If $sClass = "" Then Return SetError(@error, 0, $aDriver)
    If Not FileExists($sInf) Then Return SetError(2, 0, $aDriver)
    $hInf = _SetupOpenInfFile($sInf, $sClass, 2)
    $aSection = IniReadSection($sInf, $sSection)
    If @error Then
        $sSection &= ".NT"
        $aSection = IniReadSection($sInf, $sSection)
        If @error Then
            $sSection &= "x86"
            $aSection = IniReadSection($sInf, $sSection)
            If @error Then Return SetError(2, _SetupCloseInfFile($hInf), $aDriver)
        EndIf
    EndIf

    For $i = 1 To $aSection[0][0]
        If $aSection[$i][0] <> "CopyFiles" Then ContinueLoop
        $pContext = _SetupGetLineByIndex($hInf, $sSection, $i - 1)
        $aSect = _SetupGetLineText($pContext)
        If StringLeft($aSect, 1) = "@" Then
            $sPath = _SetupGetTargetPath($hInf, $pContext)
            $aDriver[0] += 1
            Redim $aDriver[$aDriver[0] + 1]
            $aDriver[$aDriver[0]] = $sPath & "\" & StringTrimLeft($aSect, 1)
            $aSect = ""
        EndIf
        _HeapFree($pContext)
        If $aSect = "" Then ContinueLoop
        $aSect = StringSplit($aSect, ",")
        For $j = 1 To $aSect[0]
            If $aSect[$j] = "" Then ContinueLoop
            For $k = 0 To _SetupGetLineCount($hInf, $aSect[$j]) - 1
                $pContext = _SetupGetLineByIndex($hInf, $aSect[$j], $k)
                $sPath = _SetupGetTargetPath($hInf, $pContext)
                $sName = _SetupGetLineText($pContext)
                If StringInStr($sName, ",") Then
                    $iVal = StringInStr($sName,",") - 1
                    $sName = StringLeft($sName, $iVal)
                EndIf
                $aDriver[0] += 1
                Redim $aDriver[$aDriver[0] + 1]
                $aDriver[$aDriver[0]] = $sPath & "\" & $sName
                _HeapFree($pContext)
            Next
        Next
    Next
    Return SetExtended(_SetupCloseInfFile($hInf), $aDriver)
EndFunc ;==>_CM_Get_Device_Driver_Files

; #### FUNCTION ####
; ================================================================================
; Name  : _CM_Wait_No_Pending_Install_Events
; Description   : This function waits until there are no pending device installation activities for the PnP manager to perform.
; Parameter(s)  : $iTimeout - Specifies a time-out interval, in milliseconds. If $iTimeout is zero, the function tests whether there are pending installation events and returns immediately. If $iTimeout is -1, the function's time-out interval never elapses. For all other values, the function returns when the specified interval elapses, even if there are still pending installation events.
; Return values : 0 - There are no pending installation activities.
;       : 20 - The time-out interval elapsed, and installation activities are still pending.
;       : -1 - The function failed, the additional error code is set in @error.
; Author    : Pusofalse
; ================================================================================
Func _CM_Wait_No_Pending_Install_Events($iTimeout = -1)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "dword", "CMP_WaitNoPendingInstallEvents", _
            "dword", $iTimeout)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_CM_Wait_No_Pending_Install_Events

Func _SetupCloseInfFile($hInf)
    DllCall($SETUPAPI_DllHandle, "none", "SetupCloseInfFile", "long", $hInf)
EndFunc ;==>_SetupCloseInfFile

Func _SetupGetLineText($pContext)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupGetLineText", "ptr", $pContext, _
            "long", 0, "str", "", "str", "", "str", "", "dword", 260, "dword*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[5])
EndFunc ;==>_SetupGetLineText

Func _SetupGetLineByIndex($hInf, $sSection, $iIndex)
    Local $hContext, $tContext, $pContext

    $pContext = _CM_Heap_Alloc(4 * 4)

    $hContext = DllCall($SETUPAPI_DllHandle, "int", "SetupGetLineByIndex", "long", $hInf, _
            "str", $sSection, "dword", $iIndex, "ptr", $pContext)
    If $hContext[0] = 1 Then
        Return SetExtended(1, $pContext)
    Else
        Return SetError(_CM_Get_Last_Error(), _CM_Heap_Free($pContext) * 0, 0)
    EndIf
EndFunc ;==>_SetupGetLineByIndex

Func _SetupGetTargetPath($hInf, $pContext)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "int", "SetupGetTargetPath", "long", $hInf, _
            "ptr", $pContext, "str", "", "str", "", "dword", 260, "dword*", 0)
    Return SetError(_CM_Get_Last_Error(), $iResult[0], $iResult[4])
EndFunc ;==>_SetupGetTargetPath

Func _SetupOpenInfFile($sInfFile, $sClass, $iStyle)
    Local $hInf

    $hInf = DllCall($SETUPAPI_DllHandle, "long", "SetupOpenInfFile", "str", $sInfFile, _
            "str", $sClass, "int", $iStyle, "uint*", 0)
    Return SetError(_CM_Get_Last_Error(), $hInf[4], $hInf[0])
EndFunc ;==>_SetupOpenInfFile

Func _SetupGetLineCount($hInf, $sSection)
    Local $iResult
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "SetupGetLineCount", _
            "long", $hInf, "str", $sSection)
    Return SetError(_CM_Get_Last_Error(), 0, $iResult[0])
EndFunc ;==>_SetupGetLineCount

Func _CM_Get_Hardware_Profile_Info($iIndex)
    Local $iResult, $tBuffer
    $tBuffer = DllStructCreate($tagHWPROFILE_INFO)
    $pBuffer = DllStructGetPtr($tBuffer)
    $iResult = DllCall($SETUPAPI_DllHandle, "long", "CM_Get_Hardware_Profile_Info", _
            "ulong", $iIndex, "ptr", $pBuffer, "ulong", 0)
    If $iResult[0] Then $tBuffer = 0
    Return SetError($iResult[0], 0, $tBuffer)
EndFunc ;==>_CM_Get_Hardware_Profile_Info


Func _CM_Format_Resource_Flags(ByRef $hResDes, $iResourceID, $pBuffer)
    Local $tBuffer, $sVar, $sMsg, $iFlags

    If Not IsPtr($pBuffer) Then Return SetError(87, 0, "Undetermined. (Code -1)")
    Switch $iResourceID
    Case $RESTYPE_MEM
        $sVar = $tagMEM_DES
    Case $RESTYPE_IO
        $sVar = $tagIO_DES
    Case $RESTYPE_IRQ
        $sVar = $tagIRQ_DES
    Case $RESTYPE_DMA
        $sVar = $tagDMA_DES
    Case $RESTYPE_BUSNUMBER
        $sVar = $tagBUSNUMBER_DES
    Case Else
        Return SetError(50, 0, "Undetermined. (Code -1)")
    EndSwitch
    $tBuffer = DllStructCreate($sVar, $pBuffer)
    $iFlags = DllStructGetData($tBuffer, "Flags")

    Switch $iResourceID
    Case $RESTYPE_MEM
        If $iFlags = 0 Then Return "Read-only. (Code 0)"
        If bitAND($iFlags, 1) Then $sMsg &= "Writable. (Code 1)"
        If bitAND($iFlags, 2) Then $sMsg &= "32-bit addressing. (Code 2)"
        If bitAND($iFlags, 4) Then $sMsg &= "Prefetchable. (Code 4)"
        If bitAND($iFlags, 8) Then $sMsg &= "Write-only. (Code 8)"
        If bitAND($iFlags, 16) Then $sMsg &= "Combined-write caching. (Code 16)"
        If bitAND($iFlags, 32) Then $sMsg &= "Cacheable. (Code 32)"
    Case $RESTYPE_IO
        If $iFlags = 0 Then Return "Accessed in memory address space. (Code 0)"
        If bitAND($iFlags, 1) Then $sMsg &= "Accessed in I/O address space. (Code 1)"
        If bitAND($iFlags, 4) Then $sMsg &= "Decodes 10-bits. (Code 4)"
        If bitAND($iFlags, 8) Then $sMsg &= "Decodes 12-bits. (Code 8)"
        If bitAND($iFlags, 16) Then $sMsg &= "Decodes 16-bits. (Code 16)"
        If bitAND($iFlags, 32) Then $sMsg &= "Positive decode. (Code 32)"
    Case $RESTYPE_IRQ
        If $iFlags = 0 Then Return "Exclusive. (Code 0)"
        If bitAND($iFlags, 1) Then $sMsg &= "Shared. (Code 1)"
        If bitAND($iFlags, 2) Then $sMsg &= "Edge-Triggered. (Code 2)"
    Case $RESTYPE_DMA
        If $iFlags = 0 Then Return "8-bits standard. (Code 0)"
        Select
        Case bitAND($iFlags, 1)
            $sMsg &= "8-bits. (Code 1)"
        Case bitAND($iFlags, 2)
            $sMsg &= "32-bits. (Code 2)"
        Case bitAND($iFlags, 3)
            $sMsg &= "8-bits / 16-bits. (Code 3)"
        EndSelect
        If bitAND($iFlags, 4) Then $sMsg &= "Bus mastering. (Code 4)"
        Select
        Case bitAND($iFlags, 8)
            $sMsg &= "Type-A. (Code 8)"
        Case bitAND($iFlags, 16)
            $sMsg &= "Type-B. (Code 16)"
        Case bitAND($iFlags, 24)
            $sMsg &= "Type-F. (Code 24)"
        EndSelect
    Case $RESTYPE_BUSNUMBER
        Return "Not used. (Code 0)"
    EndSwitch
    $sMsg = StringReplace($sMsg, ")", ")" & @CRLF)
    If $sMsg <> "" Then Return StringTrimRight($sMsg, 2)
    Return "Undetermined. (Code -1)"
EndFunc ;==>_CM_Format_Resource_Flags

; #### FUNCTION ####
; ====================================================================================
; Name  : _CM_Bind_Device
; Description   : Reenumerates a device tree, and binds device with specified options.
; Parameter(s)  : $iMask    - Bind options, can be a combination of the following values:
;       :   $CM_BIND_DEVINST_BIND_ID (1) - Binds the device instance to its device ID string.
;       :   $CM_BIND_DEVINST_BIND_PHYSNAME (2) - Binds the device instance to its physical object name.
;       :   $CM_BIND_PHYSNAME_BIND_DEVINST (4) - Binds the physical object name to a device instance handle.
;       :   $CM_BIND_DEVINST_BIND_CLASS (8) - Binds a device instance to its device setup class it belongs.
;       :   $CM_BIND_CLASS_BIND_DEVINST (16) - Binds a device class to a string that contains all the device members.
;       :   $CM_BIND_ENUMERATOR_BIND_DEVINST (32) - Binds a device enumerator to a string contains all the device members.
;       : For more information, see Remarks section.
;       : $sDeviceID - Reserved, must be NULL.
; Return values : This function does not return a value.
; Author    : Pusofalse
; Remarks   : If CM_BIND_DEVINST_BIND_PHYSNAME is specified in $iMask parameter, the function creates a standard AutoIt variable in the following form:
;       :   - $_CM_DEVINST_BIND_PHYSNAME_xxxx, xxxx is the handle the a device instance, for example, 7098.
;       : If CM_BIND_CLASS_BIND_DEVINST is specified, the function creates a variable in the form:
;       :   - $_CM_CLASS_BIND_DEVINST_xxxx, where 'xxxx' is the device class name, for example, $_CM_CLASS_BIND_DEVINST_DiskDrive. Its value is a set of the handles to the device instances which class is 'xxxx', in string format, end with ', '.
;       : But for security, caller must use Eval function instead of the standard way of outputting variable to output a variable's value.
; ===================================================================================
Func _CM_Bind_Device($iMask = $CM_BIND_DEVINST_BIND_ID, $sDeviceID = "")
    Local $aChild, $hDevInst, $vReturn, $sVariable

    If IsString($sDeviceID) = 0 Then
        $hDevInst = $sDeviceID
    Else
        $hDevInst = _CM_Locate_DevNode($hDevInst)
    EndIf

    $aChild = _CM_Enumerate_Children($hDevInst)
    For $i = 1 To $aChild[0]
        $hDevInst = _CM_Locate_DevNode($aChild[$i])
        If bitAND($iMask, $CM_BIND_DEVINST_BIND_ID) Then
            Assign("_CM_DEVINST_BIND_ID_" & $hDevInst, $aChild[$i], 2)
        EndIf
        If bitAND($iMask, $CM_BIND_DEVINST_BIND_PHYSNAME) Then
            $vReturn = _CM_Get_DevNode_Registry_Property($hDevInst, 0xF)
            $vReturn = StringReplace($vReturn, "\", "_")
            Assign("_CM_DEVINST_BIND_PHYSNAME_" & $hDevInst, $vReturn, 2)
        EndIf
        If bitAND($iMask, $CM_BIND_PHYSNAME_BIND_DEVINST) Then
            If $vReturn = "" Then
                $vReturn = _CM_Get_DevNode_Registry_Property($hDevInst, 0xF)
                $vReturn = StringReplace($vReturn, "\", "_")
            EndIf
            Assign("_CM_PHYSNAME_BIND_DEVINST_" & $vReturn, $hDevInst, 2)
        EndIf
        $vReturn = ""
        If bitAND($iMask, $CM_BIND_DEVINST_BIND_CLASS) Then
            $vReturn = _CM_Get_DevNode_Registry_Property($hDevInst, 9)
            $vReturn = _SetupDiClassNameFromGuid($vReturn)
            Assign("_CM_DEVINST_BIND_CLASS_" & $hDevInst, $vReturn, 2)
        EndIf
        If bitAND($iMask, $CM_BIND_CLASS_BIND_DEVINST) Then
            If $vReturn = "" Then
                $vReturn = _CM_Get_DevNode_Registry_Property($hDevInst, 9)
                $vReturn = _SetupDiClassNameFromGuid($vReturn)
            EndIf
            $sVariable = Eval("_CM_CLASS_BIND_DEVINST_" & $vReturn)
        Assign("_CM_CLASS_BIND_DEVINST_" & $vReturn, $sVariable & $hDevInst & ", ", 2)
        EndIf
        If bitAND($iMask, $CM_BIND_ENUMERATOR_BIND_DEVINST) Then
            $vReturn = _CM_Get_DevNode_Registry_Property($hDevInst, 23)
            $sVariable = Eval("_CM_ENUMERATOR_BIND_DEVINST_" & $vReturn)
    Assign("_CM_ENUMERATOR_BIND_DEVINST_" & $vReturn, $sVariable & $hDevInst & ", ", 2)
        EndIf
        _CM_Bind_Device($iMask, $hDevInst)
    Next
EndFunc ;==>_CM_Bind_Device

Func _CM_Get_Device_Power_Capabilities_Ex($hDevInst, $hMachine = 0)
    Local $iFlags, $tBuffer, $tPowerData, $aResult[1], $sText

    $tBuffer = _CM_Get_Device_Power_Ex($hDevInst, $hMachine)
    If $tBuffer = 0 Then Return SetError(@error, 0, $aResult)


    $tPowerData = DllStructCreate($tagCM_POWER_DATA, DllStructGetPtr($tBuffer))
    $iFlags = DllStructGetData($tPowerData, "Capabilities")
    If bitAND($iFlags, $PDCAP_D0_SUPPORTED) Then $sText &= "D0" & @LF
    If bitAND($iFlags, $PDCAP_D1_SUPPORTED) Then $sText &= "D1" & @LF
    If bitAND($iFlags, $PDCAP_D2_SUPPORTED) Then $sText &= "D2" & @LF
    If bitAND($iFlags, $PDCAP_D3_SUPPORTED) Then $sText &= "D3" & @LF
    If bitAND($iFlags, $PDCAP_S0_SUPPORTED) Then $sText &= "S0" & @LF
    If bitAND($iFlags, $PDCAP_S1_SUPPORTED) Then $sText &= "S1" & @LF
    If bitAND($iFlags, $PDCAP_S2_SUPPORTED) Then $sText &= "S2" & @LF
    If bitAND($iFlags, $PDCAP_S3_SUPPORTED) Then $sText &= "S4" & @LF
    If bitAND($iFlags, $PDCAP_S4_SUPPORTED) Then $sText &= "S4" & @LF
    If bitAND($iFlags, $PDCAP_S5_SUPPORTED) Then $sText &= "S5" & @LF

    If bitAND($iFlags, $PDCAP_WAKE_FROM_D0_SUPPORTED) Then $sText &= "WAKE_FROM_D0" & @LF
    If bitAND($iFlags, $PDCAP_WAKE_FROM_D1_SUPPORTED) Then $sText &= "WAKE_FROM_D1" & @LF
    If bitAND($iFlags, $PDCAP_WAKE_FROM_D2_SUPPORTED) Then $sText &= "WAKE_FROM_D2" & @LF
    If bitAND($iFlags, $PDCAP_WAKE_FROM_D3_SUPPORTED) Then $sText &= "WAKE_FROM_D3" & @LF
    If bitAND($iFlags, $PDCAP_WAKE_FROM_S0_SUPPORTED) Then $sText &= "WAKE_FROM_S0" & @LF
    If bitAND($iFlags, $PDCAP_WAKE_FROM_S1_SUPPORTED) Then $sText &= "WAKE_FROM_S1" & @LF
    If bitAND($iFlags, $PDCAP_WAKE_FROM_S2_SUPPORTED) Then $sText &= "WAKE_FROM_S2" & @LF
    If bitAND($iFlags, $PDCAP_WAKE_FROM_S3_SUPPORTED) Then $sText &= "WAKE_FROM_S3" & @LF
    If bitAND($iFlags, $PDCAP_WARM_EJECT_SUPPORTED) Then $sText &= "WARM_EJECT" & @LF

    $sText = StringReplace($sText, @LF, "_SUPPORTED" & @LF & "PDCAP_")
    Return StringSplit(StringTrimRight("PDCAP_" & $sText, 7), @LF)
EndFunc ;==>_CM_Get_Device_Power_Capabilities_Ex

Func _CM_Get_Device_Power_Ex($hDevInst, $hMachine = 0)
    Local $bPowerData, $tBuffer, $iLength, $pBuffer

    $bPowerData = _CM_Get_DevNode_Registry_Property_Ex($hDevInst, 0x1F, $hMachine)
    If @error Then Return SetError(@error, 0, 0)

    $iLength = BinaryLen($bPowerData)
    $tBuffer = DllStructCreate("byte PowerData[" & $iLength & "]")
    DllStructSetData($tBuffer, "PowerData", $bPowerData)
    Return $tBuffer
EndFunc ;==>_CM_Get_Device_Power_Data_Ex

Func _CM_Get_Device_Power_State_Ex($hDevInst, $hMachine = 0)
    Local $tBuffer, $tPowerData, $iVal
    Local $aBuffer[6] = ["Unspecified", "D0", "D1", "D2", "D3", "Maximum"]

    $tBuffer = _CM_Get_Device_Power_Ex($hDevInst, $hMachine)
    If $tBuffer = 0 Then Return SetError(@error, 0, "Unknown")

    $tPowerData = DllStructCreate($tagCM_POWER_DATA, DllStructGetPtr($tBuffer))
    $iVal = DllStructGetData($tPowerData, "MostRecentPowerState")
    _CM_Free_Variable($tPowerData)
    If $iVal < 0 OR $iVal > 5 Then
        Return "Unknown"
    Else
        Return $aBuffer[$iVal]
    EndIf
EndFunc ;==>_CM_Get_Device_Power_State_Ex

Func _CM_Get_Device_Power_Mapping_Ex($hDevInst, $hMachine = 0)
    Local $tBuffer, $aResult[6], $tPowerData, $iVal
    Local $aMapping[6] = ["Unspecified", "D0", "D1", "D2", "D3", "Maximum"]

    $tBuffer = _CM_Get_Device_Power_Ex($hDevInst, $hMachine)
    If $tBuffer = 0 Then Return SetError(@error, 0, $aResult)

    $tPowerData = DllStructCreate($tagCM_POWER_DATA, DllStructGetPtr($tBuffer))
    For $i = 0 To 5
        $iVal = DllStructGetData($tPowerData, "PowerStateMapping", $i + 2)
        If $iVal < 0 OR $iVal > 5 Then
            $aResult[$i] = "S" & $i & " -> Unknown"
        Else
            $aResult[$i] = "S" & $i & " -> " & $aMapping[$iVal]
        EndIf
    Next
    Return SetError(0, _CM_Free_Variable($tPowerData), $aResult)
EndFunc ;==>_CM_Get_Device_Power_Mapping_Ex

Func _CM_Locate_Disk_DevInst($sDrive, $fReturnDisk = 1)
    Local $sClassGuid, $iMask, $hDevs, $tDevInfo, $tDevIfInfo, $iIndex, $sDevicePath, $vDisk

    If ($fReturnDisk) Then
        $vDisk = _CM_Get_Drive_Disk_Number($sDrive)
        If (@Extended = 7) Then
            $sClassGuid = $GUID_DEVINTERFACE_DISK
        ElseIf (@Extended = 2) Then
            $sClassGuid = $GUID_DEVINTERFACE_CDROM
        Else
            Return SetError(1, 0, 0)
        EndIf
    Else
        $sClassGuid = $GUID_DEVINTERFACE_VOLUME
        $vDisk = _CM_Get_Volume_Name($sDrive)
    EndIf
    If ($vDisk == "") Or ($vDisk == -1) Then Return SetError(@error, 0, 0)

    $iMask = bitOr($DIGCF_PRESENT, $DIGCF_DEVICEINTERFACE)
    $hDevs = _SetupDiGetClassDevs($iMask, $sClassGuid)
    If ($hDevs < 1) Then Return SetError(@error, 0, 0)

    While _SetupDiEnumDeviceInterfaces($hDevs, 0, $sClassGuid, $iIndex, $tDevIfInfo)
        $iIndex += 1
        $sDevicePath = _SetupDiGetDeviceInterfaceDetail($hDevs, $tDevIfInfo, $tDevInfo)
        If ($fReturnDisk) Then
            $sDevicePath = StringTrimLeft($sDevicePath, 4)
            If (_CM_Get_Drive_Disk_Number($sDevicePath) = $vDisk) Then
                ExitLoop _CM_Assign_Var($sDevicePath, "\\?\", 1)
            EndIf
        Else
            If (_CM_Get_Volume_Name($sDevicePath) = $vDisk) Then

                ExitLoop _CM_Assign_Var($sDevicePath, "\\?\", 1)
            EndIf
        EndIf
    WEnd
    _CM_Assign_Var($iMask, @error)
    _CM_Assign_Var($vDisk, DllStructGetData($tDevInfo, "DevInst"))

    _SetupDiDestroyDeviceInfoList($hDevs)
    _SetupDiApiBufferFree($tDevIfInfo)
    _SetupDiApiBufferFree($tDevInfo)

    If ($sDevicePath <> "\\?\") Then
        Return SetError($iMask, 0, 0)
    Else
        Return SetError($iMask, 0, $vDisk)
    EndIf
EndFunc ;==>_CM_Locate_Disk_DevInst

; #### FUNCTION ####
; ====================================================================================================
; Name  : _CM_Enumerate_Siblings
; Description   : Specify a device, retrieves the siblings for this device.
; Parameter(s)  : $hDevInst - Handle to the device instance, typically returned by _CM_Locate_DevNode.
; Return values : An array in the form:
;       :   $aArray[0][0] - The number of sibling devices.
;       :   $aArray[1][0] - Handle to the 1st device.
;       :   $aArray[1][1] - Display name of the 1st device.
;       :   ... ...
; Author    : Pusofalse
; ====================================================================================================
Func _CM_Enumerate_Siblings($hDevInst)
    Local $aChild[1], $aSibling[1][2]

    $hDevInst = _CM_Get_Parent($hDevInst)
    If ($hDevInst = 0) Then Return SetError(@error, 0, $aSibling)

    $aChild = _CM_Enumerate_Children($hDevInst)
    Redim $aSibling[$aChild[0] + 1][2]
    For $i = 1 To $aChild[0]
        $aSibling[$i][0] = _CM_Locate_DevNode($aChild[$i])
        $aSibling[$i][1] = _CM_Get_Device_Display_Name_Ex($aSibling[$i][0], 0)
    Next
    $aSibling[0][0] = $aChild[0]
    Return $aSibling
EndFunc ;==>_CM_Enumerate_Siblings

; #### FUNCTION ####
; ====================================================================================================
; Name  : _CM_Get_Device_Display_Name_Ex
; Description   : This function retrieves the device friendly name displays in Device Manager.
; Parameter(s)  : $hDevInst - Handle to the device instance.
;       : $hMachine - Handle to the machine, or zero on local.
; Return values : Display name of the device.
; Author    : Pusofalse
; ====================================================================================================
Func _CM_Get_Device_Display_Name_Ex($hDevInst, $hMachine = 0)
    Local $sDescr

    $sDescr = _CM_Get_DevNode_Registry_Property_Ex($hDevInst, 13, $hMachine)
    If ($sDescr = "") Then
        $sDescr = _CM_Get_DevNode_Registry_Property_Ex($hDevInst, 1, $hMachine)
        If ($sDescr = "") Then $sDescr = "(Unrecognized device)"
    EndIf
    Return $sDescr
EndFunc ;==>_CM_Get_Device_Display_Name_Ex

Func _CM_Is_Device_Resource_Present($sDeviceID)
    Local $sRegKey

    $sRegKey = $REGKEY_HARDWARE_CS002_ENUM & $sDeviceID & "\LogConf"
    RegRead($sRegKey, "BasicConfigVector")
    Return (@error = -2)
EndFunc ;==>_CM_Is_Device_Resource_Present

Func _CM_Make_IO_Request($iDeviceType, $iFunction, $iAccess, $iMethod = 0)
    Local $iIoRequest
    $iIoRequest = BitOR( BitShift($iDeviceType, -16), _
            BitShift($iAccess, -14), _
            BitShift($iFunction, -2), _
            $iMethod)
    Return $iIoRequest
EndFunc ;==>_CM_Make_IO_Request

LocalSecurityAuthority.au3

#include-once
#include <Array.au3>

; #### HEADER INFORMATION ####
; =======================================================================
; Title : Local Security Authority
; Description   : Local security authority related. Functions this library can do:
;       1.  Set/get the security information of a file, service, registry key, share resource or a kernel object.
;       2.  Assign/remove the user rights to/from an user account/group.
;       3.  Create/delete the user account (or set/get the user account information) from the specified system.
;       4.  Verify the any object's hash value.
;       5.  Query the secrets stored in the system.
;       6.  Create/delete the private data in private database.
;       7.  Encrypt/decrypt messages.
;       8.  Create process with higher privileges (or adjust privileges) so that you can debug any other processes.
;       9.  Else more...
; Author    : Pusofalse
; Requirements  :
;   Modules     : Advapi32.dll, Crypt32.dll, Netapi32.dll, Kernel32.dll, Secur32.dll, Ole32.dll
;   AutoIt Version  : AutoIt v3 ++
;   Minimum Client  : Windows 2000 Professional
; =======================================================================

; SYSTEM ERROR CODE CONSTANTS
; =======================================================================
If Not IsDeclared("ERROR_SUCCESS") Then Global Const $ERROR_SUCCESS = 0
If Not IsDeclared("ERROR_INCORRECT_FUNCTION") Then Global Const $ERROR_INCORRECT_FUNCTION = 1
If Not IsDeclared("ERROR_FILE_NOT_FOUND") Then Global Const $ERROR_FILE_NOT_FOUND = 2
If Not IsDeclared("ERROR_ACCESS_DENIED") Then Global Const $ERROR_ACCESS_DENIED = 5
If Not IsDeclared("ERROR_INVALID_HANDLE") Then Global Const $ERROR_INVALID_HANDLE = 6
If Not IsDeclared("ERROR_INVALID_DATA") Then Global Const $ERROR_INVALID_DATA = 13
If Not IsDeclared("ERROR_NO_MORE_FILES") Then Global Const $ERROR_NO_MORE_FILES = 18
If Not IsDeclared("ERROR_INVALID_PARAMETER") Then Global Const $ERROR_INVALID_PARAMETER = 87
If Not IsDeclared("ERROR_MORE_DATA") Then Global Const $ERROR_MORE_DATA = 234
If Not IsDeclared("ERROR_NO_MORE_ITEMS") Then Global Const $ERROR_NO_MORE_ITEMS = 259
If Not IsDeclared("ERROR_INVALID_ACL") Then Global Const $ERROR_INVALID_ACL = 1336
If Not IsDeclared("ERROR_INVALID_SID") Then Global Const $ERROR_INVALID_SID = 1337
If Not IsDeclared("ERROR_INVALID_SECURITY_DESCR") Then Global Const $ERROR_INVALID_SECURITY_DESCR = 1338
; =======================================================================

; #### PRIVILEGE CONSTANTS ####
; =======================================================================
;If Not IsDeclared("SE_CREATE_TOKEN_NAME") Then Global Const $SE_CREATE_TOKEN_NAME = "SeCreateTokenPrivilege"
;If Not IsDeclared("SE_ASSIGNPRIMARYTOKEN_NAME") Then Global Const $SE_ASSIGNPRIMARYTOKEN_NAME = "SeAssignPrimaryTokenPrivilege"
;If Not IsDeclared("SE_LOCK_MEMORY_NAME") Then Global Const $SE_LOCK_MEMORY_NAME = "SeLockMemoryPrivilege"
;If Not IsDeclared("SE_INCREASE_QUOTA_NAME") Then Global Const $SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege"
;If Not IsDeclared("SE_UNSOLICITED_INPUT_NAME") Then Global Const $SE_UNSOLICITED_INPUT_NAME = "SeUnsolicitedInputPrivilege"
;If Not IsDeclared("SE_MACHINE_ACCOUNT_NAME") Then Global Const $SE_MACHINE_ACCOUNT_NAME = "SeMachineAccountPrivilege"
;If Not IsDeclared("SE_TCB_NAME") Then Global Const $SE_TCB_NAME = "SeTcbPrivilege"
;If Not IsDeclared("SE_SECURITY_NAME") Then Global Const $SE_SECURITY_NAME = "SeSecurityPrivilege"
;If Not IsDeclared("SE_TAKE_OWNERSHIP_NAME") Then Global Const $SE_TAKE_OWNERSHIP_NAME = "SeTakeOwnershipPrivilege"
;If Not IsDeclared("SE_LOAD_DRIVER_NAME") Then Global Const $SE_LOAD_DRIVER_NAME = "SeLoadDriverPrivilege"
;If Not IsDeclared("SE_SYSTEM_PROFILE_NAME") Then Global Const $SE_SYSTEM_PROFILE_NAME = "SeSystemProfilePrivilege"
;If Not IsDeclared("SE_SYSTEMTIME_NAME") Then Global Const $SE_SYSTEMTIME_NAME = "SeSystemtimePrivilege"
;If Not IsDeclared("SE_PROF_SINGLE_PROCESS_NAME") Then Global Const $SE_PROF_SINGLE_PROCESS_NAME = "SeProfileSingleProcessPrivilege"
;If Not IsDeclared("SE_INC_BASE_PRIORITY_NAME") Then Global Const $SE_INC_BASE_PRIORITY_NAME = "SeIncreaseBasePriorityPrivilege"
;If Not IsDeclared("SE_CREATE_PAGEFILE_NAME") Then Global Const $SE_CREATE_PAGEFILE_NAME = "SeCreatePagefilePrivilege"
;If Not IsDeclared("SE_CREATE_PERMANENT_NAME") Then Global Const $SE_CREATE_PERMANENT_NAME = "SeCreatePermanentPrivilege"
;If Not IsDeclared("SE_BACKUP_NAME") Then Global Const $SE_BACKUP_NAME = "SeBackupPrivilege"
;If Not IsDeclared("SE_RESTORE_NAME") Then Global Const $SE_RESTORE_NAME = "SeRestorePrivilege"
;If Not IsDeclared("SE_SHUTDOWN_NAME") Then Global Const $SE_SHUTDOWN_NAME = "SeShutdownPrivilege"
;If Not IsDeclared("SE_DEBUG_NAME") Then Global Const $SE_DEBUG_NAME = "SeDebugPrivilege"
;If Not IsDeclared("SE_AUDIT_NAME") Then Global Const $SE_AUDIT_NAME = "SeAuditPrivilege"
;If Not IsDeclared("SE_SYSTEM_ENVIRONMENT_NAME") Then Global Const $SE_SYSTEM_ENVIRONMENT_NAME = "SeSystemEnvironmentPrivilege"
;If Not IsDeclared("SE_CHANGE_NOTIFY_NAME") Then Global Const $SE_CHANGE_NOTIFY_NAME = "SeChangeNotifyPrivilege"
;If Not IsDeclared("SE_REMOTE_SHUTDOWN_NAME") Then Global Const $SE_REMOTE_SHUTDOWN_NAME = "SeRemoteShutdownPrivilege"
;If Not IsDeclared("SE_UNDOCK_NAME") Then Global Const $SE_UNDOCK_NAME = "SeUndockPrivilege"
;If Not IsDeclared("SE_TRUSTED_CERDMAN_ACCESS_NAME") Then Global Const $SE_TRUSTED_CREDMAN_ACCESS_NAME = "SeTrustedCredManAccessPrivilege"

; #### USER ACCOUNT RIGHTS CONSTANTS ####
; =======================================================================
Global Const $SE_BATCH_LOGON_NAMETEXT="SeBatchLogonRight"
; Required for an account to log on using the batch logon type.

Global Const $SE_DENY_BATCH_LOGON_NAMETEXT="SeDenyBatchLogonRight"
; Explicitly denies an account the right to log on using the batch logon type.

Global Const $SE_DENY_INTERACTIVE_LOGON_NAMETEXT="SeDenyInteractiveLogonRight"
; Explicitly denies an account the right to log on using the interactive logon type.

Global Const $SE_DENY_NETWORK_LOGON_NAMETEXT="SeDenyNetworkLogonRight"
; Explicitly denies an account the right to log on using the network logon type.

Global Const $SE_DENY_REMOTE_INTERACTIVE_LOGON_NAMETEXT="SeDenyRemoteInteractiveLogonRight"
; Explicitly denies an account the right to log on remotely using the interactive logon type.

Global Const $SE_DENY_SERVICE_LOGON_NAMETEXT="SeDenyServiceLogonRight"
; Explicitly denies an account the right to log on using the service logon type.

Global Const $SE_INTERACTIVE_LOGON_NAMETEXT="SeInteractiveLogonRight"
; Required for an account to log on using the interactive logon type.

Global Const $SE_NETWORK_LOGON_NAMETEXT="SeNetworkLogonRight"
; Required for an account to log on using the network logon type.

Global Const $SE_REMOTE_INTERACTIVE_LOGON_NAMETEXT="SeRemoteInteractiveLogonRight"
; Required for an account to log on remotely using the interactive logon type.

Global Const $SE_SERVICE_LOGON_NAMETEXT= "SeServiceLogonRight"
; Required for an account to log on using the service logon type.
; =======================================================================

; SECURITY INFORMATION CONSTANTS
; =======================================================================
Global Const $OWNER_SECURITY_INFORMATION = 1
Global Const $GROUP_SECURITY_INFORMATION = 2
Global Const $DACL_SECURITY_INFORMATION = 4
Global Const $SACL_SECURITY_INFORMATION = 8
; =======================================================================

; SECURITY OBJECT TYPE CONSTANTS
; =======================================================================
Global Const $SE_UNKNOWN_OBJECT_TYPE = 0
Global Const $SE_FILE_OBJECT = 1
Global Const $SE_SERVICE = 2
Global Const $SE_PRINTER = 3
Global Const $SE_REGISTRY_KEY = 4
Global Const $SE_LMSHARE = 5
Global Const $SE_KERNEL_OBJECT = 6
Global Const $SE_WINDOW_OBJECT = 7
Global Const $SE_DS_OBJECT = 8
Global Const $SE_DS_OBJECT_ALL = 9
Global Const $SE_PROVIDER_DEFINED_OBJECT = 10
Global Const $SE_WMIGUID_OBJECT = 11
Global Const $SE_REGISTRY_WOW64_32KEY = 12
; =======================================================================

; ACCESS TOKEN SECURITY AND ACCESS RIGHTS CONSTANTS
; ======================================================================
;If Not IsDeclared("TOKEN_ADJUST_DEFAULT") Then Global Const $TOKEN_ADJUST_DEFAULT = 0x80
;If Not IsDeclared("TOKEN_ADJUST_GROUPS") Then Global Const $TOKEN_ADJUST_GROUPS = 0x40
;If Not IsDeclared("TOKEN_ADJUST_PRIVILEGES") Then Global Const $TOKEN_ADJUST_PRIVILEGES = 0x20
;If Not IsDeclared("TOKEN_ADJUST_SESSIONID") Then Global Const $TOKEN_ADJUST_SESSIONID = 0x100
;If Not IsDeclared("TOKEN_ASSIGN_PRIMARY") Then Global Const $TOKEN_ASSIGN_PRIMARY = 0x01
;If Not IsDeclared("TOKEN_DUPLICATE") Then Global Const $TOKEN_DUPLICATE = 0x2
;If Not IsDeclared("TOKEN_EXECUTE") Then Global Const $TOKEN_EXECUTE = 0x20000
;If Not IsDeclared("TOKEN_IMPERSONATE") Then Global Const $TOKEN_IMPERSONATE = 0x4
;If Not IsDeclared("TOKEN_QUERY") Then Global Const $TOKEN_QUERY = 0x8
;If Not IsDeclared("TOKEN_QUERY_SOURCE") Then Global Const $TOKEN_QUERY_SOURCE = 0x10
;If Not IsDeclared("TOKEN_READ") Then Global Const $TOKEN_READ = 0x20008
;If Not IsDeclared("TOKEN_WRITE") Then Global Const $TOKEN_WRITE = 0x200e0
;If Not IsDeclared("TOKEN_ALL_ACCESS") Then Global Const $TOKEN_ALL_ACCESS = 0xF01FF
; =======================================================================

; TRUSTEE FROM CONSTANTS
; =======================================================================
Global Const $TRUSTEE_IS_SID = 0
Global Const $TRUSTEE_IS_NAME = 1
Global Const $TRUSTEE_BAD_NAME = 2
Global Const $TRUSTEE_IS_OBJECTS_AND_SID = 3
Global Const $TRUSTEE_IS_OBJECTS_AND_NAME = 4
; =======================================================================

; TRUSTEE TYPE CONSTANTS
; =======================================================================
;Global Const $TRUSTEE_IS_UNKNOWN = 0
;Global Const $TRUSTEE_IS_USER = 1
;Global Const $TRUSTEE_IS_GROUP = 2
;Global Const $TRUSTEE_IS_DOMAIN = 3
;Global Const $TRUSTEE_IS_ALIAS = 4
;Global Const $TRUSTEE_IS_WELL_KNOWN_GROUP = 5
;Global Const $TRUSTEE_IS_DELETED = 6
;Global Const $TRUSTEE_IS_INVALID = 7
;Global Const $TRUSTEE_IS_COMPUTER = 8
; =======================================================================

; ACCESS TYPE CONSTANTS
; =======================================================================
;Global Const $NOT_USED_ACCESS = 0
;Global Const $GRANT_ACCESS = 1
;Global Const $SET_ACCESS = 2
;Global Const $DENY_ACCESS = 3
;Global Const $REVOKE_ACCESS = 4
;Global Const $SET_AUDIT_SUCCESS = 5
;Global Const $SET_AUDIT_FAILURE = 6
; =======================================================================

; SID NAME USE CONSTANTS ( SID TYPE )
; =======================================================================
Global Const $SID_IS_USER = 1
Global Const $SID_IS_GROUP = 2
Global Const $SID_IS_DOMAIN = 3
Global Const $SID_IS_ALIAS = 4
Global Const $SID_IS_WELLKNOWN_GROUP = 5
Global Const $SID_IS_DELETED_ACCOUNT = 6
Global Const $SID_IS_INVALID = 7
Global Const $SID_IS_UNKNOWN = 8
Global Const $SID_IS_COMPUTER = 9
Global Const $SID_IS_LABEL = 10
; =======================================================================

; #### INHERITANCE CONSTANTS ####
; =======================================================================
Global Const $NO_INHERITANCE = 0x0
Global Const $SUB_OBJECTS_ONLY_INHERIT            = 0x1
Global Const $SUB_CONTAINERS_ONLY_INHERIT         =0x2
Global Const $SUB_CONTAINERS_AND_OBJECTS_INHERIT = 0x3
Global Const $INHERIT_NO_PROPAGATE              =  0x4
Global Const $INHERIT_ONLY                        = 0x8
Global Const $INHERITED_ACCESS_ENTRY      =        0x10
Global Const $INHERITED_PARENT                    = 0x10000000
Global Const $INHERITED_GRANDPARENT           =    0x20000000
; =======================================================================

; #### STANDARD ACCESS RIGHTS ####
; =======================================================================
;If Not IsDeclared("GENERIC_READ") Then Global Const $GENERIC_READ = 0x80000000
;If Not IsDeclared("GENERIC_WRITE") Then Global Const $GENERIC_WRITE = 0x40000000
;If Not IsDeclared("GENERIC_EXECUTE") Then Global Const $GENERIC_EXECUTE = 0x20000000
;If Not IsDeclared("GENERIC_ALL") Then Global Const $GENERIC_ALL = 0x10000000
If Not IsDeclared("DELETE") Then Global Const $DELETE = 0x10000 ; Required to delete the object.
;If Not IsDeclared("READ_CONTROL") Then Global Const $READ_CONTROL = 0x20000 ; Required to read the DACL information.
;If Not IsDeclared("WRITE_DAC") Then Global Const $WRITE_DAC = 0x40000  ; Required to change the DACL information.
;If Not IsDeclared("WRITE_OWNER") Then Global Const $WRITE_OWNER = 0x80000  ; Required to change the owner of the object.
;If Not IsDeclared("ACCESS_SYSTEM_SECURITY") Then Global Const $ACCESS_SYSTEM_SECURITY = 0x1000000 ; Required to read or change the SACL security information of the object.
; =======================================================================

; LSA VARIABLES
; =======================================================================
GLOBAL $__LSA_ACE_INDEX_CURRENT_SELECT
GLOBAL $__LSA_ACE_POINTER_CURRENT_SELECT
GLOBAL $__LSA_ACL_POINTER_CURRENT_SELECT
GLOBAL $__LSA_SECURITY_USE_NAME = 1
; =======================================================================

; #### LSA DEDICATED KEY ####
; =======================================================================================
Local $p_LsaTempDedicatedKey = _LookupAccountName(_LsaGetUserName())
Local $s_LsaTempDedicatedKey = "#" & _ConvertSidToStringSid($p_LsaTempDedicatedKey) & "#"
$p_LsaTempDedicatedKey = _HeapFree($p_LsaTempDedicatedKey) * _FreeVariable($p_LsaTempDedicatedKey)

Global Const $LSA_DEDICATED_KEY0 = "_LocalSecurityAuthority_DedicatedKey0" & $s_LsaTempDedicatedKey
Global Const $LSA_DEDICATED_KEY1 = "_LocalSecurityAuthority_DedicatedKey1" & $s_LsaTempDedicatedKey
Global Const $LSA_DEDICATED_KEY2 = "_LocalSecurityAuthority_DedicatedKey2" & $s_LsaTempDedicatedKey
Global Const $LSA_DEDICATED_KEY3 = "_LocalSecurityAuthority_DedicatedKey3" & $s_LsaTempDedicatedKey
; =======================================================================================

; LSA STRUCTURE CONSTANTS
; =======================================================================
Global Const $tagLUID = "dword Low;long High"
Global Const $tagLUIDANDATTR = $tagLUID & ";dword Attribute"
Global Const $tagTOKENPRIVILEGE = "dword Count;" & $tagLUIDANDATTR
Global Const $tagPRIVILEGESET = "dword Count;dword Control;" & $tagLUIDANDATTR
Global Const $tagTRUSTEE = "ptr pMultTrustee;int MultTrusteeOpe;int From;int Type;ptr Name"
Global Const $tagEXPLICITACCESS = "dword AccessMask;dword AccessMode;dword Inheritance;" & $tagTRUSTEE
Global Const $tagLSAANSI= "ushort Length;ushort MaxLength;ptr Buffer"
Global Const $tagLSAUNICODE = "ushort Length;ushort MaxLength;ptr Wbuffer"
Global Const $tagLSAOBJATTR = "ulong Length;hWnd RootDir;ptr objName;ulong Attr;ptr SecurDescr;ptr SecurQuality"
Global Const $tagACLSIZEINFO = "dword AceCount;dword BytesInUse;dword BytesFree"
Global Const $tagPOLICYAUDITEVENTSINFO = "int AuditMode;ptr EventAuditOpt;ulong MaxAuditEventCount"
Global Const $tagLSAUSERMODALS = "dword MinPwdLen;dword MaxPwdAge;dword MinPwdAge;dword ForceLogoff;dword PwdHistLen"
Global Const $tagSIDIDENTIFIERAUTHORITY = "byte Reserved[5];byte Authority"
Global Const $tagDATABLOB = "dword Length;ptr Buffer"
Global Const $tagCRYPTPROTECT_PROMPT = "dword Size;dword Flags;hWnd hWndOwner;ptr Prompt"
Global Const $tagSECRET = "ptr SecretName;byte Guid[16];ptr SecretData;ptr Sid1;ptr Sid2"
Global Const $tagUSERMODALSINFO3 = "dword Duration;dword ObservationWin;dword Threshold"
Global Const $tagACEHEADER = "byte AceType;byte AceFlags;short AceSize"
Global Const $tagACCESSALLOWEDACE = $tagACEHEADER & ";dword AccessMask;dword SidStart"
Global Const $tagACCESSACE = $tagACCESSALLOWEDACE
Global Const $tagTOKENSTATISTICS = "int64 TokenId;int64 AuthenticationId;int64 ExpirationTime;int TokenType;int ImpersonationLevel;dword DynamicCharged;dword DynamicAvailable;dword GroupCount;dword PrivilegeCount;int64 ModifiedId"
Global Const $tagSECUR_ATTRIBUTES = "int Size;ptr Securitydescriptor;int Inherit"
Const $tagOBJECT_ATTRIBUTES = "ulong Length;hWnd RootDir;ptr BufferW;ulong Attributes;ptr Securitydescriptor;ptr SecurityQualityOfService"
Global Const $tagQueryServiceCfg = "dword Type;dword StartType;dword ErrorCtrl;ptr BinPath;ptr LoadOrderGroup;dword TagId;ptr Dependence;ptr StartName;ptr DisplayName"
Global Const $tagServiceStatusProcess = "dword ServiceType;dword CurrentState;dword ControlsAccepted;dword Win32ExitCode;dword ServiceSpecificExitCode;dword CheckPoint;dword WaitHint;dword ProcessId;dword ServiceFlags"
; =======================================================================

; LSA POLICY ACCESS RIGHT CONSTANTS
; =======================================================================
Global Const $POLICY_VIEW_LOCAL_INFORMATION = 1
Global Const $POLICY_VIEW_AUDIT_INFORMATION = 2
Global Const $POLICY_GET_PRIVATE_INFORMATION = 4
Global Const $POLICY_TRUST_ADMIN = 8
Global Const $POLICY_CREATE_ACCOUNT = 16
Global Const $POLICY_CREATE_SECRET = 32
Global Const $POLICY_CREATE_PRIVILEGE = 64
Global Const $POLICY_SET_DEFAULT_QUOTA_LIMITS = 128
Global Const $POLICY_SET_AUDIT_REQUIREMENTS = 256
Global Const $POLICY_AUDIT_LOG_ADMIN = 512
Global Const $POLICY_SERVER_ADMIN = 1024
Global Const $POLICY_LOOKUP_NAMES = 2048
Global Const $POLICY_NOTIFICATION = 4096
; =======================================================================

; POLICY AUDIT EVENT OPTION CONSTANTS
; =======================================================================
Global Const $POLICY_AUDIT_SYSTEM = "AuditCategorySystem"
Global Const $POLICY_AUDIT_LOGON = "AuditCategoryLogon"
Global Const $POLICY_AUDIT_OBJECT_ACCESS = "AuditCategoryObjectAccess"
Global Const $POLICY_AUDIT_PRIVILEGE_USE = "AuditCategoryPrivilegeUse"
Global Const $POLICY_AUDIT_DETAILTRACKING = "AuditCategoryDetailedTracking"
Global Const $POLICY_AUDIT_POLICY_CHANGE = "AuditCategoryPolicyChange"
Global Const $POLICY_AUDIT_ACCOUNT_MANAGE = "AuditCategoryAccountManagement"
Global Const $POLICY_AUDIT_DIRECTORY_SERVICE_ACCESS = "AuditCategoryDirectoryServiceAccess"
Global Const $POLICY_AUDIT_ACCOUNT_LOGON = "AuditCategoryAccountLogon"
; =======================================================================

; POLICY AUDIT EVENT CONSTANTS
; =======================================================================
Global Const $POLICY_AUDIT_EVENT_UNCHANGED = 0
Global Const $POLICY_AUDIT_EVENT_SUCCESS = 1
Global Const $POLICY_AUDIT_EVENT_FAILURE = 2
Global Const $POLICY_AUDIT_EVENT_NONE = 4
Global Const $POLICY_AUDIT_EVENT_MASK = bitOR(0, 1, 2, 4)
; =======================================================================

; USER ACCOUNT FILETER CONSTANTS
; =======================================================================
Global Const $FILTER_ALL_USER_ACCOUNTS = 0
Global Const $FILTER_TEMP_DUPLICATE_ACCOUNT = 1
Global Const $FILTER_NORMAL_ACCOUNT = 2
Global Const $FILTER_PROXY_ACCOUNT = 4
Global Const $FILTER_INTERDOMAIN_TRUST_ACCOUNT = 8
Global Const $FILTER_WORKSTATION_TRUST_ACCOUNT = 0x10
Global Const $FILTER_SERVER_TRUST_ACCOUNT = 0x20
; =======================================================================

; #### LOGON TYPE CONSTANTS ####
; =======================================================================
Global Const $LOGON32_LOGON_INTERACTIVE  = 2
Global Const $LOGON32_LOGON_NETWORK       = 3
Global Const $LOGON32_LOGON_BATCH        = 4
Global Const $LOGON32_LOGON_SERVICE      = 5

; #### LOGON PROVIDER CONSTANTS ####
; =======================================================================
Global Const $LOGON32_PROVIDER_DEFAULT  =  0
Global Const $LOGON32_PROVIDER_WINNT35   = 1
Global Const $LOGON32_PROVIDER_WINNT40   = 2
Global Const $LOGON32_PROVIDER_WINNT50   = 3
; =======================================================================

; #### LSA SECRET CONSTANTS ####
; =======================================================================
Global Const $SECRET_INFORMATION_NAME = 0
Global Const $SECRET_INFORMATION_GUID = 1
Global Const $SECRET_INFORMATION_DATA = 2
Global Const $SECRET_INFORMATION_SID1 = 4
Global Const $SECRET_INFORMATION_SID2 = 8
; =======================================================================

; #### USER ACCOUNT RID (Relative Identifier) CONSTANTS ####
; =======================================================================
Global Const $DOMAIN_ALIAS_RID_ADMINS = 0x00000220
Global Const $DOMAIN_ALIAS_RID_USERS  = 0x00000221
Global Const $DOMAIN_ALIAS_RID_GUESTS = 0x00000222
Global Const $DOMAIN_ALIAS_RID_POWER_USERS = 0x00000223
Global Const $DOMAIN_ALIAS_RID_ACCOUNT_OPS = 0x00000224
Global Const $DOMAIN_ALIAS_RID_SYSTEM_OPS = 0x00000225
Global Const $DOMAIN_ALIAS_RID_PRINT_OPS = 0x00000226
Global Const $DOMAIN_ALIAS_RID_BACKUP_OPS = 0x00000227
Global Const $DOMAIN_ALIAS_RID_REPLICATOR = 0x00000228
; =======================================================================

; #### CRYPTOGRAPHY VERIFICATION OBJECT GLOBAL CONSTANTS ####
; =======================================================================
Global Const $CRYPT_OBJECT_STRING = 0
Global Const $CRYPT_OBJECT_FILE = 1
Global Const $CRYPT_OBJECT_SERVICE = 2
Global Const $CRYPT_OBJECT_REGISTRY_KEY = 3
Global Const $CRYPT_OBJECT_LMSHARE = 4
Global Const $CRYPT_OBJECT_KERNEL = 5
; =======================================================================

; #### CRYPTOGRAPHY VERIFICATION MASK GLOBAL CONSTANTS ####
; =======================================================================
Global Const $CRYPT_MASK_DATA = 1
Global Const $CRYPT_MASK_TIME = 2
Global Const $CRYPT_MASK_ATTRIBUTES = 4
Global Const $CRYPT_MASK_OWNER = 8
Global Const $CRYPT_MASK_GROUP = 0x10
Global Const $CRYPT_MASK_DACL = 0x20
Global Const $CRYPT_MASK_SACL = 0x40
Global Const $CRYPT_MASK_TYPE = 0x80
Global Const $CRYPT_MASK_STATE = 0x100
Global Const $CRYPT_MASK_SELF = 0x200
Global Const $CRYPT_MASK_SYNCHRONIZE = 0x400
Global Const $CRYPT_MASK_PARAM = 0x800
; =======================================================================

; #### ALG_ID (ALGORIGHM IDENTIFER) CONSTANTS ####
; =======================================================================
Global Const $CALG_3DES = 0x6603
Global Const $CALG_3DES_112 = 0x6609
Global Const $CALG_AES = 0x6611
Global Const $CALG_AES_128 = 0x660e
Global Const $CALG_MD2 = 0x8001
Global Const $CALG_MD4 = 0x8002
Global Const $CALG_SHA = 0x8004
Global Const $CALG_SHA1 = $CALG_SHA
Global Const $CALG_MD5 = 0x8003
Global Const $CALG_MAC = 0x8005
Global Const $CALG_SHA_256 = 0x800c
Global Const $CALG_SHA_384 = 0x800d
Global Const $CALG_SHA_512 = 0x800e
Global Const $CALG_RC2 = 0x6602
Global Const $CALG_RC4 = 0x6801
Global Const $CALG_RC5 = 0x660d
Global Const $CALG_RSA_KEYX = 0xa400
Global Const $CALG_RSA_SIGN = 0x2400
; =======================================================================

; #### CRYPT PROTECT DATA CONSTANTS ####
; =======================================================================
Global Const $CRYPTPROTECT_LOCAL_MACHINE = 4
Global Const $CRYPTPROTECT_UI_FORBIDDEN = 1
Global Const $CRYPTPROTECT_AUDIT = 16
Global Const $CRYPTPROTECT_VERIFY_PROTECTION = 64
Global Const $CRYPTPROTECT_PROMPT_ON_UNPROTECT = 1
Global Const $CRYPTPROTECT_PROMPT_ON_PROTECT = 2
; =======================================================================

; #### FUNCTIONS ####
; =======================================================================
; _AccessCheck
; _AddAce
; _AddAceEx
; _AddAceEx1
; _AdjustPolicyAuditEvents
; _AdjustTokenPrivileges
; _AllocateAndInitializeSid
; _AllocateGUID
; _AllocateLUID
; _BuildSecurityDescriptor
; _CheckAclMemberShip
; _CheckTokenMembership
; _CombineAcl
; _ConvertSdToStringSd
; _ConvertSidToStringSid
; _ConvertStringSdToSd
; _ConvertStringSidToSid
; _CopySid
; _CreateProcessAsSystem
; _CreateProcessAsUser
; _CreateProcessWithLogon
; _CreateWellKnownSid
; _CryptAcquireContext
; _CryptBinaryToString
; _CryptCreateHash
; _CryptDecrypt
; _CryptDestroyHash
; _CryptDestroyKey
; _CryptDuplicateHash
; _CryptDuplicateKey
; _CryptEncrypt
; _CryptGenKey
; _CryptGetHashParam
; _CryptHashCeritificate
; _CryptHashData
; _CryptProtectData
; _CryptReleaseContext
; _CryptStringToBinary
; _CryptUnprotectData
; _CryptVerifyObjectHashValue
; _DeleteAce
; _DeleteAceByOwner
; _DuplicateAcl
; _DuplicateTokenEx
; _EqualPrefixSid
; _EqualSid
; _FreeSid
; _FreeVariable
; _GetAce
; _GetAclInformation
; _GetAuditedPermissionsFromAcl
; _GetEffectiveRightsFromAcl
; _GetExplicitAce
; _GetExplicitEntriesFromAcl
; _GetExplicitEntriesFromAcl1
; _GetLastError
; _GetLengthSid
; _GetNamedSecurityInfo
; _GetProcessHeap
; _GetSecurityDescriptorDacl
; _GetSecurityDescriptorGroup
; _GetSecurityDescriptorLength
; _GetSecurityDescriptorOwner
; _GetSecurityDescriptorSacl
; _GetSecurityInfo
; _GetSidIdentifierAuthority
; _GetSidSubAuthority
; _GetSidSubAuthorityCount
; _GetTokenGroups
; _GetTokenImpersonationLevel
; _GetTokenInformation
; _GetTokenOwner
; _GetTokenPrivileges
; _GetTokenSource
; _GetTokenType
; _GetTokenUser
; _GUIDFromString
; _HeapAlloc
; _HeapFree
; _HeapSize
; _ImpersonateLoggedOnUser
; _ImpersonateSystemContext
; _InitializeAcl
; _InitializeExplicitAccess
; _InitializeLuid
; _InitializeSecurityDescriptor
; _InitializeTrustee
; _IsNtfs
; _IsPrivilegeEnabled
; _IsTokenRestricted
; _IsValidAce
; _IsValidAcl
; _IsValidSecurityDescriptor
; _IsValidSid
; _IsWellKnownSid
; _LogonUser
; _LookupAccountName
; _LookupAccountSid
; _LookupPrivilegeDisplayName
; _LookupPrivilegeName
; _LookupPrivilegeOnLoggedUser
; _LookupPrivilegeValue
; _LsaAccountRightIsEnabled
; _LsaAddAccountRight
; _LsaAddLocalGroup
; _LsaAddLocalUser
; _LsaApiBufferFree
; _LsaApiBufferSize
; _LsaAu3LibraryIsIncluded
; _LsaClose
; _LsaCloseHandle
; _LsaCloseServiceHandle
; _LsaCreateSecret
; _LsaCreateSecretManager
; _LsaDeleteSecret
; _LsaDelLocalGroup
; _LsaDelLocalUser
; _LsaEnumerateAccountRights
; _LsaEnumerateAccountsWithUserRight
; _LsaEnumerateLocalAccounts
; _LsaEnumerateLocalGroups
; _LsaEnumerateLogonSessions
; _LsaEnumerateSecrets
; _LsaEnumerateWellKnownAccounts
; _LsaFreeMemory
; _LsaFreeReturnBuffer
; _LsaGetLogonSessionData
; _LsaGetSecret
; _LsaGetUserName
; _LsaGetUserRelativeID
; _LsaHiLong64
; _LsaInitializeBufferW
; _LsaLocalFree
; _LsaLocalGroupAddMembers
; _LsaLocalGroupDelMembers
; _LsaLocalGroupGetMembers
; _LsaLocalSize
; _LsaLocalUserGetGroups
; _LsaLocalUserGetInfo
; _LsaLocalUserGetPasswordPolicy
; _LsaLocalUserSetForceLogoff
; _LsaLocalUserSetMaxPasswordAge
; _LsaLocalUserSetMinPasswordAge
; _LsaLocalUserSetMinPasswordLength
; _LsaLocalUserSetModals
; _LsaLocalUserSetPasswordHistLength
; _LsaLoLong64
; _LsaLookupAccountsRight
; _LsaMakeLong64
; _LsaNtStatusToWinError
; _LsaOpenPolicy
; _LsaOpenSCManager
; _LsaOpenService
; _LsaQuerySecret
; _LsaRemoveAccountRight
; _LsaRetrieveFingerprint
; _LsaRetrievePrivateData
; _LsaStorePrivateData
; _LsaStringIsUserAccount
; _MakeAbsoluteSD
; _MakeSelfRelativeSD
; _OpenProcess
; _OpenProcessToken
; _QueryFileSecurity
; _QueryFileSecurityDacl
; _QueryFileSecurityOwner
; _QueryKernelObjectSecurity
; _QueryKernelObjectSecurityDacl
; _QueryKernelObjectSecurityOwner
; _QueryPolicyAuditEvents
; _QueryServiceObjectSecurity
; _QueryServiceObjectSecurityDacl
; _QueryServiceObjectSecurityOwner
; _QueryServiceObjectSecuritySacl
; _QueryShareObjectSecurityDacl
; _QueryShareObjectSecurityOwner
; _RegCloseKey
; _RegGetKeySecurity
; _RegGetKeySecurityDacl
; _RegGetKeySecurityOwner
; _RegGetKeySecuritySacl
; _RegOpenKeyEx
; _RegSetKeySecurity
; _RegSetKeySecurityDacl
; _RegSetKeySecurityOwner
; _RevertToSelf
; _SetAccessControlEntriesOrder
; _SetEntriesInAcl
; _SetEntriesInAcl1
; _SetFileSecurity
; _SetFileSecurityDacl
; _SetFileSecurityOwner
; _SetKernelObjectSecurity
; _SetKernelObjectSecurityDacl
; _SetKernelObjectSecurityOwner
; _SetNamedSecurityInfo
; _SetSecurityDescriptorDacl
; _SetSecurityDescriptorGroup
; _SetSecurityDescriptorOwner
; _SetSecurityDescriptorSacl
; _SetSecurityInfo
; _SetServiceObjectSecurity
; _SetServiceObjectSecurityDacl
; _SetServiceObjectSecurityOwner
; _SetServiceObjectSecuritySacl
; _SetShareObjectSecurityDacl
; _StringFromGUID
; =======================================================================

; #### FUNCTION ####
; =======================================================================
; Name  : _LookupAccountName
; Description   : Retrieves the SID pointer of the specified user account from the specified system.
; Parameter(s)  : $sName    - The name of the user account.
;       : $sSystem  - An optional string contains the system name on which the function executes, default to local.
; Return values : If succeeds, returns a SID pointer, else sets @error to a system error code.
; Author    : Pusofalse
; Remarks   : When you finished with the SID pointer, call _HeapFree function to free it.
; =======================================================================
Func _LookupAccountName($sName, $sSystem = "")
    Local $iResult, $pSid, $pDomain, $iSysError

    $iResult = DllCall("Advapi32.dll", "int", "LookupAccountName", _
            "str", $sSystem, "str", $sName, "ptr", 0, "int*", 0, _
            "ptr", 0, "int*", 0, "int*", 0)
    $pSid = _HeapAlloc($iResult[4])
    $pDomain = _HeapAlloc($iResult[6])
    $iResult = DllCall("Advapi32.dll", "int", "LookupAccountName", _
            "str", $sSystem, "str", $sName, _
            "ptr", $pSid, "int*", $iResult[4], _
            "ptr", $pDomain, "int*", $iResult[6], "int*", 0)
    $iSysError = _GetLastError()
    _HeapFree($pDomain)
    Return SetError($iSysError, $iResult[7], $pSid)
EndFunc ;==>_LookupAccountName

; #### FUNCTION ####
; =======================================================================
; Name  : _HeapFree
; Description   : Free the memory allocated by _HeapAlloc function.
; Parameter(s)  : $pMem - A pointer to the block of memory, returned by _HeapAlloc function.
; Return values : True indicates success, false to failure.
; Author    : Pusofalse
; Remarks   : The _Query*Security functions and some SID-related functions require the $pMem parameter, When you finished with this pointer, must call this function to free it, else, the running application must crumble!!!
; =======================================================================
Func _HeapFree(ByRef $pMem)
    If $pMem < 1 Then Return SetError(87, 0, False)

    Local $iResult, $hHeap = _GetProcessHeap()
    $iResult = DllCall("Kernel32.dll", "int", "HeapFree", "hWnd", $hHeap, _
            "dword", 0, "ptr", $pMem)
    If $iResult[0] Then $pMem = Ptr(0)
    Return $iResult[0] <> 0
EndFunc ;==>_HeapFree

; #### FUNCTION ####
; =======================================================================
; Name  : _HeapAlloc
; Description   : Allocates a block of memory from the process heap.
; Parameter(s)  : $iSize    - The size of block.
;       : $iAllocOption - Allocation option, default to ZERO_MEMORY.
; Return values : If succeeds, returns a pointer to the newly allocated memory block.
; Author    : Pusofalse
; Remarks       : When you no longer use this pointer, call _HeapFree function to free it.
; =======================================================================
Func _HeapAlloc($iSize, $iAllocOption = 8)
    If $iSize < 1 Then Return 0

    Local $pMem, $hHeap = _GetProcessHeap()
    $pMem = DllCall("Kernel32.dll", "ptr", "HeapAlloc", "hWnd", $hHeap, _
            "dword", $iAllocOption, "dword", $iSize)
    Return $pMem[0]
EndFunc ;==>_HeapAlloc

; #### INTERNAL USED ONLY FUNCTION ####
; =======================================================================
; Retrieves the handle to the process heap, _HeapAlloc function requires it.
; =======================================================================
Func _GetProcessHeap()
    Local $hHeap = DllCall("Kernel32.dll", "hWnd", "GetProcessHeap")
    Return $hHeap[0]
EndFunc ;==>_GetProcessHeap

; #### FUNCTION ####
; =======================================================================
; Name  : _HeapSize
; Description   : Retrieves the size of the memory block.
; Parameter(s)  : $pMem - A pointer to the memory block, returned by _HeapAlloc function.
; Return values : If succeeds, returns the size of the memory block.
; Author    : Pusofalse
; =======================================================================
Func _HeapSize($pMem)
    If $pMem < 1 Then Return 0
    Local $iSize, $hHeap

    $hHeap = _GetProcessHeap()
    $iSize = DllCall("Kernel32.dll", "long", "HeapSize", "hWnd", $hHeap, _
            "dword", 0, "ptr", $pMem)
    Return $iSize[0]
EndFunc ;==>_HeapSize

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaLocalSize
; Description   : Retrieves the size of the memory block.
; Parameter(s)  : $pMem - A pointer to a memory block.
; Return values : If suceeds, returns the size of the memory block.
; Author    : Pusofalse
; Remarks       : You can determine the size of the pointer to the access control list returned by _SetEntriesInAcl function.
; =======================================================================
Func _LsaLocalSize($pMem)
    Local $iSize = DllCall("Kernel32.dll", "long", "LocalSize", "ptr", $pMem)
    Return $iSize[0]
EndFunc ;==>_LsaLocalSize

; #### INTERNAL USED ONLY FUNCTION ####
; =======================================================================
; Retrieves the error code last set by the calling thread.
; =======================================================================
Func _GetLastError()
    Local $iSysError = DllCall("Kernel32.dll", "long", "GetLastError")
    Return $iSysError[0]
EndFunc ;==>_GetLastError

; #### FUNCTION ####
; =======================================================================
; Name  : _LookupAccountSid
; Description   : Retrieves the user account name string corresponded to the SID.
; Parameter(s)  : $pSid - A SID pointer.
;       : $sSystem  - The system name on which the function to execute, default to local system.
; Return values : If succeeds, returns a string contains the name of the user account.
; Author    : Pusofalse
; =======================================================================
Func _LookupAccountSid($pSid, $sSystem = "")
    Local $iResult, $sResult, $iSysError

    If Not _IsValidSid($pSid) Then Return SetError(@error, 0, "")
    $iResult = DllCall("Advapi32.dll", "int", "LookupAccountSid", _
            "str", $sSystem, "ptr", $pSid, "str", "", "int*", 0, _
            "str", "", "int*", 0, "int*", 0)
    $iResult = DllCall("Advapi32.dll", "int", "LookupAccountSid", _
            "str", $sSystem, "ptr", $pSid, _
            "str", "", "int*", $iResult[4], _
            "str", "", "int*", $iResult[6], "int*", 0)
    $iSysError = _GetLastError()
    $sResult = $iResult[3]
    If $iResult[5] Then $sResult = $iResult[5] & "\" & $iResult[3]
    Return SetError($iSysError, $iResult[7], $sResult)
EndFunc ;==>_LookupAccountSid

; #### FUNCTION ####
; =======================================================================
; Name  : _OpenProcess
; Description   : Open a process and retrieve its handle.
; Parameter(s)  : $iProcessId   - The process ID or image name.
;       : $iAccess  - Access rights, default to PROCESS_ALL_ACCESS.
;       : $iInherit - True indicates the handle will be inherited by the child process, default to false.
; Return values : If succeeds, returns the handle to the process, else returns zero and sets @error to a system error code.
; Author    : Pusofalse
; Remarks   : When you finished with the handle, call _LsaCloseHandle function to close it.
; =======================================================================
Func _OpenProcess($iProcessId, $iAccess = 0x1F0FFF, $iInherit = 0)
    Local $hProcess

    If $iProcessId = -1 Then $iProcessId = @AutoItPid
    $iProcessId = ProcessExists($iProcessId)
    If $iProcessId = 0 Then Return SetError(2, 0, 0)
    $hProcess = DllCall("Kernel32.dll", "long", "OpenProcess", _
            "int", $iAccess, "int", $iInherit, "int", $iProcessId)
    Return SetError(_GetLastError(), 0, $hProcess[0])
EndFunc ;==>_OpenProcess

; #### FUNCTION ####
; =======================================================================
; Name  : _OpenProcessToken
; Description   : Open the access token associated with the specified process and retrieve its handle.
; Parameter(s)  : $iProcessId   - Process ID or image name from which the access token is returned.
;       : $iAccess  - TOKEN ACCESS RIGHTS, default to TOKEN_ALL_ACCESS.
; Return values : If succeeds, returns the handle to the access token, otherwise returns zero and sets @error.
; Author    : Pusofalse
; Remarks   : When you finished with the handle, call _LsaCloseHandle function to close it.
; =======================================================================
Func _OpenProcessToken($iProcessId, $iAccess = $TOKEN_ALL_ACCESS)
    Local $iResult, $hProcess
    If $iProcessId = -1 Then $iProcessId = @AutoItPid
    $iProcessId = ProcessExists($iProcessId)
    $hProcess = _OpenProcess($iProcessId, 0x400)
    If Not $hProcess Then Return SetError(@ERROR, 0, 0)

    $iResult = DllCall("Advapi32.dll", "int", "OpenProcessToken", _
            "hWnd", $hProcess, "dword", $iAccess, "long*", 0)
    Return SetError(_GetLastError(), _LsaCloseHandle($hProcess), $iResult[3])
EndFunc ;==>_OpenProcessToken

; #### INTERNAL USED ONLY FUNCTION ####
; =======================================================================
; Retrieves the information of the access token, _GetToken* functions require this function.
; =======================================================================
Func _GetTokenInformation($hToken, $iTokenInfo)
    Local $pBuffer, $iResult

    $iResult = DllCall("Advapi32.dll", "int", "GetTokenInformation", _
            "hWnd", $hToken, "int", $iTokenInfo, _
            "ptr", 0, "dword", 0, "dword*", 0)
    $pBuffer = _HeapAlloc($iResult[5])
    $iResult = DllCall("Advapi32.dll", "int", "GetTokenInformation", _
            "hWnd", $hToken, "int", $iTokenInfo, _
            "ptr", $pBuffer, "dword", $iResult[5], "dword*", 0)
    Return SetError(_GetLastError(), $iResult[0], $pBuffer)
EndFunc ;==>_GetTokenInformation

; #### FUNCTION ####
; =======================================================================
; Name  : _GetTokenGroups
; Description   : Retrieves the group information of the access token.
; Parameter : $hToken   - Handle to the access token.
; Return values : An array with following format:
;       :   - $array[0][0] - The number of entries returned.
;       :   - $array[1][0] - The name of the first group.
;       :   - $array[1][1] - The Attribute of the SID of the first group.
;       :   - $array[2][0] - The name of the second group.
;       :   - ... ...
; Author    : Pusofalse
; =======================================================================
Func _GetTokenGroups($hToken)
    Local $pBuffer, $tBuffer, $tNum, $aResult[1][3], $tagBuffer, $iSysError, $pSid

;   _GetTokenPrivileges($hToken)
    $pBuffer = _GetTokenInformation($hToken, 2)
    $iSysError = @error
    $tNum = DllStructCreate("dword", $pBuffer)
    $aResult[0][0] = DllStructGetData($tNum, 1)
    Redim $aResult[$aResult[0][0] + 1][3]
    For $i = 1 to $aResult[0][0]
        $tagBuffer &= ";ptr;dword"
    Next
    $tBuffer = DllStructCreate("dword" & $tagBuffer, $pBuffer)
    For $i = 1 to $aResult[0][0]
        $pSid = DllStructGetData($tBuffer, ($i - 1) * 2 + 2)
        $pSid = _CopySid($pSid)
        $aResult[$i][2] = DllStructGetData($tBuffer, ($i - 1) * 2 + 3)
        $aResult[$i][0] = _LookupAccountSid($pSid)
        $aResult[$i][1] = _ConvertSidToStringSid($pSid)
        _HeapFree($pSid)
    Next
    _HeapFree($pBuffer)
    _FreeVariable($tNum)
    _FreeVariable($tBuffer)
    Return SetError($iSysError, 0, $aResult)
EndFunc ;==>_GetTokenGroups

; #### FUNCTION ####
; =======================================================================
; Name  : _GetTokenType
; Description   : Return an integer value indicating whether the token is a primary or impersonation token.
; Parameters    : $hToken   - The handle to the access token to be checked.
; Return values : If $hToken is a primary token, returns 1, if $hToken is an impersonation token, returns 2.
; Author        : Pusofalse
; =======================================================================
Func _GetTokenType($hToken)
    Local $pBuffer, $tBuffer, $iType, $iSysError

    $pBuffer = _GetTokenInformation($hToken, 8)
    $iSysError = @error
    $tBuffer = DllStructCreate("dword", $pBuffer)
    $iType = DllStructGetData($tBuffer, 1)
    _FreeVariable($tBuffer)
    Return SetError($iSysError, _HeapFree($pBuffer), $iType)
EndFunc ;==>_GetTokenType

; #### FUNCTION ####
; =======================================================================
; Name  : _GetTokenImpersonationLevel
; Description   : Retrieve the impersonation-level of the specified access token.
; Parameter(s)  : $hToken   - Handle to the access token.
; Return values : If succeeds, returns the level of impersonation of the token, else sets @error.
; Author    : Pusofalse
; =======================================================================
Func _GetTokenImpersonationLevel($hToken)
    Local $pBuffer, $tBuffer, $iSysError, $iLevel

    $pBuffer = _GetTokenInformation($hToken, 9)
    $iSysError = @error
    $tBuffer = DllStructCreate("dword", $pBuffer)
    $iLevel = DllStructGetData($tBuffer, 1)
    _FreeVariable($tBuffer)
    Return SetError($iSysError, _HeapFree($pBuffer), $iLevel)
EndFunc ;==>_GetTokenImpersonationLevel

; #### FUNCTION ####
; =======================================================================
; Name  : _GetTokenOwner
; Description   : Retrieves the owner the access token.
; Parameter(s)  : $hToken   - Handle to the access token.
; Return values : If succeeds, returns the owner of the token, in string format. Otherwise returns NULL and sets @error.
; Author    : Pusofalse
; =======================================================================
Func _GetTokenOwner($hToken)
    Local $pBuffer, $tBuffer, $iSysError, $sOwner, $pSid

    $pBuffer = _GetTokenInformation($hToken, 4)
    $iSysError = @error
    $tBuffer = DllStructCreate("ptr", $pBuffer)
    $pSid = DllStructGetData($tBuffer, 1)
    $sOwner = _LookupAccountSid($pSid)
    _FreeVariable($tBuffer)
    Return SetError($iSysError, _HeapFree($pBuffer), $sOwner)
EndFunc ;==>_GetTokenOwner

; #### FUNCTION ####
; =======================================================================
; Name  : _GetTokenUser
; Description   : Retrieves the user associated with specified token.
; Parameter(s)  : $hToken   - Handle to the access token.
; Return values : If succeeds, returns the user of the token, in string format, else returns NULL and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _GetTokenUser($hToken)
    Local $pBuffer, $tBuffer, $iSysError, $pSid, $sUser, $iAttr

    $pBuffer = _GetTokenInformation($hToken, 1)
    $iSysError = @error
    $tBuffer = DllStructCreate("ptr;dword", $pBuffer)
    $pSid = DllStructGetData($tBuffer, 1)
    $sUser = _LookupAccountSid($pSid)
    $iAttr = DllStructGetData($tBuffer, 2)
    _FreeVariable($tBuffer)
    Return SetError($iSysError, $iAttr, $sUser)
EndFunc ;==>_GetTokenUser

; #### FUNCTION ####
; =======================================================================
; This function is only internal used, frees the variable with the type of DLL structure, array, object...
; =======================================================================
Func _FreeVariable(ByRef $vVariable, $vValue = 0, $vReturn = "", $iError = @error, $iExtended = @extended)
    $vVariable = $vValue
    Return SetError($iError, $iExtended, $vReturn)
EndFunc ;==>_FreeVariable

; #### FUNCTION ####
; =======================================================================
; Name  : _GetTokenPrivileges
; Description   : Returns an array contains the token's privilege information.
; Parameter : $hToken   - Handle to the token.
; Return values : An array with following format:
;       :   - $aArray[0][0] - The number of privileges returned.
;       :   - $aArray[1][0] - The first privilege's name.
;       :   - $aArray[1][1] - The first privilege's display name.
;       :   - $aArray[1][2] - The first privilege's attribute. 0 indicates disabled, 2 indicates enabled, 3 indicates default-enabled.
;       :   - ... ...
; Author    : Pusofalse
; =======================================================================
Func _GetTokenPrivileges($hToken)
    Local $tBuffer, $pBuffer, $tagBuffer, $iSysError, $tNum, $aResult[1][3]

    If $hToken = 0 Then Return SetError(6, 0, $aResult)
    $pBuffer = _GetTokenInformation($hToken, 3)
    $iSysError = @error
    $tNum = DllStructCreate("dword", $pBuffer)
    $aResult[0][0] = DllStructGetData($tNum, 1)
    Redim $aResult[$aResult[0][0] + 1][3]
    For $i = 1 to $aResult[0][0]
        $tagBuffer &= ";dword;long;dword"
    Next
    $tBuffer = DllStructCreate("dword" & $tagBuffer, $pBuffer)
    For $i = 1 to $aResult[0][0]
        $aResult[$i][0] = _LookupPrivilegeName(DllStructGetData($tBuffer, ($i - 1) * 3 + 2))
        $aResult[$i][1] = _LookupPrivilegeDisplayName($aResult[$i][0])
        $aResult[$i][2] = DllStructGetData($tBuffer, ($i - 1) * 3 + 4)
    Next
    _HeapFree($pBuffer)
    _FreeVariable($tNum)
    _FreeVariable($tBuffer)
    Return SetError($iSysError, 0, $aResult)
EndFunc ;==>_GetTokenPrivileges

Func _GetTokenSource($hToken)
    Local $pBuffer, $tBuffer, $sSource, $iLuid, $iSysError

    $pBuffer = _GetTokenInformation($hToken, 7)
    $iSysError = @error
    $tBuffer = DllStructCreate("char SrcName[8];" & $tagLUID, $pBuffer)
    $sSource = DllStructGetData($tBuffer, "SrcName")
    $iLuid = DllStructGetData($tBuffer, "Low")
    _HeapFree($pBuffer)
    _FreeVariable($tBuffer)
    Return SetError($iSysError, $iLuid, $sSource)
EndFunc ;==>_GetTokenSource

Func _GetTokenStatistics($hToken)
    Local $tBuffer, $pBuffer, $aResult[10], $iSysError

    $pBuffer = _GetTokenInformation($hToken, 10)
    $tBuffer = DllStructCreate($tagTOKENSTATISTICS, $pBuffer)
    $iSysError = @error
    $aResult[0] = DllStructGetData($tBuffer, "TokenId")
    $aResult[1] = DllStructGetData($tBuffer, "AuthenticationId")
    $aResult[2] = DllStructGetData($tBuffer, "ExpirationTime")
    $aResult[3] = DllStructGetData($tBuffer, "TokenType")
    $aResult[4] = DllStructGetData($tBuffer, "ImpersonationLevel")
    $aResult[5] = DllStructGetData($tBuffer, "DynamicCharged")
    $aResult[6] = DllStructGetData($tBuffer, "DynamicAvalable")
    $aResult[7] = DllStructGetData($tBuffer, "GroupCount")
    $aResult[8] = DllStructGetData($tBuffer, "PrivilegeCount")
    $aResult[9] = DllStructGetData($tBuffer, "ModifiedId")
    _HeapFree($pBuffer)
    Return SetError($iSysError, _FreeVariable($tBuffer), $aResult)
EndFunc ;==>_GetTokenStatistics

; #### FUNCTION ####
; =======================================================================
; Name  : _LookupPrivilegeName
; Description   : Retrieves the name of privilege by using the given LUID.
; Parameter(s)  : $iLow - The value in low part of the LUID.
;       : $iHigh    - The value in high part of the LUID.
;       : $sSystem  - System name on which the function executes, default to local.
; Return values : If succeeds, returns a string contains the privilege name.
; Author    : Pusofalse
; =======================================================================
Func _LookupPrivilegeName($iLow, $iHigh = 0, $sSystem = "")
    Local $pLuid, $iResult

    $pLuid = _InitializeLuid($iLow, $iHigh)
    $iResult = DllCall("Advapi32.dll", "int", "LookupPrivilegeName", _
            "str", $sSystem, "ptr", $pLuid, "str", "", "int*", 0)
    $iResult = DllCall("Advapi32.dll", "int", "LookupPrivilegeName", _
            "str", $sSystem, "ptr", $pLuid, "str", "", "int*", $iResult[4])
    Return SetError(_GetLastError(), _HeapFree($pLuid), $iResult[3])
EndFunc ;==>_LookupPrivilegeName

; #### FUNCTION ####
; =======================================================================
; Name  : _LookupPrivilegeDisplayName
; Description   : Retrieves the display name of the privilege by using the privilege name.
; Parameter(s)  : $sName    - The name of the privilege.
;       : $sSystem  - System name on which the function executes, default is local system.
; Return values : If succeeds, returns a string contains the privilege's display name, else returns NULL and sets @error.
; Author    : Pusofalse
; =======================================================================
Func _LookupPrivilegeDisplayName($sName, $sSystem = "")
    Local $iResult

    $iResult = DllCall("Advapi32.dll", "int", "LookupPrivilegeDisplayName", _
            "str", $sSystem, "str", $sName, "str", "", _
            "dword*", 0, "dword*", 0)
    $iResult = DllCall("Advapi32.dll", "int", "LookupPrivilegeDisplayName", _
            "str", $sSystem, "str", $sName, "str", "", _
            "dword*", $iResult[4] * 2, "dword*", 0)
    Return SetError(_GetLastError(), 0, $iResult[3])
EndFunc ;==>_LookupPrivilegeDisplayName

; #### FUNCTION ####
; =======================================================================
; Name  : _LookupPrivilegeValue
; Description   : Retrieves the local unique identifier of the privilege.
; Parameter(s)  : $sName    - Privilege's name.
;       : $sSystem  - Specifies the system on which the function executes, default to local.
; Return values : If succeeds, returns a pointer to a LUID structure, contains the LUID of the specified privilege.
; Author    : Pusofalse
; Remarks   : When you finished with the LUID pointer, call _HeapFree function to free it.
; =======================================================================
Func _LookupPrivilegeValue($sName, $sSystem = "")
    Local $pLuid, $iResult

    $pLuid = _InitializeLuid(0)
    $iResult = DllCall("Advapi32.dll", "int", "LookupPrivilegeValue", _
            "str", $sSystem, "str", $sName, "ptr", $pLuid)
    Return SetError(_GetLastError(), $iResult[0], $pLuid)
EndFunc ;==>_LookupPrivilegeValue

; #### FUNCTION ####
; =======================================================================
; Name  : _InitializeLuid
; Description   : This function initializes a LUID structure.
; Parameter(s)  : $iLowPart - The value in low part of LUID.
;       : $iHighPart    - The value in high part of LUID.
; Return values : A pointer to a LUID structure.
; Author    : Pusofalse
; Remarks   : When you finished with this LUID pointer, call _HeapFree function to free it.
; =======================================================================
Func _InitializeLuid($iLowPart, $iHighPart = 0)
    Local $pLuid, $tLuid

    $pLuid = _HeapAlloc(8)
    $tLuid = DllStructCreate($tagLUID, $pLuid)
    DllStructSetData($tLuid, "Low", $iLowPart)
    DllStructSetData($tLuid, "High", $iHighPart)
    Return $pLuid
EndFunc ;==>_InitializeLuid

; #### FUNCTION ####
; =======================================================================
; Name  : _IsTokenRestricted
; Description   : Determines the token whether is a restricted token or not.
; Parameter(s)  : $hToken   - Handle to the access token to determine.
; Return values : True indicates the token is a restricted token, false otherwise.
; Author    : Pusofalse
; =======================================================================
Func _IsTokenRestricted($hToken)
    Local $iResult = DllCall("Advapi32.dll", "int", "IsTokenRestricted", "hWnd", $hToken)
    Return SetError(_GetLastError(), 0, $iResult[0] <> 0)
EndFunc ;==>_IsTokenRestricted

; #### FUNCTION ####
; =======================================================================
; Name  : _IsValidAcl
; Description   : Validates an access control list.
; Parameter(s)  : $pAcl - A pointer to an access control list, either a DACL or a SACL.
; Return values : If $pAcl is a valid access control list pointer, returns true, otherwise returns false, in this case to sets @error to ERROR_INVALID_ACL.
; Author    : Pusofalse
; =======================================================================
Func _IsValidAcl($pAcl)
    Local $iResult = DllCall("Advapi32.dll", "int", "IsValidAcl", "ptr", $pAcl)
    If $iResult[0] Then Return SetError(0, 0, True)
    Return SetError($ERROR_INVALID_ACL, 0, False)
EndFunc ;==>_IsValidAcl

; #### FUNCTION ####
; =======================================================================
; Name  : _IsValidSid
; Description   : Validate a pointer to a SID.
; Parameter(s)  : $pSid - A pointer to a SID structure.
; Return values : If $pSid is a valid SID pointer, returns true, otherwise returns false, in the case to set @error to ERROR_INVALID_SID.
; Author    : Pusofalse
; =======================================================================
Func _IsValidSid($pSid)
    Local $iResult
    $iResult = DllCall("Advapi32.dll", "int", "IsValidSid", "ptr", $pSid)
    If $iResult[0] Then Return SetError(0, 0, True)
    Return SetError($ERROR_INVALID_SID, 0, False)
EndFunc ;==>_IsValidSid

; #### FUNCTION ####
; =======================================================================
; Name  : _GetSidIdentifierAuthority
; Description   : Retrieves the authority of the SID.
; Parameter(s)  : $pSid - A pointer to a SID.
; Return values : Returns the authority, in string format if succeeds; else returns NULL and sets @error to ERROR_INVALID_SID.
; Author    : Pusofalse
; =======================================================================
Func _GetSidIdentifierAuthority($pSid)
    Local $iResult, $tBuffer, $iAuty, $sAuty
    Local $aAuty[10] = ["NULL", "WORLD", "LOCAL", "CREATOR", "NON UNIQUE", "NT", "UNKNOWN", "UNKNOWN", "UNKNOWN", "RESOURCE MANAGER"]

    If Not _IsValidSid($pSid) Then Return SetError(@error, 0, "")
    $iResult = DllCall("Advapi32.dll", "ptr", "GetSidIdentifierAuthority", "ptr", $pSid)
    $tBuffer = DllStructCreate($tagSIDIDENTIFIERAUTHORITY, $iResult[0])
    $iAuty = DllStructGetData($tBuffer, "Authority")
    _FreeVariable($tBuffer)
    Return SetExtended($iAuty, $aAuty[$iAuty] & " SID AUTHORITY")
EndFunc ;==>_GetSidIdentifierAuthority

; #### FUNCTION ####
; =======================================================================
; Name  : _GetSidSubAuthorityCount
; Description   : Retrieves the number of the sub-authorities of the SID.
; Parameter(s)  : $pSid - A pointer to a SID.
; Return values : If succeeds, returns the number of the sub-authorities of the SID.
; Author    : Pusofalse
; =======================================================================
Func _GetSidSubAuthorityCount($pSid)
    Local $iResult, $tCount, $iCount

    If Not _IsValidSid($pSid) Then Return SetError(@error, 0, 0)
    $iResult = DllCall("Advapi32.dll", "ptr", "GetSidSubAuthorityCount", "ptr", $pSid)
    $tCount = DllStructCreate("byte Count", $iResult[0])
    $iCount = DllStructGetData($tCount, "Count")
    Return SetExtended(_FreeVariable($tCount), $iCount)
EndFunc ;==>_GetSidSubAuthorityCount

; #### FUNCTION ####
; =======================================================================
; Name  : _GetSidSubAuthority
; Description   : This function queries the sub-authority of the SID.
; Parameter(s)  : $pSid - A pointer to a SID.
;       : $iIndex   - Zero-based index of the authority which to query.
; Return values : If succeeds, returns the specified sub-authority of the SID.
; Author    : Pusofalse
; =======================================================================
Func _GetSidSubAuthority($pSid, $iIndex)
    Local $iResult, $tSubAuthority, $iSubAuthority

    If Not _IsValidSid($pSid) Then Return SetError(@error, 0, 0)
    $iResult = DllCall("Advapi32.dll", "ptr", "GetSidSubAuthority", _
            "ptr", $pSid, "dword", $iIndex)
    $tSubAuthority = DllStructCreate("dword SubAuthority", $iResult[0])
    $iSubAuthority = DllStructGetData($tSubAuthority, "SubAuthority")
    Return SetExtended(_FreeVariable($tSubAuthority), $iSubAuthority)
EndFunc ;==>_GetSidSubAuthority

; #### FUNCTION ####
; =======================================================================
; Name  : _IsWellKnownSid
; Description   : Determines the SID whether is a well-known SID or not.
; Parameter(s)  : $pSid - A pointer to a SID to validate.
;       : $iType    - The type of the SID to determine, or -1, in which case, the function sets @extended to the type of the SID.
; Return values : If $pSid is a well-known SID, returns true, if $iType is set to -1 by the caller, the @extended is set to the type of the SID. If $pSid is not a well-known SID, returns false. If $pSid is not a valid SID, returns zero also and sets @error to ERROR_INVALID_SID.
; Author    : Pusofalse
; =======================================================================
Func _IsWellKnownSid($pSid, $iType)
    Local $iResult

    If Not _IsValidSid($pSid) Then Return SetError(@error, -1, 0)
    If $iType = -1 Then
        For $i = 0 to 78
            $iResult = DllCall("Advapi32.dll", "int", "IsWellKnownSid", "ptr", $pSid, "int", $i)
            If $iResult[0] Then Return SetError(_GetLastError(), $i, 1)
        Next
        Return False
    Else
        $iResult = DllCall("Advapi32.dll", "int", "IsWellKnownSid", "ptr", $pSid, "int", $iType)
        Return $iResult[0] <> 0
    EndIf
EndFunc ;==>_IsWellKnownSid

; #### FUNCTION ####
; =======================================================================
; Name  : _GetLengthSid
; Description   : Retrieve the length of the SID.
; Parameter(s)  : $pSid - A pointer to SID from which the length is retrieved.
; Return values : If succeeds, returns the length of the SID. If $pSid isn't a valid SID pointer, returns zero and sets @error to ERROR_INVALID_SID.
; Author    : Pusofalse
; =======================================================================
Func _GetLengthSid($pSid)
    Local $iResult
    If Not _IsValidSid($pSid) Then Return SetError(@error, 0, 0)
    $iResult = DllCall("Advapi32.dll", "dword", "GetLengthSid", "ptr", $pSid)
    Return $iResult[0]
EndFunc ;==>_GetLengthSid

; #### FUNCTION ####
; =======================================================================
; Name  : _CopySid
; Description   : This function duplicates a SID pointer.
; Parameter(s)  : $pSid - A pointer to a SID structure.
; Return values : If succeeds, return the pointer to the new copied SID, else sets @error to a system error code.
; Author    : Pusofalse
; Remarks   : When you finished with this new copied SID, call _HeapFree function to free it.
; =======================================================================
Func _CopySid($pSid)
    Local $pNewSid, $iResult, $iLength

    If Not _IsValidSid($pSid) Then Return SetError(@error, 0, 0)
    $iLength = _GetLengthSid($pSid)
    $pNewSid = _HeapAlloc($iLength)
    $iResult = DllCall("Advapi32.dll", "int", "CopySid", "dword", $iLength, _
            "ptr", $pNewSid, "ptr", $pSid)
    Return SetError(_GetLastError(), 0, $pNewSid)
EndFunc ;==>_CopySid

; #### FUNCTION ####
; =======================================================================
; Name  : _IsValidSecurityDescriptor
; Description   : Validates a security descriptor.
; Parameter(s)  : $pSecurDesc   - A pointer to a security descriptor.
; Return values : If $pSecurDesc is a valid security descriptor, returns true, else returns false and sets @error to ERROR_INVALID_SECURITY_DESCR.
; Author    : Pusofalse
; =======================================================================
Func _IsValidSecurityDescriptor($pSecurDesc)
    Local $iResult = DllCall("Advapi32.dll", "int", "IsValidSecurityDescriptor", "ptr", $pSecurDesc)
    If $iResult[0] Then Return SetError(0, 0, True)
    Return SetError($ERROR_INVALID_SECURITY_DESCR, 0, False)
EndFunc ;==>_IsValidSecurityDescriptor

; #### FUNCTION ####
; =======================================================================
; Name  : _InitializeAcl
; Description   : This function initializes an access control list.
; Parameter(s)  : $pAcl - A pointer to an access control list.
;       : $iAclSize - The size of the $pAcl.
;       : $iRevision    - The revision level of the ACL structure being created, default is ACL_REVISION.
; Return values : True indicates success, false indicates failure.
; Author    : Pusofalse
; =======================================================================
Func _InitializeAcl($pAcl, $iAclSize, $iRevision = 2)
    Local $iResult
    $iResult = DllCall("Advapi32.dll", "int", "InitializeAcl", _
            "ptr", $pAcl, "int", $iAclSize, "int", $iRevision)
    Return SetError(_GetLastError(), 0, $iResult[0] <> 0)
EndFunc ;==>_InitializeAcl

; #### FUNCTION ####
; =======================================================================
; Name  : _InitializeSecurityDescriptor
; Description   : Initializes a new security descriptor.
; Parameter(s)  : $iLength   - The length of the security descriptor, the minimum length is 20 bytes.
; Return values : If succeeds, returns the pointer to the new security descriptor, else sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _InitializeSecurityDescriptor($iLength = 20)
    If $iLength < 20 Then $iLength = 20

    Local $pSecurDesc = _HeapAlloc($iLength), $iResult
    $iResult = DllCall("Advapi32.dll", "int", "InitializeSecurityDescriptor", _
            "ptr", $pSecurDesc, "dword", 1)
    Return SetError(_GetLastError(), $iResult, $pSecurDesc)
EndFunc ;==>_InitializeSecurityDescriptor

; #### FUNCTION ####
; =======================================================================
; Name  : _GetNamedSecurityInfo
; Description   : Retrieves the security information of the specified securable object.
; Parameter(s)  : $sObject  - The name the object, in string format, can be a file, registry key, service, share resource, event, user object and so on.
;       : $iType    - The type of the object, if the $sObject is a file, this parameter must set to SE_FILE_OBJECT. For more options, see SECURITY OBJECT TYPE CONSTANTS.
;       : $iSecurLevel  - Specifies the security information to be retrieved. can be one or more of the following values:
;       :   - $OWNER_SECURITY_INFORMATION (1) - Retrieve the owner information of the object.
;       :   - $GROUP_SECURITY_INFORMATION (2) - Retrieve the primary group of the object.
;       :   - $DACL_SECURITY_INFORMATION (4) - Retrieve the DACL information of the object.
;       :   - $SACL_SECURITY_INFORMATION (8) - Retrieve the SACL information of the object.
; Return values : Returns an array with following format:
;       :   - $aArray[0] - A DWORD value indicates the success or failure of the call to this function, zero indicates success.
;       :   - $aArray[1] - The name of the object, same to $sObject parameter.
;       :   - $aArray[2] - The type of the object, same to $iType parameter.
;       :   - $aArray[3] - The security information requested, same to $iSecurLevel parameter.
;       :   - $aArray[4] - The owner of the object, if OWNER_SECURITY_INFORMATION is not specified in $iSecurLevel parameter, this value is zero.
;       :   - $aArray[5] - The primary group of the object, if $iSecurLevel does not contains the GROUP_SECURITY_INFORMATION, this value is zero.
;       :   - $aArray[6] - The DACL of the object, if $iSecurLevel does not contains the DACL_SECURITY_INFORMATION, this value is 0.
;       :   - $aArray[7] - The SACL of the object, if $iSecurLevel doest not contains the SACL_SECURITY_INFORMATION, this value is 0.
;       :   - $aArray[8] - A pointer to security descriptor that contains the security information of the object.
; Author    : Pusofalse
; Remarks   : If SACL_SECURITY_INFORMATION is specified in $iSecurLevel parameter, the caller must have the SE_SECURITY_NAME privilege enabled, to enable this privilege, call _AdjustTokenPrivileges function. The $iSecurLevel must not be zero.
; =======================================================================
Func _GetNamedSecurityInfo($sObject, $iType, $iSecurLevel = 4)
    Local $iResult
    $iResult = DllCall("Advapi32.dll", "dword", "GetNamedSecurityInfo", _
            "str", $sObject, "int", $iType, "int", $iSecurLevel, _
            "ptr*", 0, "ptr*", 0, "ptr*", 0, "ptr*", 0, "ptr*", 0)
    Return $iResult
EndFunc ;==>_GetNamedSecurityInfo

; #### FUNCTION ####
; =======================================================================
; Name  : _SetNamedSecurityInfo
; Description   : Sets the security information for the specified object.
; Parameter(s)  : $sObject  - The name of the object, in string format, can be a name of a file, service, registry key, share resource, event, and so on.
;       : $iType    - The type of the object. If $sObject is a service name, this value must set to SE_SERVICE, for more options, see SECURITY OBJECT TYPE CONSTANTS.
;       : $iSecurLevel  - Specifies the security information to be set, can be one or more of the following values:
;       :   - $OWNER_SECURITY_INFORMATION (1) - $pOwner is valid, contains the owner the object.
;       :   - $GROUP_SECURITY_INFORMATION (2) - $pGroup is valid, contains the primary group of the object.
;       :   - $DACL_SECURITY_INFORMATION (4) - $pDacl is valid, contains the DACL of the object.
;       :   - $SACL_SECURITY_INFORMATION (8) - $pSacl is valid, contains the SACL of the object.
;       ; $pOwner   - The owner of the object, if OWNER_SECURITY_INFORMATION is not specified in $iSecurLevel parameter, this parameter is ignored.
;       : $pGroup   - The primary group of the object, if GROUP_SECURITY_INFORMATION is not specified in $iSecurLevel parameter, this value is ignored.
;       : $pDacl    - The DACL of the object, if DACL_SECURITY_INFORMATION is not specified in $iSecurLevel, this value is ignored.
;       : $pSacl    - The SACL of the object, if SACL_SECURITY_INFORMATION is not specified, this parameter can be zero.
; Return values : True indicates success, false to failure, in this case to set @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _SetNamedSecurityInfo($sObject, $iType, $iSecurLevel, $pOwner, $pGroup, $pDacl, $pSacl)
    Local $iResult
    $iResult = DllCall("Advapi32.dll", "dword", "SetNamedSecurityInfo", _
            "str", $sObject, "int", $iType, "int", $iSecurLevel, _
            "ptr", $pOwner, "ptr", $pGroup, _
            "ptr", $pDacl, "ptr", $pSacl)
    Return SetError($iResult[0], 0, $iResult[0] = 0)
EndFunc ;==>_SetNamedSecurityInfo

#### FUNCTION ####
; =======================================================================
; Name  : _GetAclInformation
; Description   : This function retrieves the informaton about an access control list.
; Parameter(s)  : $pAcl - A pointer to an access control list.
; Return values : If succeeds, @extended is set to number of access control entries, the low order word of return value is set to the number of bytes that is in use of ACL, high order word of return value is set to the number of bytes that is in unuse of ACL. If fails, returns zero and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _GetAclInformation($pAcl)
    Local $tAclSize, $pAclSize, $aResult[3], $iResult, $iSysError

    If Not _IsValidAcl($pAcl) Then Return SetError(@error, 0, 0)
    $tAclSize = DllStructCreate($tagACLSIZEINFO)
    $pAclSize = DllStructGetPtr($tAclSize)
    $iResult = DllCall("Advapi32.dll", "int", "GetAclInformation", _
            "ptr", $pAcl, "ptr", $pAclSize, "int", 12, "int", 2)
    $iSysError = _GetLastError()
    $aResult[0] = DllStructGetData($tAclSize, "AceCount")
    $aResult[1] = DllStructGetData($tAclSize, "BytesInUse")
    $aResult[2] = DllStructGetData($tAclSize, "BytesFree")
    _FreeVariable($tAclSize)
    $iResult = bitOR(bitAND($aResult[1], 0xFFFF), bitShift($aResult[2], -16))
    Return SetError($iSysError, $aResult[0], $iResult)
EndFunc ;==>_GetAclInformation

; =======================================================================
; Bad function, do not use it.
; =======================================================================
Func _CombineAcl(ByRef $pAcl1, ByRef $pAcl2)
    Local $pNewAcl, $iNumofAce

    If Not _IsValidAcl($pAcl1) Then Return SetError(@error, 1, 0)
    If Not _IsValidAcl($pAcl2) Then Return SetError(@error, 2, 0)

    $iNumOfAce = _GetExplicitEntriesFromAcl($pAcl2)
    $iNumOfAce = $iNumOfAce[0][0]
    If $iNumOfAce = 0 Then Return SetError(0, 0, $pAcl1)

    For $i = 0 to $iNumOfAce - 1
        _AddAce($pAcl1, _GetAce($pAcl2, $i))
    Next
    Return 1
EndFunc ;==>_CombineAcl

; =======================================================================
; Bad function, do not use it.
; =======================================================================
Func _DuplicateAcl(ByRef $pAcl, $fTrimSize = True)
    Local $pNewAcl, $iSize, $iNum

    If Not _IsValidAcl($pAcl) Then Return SetError(@error, 0, 0)
    $iSize = _GetAclInformation($pAcl)
    $iNum = _GetExplicitEntriesFromAcl($pAcl)
    $iNum = $iNum[0][0]
    If $fTrimSize = True Then
        $iSize = bitAND($iSize, 0xFFFF)
    Else
        $iSize = bitAND($iSize, 0xFFFF) + bitShift($iSize, 0x10)
    EndIf
    $pNewAcl = _HeapAlloc($iSize)
    _InitializeAcl($pNewAcl, $iSize)
    For $i = $iNum - 1 to 0 Step - 1
        _AddAce($pNewAcl, _GetAce($pAcl, $i))
    Next
    Return SetError(@error, 0, $pNewAcl)
EndFunc ;==>_DuplicateAcl

; #### FUNCTION ####
; =======================================================================
; Name  : _GetExplicitEntriesFromAcl
; Description   : Retrieves an array contains the data described as the access control entry (ACE) in an access control list (ACL).
; Parameter(s)  : $pAcl - A pointer to an access control list.
; Return values : If succeeds, returns an array with following format:
;       :   - $aArray[0][0] - The number of ACE returned.
;       :   - $aArray[1][0] - The owner of the first ACE.
;       :   - $aArray[1][1] - The SID string of the owner in first ACE.
;       :   - $aArray[1][2] - The access mask value assigned to the first owner.
;       :   - $aArray[1][3] - The access mode, DENY_ACCESS and GRANT_ACCESS are defined in DACL.
;       :   - $aArray[1][4] - The inheritance options, see INHERITANCE CONTANTS for possible values.
;       :   - ... ...
;       : If fails, the value of $aArray[0][0] is set to zero, and sets @error to a system error code.
; Author    : Pusofalse
; Remarks       : If $pAcl is a NULL ACL pointer, the value of $aArray[0][0] and @error are both set to zero.
; =======================================================================
Func _GetExplicitEntriesFromAcl($pAcl)
    Local $iResult, $aResult[1][5] = [[0]], $tagBuffer, $tBuffer, $vTemp

    If Not _IsValidAcl($pAcl) Then Return SetError(@error, 0, $aResult)
    $iResult = DllCall("Advapi32.dll", "dword", "GetExplicitEntriesFromAcl", _
            "ptr", $pAcl, "ulong*", 0, "ptr*", 0)
    $aResult[0][0] = $iResult[2]
    Redim $aResult[$iResult[2] + 1][5]
    For $i = 1 to $iResult[2]
        $tagBuffer &= "dword[3];ptr;int[3];ptr;"
    Next
    $tBuffer = DllStructCreate($tagBuffer, $iResult[3])
    For $i = 1 to $iResult[2]
        $vTemp = DllStructGetData($tBuffer, ($i - 1) * 4 + 3, 2)
        $aResult[$i][0] = DllStructGetData($tBuffer, ($i - 1) * 4 + 4)
        If $vTemp = $TRUSTEE_IS_SID Then
            $pSid = _CopySid($aResult[$i][0])
            $aResult[$i][0] = _LookupAccountSid($pSid)
            $aResult[$i][1] = _ConvertSidToStringSid($pSid)
            _HeapFree($pSid)
        ElseIf $vTemp = $TRUSTEE_IS_NAME Then
            $tTemp = DllStructCreate("char[256]", $aResult[$i][0])
            $aResult[$i][0] = DllStructGetData($tTemp, 1)
            $pSid = _LookupAccountName($aResult[$i][0])
            $aResult[$i][1] = _ConvertSidToStringSid($pSid)
            _FreeVariable($tTemp)
            _HeapFree($pSid)
        Else
            $aResult[$i][1] = $vTemp
        EndIf
        $aResult[$i][2] = "0x" & Hex(DllStructGetData($tBuffer, ($i - 1) * 4 + 1, 1))
        $aResult[$i][3] = DllStructGetData($tBuffer, ($i - 1) * 4 + 1, 2)
        $aResult[$i][4] = DllStructGetData($tBuffer, ($i - 1) * 4 + 1, 3)
    Next
    _FreeVariable($tBuffer)
    _LsaLocalFree($iResult[3])
    Return SetError($iResult[0], 0, $aResult)
EndFunc ;==>_GetExplicitEntriesFromAcl

; #### FUNCTION ####
; =======================================================================
; Name  : _GetExplicitEntriesFromAcl1
; Description   : Retrieve the ACE information from an access control list (ACL).
; Parameter(s)  : $pAcl - A pointer to an access control list from which the ACEs is retrieved.
; Return values : An array with following format:
;       : - $aArray[0][0]   - The number of the returned ACEs.
;       : - $aArray[1][0]   - The trustee of the first entry.
;       : - $aArray[1][1]   - The SID (in string format) of the first trustee.
;       : - $aArray[1][2]   - Access mask.
;       : - $aArray[1][3]   - Access type specified for the first trustee. ACCESS_ALLOWED_ACE_TYPE (0) indicates the ACE is an allowed-ace, ACCESS_DENIED_ACE_TYPE (1) indicates the ACE is an denied-ace. SYSTEM_ALARM_ACE_TYPE is for a system-audit ace.
;       : - $aArray[1][4]   - Access flags (inheritance options).
;       : - ... ...
; Author    : Pusofalse
; Remarks   : If $pAcl is a NULL-ACL, the values of $aArray[0][0] and @error are set to zero. The diversity between this function and _GetExplicitEntriesFromAcl function is that, if one ACE is inherited from its parent object, _GetExplicitEntriesFromAcl function does not return this ACE (in other words, _GetExplicitEntriesFromAcl function can only return the non-inherited ACE). This function returns all ACEs.
; =======================================================================
Func _GetExplicitEntriesFromAcl1(ByRef $pAcl)
    Local $aResult[1][5] = [[0]], $pSid, $tAccessAce, $pAce

    If Not _IsValidAcl($pAcl) Then Return SetError(@error, 0, $aResult)
    _GetAclInformation($pAcl)
    $aResult[0][0] = @extended
    If $aResult[0][0] = 0 Then Return SetError(@error, 0, $aResult)
    Redim $aResult[$aResult[0][0] + 1][5]

    For $i = 1 to $aResult[0][0]
        $pAce = _GetAce($pAcl, $i - 1)
        $tAccessAce = DllStructCreate($tagACCESSACE, $pAce)
        $pSid = _CopySid(DllStructGetPtr($tAccessAce, "SidStart"))
        $aResult[$i][0] = _LookupAccountSid($pSid)
        $aResult[$i][1] = _ConvertSidToStringSid($pSid)
        $aResult[$i][2] = "0x" & Hex(DllStructGetData($tAccessAce, "AccessMask"))
        $aResult[$i][3] = DllStructGetData($tAccessAce, "AceType")
        $aResult[$i][4] = DllStructGetData($tAccessAce, "AceFlags")
        _HeapFree($pSid)
    Next
    Return $aResult
EndFunc ;==>_GetExplicitEntriesFromAcl1

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaLocalFree
; Parameter(s)  : $pMem - A pointer to a block of memory.
; Return values : True indicates success, false to failure.
; Author    : Pusofalse
; Remarks   : _SetEntriesInAcl or other functions return this $pMem parameter.
; =======================================================================
Func _LsaLocalFree($pMem)
    Local $iResult
    $iResult = DllCall("Kernel32.dll", "int", "LocalFree", "ptr", $pMem)
    Return $iResult[0] <> $pMem
EndFunc ;==>_LsaLocalFree

; #### FUNCTION ####
; =======================================================================
; Name  : _BuildSecurityDescriptor
; Description   : This function initializes a security descriptor structure.
; Parameters    : $sOwner   - The name of the owner of this security descriptor, can be blank.
;       : $sGroup   - The primary group name of this security descriptor, can be blank.
;       : $aAccess  - The DACL security information for this security descriptor, can be zero.
;       : $aAudit       - The SACL security information for this security descriptor, can be zero.
;       : $pOldSd       - Specify an exising pointer to a self-relative security descriptor, if non-zero, in which case, the function builds the new security descriptor by merging the specified owner, group, access control, and audit-control information with the information in this security descriptor. This parameter can NULL.
; Return values : If succeeds, the return value is a pointer to a new security descriptor contains the security data you specified, otherwise, the value is set to zero, and @error is set to ERROR_INVALID_PARAMETER, and @extended is set to:
;       : If $aAccess parameter contains invalid data, @extended is set to 1.
;       : If $aAudit parameter contains invalid data, @extended is set to 2.
;       : If $sOwner is non-null, and it is an invalid owner, @extended is set to 3.
;       : If $sGroup is non-null, and it is an invalid primary group, @extended is set to 4.
;       : If $pOldSd is non-zero, and it is an invalid security descriptor, @extended is set to 5.
; Author        : Pusofalse
; Remarks       : For the format of $aAccess and $aAudit parameter can only be token, see _SetEntriesInAcl function's PARAMETERS section. When you no longer use the pointer to the new security descriptor, call _LsaLocalFree function to free it.
; =======================================================================
Func _BuildSecurityDescriptor($sOwner, $sGroup, $aAccess, $aAudit, $pOldSd = 0)
    Local $iResult, $pOwner, $pGroup, $pDacl, $pSacl

    If IsArray($aAccess) Then $pDacl = _InitializeExplicitAccess($aAccess)
    If IsArray($aAudit) Then $pSACL = _InitializeExplicitAccess($aAudit)
    If $sOwner Then $pOwner = _InitializeTrustee($sOwner)
    If $sGroup Then $pGroup = _InitializeTrustee($sGroup)

    If IsArray($aAccess) And Not $pDacl Then Return SetError(87, 1, 0)
    If IsArray($aAudit) And Not $pSACL Then Return SetError(87, 2, 0)
    If $sOwner <> "" And Not $pOwner Then Return SetError(87, 3, 0)
    If $sGroup <> "" And Not $pGroup Then Return SetError(87, 4, 0)
    If $pOldSd And Not _IsValidSecurityDescriptor($pOldSd) Then Return SetError(87, 5, 0)

    $iResult = DllCall("Advapi32.dll", "dword", "BuildSecurityDescriptor", _
            "ptr", $pOwner, "ptr", $pGroup, _
            "ulong", Ubound($aAccess), "ptr", $pDacl, _
            "ulong", Ubound($aAudit), "ptr", $pSacl, _
            "ptr", $pOldSd, "ulong*", 0, "ptr*", 0)
    If $pOwner Then _HeapFree($pOwner)
    If $pGroup Then _HeapFree($pGroup)
    If $pDacl Then _HeapFree($pDacl)
    If $pSACL Then _HeapFree($pSACL)
    Return SetError($iResult[0], $iResult[8], $iResult[9])
EndFunc ;==>_BuildSecurityDescriptor

; #### FUNCTION ####
; =======================================================================
; Name  : _InitializeTrustee
; Description   : This function initializes a tagTRUSTEE structure.
; Parameter(s)  : $pSid - Either a SID pointer to an user account, or a string contains the user account's name.
; Return values : If $pSid is a valid pointer or a valid user account string, return value is a pointer a tagTRUSTEE structure, otherwise, the return value is set to zero, and @error is set to ERROR_INVALID_PARAMETER.
; Author    : Pusofalse
; Remarks       : When you finished with this pointer, call _HeapFree function to free it.
; =======================================================================
Func _InitializeTrustee($pSid)
    Local $iResult, $tTRUSTEE, $pTRUSTEE

    If Not _IsValidSid($pSid) Then
        $pSid = _LookupAccountName($pSid)
        If Not _IsValidSid($pSid) Then Return SetError(87, 1, 0)
    EndIf
    $pTRUSTEE = _HeapAlloc(20)
    $tTRUSTEE = DllStructCreate($tagTRUSTEE, $pTRUSTEE)
    DllStructSetData($tTRUSTEE, "From", $TRUSTEE_IS_SID)
    DllStructSetData($tTRUSTEE, "Name", $pSid)
    Return $pTRUSTEE
EndFunc ;==>_InitializeTrustee

; #### FUNCTION ####
; =======================================================================
; Name  : _InitializeExplicitAccess
; Description   : The function intializes a tagEXPLICITACCESS structure by using the data caller specified.
; Parameters    : $aAccess  - An array with following format:
;       :   - $aAccess[0][0] - Either an user account name or an user account SID pointer.
;       :   - $aAccess[0][1] - The user's access rights assigned to the first account specified in $aAccess[0][0].
;       :   - $aAccess[0][2] - The access type, see ACCESS TYPE CONSTANTS for a value.
;       :   - $aAccess[0][3] - The inheritance options, see INHERITANCE CONSTANTS for a value.
;       :   - $aAccess[1][0] - The second usr account, can be either an user name or an user account SID pointer.
;       :   - $aAccess[1][1] - The user's access rights assigned to the second account specified in $aAccess[1][0].
;       :   - $aAccess[1][2] - Access type.
;       :   - $aAccess[1][3] - Inheritance options, can be NO_INHERITANCE.
;       :   - ... ...
; Return values : If succeeds, the function returns a pointer to an array of tagEXPLICITACCESS structure, otherwise, returns zero, and sets @error to non-zero.
; Author        : Pusofalse
; Remarks       : When you no longer use this pointer, call _HeapFree function to free it.
; =======================================================================
Func _InitializeExplicitAccess($aAccess)
    Local $tAccess, $pAccess, $iSizeofAccess, $tagAccess, $pSid

    If Ubound($aAccess, 0) <> 2 Then Return SetError(13, 0, 0)
    If Ubound($aAccess, 2) <> 4 Then Return SetError(13, 0, 0)

    $tAccess = DllStructCreate($tagEXPLICITACCESS)
    $iSizeOfAccess = DllStructGetSize($tAccess)
    $pAccess = _HeapAlloc($iSizeofAccess * Ubound($aAccess))
    _FreeVariable($tAccess)
    For $i = 1 to Ubound($aAccess)
        $tagAccess &= "dword[3];ptr;int[3];ptr;"
    Next
    $tAccess = DllStructCreate($tagAccess, $pAccess)
    For $i = 0 to Ubound($aAccess) - 1
        If _IsValidSid($aAccess[$i][0]) Then
            $pSid = $aAccess[$i][0]
        Else
            $pSid = _LookupAccountName($aAccess[$i][0])
            If Not _IsValidSid($pSid) Then
                _HeapFree($pAccess)
                _FreeVariable($tAccess)
                Return SetError($ERROR_INVALID_SID, $i, 0)
            EndIf
        EndIf
        DllStructSetData($tAccess, $i * 4 + 4, $pSid)
        DllStructSetData($tAccess, $i * 4 + 1, $aAccess[$i][1], 1)
        DllStructSetData($tAccess, $i * 4 + 1, $aAccess[$i][2], 2)
        DllStructSetData($tAccess, $i * 4 + 1, $aAccess[$i][3], 3)
        DllStructSetData($tAccess, $i * 4 + 3, $TRUSTEE_IS_SID, 2)
    Next
    Return $pAccess
EndFunc ;==>_InitializeExplicitAccess

; #### FUNCTION ####
; =======================================================================
; Name  : _SetEntriesInAcl1
; Description   : Creates an access control list (ACL) pointer by using the information the caller specified.
; Parameter(s)  : $pUserSid - Either an user account SID pointer or an user account name string.
;       : $iAccess      - The access mask value as the user access rights assigned to $pUserSid user account.
;       : $iAccessType  - A value of ACCESS TYPE CONSTANTS.
;       : $iInheritOpt  - Inheritance options, see INHERITANCE OPTIONS for a value.
;       : $pAcl     - An existing ACL pointer, if non-zero, the function creates a new ACL pointer by merging the $pACL ACL pointer.
; Return values : If succeeds, the return value is a pointer to the newly created access control list, otherwise, returns zero, and sets @error to a system error code.
; Author        : Pusofalse
; Remarks       : When you no longer use this pointer, call _LsaLocalFree to free it.
; =======================================================================
Func _SetEntriesInAcl1($pUserSid, $iAccess, $iAccessType, $iInheritOpt = 0, $pAcl = 0)
    Local $pNewAcl, $tAccess, $pAccess, $iFlag

    If Not _IsValidSid($pUserSid) Then
        $pUserSid = _LookupAccountName($pUserSid)
        If Not _IsValidSid($pUserSid) Then Return SetError(@error, 0, 0)
        $iFlag = 1
    EndIf
    _LookupAccountSid($pUserSid)
    $iType = @Extended
    $tAccess = DllStructCreate($tagEXPLICITACCESS)
    $pAccess = DllStructGetPtr($tAccess)
    DllStructSetData($tAccess, "AccessMask", $iAccess)
    DllStructSetData($tAccess, "AccessMode", $iAccessType)
    DllStructSetData($tAccess, "Inheritance", $iInheritOpt)
    DllStructSetData($tAccess, "From", $TRUSTEE_IS_SID)
    DllStructSetData($tAccess, "Type", $iType)
    DllStructSetData($tAccess, "Name", $pUserSid)

    $iResult = DllCall("Advapi32.dll", "dword", "SetEntriesInAcl", _
            "ulong", 1, "ptr", $pAccess, "ptr", $pAcl, "ptr*", 0)
    If $iFlag Then _HeapFree($pUserSid)
    Return SetError($iResult[0], _FreeVariable($tAccess), $iResult[4])
EndFunc ;==>_SetEntriesInAcl1

; #### FUNCTION ####
; =======================================================================
; Name  : _SetEntriesInAcl
; Description   : Creates an access control list (ACL) pointer by using the information the caller specified in an array.
; Parameter(s)  : $aAccess  - An array with following format:
;       :   - $aAccess[0][0] - The owner of the first entry.
;       :   - $aAccess[0][1] - The access rights value assigned to the first owner.
;       :   - $aAccess[0][2] - The access mode, DENY_ACCESS or GRANT_ACCESS is defined in DACL, SET_AUDIT_SUCCESS or SET_AUDIT_FAILURE is defined in SACL.
;       :   - $aAccess[0][3] - The inheritance options, see INHERITANCE CONSTANTS for possible values, default to NO_INHERITANCE
;       :   - $aAccess[1][0] - The owner of the second entry.
;       :   - ... ...
;       : $pOldAcl  - An existing ACL pointer, if non-zero, the function creates a new ACL pointer by merging the $pACL ACL pointer.
; Return values : If succeeds, returns a pointer to an acceess control list, must pass to _LsaLocalFree function to free it after finishing with it. If fails, returns zero and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _SetEntriesInAcl($aAccess, $pOldAcl = 0)
    Local $pAcl, $pAccess

    If Ubound($aAccess, 0) <> 2 Then Return SetError(13, 0, 0)
    If Ubound($aAccess, 2) <> 4 Then Return SetError(13, 0, 0)

    $pAccess = _InitializeExplicitAccess($aAccess)
    If @error or $pAccess = 0 Then Return SetError(@error, 0, 0)
    $pAcl = DllCall("Advapi32.dll", "dword", "SetEntriesInAcl", _
            "ulong", Ubound($aAccess), "ptr", $pAccess, _
            "ptr", $pOldAcl, "ptr*", 0)
    _HeapFree($pAccess)
    Return SetError($pAcl[0], 0, $pAcl[4])
EndFunc ;==>_SetEntriesInAcl

; #### FUNCTION ####
; =======================================================================
; Name  : _SetAccessControlEntriesOrder
; Description   : This function changes the order of the ACEs in an ACL.
; Parameter(s)  : $pAcl - A pointer to an access control list (ACL).
;       : $vOrder   - Specifies the order, if is an array, it must be in the following format:
;       :   - $vOrder[0] - The index (zero-based) of the first ACE.
;       :   - $vOrder[1] - The index (zero-based) of the second ACE.
;       : ... ...
;       : If is in string format, $vOrder must be in the form:
;       :   - 1st ace|2nd ace|3rd ace|...|nth ace
;       :   - (Each index is splitted by using the '|')
; Return values : True indicates success, false indicates failure.
; Author    : Pusofalse
; Remarks   : When you finished with the $pAcl pointer, call _HeapFree function to free it.
; =======================================================================
Func _SetAccessControlEntriesOrder(ByRef $pAcl, $vOrder)
    Local $iSize, $iNum, $pNewAcl, $aOrder

    If Not _IsValidAcl($pAcl) Then Return SetError(@error, 0, 0)
    If IsArray($vOrder) Then
        If Ubound($aOrder, 0) <> 1 Then Return SetError(87, 0, 0)
        $aOrder = $vOrder
    Else
        $aOrder = StringRegExp($vOrder, "(\d+)\|?", 3)
    EndIf
    $iSize = _GetAclInformation($pAcl)
    $iNum = @extended
    $iSize = bitAND($iSize, 0xFFFF)
    If $iSize < 1 Then Return SetError(13, 0, 0)
    If Ubound($aOrder) <> $iNum Then Return SetError(87, 0, 0)

    $pNewAcl = _HeapAlloc($iSize)
    If _InitializeAcl($pNewAcl, $iSize) = 0 Then Return SetError(@error, 0, 0)
    For $i = 0 to Ubound($aOrder) - 1
        If $aOrder[$i] > $iNum - 1 Then Return SetError(13, 0, 0)
        If _AddAceEx($pNewAcl, _GetAce($pAcl, $aOrder[$i]), $i) = 0 Then
            Return SetError(@error, _HeapFree($pNewAcl), 0)
        EndIf
    Next
    $pAcl = $pNewAcl
    Return SetExtended(_FreeVariable($aOrder), 1)
EndFunc ;==>_SetAccessControlEntriesOrder

Func _ImpersonateLoggedOnUser($hToken)
    Local $iResult = DllCall("Advapi32.dll", "int", "ImpersonateLoggedOnUser", "hWnd", $hToken)
    Return SetError(_GetLastError(), 0, $iResult[0] <> 0)
EndFunc ;==>_ImpersonateLoggedOnUser

Func _RevertToSelf()
    Local $iResult = DllCall("Advapi32.dll", "int", "RevertToSelf")
    Return SetError(_GetLastError(), 0, $iResult[0] <> 0)
EndFunc ;==>_RevertToSelf

Func _GetAuditedPermissionsFromAcl($pAcl, $pSid)
    Local $iResult, $iFlag, $hToken
    Local $aPriv[1][2] = [[$SE_SECURITY_NAME, 2]]

    If Not _IsValidAcl($pAcl) Then Return SetError(@error, 0, 0)
    If Not _IsValidSid($pSid) Then
        $pSid = _LookupAccountName($pSid)
        If Not _IsValidSid($pSid) Then Return SetError(@error, 0, 0)
        $iFlag = 1
    EndIf
    $tTRUSTEE = DllStructCreate($tagTRUSTEE)
    $pTRUSTEE = DllStructGetPtr($tTRUSTEE)
    DllStructSetData($tTRUSTEE, "From", $TRUSTEE_IS_SID)
    DllStructSetData($tTRUSTEE, "Name", $pSid)

    $hToken = _OpenProcessToken(-1)
    If Not _IsPrivilegeEnabled($hToken, $SE_SECURITY_NAME) Then
        _AdjustTokenPrivileges($hToken, $aPriv)
    EndIf
    _LsaCloseHandle($hToken)
    $iResult = DllCall("Advapi32.dll", "dword", "GetAuditedPermissionsFromAcl", _
            "ptr", $pAcl, "ptr", $pTrustee, "dword*", 0, "dword*", 0)
    If $iFlag Then _HeapFree($pSid)
    _FreeVariable($tTrustee)
    Return SetError($iResult[0], $iResult[3], "0x" & Hex($iResult[4]))
EndFunc ;==>_GetAuditedPermissionsFromAcl

; #### FUNCTION ####
; =======================================================================
; Name  : _GetEffectiveRightsFromAcl
; Description   : Retrieves the access mask value for the user from the specified access control list (ACL).
; Parameter(s)  : $pAcl - A pointer to an access control list.
;       : $pUserSid - Either an user account name string or an user account SID.
; Return values : If succeeds, returns the access right masks value assigned to $pUserSid. If fails, returns -1 and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _GetEffectiveRightsFromAcl($pAcl, $pUserSid)
    Local $iResult, $tTRUSTEE, $pTRUSTEE, $iFlag

    If Not _IsValidSid($pUserSid) Then
        $pUserSid = _LookupAccountName($pUserSid)
        If Not _IsValidSid($pUserSid) Then Return SetError(@error, 0, -1)
        $iFlag = 1
    EndIf
    $tTRUSTEE = DllStructCreate($tagTRUSTEE)
    $pTRUSTEE = DllStructGetPtr($tTRUSTEE)
    DllStructSetData($tTRUSTEE, "From", $TRUSTEE_IS_SID)
    DllStructSetData($tTRUSTEE, "Name", $pUserSid)
    $iResult = DllCall("Advapi32.dll", "dword", "GetEffectiveRightsFromAcl", _
            "ptr", $pAcl, "ptr", $pTRUSTEE, "dword*", 0)
    If $iFlag Then _HeapFree($pUserSid)
    Return SetError($iResult[0], _FreeVariable($tTRUSTEE), "0x" & Hex($iResult[3]))
EndFunc ;==>_GetEffectiveRightsFromAcl

; #### FUNCTION ####
; =======================================================================
; Name  : _IsPrivilegeEnabled
; Description   : The function checks the specified privilege whether is enabled in the access token.
; Parameter(s)  : $hToken       - Handle to the access token.
;       : $sPrivilege   - The name of the privilege, see PRIVILEGE CONSTANTS for a value.
; Return values : If the $sPrivilege is enabled in $hToken, returns true, otherwise returns false.
; Author    : Pusofalse
; =======================================================================
Func _IsPrivilegeEnabled($hToken, $sPrivilege)
    Local $iResult, $tLuid, $pLuid, $tPrivSet, $pPrivSet, $vNul

    $pLuid = _LookupPrivilegeValue($sPrivilege)
    $tLuid = DllStructCreate($tagLuid, $pLuid)
    $tPrivSet = DllStructCreate($tagPRIVILEGESET)
    $pPrivSet = DllStructGetPtr($tPrivSet)
    DllStructSetData($tPrivSet, "Count", 1)
    DllStructSetData($tPrivSet, "Low", DllStructGetData($tLuid, "Low"))
    DllStructSetData($tPrivSet, "High", DllStructGetData($tLuid, "High"))
    $iResult = DllCall("Advapi32.dll", "int", "PrivilegeCheck", _
            "hWnd", $hToken, "ptr", $pPrivSet, "int*", 0)
    _HeapFree($pLuid)
    _FreeVariable($tLuid)
    _FreeVariable($tPrivSet)
    Return $iResult[3] <> 0
EndFunc ;==>_IsPrivilegeEnabled

; #### FUNCTION #### $tagTOKENPRIVILEGE
; =======================================================================
; Name  : _AdjustTokenPrivileges
; Description   : The function enables or disables the privileges of specified access token.
; Parameter(s)  : $hToken   - Handle to the access token.
;       : $aPrivilege   - An array with following format:
;       :       - $aPrivilege[0][0] - The name of the first privilege.
;       :       - $aPrivilege[0][1] - The first privilege's state.
;       :       - $aPrivilege[1][0] - The second privilege's name.
;       :       - $aPrivilege[1][1] - The second privilege's state.
;       :       - ... ...
;       : $fDisableAll  - If true, the function ignores the $aPrivilege parameter, and disables all privileges for $hToken.
; Return values : True indicates success, false to failure, and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _AdjustTokenPrivileges($hToken, $aPrivilege, $fDisableAll = 0)
    Local $iResult, $tTokenPriv, $pTokenPriv, $iSysError
    Local $pLuid, $tLuid, $tagTP, $pPrivState, $iSizeofBuffer

    If $fDisableAll = 0 And Ubound($aPrivilege, 0) <> 2 Then Return SetError(@ERROR, 0, 0)
    For $i = 0 to Ubound($aPrivilege) - 1
        $tagTP &= ";dword;long;dword"
    Next
    $tTokenPriv = DllStructCreate("dword" & $tagTP)
    $pTokenPriv = DllStructGetPtr($tTokenPriv)
    DllStructSetData($tTokenPriv, 1, Ubound($aPrivilege))
    For $i = 0 to Ubound($aPrivilege) - 1
        $pLuid = _LookupPrivilegeValue($aPrivilege[$i][0])
        $tLuid = DllStructCreate($tagLUID, $pLuid)
        DllStructSetData($tTokenPriv, $i * 3 + 2, DllStructGetData($tLuid, "Low"))
        DllStructSetData($tTokenPriv, $i * 3 + 3, DllStructGetData($tLuid, "High"))
        DllStructSetData($tTokenPriv, $i * 3 + 4, $aPrivilege[$i][1])
        _HeapFree($pLuid)
        _FreeVariable($tLuid)
    Next
    $iSizeofBuffer = DllStructGetSize($tTokenPriv)
    $pPrivState = _HeapAlloc( $iSizeofBuffer )
    $iResult = DllCall("Advapi32.dll", "int", "AdjustTokenPrivileges", _
            "hWnd", $hToken, "int", $fDisableAll, _
            "ptr", $pTokenPriv, "dword", $iSizeofBuffer, _
            "ptr", $pPrivState, "dword*", $iSizeofBuffer)
    $iSysError = _GetLastError()
    _FreeVariable($tTokenPriv)
    Return SetError($iSysError, _HeapFree($pPrivState), $iResult[0] <> 0)
EndFunc ;==>_AdjustTokenPrivilege

; #### FUNCTION ####
; =======================================================================
; Name  : _DeleteAce
; Description   : The _DeleteAce function deletes an access control entry (ACE) from an access control list (ACL).
; Parameter(s)  : $pAcl - A pointer to an access control list.
;       : $iIndex   - Zero-based ACE index to be deleted, call _GetAclInformation for the number of ACE.
; Return values : If succeeded, returned true, otherwise returned false, and set @error to a system error code.
; Author        : Pusofalse
; =======================================================================
Func _DeleteAce(ByRef $pAcl, $iIndex)
    Local $iResult
    If Not _IsValidAcl($pAcl) Then Return SetError(@error, 0, 0)
    $iResult = DllCall("Advapi32.dll", "int", "DeleteAce", "ptr", $pAcl, "int", $iIndex)
    Return SetError(_GetLastError(), 0, $iResult[0] <> 0)
EndFunc ;==>_DeleteAce

; #### FUNCTION ####
; =======================================================================
; Name  : _DeleteAceByOwner
; Description   : Deletes an access control entry from an access control list by using the owner of the call specified.
; Parameter(s)  : $pAcl - A pointer to a ACL (access control list) structure from which the ACE is deleted.
;       : $pSid - Either an user account SID or an user account name string.
;       : $fDeleteAll   - If true, the function deletes all the ACEs which the owner is $pSid. Default to true.
; Return values : If succeeds, returns true, else returns false.
; Author    : Pusofalse
; =======================================================================
Func _DeleteAceByOwner(ByRef $pAcl, $pSid, $fDeleteAll = True)
    Local $iResult, $aAceList, $pSid2

    If Not _IsValidSid($pSid) Then $pSid = _LookupAccountName($pSid)
    If Not _IsValidSid($pSid) Then Return SetError(@error, 0, 0)
    If Not _IsValidAcl($pAcl) Then Return SetError(@error, 0, 0)
    $aAceList = _GetExplicitEntriesFromAcl($pAcl)
    For $i = 1 to $aAceList[0][0]
        $pSid2 = _LookupAccountName($aAceList[$i][0])
        If _EqualSid($pSid, $pSid2) Then
            $iResult = _DeleteAce($pAcl, $i - 1)
            If $fDeleteAll = False Then ExitLoop
        EndIf
        _HeapFree($pSid2)
    Next
    _HeapFree($pSid)
    Return $iResult
EndFunc ;==>_DeleteAceByOwner

; #### FUNCTION ####
; =======================================================================
; Name  : _GetAce
; Description   : Retrieves an access control entry (ACE) pointer from an access control list.
; Parameter(s)  : $pAcl - A pointer to an access control list.
;       : $iIndex   - Zero-based ACE index to be retrieved.
; Return values : If succeed, return a pointer to a specified ACE, otherwise return zero and set @error to a system error code.
; Author        : Pusofalse
; =======================================================================
Func _GetAce(ByRef $pAcl, $iIndex)
    Local $pAce
    If Not _IsValidAcl($pAcl) Then Return SetError(@error, 0, 0)
    $pAce = DllCall("Advapi32.dll", "int", "GetAce", "ptr", $pAcl, _
            "int", $iIndex, "ptr*", 0)
    If $pAce[3] Then
        $__LSA_ACE_INDEX_CURRENT_SELECT = $iIndex
        $__LSA_ACE_POINTER_CURRENT_SELECT = $pAce[3]
        $__LSA_ACL_POINTER_CURRENT_SELECT = $pAcl
    Else
        $__LSA_ACE_INDEX_CURRENT_SELECT = -1
        $__LSA_ACE_POINTER_CURRENT_SELECT = 0
        $__LSA_ACL_POINTER_CURRENT_SELECT = 0
    EndIf
    Return SetError(_GetLastError(), 0, $pAce[3])
EndFunc ;==>_GetAce

; #### FUNCTION ####
; =======================================================================
; Name  : _GetExplicitAce
; Description   : Retrieves an array contains the access control entry (ACE) information.
; Parameter(s)  : $pAce - A pointer to an access control entry (ACE), returned by _GetAce function.
; Return values : If succeed, return an array with following format:
;       :   - $aArray[0]    - The owner of the access control entry.
;       :   - $aArray[1]    - The owner SID of the access control entry, in string format.
;       :   - $aArray[2]    - The access mask.
;       :   - $aArray[3]    - The access type.
;       :   - $aArray[4]    - The inheritance options.
;       : If fail, all elements are to NULL, and sets @error to system error code.
; Author    : Pusofalse
; =======================================================================
Func _GetExplicitAce($pAce)
    Local $aEntry, $aResult[5]

    If $__LSA_ACL_POINTER_CURRENT_SELECT = 0 Then Return SetError($ERROR_INCORRECT_FUNCTION, 0, $aResult)
    If _GetAce($__LSA_ACL_POINTER_CURRENT_SELECT, $__LSA_ACE_INDEX_CURRENT_SELECT) <> $pAce Then
        Return SetError($ERROR_INCORRECT_FUNCTION, 0, $aResult)
    EndIf
    $aEntry = _GetExplicitEntriesFromAcl($__LSA_ACL_POINTER_CURRENT_SELECT)
    If $__LSA_ACE_INDEX_CURRENT_SELECT > $aEntry[0][0] Then Return SetError($ERROR_INVALID_PARAMETER, 0, $aResult)
    For $i = 0 to 4
        $aResult[$i] = $aEntry[$__LSA_ACE_INDEX_CURRENT_SELECT + 1][$i]
    Next
    Return $aResult
EndFunc ;==>_GetExplicitAce

; #### FUNCTION ####
; =======================================================================
; Name  : _AddAce
; Description   : The function adds an access control entry (ACE) into an access control list (ACL).
; Parameter(s)  : $pAcl - An ACL pointer to which the ACE is added to.
;       : $pAce - An ACE pointer which the function adds to.
; Return values : If success, return true, else return false and set @error to non-zero.
; Author        : Pusofalse
; Remarks       : The $pAce parameter is returned by _GetAce function, but the $pAce can not be from $pAcl pointer, otherwise, the function returns zero, and sets @error to ERROR_SAME_ACL (-1).
; =======================================================================
Func _AddAce(ByRef $pAcl, $pAce)
    Local Const $ERROR_SAME_ACL = -1
    Local $aData = _GetExplicitAce($pAce)
    If @error Then Return SetError(@error, _FreeVariable($aData), 0)
    If $pAcl = $__LSA_ACL_POINTER_CURRENT_SELECT Then Return SetError($ERROR_SAME_ACL, 0, 0)
    $pAcl = _SetEntriesInAcl1($aData[0],$aData[2], $aData[3], $aData[4], $pAcl)
    Return SetError(@error, 0, @error = 0)
EndFunc ;==>_AddAce

; #### FUNCTION ####
; =======================================================================
; Name  : _AddAceEx1
; Description   : This function adds an access control entry to an access control list.
; Parameter(s)  : $pAcl - A variable of pointer to an access control list to which the access control entry is added.
;       : $aAce - An array in the following format:
;       :   - $aAce[0] - The owner of the ACE.
;       :   - $aAce[1] - Access rights assigned to the owner.
;       :   - $aAce[2] - Access type, GRANT_ACCESS and DENY_ACCESS are defined in DACL.
;       :   - $aAce[3] - Inheritance options, can be NO_INHERITANCE (0).
;       : $iStartIndex  - Specifies the position in the ACL's list of ACEs at which to add the new ACEs. A value of zero inserts the ACEs at the beginning of the list. A value of MAXDWORD (-1) appends the ACEs to the end of the list.
;       : $iSizeofAce   - Specifies the size of the ACE, the calculation formula of the size of the ACE is following:
;       :   DllStructGetSize($tACCESSACE) + _GetLengthSid($pAceOwnerSid) - 4.
;       : $iRevision    - Revision level, can be ACL_REVISION or ACL_REVISION_DS, default to ACL_REVISION (2).
; Return values : Returns true if succeeds, else returns false indicates failure, in which case, @error is set to a system error code.
; Author    : Pusofalse
; Remarks   : If the size of the access control list is not enough large to store the new access control entry, the function fails and sets @error to ERROR_INSUFFICIENT_BUFFER (122), indicates a larger memory area for the access control list is requested.
; =======================================================================
Func _AddAceEx1(ByRef $pAcl, $aAce, $iStartIndex, $iSizeofAce = -1, $iRevision = 2)
    Local $iResult, $pNewAcl, $pAce, $aAceList[1][4]

    If Not _IsValidAcl($pAcl) Then Return SetError(@error, 0, 0)
    If Ubound($aAce, 0) <> 1 Then Return SetError($ERROR_INVALID_PARAMETER, 0, 0)
    If Ubound($aAce, 1) <> 4 Then Return SetError($ERROR_INVALID_PARAMETER, 0, 0)

    For $i = 0 to 3
        $aAceList[0][$i] = $aAce[$i]
    Next
    $pNewAcl = _SetEntriesInAcl($aAceList, 0)
    If @error OR $pNewAcl = 0 OR Not _IsValidAcl($pNewAcl) Then Return SetError(@error, 0, 0)
    $pAce = _GetAce($pNewAcl, 0)
    $iResult = _AddAceEx($pAcl, $pAce, $iStartIndex, $iSizeofAce, $iRevision)
    Return SetError(@error, _LsaLocalFree($pNewAcl), $iResult)
EndFunc ;==>_AddAceEx1

; #### FUNCTION ####
; =======================================================================
; Name  : _AddAceEx
; Description   : This function adds one or more access control entries (ACEs) to a specified access contol list.
; Parameter(s)  : $pAcl - A pointer to an ACL. This function adds an ACE to this ACL.
;       : $pAce - A pointer to an ACE, or ACEs' list.
;       : $iStartIndex  - Specifies the position at which to add the ACEs' list, zero indicates the beginning of the list, -1 indicates the end of the list.
;       : $iSizeofAce   - Specifies the size of the ACE, the calculation formula of the size of one ACE is following:
;       :   DllStructGetSize($tACCESSACE) + _GetLengthSid($pAceOwnerSid) - 4.
;       : $iRevision    - Revision level. This value can be ACL_REVISION or ACL_REVISION_DS. Use ACL_REVISION_DS if the ACL contains object-specific ACEs. This value must be compatible with the AceType field of all ACEs in pAceList. Otherwise, the function will fail and return STATUS_INVALID_PARAMETER.
; Return values : If succeeds, returns true. Otherwise returns false and sets @error to a system error code.
; Author    : Pusofalse
; Remarks   : If $pAcl is not large enough to store the new acess control entry, the function fails and sets @error to ERROR_INSUFFICIENT_BUFFER (122), means a larger area is requested.
; =======================================================================
Func _AddAceEx(ByRef $pAcl, $pAce, $iStartIndex, $iSizeofAce = -1, $iRevision = 2)
    Local $iResult

    If Not _IsValidAcl($pAcl) Then Return SetError(@error, 0, 0)
    If Not _IsValidAce($pAce) Then Return SetError(@error, 0, 0)
    If $iSizeofAce = -1 Then
        Local $tAce = DllStructCreate($tagACCESSACE, $pAce)
        $iSizeofAce = DllStructGetData($tAce, "AceSize")
    EndIf
    $iResult = DllCall("Advapi32.dll", "int", "AddAce", "ptr", $pAcl, _
            "dword", $iRevision, "dword", $iStartIndex, _
            "ptr", $pAce, "dword", $iSizeofAce)
    Return SetError(_GetLastError(), 0, $iResult[0] <> 0)
EndFunc ;==>_AddAceEx

; #### FUNCTION ####
; =======================================================================
; Name  : _IsValidAce
; Description   : Validates an access control entry.
; Parameter(s)  : $pAce - A pointer to an access control entry, returned by _GetAce function.
; Return values : If $pAce is a valid pointer to an access control entry, returns true, else returns false and sets @error to ERROR_INCORRECT_FUNCTION.
; Author    : Pusofalse
; =======================================================================
Func _IsValidAce($pAce)
    If $__LSA_ACL_POINTER_CURRENT_SELECT = 0 Then Return SetError($ERROR_INCORRECT_FUNCTION, 0, 0)
    If _GetAce($__LSA_ACL_POINTER_CURRENT_SELECT, $__LSA_ACE_INDEX_CURRENT_SELECT) <> $pAce Then
        Return SetError($ERROR_INCORRECT_FUNCTION, 0, 0)
    EndIf
    Return SetError(0, 0, 1)
EndFunc ;==>_IsValidAce

; #### FUNCTION ####
; =======================================================================
; Name  : _AccessCheck
; Description   : Determines whether a security descriptor grants a specified set of access rights to the client identified by an access token.
; Parameter(s)  : $pSecurDesc   - A pointer to a security descriptor against which access is checked.
;       : $iDesiredAccess   - A DWORD value of access mask that specifies the access rights to check.
; Return values : If no errors occur and the security descriptor allows the requested access rights to the current client identifer, the function returns the granted access rights (same to the value of $iDesiredAccess parameter), otherwise, the function returns zero and sets @error.
; Author    : Pusofalse
; =======================================================================
Func _AccessCheck($pSecurDesc, $iDesiredAccess)
    Local $iResult, $hThread, $hToken, $pMap, $pPrivSet, $iSysError

    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)
    $iResult = DllCall("Advapi32.dll", "int", "ImpersonateSelf", "int", 2)
    If $iResult[0] = 0 Then Return SetError(_GetLastError(), 0, 0)

    $hThread = DllCall("Kernel32.dll", "hWnd", "GetCurrentThread")
    $iResult = DllCall("Advapi32.dll", "int", "OpenThreadToken", "hWnd", $hThread[0], _
            "dword", $TOKEN_QUERY, "int", 0, "hWnd*", 0)
    If $iResult[4] = 0 Then Return SetError(_GetLastError(), _RevertToSelf(), 0)

    $hToken = $iResult[4]
    $pMap = _HeapAlloc(0x10)
    $pPrivSet = _HeapAlloc(20)
    $iResult = DllCall("Advapi32.dll", "int", "AccessCheck", "ptr", $pSecurDesc, _
            "hWnd", $hToken, "dword", $iDesiredAccess, "ptr", $pMap, _
            "ptr", $pPrivSet, "dword*", 20, "dword*", 0, "dword*", 0)
    If $iResult[0] = 0 Then $iSysError = _GetLastError()
    _RevertToSelf()
    _LsaCloseHandle($hToken)
    Return SetError($iSysError, _HeapFree($pMap)*_HeapFree($pPrivSet), $iResult[7])
EndFunc ;==>_AccessCheck

; #### FUNCTION ####
; =======================================================================
; Name  : _ConvertSidToStringSid
; Description   : Converts a SID pointer to a string SID.
; Parameter(s)  : $pSid - A pointer to a SID.
; Return values : If succeeds, the return value is a string in the form: x-x-x...
;       : If fails, the return value is set to empty and sets @error to ERROR_INVALID_SID.
; Author        : Pusofalse
; =======================================================================
Func _ConvertSidToStringSid($pSid)
    Local $iResult, $tBuffer, $iSysError, $sResult

    If Not _IsValidSid($pSid) Then Return SetError(@error, 0, "")
    $iResult = DllCall("Advapi32.dll", "int", "ConvertSidToStringSid", _
            "ptr", $pSid, "ptr*", 0)
    If $iResult[0] = 0 Then $iSysError = _GetLastError()
    If $iResult[2] = 0 Then Return SetError($iSysError, 0, "")
    $tBuffer = DllStructCreate("char[256]", $iResult[2])
    $sResult = DllStructGetData($tBuffer, 1)
    _LsaLocalFree($iResult[2])
    Return SetError($iSysError, _FreeVariable($tBuffer), $sResult)
EndFunc ;==>_ConvertSidToStringSid

; #### FUNCTION ####
; =======================================================================
; Name  : _ConvertStringSidToSid
; Description   : This function converts a SID string to a SID pointer.
; Parameter(s)  : $sStringSid   - A SID string in the form: x-x-x...
; Return values : If succeed, the return value is a pointer to a SID, else, return zero and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _ConvertStringSidToSid($sStringSid)
    Local $iResult
    $iResult = DllCall("Advapi32.dll", "int", "ConvertStringSidToSid", _
            "str", $sStringSid, "ptr*", 0)
    Return SetError(_GetLastError(), 0, $iResult[2])
EndFunc ;==>_ConvertStringSidToSid

; #### FUNCTION ####
; =======================================================================
; Name  : _EqualSid
; Description   : Returns true if 2 SIDs are equaled.
; Parameter(s)  : $pSid1    - The first SID to validate.
;       : $pSid2    - The second SID to validate.
; Return values : Returns true if 2 SIDs are equaled.
; Author    : Pusofalse
; =======================================================================
Func _EqualSid($pSid1, $pSid2)
    Local $iResult

    If Not _IsValidSid($pSid1) Then Return SetError(@ERROR, 1, 0)
    If Not _IsValidSid($pSid2) Then Return SetError(@ERROR, 2, 0)
    $iResult = DllCall("Advapi32.dll", "int", "EqualSid", "ptr", $pSid1, "ptr", $pSid2)
    Return $iResult[0] <> 0
EndFunc ;==>_EqualSid

Func _EqualPrefixSid($pSid1, $pSid2)
    If Not _IsValidSid($pSid1) Then Return SetError(@ERROR, 1, 0)
    If Not _IsValidSid($pSid2) Then Return SetError(@ERROR, 2, 0)
    $iResult = DllCall("Advapi32.dll", "int", "EqualPrefixSid", "ptr", $pSid1, "ptr", $pSid2)
    Return $iResult[0] <> 0
EndFunc ;==>_EqualPrefixSid

; #### FUNCTION ####
; =======================================================================
; Name  : _GetSecurityInfo
; Description   : Returns the security information of the specified object.
; Parameter(s)  : $hObject - Handle to the object, must opened with READ_CONTROL access right.
;       : $iType    - Specifies the type of the object, see SECURITY OBJECT TYPE CONSTANTS.
;       : $iSecurLevel  - The security information to be retrieved, default to DACL_SECURITY_INFORMATION, can be one or more values of SECURITY INFORMATION CONSTANTS.
; Return values : An array with following format:
;       :   - $aArray[0] - The return value of API GetSecurityInfo, zero indicates success.
;       :   - $aArray[1] - The handle of the object, same to $hObject parameter.
;       :   - $aArray[2] - The object type, same to $iType parameter.
;       :   - $aArray[3] - The security information retrieved, same to $iSecurLevel parameter.
;       :   - $aArray[4] - The pointer to the owner SID, if $iSecurLevel does not contain the OWNER_SECURITY_INFORMATION, it is zero.
;       :   - $aArray[5] - The pointer to the primary group SID, if $iSecurLevel does not contain the GROUP_SECURITY_INFORMATION, it can be zero.
;       :   - $aArray[6] - The pointer to the DACL, if $iSecurLevel does not contain the DACL_SECURITY_INFORMATION, it can be zero.
;       :   - $aArray[7] - The pointer to the SACL, if $iSecurLevel does not contain the SACL_SECURITY_INFORMATION, it can be zero.
;       :   - $aArray[8] - The pointer to the security descriptor.
;       : If the function fails, the $aArray[0] is set to a system error code, pass to FormatMessage for an error message.
; Author    : Pusofalse
; Remarks       : The $hObject is a handle but not a string, it must opened with READ_CONTROL access right. If $iSecurLevel parameter contains the SACL_SECURITY_INFORMATION, $hObject must opened with ACCESS_SYSTEM_SECURITY access right either, in which case, before retrieving the handle, be sure the calling process owns the SE_SECURITY_NAME privilege, use _IsPrivilegeEnabled to determine the SE_SECURITY_NAME is whether enabled for the caller, call _AdjustTokenPrivileges function to enable and disable the privilege.
; =======================================================================
Func _GetSecurityInfo($hObject, $iType, $iSecurLevel = 4)
    Local $iResult
    $iResult = DllCall("Advapi32.dll", "dword", "GetSecurityInfo", _
            "hWnd", $hObject, "int", $iType, "int", $iSecurLevel, _
            "ptr*", 0, "ptr*", 0, "ptr*", 0, "ptr*", 0, "ptr*", 0)
    Return $iResult
EndFunc ;==>_GetSecurityInfo

; #### FUNCTION ####
; =======================================================================
; Name  : _QueryKernelObjectSecurity
; Description   : Retrieves the security information of the kernel object.
; Parameter(s)  : $hKernel      - Handle to the kernel object, it can be a handle to process, thread, event, file, service, registry key, and so on.
;       : $iSecurLevel  - Security information to be retrieved, see SECURITY INFORMATION CONSTANTS for correct values.
; Return values : If succeeds, the return value is a pointer to a security descriptor, otherwise, returns zero and sets @error to system error code.
; Author        : Pusofalse
; Remarks       : If $iSecurLevel contains the SACL_SECURITY_INFORMATION, the caller must have the SE_SECURITY_NAME privilege, and $hKernel must opened with READ_CONTROL and ACCESS_SYSTEM_SECURITY access rights. When you no longer use the security descriptor pointer, call _HeapFree function to free it.
; =======================================================================
Func _QueryKernelObjectSecurity($hKernel, $iSecurLevel = 4)
    Local $iResult, $pSecurDesc
    $iResult = DllCall("Advapi32.dll", "int", "GetKernelObjectSecurity", _
            "hWnd", $hKernel, "int", $iSecurLevel, _
            "ptr", 0, "dword", 0, "dword*", 0)
    $pSecurDesc = _HeapAlloc($iResult[5])
    $iResult = DllCall("Advapi32.dll", "int", "GetKernelObjectSecurity", _
            "hWnd", $hKernel, "int", $iSecurLevel, _
            "ptr", $pSecurDesc, "dword", $iResult[5], "dword*", 0)
    Return SetError(_GetLastError(), 0, $pSecurDesc)
EndFunc ;==>_QueryKernelObjectSecurity

; #### FUNCTION ####
; =======================================================================
; Name  : _QueryKernelObjectSecurityOwner
; Description   : Retrieves the owner information of the kernel object.
; Parameter(s)  : $hKernel      - Handle to the kernel object, must opened with READ_CONTROL access right.
; Return values : If succeed, return the owner string, otherwise return empty string and set @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _QueryKernelObjectSecurityOwner($hKernel)
    Local $aSecur = _GetSecurityInfo($hKernel, $SE_KERNEL_OBJECT, 1)
    Return SetError($aSecur[0], 0, _LookupAccountSid($aSecur[4]))
EndFunc ;==>_QueryKernelObjectSecurityOwner

; #### FUNCTION ####
; =======================================================================
; Name  : _QueryKernelObjectSecurityDacl
; Description   : Retrieves an array contains the DACL information of the kernel object.
; Parameter(s)  : $hKernel      - Handle to the kernel object, must opened with READ_CONTROL access right.
; Return values : If succeeds, the return value is an array in the following format:
;       :   - $aArray[0][0] - The number of entry returned.
;       :   - $aArray[1][0] - The user account name of the first entry.
;       :   - $aArray[1][1] - The SID of the user account, in a string format.
;       :   - $aArray[1][2] - The access mask as the user rights assigned to the user account ($aArray[1][0]).
;       :   - $aArray[1][3] - The access type of the first entry.
;       :   - $aArray[1][4] - The inheritance options of the first entry, if any.
;       :   - ... ...
;       : If fails, all elements are set to empty and set @error to a system error code.
; Author        : Pusofalse
; =======================================================================
Func _QueryKernelObjectSecurityDacl($hKernel)
    Local $aSecur = _GetSecurityInfo($hKernel, $SE_KERNEL_OBJECT, 4)
    Return SetError($aSecur[0], 0, _GetExplicitEntriesFromAcl($aSecur[6]))
EndFunc ;==>_QueryKernelObjectSecurityDacl

; #### FUNCTION ####
; =======================================================================
; Name  : _SetKernelObjectSecurity
; Description   : The function sets the security information of a kernel object.
; Parameter(s)  : $hKernel      - Handle to the kernel object.
;       : $iSecurLevel  - Security information to be set, see SECURITY INFORMATION CONSTANTS for one or more values.
;       : $pSecurDesc   - A security descriptor pointer contains the security data.
; Return values : If succeeds, the return value is true, else returns false and sets @error.
; Author    : Pusofalse
; Remarks       : If $iSecurLevel contains the OWNER_SECURITY_INFORMATION, the $hKernel object must opened with WRITE_OWNER access right. If $iSecurLevel contains the DACL_SECURITY_INFORMATION, the object must opened with WRITE_DAC access right. If $iSecurLevel contains the SACL_SECURITY_INFORMATION, the object must opened with WRITE_DAC and ACCESS_SYSTEM_SECURITY access rights, in which case, the calling process must have the SE_SECURITY_NAME privilege, to enable this privilege, call to _AdjustTokenPrivileges function. Else, the function fails and sets @error to ERROR_ACCESS_DENIED (5), if OWNER_SECURITY_INFORMATION is specified in $iSecurLevel, the only currently logged user account/group and creator of the object can be specified as the new owner, to set to other owners, enable the SE_RESTORE_NAME privilege for the calling process.
; =======================================================================
Func _SetKernelObjectSecurity($hKernel, $iSecurLevel, $pSecurDesc)
    Local $iResult
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)
    $iResult = DllCall("Advapi32.dll", "int", "SetKernelObjectSecurity", _
            "hWnd", $hKernel, "int", $iSecurLevel, _
            "ptr", $pSecurDesc)
    Return SetError(_GetLastError(), 0, $iResult[0] <> 0)
EndFunc ;==>_SetKernelObjectSecurity

; #### FUNCTION ####
; =======================================================================
; Name  : _SetKernelObjectSecurityOwner
; Description   : Sets the owner information for a kernel object.
; Parameter(s)  : $hKernel      - Handle to the object to set the new owner.
;       : $sOwner       - The new owner's name, in string format.
; Return values : If succeeds, returns true, else returns false and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _SetKernelObjectSecurityOwner($hKernel, $sOwner)
    Local $iResult, $aPriv[1][2] = [[$SE_RESTORE_NAME, 2]], $hToken

    $pOwner = _LookupAccountName($sOwner)
    If Not _IsValidSid($pOwner) Then Return SetError(@error, 0, 0)

    $hToken = _OpenProcessToken(-1)
    If Not _IsPrivilegeEnabled($hToken, $SE_RESTORE_NAME) Then
        _AdjustTokenPrivileges($hToken, $aPriv)
    EndIf
    _LsaCloseHandle($hToken)
    $iResult = _SetSecurityInfo($hKernel, $SE_KERNEL_OBJECT, 1, $pOwner, 0, 0, 0)
    Return SetError(@ERROR, _HeapFree($pOwner), $iResult)
EndFunc ;==>_SetKernelObjectSecurityOwner

; #### FUNCTION ####
; =======================================================================
; Name  : _SetKernelObjectSecurityDacl
; Description   : Sets the DACL information for a kernel object.
; Parameter(s)  : $hKernel      - Handle to the object, must opened with WRITE_DAC access right.
;       : $aAccess  - An array with following format:
;       :   - $aAccess[0][0]    - The user account/group name of the first entry.
;       :   - $aAccess[0][1]    - The access rights assigned to the first user account/group.
;       :   - $aAccess[0][2]    - The access type of the first entry.
;       :   - $aAccess[0][3]    - The inheritance options of the first entry.
;       :   - $aAccess[1][0]    - The second user account/group.
;       :   - ... ...
;       : $pOldDacl - Specifies an optional existing DACL pointer, if non-zero, the function works by merging the $pOldDacl.
; Return values : True indicates success, false indicates failure, in this case, @error is set to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _SetKernelObjectSecurityDacl($hKernel, $aAccess, $pOldDacl = 0)
    Local $pNewAcl, $iResult

    $pNewAcl = _SetEntriesInAcl($aAccess, $pOldDacl)
    If $pNewAcl = 0 Then Return SetError(@error, 0, 0)
    $iResult = _SetSecurityInfo($hKernel, $SE_KERNEL_OBJECT, 4, 0, 0, $pNewAcl, 0)
    Return SetError(@error, _LsaLocalFree($pNewAcl), $iResult)
EndFunc ;==>_SetKernelObjectSecurityDacl

; #### FUNCTION ####
; =======================================================================
; Name   : _SetSecurityInfo
; Description   : The function sets the security information for a specified object.
; Parameter(s)  : $hObject  - Handle to the object the security information is set.
;       : $iType        - The type of the object, see OBJECT TYPE CONSTANTS.
;       : $iSecurLevel  - The security information to be set, see SECURITY INFORMATION CONSTANTS for correct values.
;       : $pOwner   - The owner SID pointer, if OWNER_SECURITY_INFORMATION is not specified in $iSecurLevel parameter, it can be zero, else, the $hObject must opened with WRITE_OWNER access right.
;       : $pGroup       - The primary group SID pointer, if GROUP_SECURITY_INFORMATION is not specified, it can be zero.
;       : $pDacl        - The DACL pointer, if DACL_SECURITY_INFORMATION is not specified, it can be zero. $hObject must opened with WRITE_DAC access right, in another case, if DACL_SECURITY_INFORMATION is specified, and $pDacl is zero, the function set a NULL DACL for the object, NULL DACL indicates that everyone access the object are all denied.
;       : $pSacl        - The SACL pointer, if SACL_SECURITY_INFORMATION is not specified, it can be zero. otherwise, the object must have ACCESS_SYSTEM_SECURITY access right, to retrieve the object handle has this right, enable the SE_SECURITY_NAME privilege for the calling process at first.
; Return values : If succeeds, returns true, otherwise returns false and sets @error to system error code.
; Author    : Pusofalse
; =======================================================================
Func _SetSecurityInfo($hObject, $iType, $iSecurLevel, $pOwner, $pGroup, $pDacl, $pSacl)
    Local $iResult
    $iResult = DllCall("Advapi32.dll", "dword", "SetSecurityInfo", _
            "hWnd", $hObject, "int", $iType, "int", $iSecurLevel, _
            "ptr", $pOwner, "ptr", $pGroup, "ptr", $pDacl, "ptr", $pSacl)
    Return SetError($iResult[0], 0, $iResult[0] = $ERROR_SUCCESS)
EndFunc ;==>_SetSecurityInfo

; #### FUNCTION ####
; =======================================================================
; Name  : _IsNtfs
; Description   : Determines the specified file whether is in NTFS file system disk.
; Parameter(s)  : $sFile    - File name to validate, need not to set to the full path name.
; Return values : If $sFile is in the NTFS file system, returns true, otherwise returns false, and sets @error to ERROR_ACCESS_DENIED (5).
; Author    : Pusofalse
; =======================================================================
Func _IsNtfs($sFile)
    Local $iResult
    If Not FileExists($sFile) Then Return SetError($ERROR_FILE_NOT_FOUND, 0, 0)
    $iResult = DllCall("Kernel32.dll", "int", "GetFullPathName", _
            "str", $sFile, "dword", 260, "str", "", "str", "")
    If DriveGetFileSystem(StringLeft($iResult[3], 3)) = "NTFS" Then Return 1
    Return SetError($ERROR_ACCESS_DENIED, 0, 0)
EndFunc ;==>_IsNtfs

; #### FUNCTION ####
; =======================================================================
; Name  : _QueryShareObjectSecurityOwner
; Description   : Retrieve the owner information for the share resource.
; Parameter(s)  : $sShare   - The name of the share resource in the form: \\RemoteSystem\ShareName or LocalShareName.
; Return values : if succeeds, return an owner string, otherwise return an empty string and sets @error to system error code.
; Author        : Pusofalse
; =======================================================================
Func _QueryShareObjectSecurityOwner($sShare)
    Local $aSecur = _GetNamedSecurityInfo($sShare, $SE_LMSHARE, 1)
    Return SetError($aSecur[0], 0, _LookupAccountSid($aSecur[4]))
EndFunc ;==>_QueryShareObjectSecurityOwner

; #### FUNCTION ####
; =======================================================================
; Name  : _QueryShareObjectSecurityDacl
; Description   : Retrieves an array contains the DACL data of a share object.
; Parameter(s)  : $sShare   - The name of the share resource in the form: \\RemoteSystem\ShareName or LocalShareName.
; Return values : If succeeds, returns an array in the form:
;       :   - $aArray[0][0] - The number of entry retrieved.
;       :   - $aArray[1][0] - The first user account/group's name.
;       :   - $aArray[1][1] - The string SID of the first user account.
;       :   - $aArray[1][2] - The user rights of the first user account.
;       :   - $aArray[1][3] - The inheritance options of the first entry, if any.
;       :   - ... ...
;       : If fails, all elements in the array are set to NULL, and sets @error to a system error code.
; Author        : Pusofalse
; =======================================================================
Func _QueryShareObjectSecurityDacl($sShare)
    Local $aSecur = _GetNamedSecurityInfo($sShare, $SE_LMSHARE, 4)
    Return SetError($aSecur[0], 0, _GetExplicitEntriesFromAcl($aSecur[6]))
EndFunc ;==>_QueryShareObjectSecurityDacl

; #### FUNCTION ####
; =======================================================================
; Name  : _SetShareObjectSecurityDacl
; Description   : This function sets the DACL security information for a share object.
; Parameter(s)  : $sShare   - The share's name in the form: \\RemoteSystem\ShareName or LocalShareName.
;       : $aAccess  - An array with following format:
;       :       - $aAccess[0][0]    - The user account/group name of the first entry.
;       :       - $aAccess[0][1]    - The access rights assigned to the first user account/group.
;       :       - $aAccess[0][2]    - The access type of the first entry.
;       :       - $aAccess[0][3]    - The inheritance options of the first entry.
;       :       - $aAccess[1][0]    - The second user account/group.
;       :       - ... ...
;       : $pOldDacl - A pointer to an existing DACL, can be zero.
; Return values : True indicates success, false to failure, in the case, @error is set to system error code.
; =======================================================================
Func _SetShareObjectSecurityDacl($sShare, $aAccess, $pOldDacl = 0)
    Local $pNewDacl, $iResult

    $pNewDacl = _SetEntriesInAcl($aAccess, $pOldDacl)
    If $pNewDacl = 0 Then Return SetError(@error, 0, 0)
    $iResult = _SetNamedSecurityInfo($sShare, $SE_LMSHARE, 4, 0, 0, $pNewDacl, 0)
    Return SetError(@error, _LsaLocalFree($pNewDacl), $iResult)
EndFunc ;==>_SetShareObjectSecurityDacl

; #### FUNCTION ####
; =======================================================================
; Name  : _QueryServiceObjectSecurity
; Description   : Retrieve the security descriptor of a service object.
; Parameter(s)  : $sService - Service name in the form: RemoteSystem\ServiceName or LocalServiceName.
;       : $iSecurLevel  - Security information to retrieve, can be a combination of SECURITY INFORMTION CONSTANTS.
; Return values : If succeed, the return value is a pointer to the service's security descriptor. otherwise, returns zero and sets @error to a system error code, if @error is ERROR_ACCESS_DENIED (5), set the global variable $__LSA_SECURITY_USE_NAME to False, then re-call the function, the function will open the service by using a possible access mask value.
; Author    : Pusofalse
; =======================================================================
Func _QueryServiceObjectSecurity($sService, $iSecurLevel = 4)
    Local $hToken, $aPriv[1][2] = [[$SE_SECURITY_NAME, 2]]
    Local $hService, $iResult, $pSecurDesc, $iDesiredAccess = $READ_CONTROL

    $hToken = _OpenProcessToken(-1)
    If _IsPrivilegeEnabled($hToken, $SE_SECURITY_NAME) = 0 Then
        _AdjustTokenPrivileges($hToken, $aPriv)
    EndIf
    _LsaCloseHandle($hToken)

    If bitAND($iSecurLevel, 8) Then $iDesiredAccess = bitOR($iDesiredAccess, $ACCESS_SYSTEM_SECURITY)
    $hService = _LsaOpenService($sService, $iDesiredAccess)
    If $hService = 0 Then Return SetError(@error, 0, 0)

    $pSecurDesc = _InitializeSecurityDescriptor(1024)
    $iResult = DllCall("Advapi32.dll", "int", "_QueryServiceObjectSecurity", "hWnd", $hService, _
            "dword", $iSecurLevel, "ptr", $pSecurDesc, "dword", 1024, "dword*", 0)
    _HeapFree($pSecurDesc)
    $pSecurDesc = _InitializeSecurityDescriptor($iResult[5])
    $iResult = DllCall("Advapi32.dll", "int", "_QueryServiceObjectSecurity", "hWnd", $hService, _
            "dword", $iSecurLevel, "ptr", $pSecurDesc, "dword", $iResult[5], "dword*", 0)
    If $iResult[0] = 0 Then _HeapFree($pSecurDesc)
    Return SetError(_GetLastError(), _LsaCloseServiceHandle($hService), $pSecurDesc)
EndFunc ;==>__QueryServiceObjectSecurity


; #### FUNCTION ####
; =======================================================================
; Name  : _QueryServiceObjectSecurityDacl
; Description   : This function retrieves an array contains the DACL information for a service object.
; Parameter(s)  : $sService - The service name in the form: RemoteSystem\ServiceName or LocalServiceName.
; Return values : If succeeds, returns an array contains the number of entries and entries' detail, see _GetExplicitEntriesFromAcl function's Return values section for the correct format. If fails, sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _QueryServiceObjectSecurityDacl($sService)
    Local $pSecurDesc, $pDacl, $aResult[1][5]

    $pSecurDesc = _QueryServiceObjectSecurity($sService, $DACL_SECURITY_INFORMATION)
    If $pSecurDesc = 0 Then Return SetError(@error, 0, $aResult)
    $pDacl = _GetSecurityDescriptorDacl($pSecurDesc)
    If _IsValidAcl($pDacl) = 0 Then Return SetError(@error, 0, $aResult)

    $aResult = _GetExplicitEntriesFromAcl($pDacl)
    Return SetError(@error, _HeapFree($pSecurDesc), $aResult)
EndFunc ;==>_QueryServiceObjectSecurityDacl


; #### FUNCTION ####
; =======================================================================
; Name  : _QueryServiceObjectSecuritySacl
; Description   : This function retrieves an array contains the SACL information for a service object.
; Parameter(s)  : $sService - The service name in the form: RemoteSystem\ServiceName or LocalServiceName.
; Return values : If succeeds, returns an array contains the number of entries and entries' detail, see _GetExplicitEntriesFromAcl function's Return values section for the correct format. If fails, sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _QueryServiceObjectSecuritySacl($sService)
    Local $pSecurDesc, $pSacl, $aResult[1][5]

    $pSecurDesc = _QueryServiceObjectSecurity($sService, $SACL_SECURITY_INFORMATION)
    If $pSecurDesc = 0 Then Return SetError(@error, 0, $aResult)
    $pSacl = _GetSecurityDescriptorSacl($pSecurDesc)
    If _IsValidAcl($pSacl) = 0 Then Return SetError(@error, 0, $aResult)

    $aResult = _GetExplicitEntriesFromAcl($pSacl)
    Return SetError(@error, _HeapFree($pSecurDesc), $aResult)
EndFunc ;==>_QueryServiceObjectSecuritySacl

; #### FUNCTION ####
; =======================================================================
; Name  : _QueryServiceObjectSecurityOwner
; Description   : Retrieves the owner information for a service object.
; Parameter(s)  : $sService - The service name in the form: RemoteSystem\ServiceName or LocalServiceName.
; Return values : If succeeds, returns an owner string, else returns NULL and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _QueryServiceObjectSecurityOwner($sService)
    Local $pSecurDesc, $pSid, $sUser

    $pSecurDesc = _QueryServiceObjectSecurity($sService, $OWNER_SECURITY_INFORMATION)
    If $pSecurDesc = 0 Then Return SetError(@error, 0, "")

    $pSid = _GetSecurityDescriptorOwner($pSecurDesc)
    $sUser = _LookupAccountSid($pSid)
    If $sUser = "" Then $sUser = _ConvertSidToStringSid($pSid)
    Return SetError(@error, _HeapFree($pSecurDesc), $sUser)
EndFunc ;==>_QueryServiceObjectSecurityOwner

; #### FUNCTION ####
; =======================================================================
; Name  : _SetServiceObjectSecurity
; Description   : Sets the security information for a service object.
; Parameter(s)  : $sService - The service name in the form: RemoteSystem\ServiceName or LocalServiceName.
;       : $iSecurLevel  - The security information to set, can be combination of the following values:
;       : $OWNER_SECURITY_INFORMATION   - Set the owner for the service. If this flag is not specified, the owner in $pSecurDesc is ignored.
;       : $GROUP_SECURITY_INFORMATION   - Set the primary group for the service. If this flag is not specified, the group in $pSecurDesc is ignored.
;       : $DACL_SECURITY_INFORMATION    - Set the DACL information for the service. If this flag is not specified, the DACL in the $pSecurDesc parameter will be ignored.
;       : $SACL_SECURITY_INFORMATION    - Set the SACL for the sevice. If this flag is not specified, the SACL in the $pSecurDesc is ignored.
;       : $pSecurDesc   - A pointer to a security descriptor that contains the security data.
; Return values : True indicates succeess, false to failure, in this case to set @error to a system error code.
; Author    : Pusofalse
; Remarks   : If $OWNER_SECURITY_INFORMATION is specified in $iSecurLevel parameter, only the currently logged user (or the local group the current user belongs) or creator of the $sService object can be specified as the new owner, to set other user account, enable the SE_RESTORE_NAME privilege for the calling process. If $SACL_SECURITY_INFORMATION is specified in $iSecurLevel, the caller must have the SE_SECURITY_NAME privilege.
; =======================================================================
Func _SetServiceObjectSecurity($sService, $iSecurLevel, $pSecurDesc)
    Local $hService, $hToken, $iResult, $iDesiredAccess = $WRITE_DAC
    Local $aPriv[2][2] = [[$SE_RESTORE_NAME, 2], [$SE_SECURITY_NAME, 2]]

    If bitAND($iSecurLevel, 1) Then $iDesiredAccess = bitOR($iDesiredAccess, $WRITE_OWNER)
    If bitAND($iSecurLevel, 2) Then $iDesiredAccess = bitOR($iDesiredAccess, $WRITE_OWNER)
    If bitAND($iSecurLevel, 4) Then $iDesiredAccess = bitOR($iDesiredAccess, $WRITE_DAC)
    If bitAND($iSecurLevel, 8) Then $iDesiredAccess = bitOR($iDesiredAccess, $ACCESS_SYSTEM_SECURITY)

    $hToken = _OpenProcessToken(-1)
    _AdjustTokenPrivileges($hToken, $aPriv)
    _LsaCloseHandle($hToken)

    $hService = _LsaOpenService($sService, $iDesiredAccess)
    If Number($hService) = 0 Then Return SetError(@error, 0, 0)

    $iResult = DllCall("AdvApi32.dll", "int", "_SetServiceObjectSecurity", "hWnd", $hService, _
            "dword", $iSecurLevel, "ptr", $pSecurDesc)
    Return SetError(_GetLastError(), _LsaCloseServiceHandle($hService), $iResult[0])
EndFunc ;==>_SetServiceObjectSecurity

; #### FUNCTION ####
; =======================================================================
; Name  : _SetServiceObjectSecurityDacl
; Description   : Sets the access rights for a service object.
; Parameter(s)  : $sService - The name of the service in the form: RemoteSystem\ServiceName or LocalServiceName.
;       : $aAccess  - An array with following format:
;       :   - $aAccess[0][0] - The owner of the 1st access control entry (ACE).
;       :   - $aAccess[0][1] - The access rights assigned to first owner.
;       :   - $aAccess[0][2] - Access type, DENY_ACCESS and GRANT_ACCESS are defined for a DACL.
;       :   - $aAccess[0][3] - Inheritance options, can be NO_INHERITANCE.
;       :   - ... ...
;       : $pOldDacl - An existing DACL pointer, can be zero.
; Return values : True indicates success, false to failure, in which case to set @error to a system error code.
; =======================================================================
Func _SetServiceObjectSecurityDacl($sService, ByRef $aAceList, $pOldDacl = 0)
    Local $pSecurDesc, $pDacl, $fResult, $iError

    If UBound($aAceList, 0) <> 2 Then Return SetError(87, 0, 0)
    If UBound($aAceList, 2) <> 4 Then Return SetError(87, 0, 0)

    $pDacl = _SetEntriesInAcl($aAceList, $pOldDacl)
    If _IsValidAcl($pDacl) = 0 Then Return SetError(@error, 0, 0)

    $pSecurDesc = _InitializeSecurityDescriptor(_LsaLocalSize($pDacl))
    Select
    Case _SetSecurityDescriptorDacl($pSecurDesc, $pDacl) = 0
        $fResult = 0
        $iError = @error
    Case _SetServiceObjectSecurity($pSecurDesc, 4, $pSecurDesc) = 0
        $fResult = 0
        $iError = @error
    Case Else
        $fResult = 1
        $iError = @error
    EndSelect
    Return SetError($iError, _HeapFree($pSecurDesc) * _LsaLocalFree($pDacl), $fResult)
EndFunc ;==>_SetServiceObjectSecurityDacl

; #### FUNCTION ####
; =======================================================================
; Name  : _SetServiceObjectSecurityOwner
; Description   : This function sets the owner for a service object.
; Parameter(s)  : $sService - Service name in the form: RemoteSystem\ServiceName or LocalServiceName.
;       : $sOwner   - The owner for the service, in the form: UserName or DomainName\UserName.
; Return values : If succeeds, returns true, else returns false and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _SetServiceObjectSecurityOwner($sService, $sOwner)
    Local $pSid, $pSecurDesc, $fResult, $iError

    $pSid = _LookupAccountName($sOwner)
    If _IsValidSid($pSid) = 0 Then Return SetError(@error, 0, 0)

    $pSecurDesc = _InitializeSecurityDescriptor(_GetLengthSid($pSid))
    Select
    Case _SetSecurityDescriptorOwner($pSecurDesc, $pSid) = 0
        $fResult = 0
        $iError = @error
    Case _SetServiceObjectSecurity($sService, 1, $pSecurDesc) = 0
        $fResult = 0
        $iError = @error
    Case Else
        $fResult = 1
        $iError = @error
    EndSelect
    Return SetError($iError, _HeapFree($pSid) * _HeapFree($pSecurDesc), $fResult)
EndFunc ;==>_SetServiceObjectSecurityOwner

; #### FUNCTION ####
; =======================================================================
; Name  : _SetServiceObjectSecuritySacl
; Description   : Sets the SACL for a service object.
; Parameter(s)  : $sService - Service name in the form: RemoteSystem\ServiceName or LocalServiceName.
;       : $aAccess  - An array with following format:
;       :   - $aAccess[0][0] - The owner of the 1st access control entry (ACE).
;       :   - $aAccess[0][1] - The access rights assigned to first owner.
;       :   - $aAccess[0][2] - Access type, SET_AUDIT_SUCCESS and SET_AUDIT_FAILURE are defined for a SACL.
;       :   - $aAccess[0][3] - Inheritance options, can be NO_INHERITANCE.
;       :   - ... ...
;       : $pOldSacl - An existing SACL pointer, can be zero.
; Return values : If succeeds, returns true, else returns false and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _SetServiceObjectSecuritySacl($sService, $aAccess, $pOldSacl = 0)
    Local $pNewSacl, $iResult, $iSysError, $pSecurDesc

    $pNewSacl = _SetEntriesInAcl($aAccess, $pOldSacl)
    If Not $pNewSacl Then Return SetError(@error, 0, 0)
    If Not _IsValidAcl($pNewSacl) Then Return SetError(@error, 0, 0)

    $pSecurDesc = _InitializeSecurityDescriptor()
    If Not $pSecurDesc Then Return SetError(@error, 0, 0)
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)
    If Not _SetSecurityDescriptorSacl($pSecurDesc, $pNewSacl) Then Return SetError(@error, 0, 0)

    $iResult = _SetServiceObjectSecurity($sService, 8, $pSecurDesc)
    _FreeVariable($iSysError, @error, _HeapFree($pSecurDesc))
    Return SetError($iSysError, _LsaLocalFree($pNewSacl), $iResult)
EndFunc ;==>_SetServiceObjectSecuritySacl

; #### INTERNAL USED ONLY FUNCTION ####
; =======================================================================
; Open a service in specified system and retrieve its handle.
; =======================================================================
Func _LsaOpenService($sService, $iAccessMask, $sSystem = "")
    Local $hService, $hSC

    $hSC = _LsaOpenSCManager($sSystem)
    If Not $hSC Then Return SetError(@ERROR, 0, 0)
    $hService = DllCall("Advapi32.dll", "hWnd", "OpenService", _
            "hWnd", $hSC, "str", $sService, "dword", $iAccessMask)
    Return SetError(_GetLastError(), _LsaCloseServiceHandle($hSC), $hService[0])
EndFunc ;==>_LsaOpenService

; #### INTERNAL USED ONLY FUNCTION ####
; =======================================================================
; Open the service controller in specified system and retrieve its handle for the subsequent calls.
; =======================================================================
Func _LsaOpenSCManager($sSystem = "", $iAccessMask = 0xF003F)
    Local $hSC
    $hSC = DllCall("Advapi32.dll", "hWnd", "OpenSCManager", _
            "str", $sSystem, "str", "ServicesActive", _
            "dword", $iAccessMask)
    Return SetError(_GetLastError(), 0, $hSC[0])
EndFunc ;==>_LsaOpenSCManager

; #### INTERNAL USED ONLY FUNCTION ####
; =======================================================================
; Close a service/service controller handle opened by the previous calls.
; =======================================================================
Func _LsaCloseServiceHandle($hService)
    Local $iResult = DllCall("Advapi32.dll", "int", "CloseServiceHandle", "hWnd", $hService)
    Return SetError(_GetLastError(), 0, $iResult[0] <> 0)
EndFunc ;==>_LsaCloseServiceHandle

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaCloseHandle
; Description   : Close a handle to a securable object.
; Parameter(s)  : $hHandle  - Handle to be closed.
; Return values : A bool value indicates success or failure.
; =======================================================================
Func _LsaCloseHandle($hHandle)
    Local $iResult = DllCall("Kernel32.dll", "int", "CloseHandle", "long", $hHandle)
    Return $iResult[0] <> 0
EndFunc ;==>_LsaCloseHandle

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaOpenPolicy
; Description   : Retrieves an handle to the policy object on local or remote system.
; Parameter(s)  : $iAccessMask  - Access mask value, depends on the subsequent calls, see LSA POLICY ACCESS RIGHT CONSTANTS for values.
;       : $sSystem  - The name of the system, empty string to local.
; Return values : If succeeds, the return value is a handle to the policy object, else returns zero and sets @error to a system error code.
; Author    : Pusofalse
; Remarks       : When you finished with this handle, call _LsaClose function to close it.
; =======================================================================
Func _LsaOpenPolicy($iAccessMask, $sSystem = "")
    Local $hPolicy, $tSystem, $pSystem, $iLength
    Local $tObjAttr, $pObjAttr, $tName, $pName

    If $sSystem <> "" Then
        $iLength = StringLen($sSystem) * 2
        $tSystem = DllStructCreate($tagLSAUNICODE)
        $pSystem = DllStructGetPtr($tSystem)
        $tName = DllStructCreate("wchar[" & $iLength & "]")
        $pName = DllStructGetPtr($tName)
        DllStructSetData($tName, 1, $sSystem)
        DllStructSetData($tSystem, "Length", $iLength)
        DllStructSetData($tSystem, "MaxLength", $iLength + 2)
        DllStructSetData($tSystem, "Wbuffer", $pName)
    EndIf
    $tObjAttr = DllStructCreate($tagLSAOBJATTR)
    $pObjAttr = DllStructGetPtr($tObjAttr)
    $hPolicy = DllCall("Advapi32.dll", "dword", "LsaOpenPolicy", _
            "ptr", $pSystem, "ptr", $pObjAttr, _
            "dword", $iAccessMask, "hWnd*", 0)
    _FreeVariable($tName)
    _FreeVariable($tObjAttr)
    _FreeVariable($tSystem)
    Return SetError(_LsaNtStatusToWinError($hPolicy[0]), 0, $hPolicy[4])
EndFunc ;==>_LsaOpenPolicy

; #### FUNCTION ####
; =======================================================================
; Name  : _AdjustPolicyAuditEvents
; Description   : This function adjusts the system's auditing policy rules.
; Parameter(s)  : $aAudit   - An array with following format:
;       :   - $aAudit[0][0] - The first policy event to set, see POLICY AUDIT EVENT OPTION CONSTANTS for a value.
;       :   - $aAudit[0][1] - Auditing options of the first policy event, see POLICY AUDIT EVENT CONSTANTS for values.
;       :   - $aAudit[1][0] - The second policy event.
;       :   - $aAudit[1][1] - Auditing options of the second policy event.
;       :   - ... ...
;       : $sSystem  - An optional string value contains the system name on which the function executes, default to local system.
; Return values : If succeeds, returns true, else returns false and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _AdjustPolicyAuditEvents($aAudit, $fAuditMode = True, $sSystem = "")
    Local $hPolicy, $tBuffer, $pBuffer, $tAudit, $pAudit, $iResult
    Local $aAudits[9] = ["System", "Logon", "ObjectAccess", _
            "PrivilegeUse", "DetailedTracking", "PolicyChange", _
            "AccountManagement", "DirectoryServiceAccess", "AccountLogon"]

    If Ubound($aAudit, 0) <> 2 Then Return SetError($ERROR_INVALID_DATA, 0, 0)
    If Ubound($aAudit, 2) <> 2 Then Return SetError($ERROR_INVALID_DATA, 0, 0)

    $hPolicy = _LsaOpenPolicy($POLICY_SET_AUDIT_REQUIREMENTS, $sSystem)
    If $hPolicy = 0 Then Return SetError(@error, 0, 0)

    $tBuffer = DllStructCreate($tagPOLICYAUDITEVENTSINFO)
    $pBuffer = DllStructGetPtr($tBuffer)
    $tAudit = DllStructCreate("int Audit[9]")
    $pAudit = DllStructGetPtr($tAudit)

    For $i = 1 to 9
        DllStructSetData($tAudit, 1, $POLICY_AUDIT_EVENT_UNCHANGED, $i)
    Next
    For $i = 0 to Ubound($aAudit) - 1
        For $j = 0 to 8
            If $aAudit[$i][0] = "AuditCategory" & $aAudits[$j] Then
                DllStructSetData($tAudit, 1, $aAudit[$i][1], $j + 1)
                ExitLoop
            EndIf
        Next
    Next

    DllStructSetData($tBuffer, "AuditMode", $fAuditMode)
    DllStructSetData($tBuffer, "EventAuditOpt", $pAudit)
    DllStructSetData($tBuffer, "MaxAuditEventCount", 9)

    $iResult = DllCall("Advapi32.dll", "dword", "LsaSetInformationPolicy", _
            "hWnd", $hPolicy, "dword", 2, "ptr", $pBuffer)
    _LsaClose($hPolicy)
    _FreeVariable($tAudit)
    _FreeVariable($tBuffer)
    Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $iResult[0] = 0)
EndFunc ;==>_AdjustPolicyAuditEvents

; #### FUNCTION ####
; =======================================================================
; Name  : _QueryPolicyAuditEvents
; Description   : Returns the policy audit events.
; Parameter(s)  : $sSystem  - The name of the system on which the function executes, default to local.
; Return values : If succeeds, returns an array in the following format:
;       : - $aArray[0][0] - The number of returned entries.
;       : - $aArray[1][0] - The name of the policy audit.
;       : - $aArray[1][1] - The events of the policy audit.
;       : - ... ...
; Author    : Pusofalse
; =======================================================================
Func _QueryPolicyAuditEvents($sSystem = "")
    Local $hPolicy, $iResult, $tBuffer, $tAudit, $pAudit, $aResult[1][2] = [[0]]
    Local $aAuditEvent[9] = ["System", "Logon", "ObjectAccess", _
            "PrivilegeUse", "DetailedTracking", "PolicyChange", _
            "AccountManagement", "DirectoryServiceAccess", "AccountLogon"]

    $hPolicy = _LsaOpenPolicy($POLICY_VIEW_AUDIT_INFORMATION, $sSystem)
    If $hPolicy = 0 Then Return SetError(@error, 0, $aResult)

    $iResult = DllCall("Advapi32.dll", "dword", "LsaQueryInformationPolicy", _
            "hWnd", $hPolicy, "dword", 2, "ptr*", 0)
    If $iResult[0] Then Return SetError(_LsaNtStatusToWinError($iResult[0]), _LsaClose($hPolicy), $aResult)

    $tBuffer = DllStructCreate($tagPOLICYAUDITEVENTSINFO, $iResult[3])
    $aResult[0][0] = DllStructGetData($tBuffer, "MaxAuditEventCount")
    Redim $aResult[$aResult[0][0] + 1][2]

    $pAudit = DllStructGetData($tBuffer, "EventAuditOpt")
    $tAudit = DllStructCreate("int Audit[" & $aResult[0][0] & "]", $pAudit)
    For $i = 1 to $aResult[0][0]
        $aResult[$i][0] = "AuditCategory" & $aAuditEvent[$i - 1]
        $aResult[$i][1] = DllStructGetData($tAudit, "Audit", $i)
    Next
    _LsaClose($hPolicy)
    _FreeVariable($tAudit)
    _FreeVariable($tBuffer)
    Return $aResult
EndFunc ;==>_QueryPolicyAuditEvents

; #### FUNCTION ####
; =======================================================================
; Name  :_LsaClose
; Description   : Close the handle to the policy object.
; Parameter(s)  : $hPolicy  - Handle to the policy, returned by _LsaOpenPolicy function.
; Return values : True indicates success, false to failure, in which case, @error is set to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _LsaClose($hPolicy)
    Local $iResult
    $iResult = DllCall("Advapi32.dll", "dword", "LsaClose", "hWnd", $hPolicy)
    Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $iResult = 0)
EndFunc ;==>_LsaClose

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaEnumerateAccountRights
; Description   : This function lists the user rights of the specified user account.
; Parameter(s)  : $sUserAccount - User account from which the user rights are retrieved.
;       : $sSystem  - System on which the function works, default to local.
; Return values : If success, return an array contains the user rights, else sets @error to system error code.
; Author    : Pusofalse
; =======================================================================
Func _LsaEnumerateAccountRights($sUserAccount, $sSystem = "")
    Local $iResult, $hPolicy, $pSid, $aResult[1]
    Local $tBuffer, $tagBuffer, $iLength, $tName

    $hPolicy = _LsaOpenPolicy($POLICY_LOOKUP_NAMES, $sSystem)
    If $hPolicy = 0 Then Return SetError(@error, 0, 0)
    $pSid = _LookupAccountName($sUserAccount, $sSystem)
    If Not _IsValidSid($pSid) Then Return SetError(@error, _LsaClose($hPolicy), 0)
    $iResult = DllCall("Advapi32.dll", "dword", "LsaEnumerateAccountRights", _
            "hWnd", $hPolicy, "ptr", $pSid, "ptr*", 0, "ulong*", 0)
    $aResult[0] = $iResult[4]
    Redim $aResult[$aResult[0] + 1]
    For $i = 1 to $aResult[0]
        $tagBuffer &= "ushort[2];ptr;"
    Next
    $tBuffer = DllStructCreate($tagBuffer, $iResult[3])
    For $i = 1 to $aResult[0]
        $iLength = DllStructGetData($tBuffer, ($i - 1) * 2 + 1, 1)
        $tName = DllStructCreate("wchar[" & $iLength & "]", DllStructGetData($tBuffer, ($i - 1) * 2 + 2))
        $aResult[$i] = DllStructGetData($tName, 1)
        $tName = 0
    Next
    _HeapFree($pSid)
    _FreeVariable($tBuffer)
    _LsaClose($hPolicy)
    _LsaFreeMemory($iResult[3])
    Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $aResult)
EndFunc ;==>_LsaEnumerateAccountRights

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaRemoveAccountRight
; Description   : This function removes an user privilege from an user account.
; Parameter(s)  : $sUserAccount - The name of the user account from which the right is removed.
;       : $sUserRight   - The name of the user right or privilege to remove, see PRIVILEGE CONSTANTS and USER ACCOUNT RIGHTS CONSTANTS for a value.
;       : $sSystem  - The system on which the function to execute, default to local system.
;       : $fRemoveAll   - If true, the function ignores the $sUserRight parameter, then remove all the user rights from the $sUserAccount. Default to false.
; Return values : If succeeds, returns true, else returns false and sets @error to system error code.
; Author    : Pusofalse
; =======================================================================
Func _LsaRemoveAccountRight($sUserAccount, $sUserRight, $sSystem = "", $fRemoveAll = false)
    Local $iResult, $pBuffer, $pSid, $hPolicy

    $hPolicy = _LsaOpenPolicy($POLICY_LOOKUP_NAMES, $sSystem)
    If Not $hPolicy Then Return SetError(@error, 0, 0)
    $pSid = _LookupAccountName($sUserAccount, $sSystem)
    If Not _IsValidSid($pSid) Then Return SetError(@error, _LsaClose($hPolicy), 0)

    $pBuffer = _LsaInitializeBufferW($sUserRight)
    $iResult = DllCall("Advapi32.dll", "dword", "LsaRemoveAccountRights", _
            "hWnd", $hPolicy, "ptr", $pSid, "int", $fRemoveAll, _
            "ptr", $pBuffer, "ulong", 1)
    _HeapFree($pSid)
    _HeapFree($pBuffer)
    _LsaClose($hPolicy)
    Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $iResult[0] = 0)
EndFunc ;==>_LsaRemoveAccountRight

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaInitializeBufferW
; Description   : The function initializes a tagLSAUNICODE structure.
; Parameter(s)  : $sData    - Data to initialize.
;       : $fDecode  - If true, the $sData parameter must be a pointer to a LSAUNICODE structure, in which case, the function returns the data stored in the buffer. If false, the function initializes a LSAUNICODE structure by using the $sData, return value is set to a pointer to LSAUNICODE structure.
; Return values : Depends on $fDecode parameter.
; Author    : Pusofalse
; Remarks       : When you finished with this pointer, call _HeapFree function to free it.
; =======================================================================
Func _LsaInitializeBufferW($sData, $fDecode = False)
    Local $pMem, $iLength, $tBuffer, $sResult

    If $fDecode = False Then
        $iLength = StringLen($sData) * 2 + 2
        $pMem = _HeapAlloc($iLength + 8)
        $tBuffer = DllStructCreate($tagLSAUNICODE & ";wchar Data[" & $iLength - 2 & "]", $pMem)
        DllStructSetData($tBuffer, "Length", $iLength - 2)
        DllStructSetData($tBuffer, "MaxLength", $iLength)
        DllStructSetData($tBuffer, "Wbuffer", $pMem + 8)
        DllStructSetData($tBuffer, "Data", $sData)
        Return $pMem
    ElseIf Not IsPtr($sData) Then
        Return ""
    EndIf

    $tBuffer = DllStructCreate($tagLSAUNICODE, $sData)
    $iLength = DllStructGetData($tBuffer, "MaxLength") * 2
    If $iLength < 1 Then Return ""
    $pMem = DllStructCreate("wchar Data[" & $iLength & "]", DllStructGetData($tBuffer, "Wbuffer"))
    $sResult = DllStructGetData($pMem, "Data")
    Return SetExtended(_FreeVariable($pMem), $sResult)
EndFunc ;==>_LsaInitializeBufferW

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaAddAccountRight
; Description   : This function assigns one privilege to an user account.
; Parameter(s)  : $sUserAccount - The name of the user account.
;       : $sUserRight   - The name of the user right, see PRIVILEGE CONSTANTS and USER ACCOUNT RIGHTS CONSTANTS.
;       : $sSystem  - The system on which the function executes.
; Return values : True indicates success, false to failure - sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _LsaAddAccountRight($sUserAccount, $sUserRight, $sSystem = "")
    Local $hPolicy, $pSid, $iResult, $pBuffer, $iAccessMask

    $iAccessMask = bitOR($POLICY_LOOKUP_NAMES, $POLICY_CREATE_ACCOUNT)
    $hPolicy = _LsaOpenPolicy($iAccessMask, $sSystem)
    If Not $hPolicy Then Return SetError(@error, 0, 0)
    $pSid = _LookupAccountName($sUserAccount, $sSystem)
    If Not _IsValidSid($pSid) Then Return SetError(@error, _LsaClose($hPolicy), 0)

    $pBuffer = _LsaInitializeBufferW($sUserRight)
    $iResult = DllCall("Advapi32.dll", "dword", "LsaAddAccountRights", _
            "hWnd", $hPolicy, "ptr", $pSid, _
            "ptr", $pBuffer, "ulong", 1)
    _HeapFree($pSid)
    _HeapFree($pBuffer)
    _LsaClose($hPolicy)
    Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $iResult[0] = 0)
EndFunc ;==>_LsaAddAccountRight

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaAccountRightIsEnabled
; Description   : This function determines the specified user right whether is assigned to the specified user account.
; Parameter(s)  : $sUserAccount - The name of the user account.
;       : $sUserRight   - The name of the user right.
;       : $sSystem  - The system on which the function to execute.
; Return values : If no errors occour, the @error is set to zero. If $sUserRight is assigned to $sUserAccount, returns true, else returns false.
; Author    : Pusofalse
; =======================================================================
Func _LsaAccountRightIsEnabled($sUserAccount, $sUserRight, $sSystem = "")
    Local $aRight, $iSysError
    $aRight = _LsaEnumerateAccountRights($sUserAccount, $sSystem)
    $iSysError = @error
    For $i = 1 to $aRight[0]
        If $aRight[$i] = $sUserRight Then Return SetError($iSysError, _FreeVariable($aRight), True)
    Next
    Return SetError($iSysError, _FreeVariable($aRight), False)
EndFunc ;==>_LsaAccountRightIsEnabled

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaLookupAccountsRight
; Description   : Compares rights of 2 user accounts and retrieves the higher one of them.
; Parameter(s)  : $pUser1   - First user account's SID pointer, or an user name string.
;       : $pUser2   - Second user account's SID pointer, or an user name string.
; Return values : If $pUser1's user right is higher than $pUser2's ( or equals to), the return value is same to $pUser1, -
;       : + otherwise, returns $pUser2 parameter. -
;       : + If $pUser1 or $pUser2 is invalid user account SID or string, @error is set to  ERROR_INVALID_SID. -
;       : + If $pUser1 or $pUser2 is not a local group alias or not a local user name, @error is set to ERROR_INVALID_DATA. -
;       : + If @error is non-zero, @extended is set to the index of the wrong parameter.
; Author    : Pusofalse
; Remarks       : Note that $pUser1 and $pUser2 must be SID pointer or user name string to a local group or local user, but not a well-known account.
; =======================================================================
Func _LsaLookupAccountsRight($pUser1, $pUser2)
    Local $aSid[2] = [$pUser1, $pUser2],$aRid[2], $aGroup[2],$aType[2],$aAlias[2],$vTemp

    For $i = 0 to 1
        If Not _IsValidSid($aSid[$i]) Then
            $aSid[$i] = _LookupAccountName($aSid[$i])
            If Not _IsValidSid($aSid[$i]) Then Return SetError(@error,$i + 1,"")
        EndIf
        $aAlias[$i] = $aSid[$i]
        $aGroup[$i] = _LookupAccountSid($aSid[$i])
        $aType[$i] = @Extended
        If ($aType[$i] <> $SID_IS_USER) AND ($aType[$i] <> $SID_IS_ALIAS) Then
            Return SetError($ERROR_INVALID_DATA, $i + 1, "")
        EndIf
        If $aType[$i] = $SID_IS_USER Then
            $vTemp = _LsaLocalUserGetGroups($aGroup[$i])
            If $vTemp[0] = 0 Then Return SetError(-1, $i + 1, "")
            $aAlias[$i] = _LookupAccountName($vTemp[1])
        EndIf
        Switch _GetSidSubAuthority($aAlias[$i], 1)
        Case $DOMAIN_ALIAS_RID_ADMINS
            $aRid[$i] = 1
        Case $DOMAIN_ALIAS_RID_POWER_USERS
            $aRid[$i] = 2
        Case $DOMAIN_ALIAS_RID_USERS
            $aRid[$i] = 3
        Case $DOMAIN_ALIAS_RID_GUESTS
            $aRid[$i] = 4
        Case Else
            $aRid[$i] = 5
        EndSwitch
        _HeapFree($aSid[$i])
    Next
    _FreeVariable($aAlias)
    _FreeVariable($aGroup)
    If $aRid[0] <= $aRid[1] Then
        Return $pUser1
    Else
        Return $pUser2
    EndIf
EndFunc ;==>_LsaLookupAccountsRight

; #### INTERNAL USED ONLY FUNCTION ####
; =======================================================================
; Just converts the return value of the _Lsa* functions to a system error code.
; =======================================================================
Func _LsaNtStatusToWinError($iNtStatus)
    Local $iSysError
    $iSysError = DllCall("Advapi32.dll", "ulong", "LsaNtStatusToWinError", "dword", $iNtStatus)
    Return $iSysError[0]
EndFunc ;==>_LsaNtStatusToWinError

; #### INTERNAL USED ONLY FUNCTION ####
; =======================================================================
Func _LsaFreeMemory($pMem)
    Local $iResult = DllCall("Advapi32.dll", "dword", "LsaFreeMemory", "ptr", $pMem)
    Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $iResult[0] = 0)
EndFunc ;==>_LsaFreeMemory

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaEnumerateAccountsWithUserRight
; Description   : Returns an array contains the user accounts that hold the specified user right.
; Parameter(s)  : $sUserRight   - User right.
;       : $sSystem  - The system on which the function executes, default to local.
; Return values : If success, returns an array in the form:
;       :   - $aArray[0][0] - The number of entry returned.
;       :   - $aArray[1][0] - The name of the first user account.
;       :   - $aArray[1][1] - The SID of the first user account, in string format.
;       :   ... ...
;       : If failure, @error is set to a system error code, and $aArray[0][0] is set to zero.
; Author    : Pusofalse
; =======================================================================
Func _LsaEnumerateAccountsWithUserRight($sUserRight, $sSystem = "")
    Local $iResult, $hPolicy, $pBuffer, $tSid, $pSid, $iAccessMask, $aResult[1][2] = [[0]]

    $iAccessMask = bitOR($POLICY_LOOKUP_NAMES, $POLICY_VIEW_LOCAL_INFORMATION)
    $hPolicy = _LsaOpenPolicy($iAccessMask, $sSystem)
    If Not $hPolicy Then Return SetError(@error, 0, $aResult)

    $pBuffer = _LsaInitializeBufferW($sUserRight)
    $iResult = DllCall("Advapi32.dll", "dword", "LsaEnumerateAccountsWithUserRight", _
            "hWnd", $hPolicy, "ptr", $pBuffer, "ptr*", 0, "int*", 0)
    $aResult[0][0] = $iResult[4]
    Redim $aResult[$aResult[0][0] + 1][2]

    $tSid = DllStructCreate("ptr[" & $iResult[4] & "]", $iResult[3])
    For $i = 1 to $aResult[0][0]
        $pSid = _CopySid(DllStructGetData($tSid, 1, $i))
        $aResult[$i][0] = _LookupAccountSid($pSid)
        $aResult[$i][1] = _ConvertSidToStringSid($pSid)
        _HeapFree($pSid)
    Next
    _LsaClose($hPolicy)
    _FreeVariable($tSid)
    _HeapFree($pBuffer)
    _LsaFreeMemory($iResult[3])
    Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $aResult)
EndFunc ;==>_LsaEnumerateAccountsWithUserRight

; #### FUNCTION ####
; =======================================================================
; Name  : _CreateWellKnownSid
; Description   : The function creates a SID for predefined aliases.
; Parameter(s)  : $iSidType - One member of WELL KNOWN SID TYPE enumeration.
;       : $pDomainSid   - A pointer to a SID that identifies the domain control to use when creating the SID. Pass NULL to use the local computer.
; Return values : If succeeds, returns a pointer to the well-known SID, when you finished with this pointer, call _HeapFree function to free it.
; Author    : Pusofalse
; =======================================================================
Func _CreateWellKnownSid($iSidType, $pDomainSid = 0)
    Local $iResult, $pSid

    If $iSidType < 0 or $iSidType > 78 Then Return SetError($ERROR_INVALID_PARAMETER, 0, 0)
    If $pDomainSid And Not _IsValidSid($pDomainSid) Then Return SetError(@error, 0, 0)
    $iResult = DllCall("Advapi32.dll", "int", "CreateWellKnownSid", _
            "int", $iSidType, "ptr", $pDomainSid, "ptr*", 0, "int*", 0)
    $pSid = _HeapAlloc($iResult[4])
    $iResult = DllCall("Advapi32.dll", "int", "CreateWellKnownSid", _
            "int", $iSidType, "ptr", $pDomainSid, "ptr", $pSid, "int*", $iResult[4])
    Return SetError(_GetLastError(), 0, $pSid)
EndFunc ;==>_CreateWellKnownSid

; #### FUNCTION ####
; =======================================================================
; Name  : _AllocatedLUID
; Description   : Allocates a local unique identifier for the caller.
; Parameter(s)  : $sNameAssoc   - The name associated to the LUID, can be in any format. If non-null, use Eval function to retrieve the value of LUID.
; Return values : If succeeds, the return value is a LUID value in 8 bytes. Use _LsaLoLong64 function to retrieve the low part of the LUID, _LsaHiLong64 to high part of the LUID.
; Author    : Pusofalse
; =======================================================================
Func _AllocateLUID($sNameAssoc = "")
    Local $pLuid, $tLuid, $iLuid, $iSysError, $iLow, $iHigh, $iResult

    $pLuid = _InitializeLuid(0, 0)
    $iResult = DllCall("Advapi32.dll", "int", "AllocateLocallyUniqueId", "ptr", $pLuid)
    $iSysError = _GetLastError()
    $tLuid = DllStructCreate($tagLUID, $pLuid)
    $iLow = DllStructGetData($tLUID, "Low")
    $iHigh = DllStructGetData($tLUID, "High")
    _HeapFree($pLuid)
    _FreeVariable($tLuid)
    $iLuid = _LsaMakeLong64($iLow, $iHigh)
    If $sNameAssoc <> "" Then Assign($sNameAssoc, $iLuid, 2)
    Return SetError($iSysError, ($iResult[0] <> 0), $iLuid)
EndFunc ;==>_AllocateLUID

; #### FUNCTION ####
; =======================================================================
; Name  : _AllocateGUID
; Description   : Allocates a global unique identifier for the caller.
; Parameter(s)  : This function has no parameters.
; Return values : If succeeds, returns a pointer to a GUID structure contains the newly allocated GUID. If fails, @error is set non-zero.
; Author    : Pusofalse
; =======================================================================
Func _AllocateGUID()
    Local $pGUID = _HeapAlloc(16), $iResult
    $iResult = DllCall("Ole32.dll", "int", "CoCreateGuid", "ptr", $pGUID)
    Return SetError($iResult[0], 0, $pGUID)
EndFunc ;==>_AllocateGUID

; #### FUNCTION ####
; =======================================================================
; Name  : _StringFromGUID
; Description   : Retrieves the GUID in string format.
; Parameter(s)  : $pGUID    - A pointer to a GUID.
; Return values : GUID in string format.
; Author    : Pusofalse
; =======================================================================
Func _StringFromGUID($pGUID)
    Local $iResult

    If IsDllStruct($pGUID) Then $pGUID = DllStructGetPtr($pGUID)
    $iResult = DllCall("Ole32.dll", "int", "StringFromGUID2", _
            "ptr", $pGUID, "wstr", "", "int", 128)
    Return $iResult[2]
EndFunc ;==>_StringFromGUID

; #### FUNCTION ####
; =======================================================================
; Name  : _GUIDFromString
; Description   : Converts a string GUID (globally unique identifier) to a GUID structure.
; Parameter(s)  : $sGUID    - GUID string.
; Return values : If succeeds, @error is set to zero, otherwise sets to non-zero. The return value is a GUID structure.
; Author    : Pusofalse
; =======================================================================
Func _GUIDFromString($sGUID)
    Local $tGUID = DllStructCreate("byte Guid[16]"), $iResult

    $iResult = DllCall("Ole32.dll", "int", "CLSIDFromString", "wstr", $sGUID, "ptr", DllStructGetPtr($tGUID))
    Return SetError($iResult[0], 0, $tGUID)
EndFunc ;==>_GUIDFromString

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaEnumerateWellKnownAccounts
; Description   : This function lists all the well known accounts in the local system.
; Parameter(s)  : This function has no parameters.
; Return values : An array with following format:
;       :   - $aArray[0][0] - The number of accounts returned.
;       :   - $aArray[1][0] - The name of first well known account.
;       :   - $aArray[1][1] - The SID of first well known account, in string format.
;       :   - $aArray[2][0] - The name of second well known account.
;       :   - ... ...
; Author    : Pusofalse
; =======================================================================
Func _LsaEnumerateWellKnownAccounts()
    Local $pSid, $aResult[1][2] = [[0]]

    For $i = 0 to 78
        $pSid = _CreateWellKnownSid($i)
        If Not _IsValidSid($pSid) Then ContinueLoop
        $aResult[0][0] += 1
        Redim $aResult[$aResult[0][0] + 1][2]
        $aResult[$aResult[0][0]][0] = _LookupAccountSid($pSid)
        $aResult[$aResult[0][0]][1] = _ConvertSidToStringSid($pSid)
        _HeapFree($pSid)
    Next
    Return $aResult
EndFunc ;==>_LsaEnumerateWellKnownAccounts

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaLocalUserGePasswordPolicy
; Description   : Returns the password policy on specified system.
; Parameter(s)  : $sSystem  - Specifies the system on which the function executes, default to local.
; Return values : If succeeds, returns an array in the format:
;       :   - $aArray[0]    - The minimum password length requires.
;       :   - $aArray[1]    - The maximum password age.
;       :   - $aArray[2]    - The minimum password age.
;       :   - $aArray[3]    - Timeout of force-logoff, in seconds.
;       :   - $aArray[4]    - Password's history length.
; Author    : Pusofalse
; =======================================================================
Func _LsaLocalUserGetPasswordPolicy($sSystem = "")
    Local $tBuffer, $pBuffer, $iResult, $aResult[5]

    $iResult = DllCall("Netapi32.dll", "long", "NetUserModalsGet", _
            "wstr", $sSystem, "dword", 0, "ptr*", 0)
    $tBuffer = DllStructCreate($tagLSAUSERMODALS, $iResult[3])
    $aResult[0] = DllStructGetData($tBuffer, "MinPwdLen")
    $aResult[1] = DllStructGetData($tBuffer, "MaxPwdAge")
    $aResult[2] = DllStructGetData($tBuffer, "MinPwdAge")
    $aResult[3] = DllStructGetData($tBuffer, "ForceLogoff")
    $aResult[4] = DllStructGetData($tBuffer, "PwdHistLen")
    _LsaApiBufferFree($iResult[3])
    Return SetError($iResult[0], _FreeVariable($tBuffer), $aResult)
EndFunc ;==>_LsaLocalUserGetPasswordPolicy

; #### FUNCTION ####
; ==============================================================================
; Name  : _LsaLocalUserSetInfo
; Description   : This function sets the user account information on the specified system.
; Parameter(s)  : $sUserName    - Specifies the user name for which to set information.
;       : $iLevel   - Specifies the information level of the data, see Msdn link for details.
;       : $pBuffer  - A pointer to a buffer that contains the information data.
;       : $sBufferType  - Specifies the type of the $pBuffer parameter, default it is recognized as a pointer.
;       : $sSystem  - System name on which the function executes, default to local.
; Return values : Returns true if succeeds, false otherwise, in this case, @error is set to a system error code.
; Author    : Pusofalse
; Link  : http://msdn.microsoft.com/en-us/library/aa370659(VS.85).aspx
; ==============================================================================
Func _LsaLocalUserSetInfo($sUserName, $iLevel, $pBuffer, $sBufferType = "ptr", $sSystem = "")
    Local $iResult

    $iResult = DllCall("Netapi32.dll", "long", "NetUserSetInfo", "wstr", $sSystem, _
            "wstr", $sUserName, "dword", $iLevel, _
            $sBufferType, $pBuffer, "dword*", 0)
    If (@error) Then Return SetError(87, 0, False)
    Return SetError($iResult[0], 0, $iResult[0] = 0)
EndFunc ;==>_LsaLocalUserSetInfo

; #### FUNCTION ####
; ==============================================================================
; Name  : _LsaLocalUserSetAttributes
; Description   : This function sets the user account attributes.
; Parameter(s)  : $sUserName    - User for which to set.
;       : $iFlags   - User flags, see Msdn link for details.
;       : $sSystem  - System on which to execute.
; Return values : Returns true if no error occurs, otherwise false, @error is a system error code.
; Author    : Pusofalse
; Link  : http://msdn.microsoft.com/en-us/library/aa370968(VS.85).aspx
; ==============================================================================
Func _LsaLocalUserSetAttributes($sUserName, $iFlags, $sSystem = "")
    Local $iResult = _LsaLocalUserSetInfo($sUserName, 1008, $iFlags, "dword*", $sSystem)
    Return SetError(@error, 0, $iResult)
EndFunc ;==>_LsaLocalUserSetAttributes

; #### FUNCTION ####
; ==============================================================================
; Name  : _LsaLocalUserSetPassword
; Description   : This function changes the password for an existing user account.
; Parameter(s)  : $sUserName    - User name for which the password is set.
;       : $sPswd    - Password, in string format.
;       : $sSystem  - System on which the function executes.
; Return values : True indicates success, false indicates failure.
; Author    : Pusofalse
; ==============================================================================
Func _LsaLocalUserSetPassword($sUserName, $sPswd, $sSystem = "")
    Local $iResult = _LsaLocalUserSetInfo($sUserName, 1003, $sPswd, "wstr*", $sSystem)
    Return SetError(@error, 0, $iResult)
EndFunc ;==>_LsaLocalUserSetPassword

; #### FUNCTION ####
; =======================================================================
; This function is only internal used, _LsaLocalUserSet* functions require this function.
; =======================================================================
Func _LsaLocalUserSetModals($iLevel, $pBuffer, $sParam = "ptr", $sSystem = "")
    Local $iResult

    $iResult = DllCall("Netapi32.dll", "long", "NetUserModalsSet", _
            "wstr", $sSystem, "dword", $iLevel, $sParam, $pBuffer, "int*", 0)
    Return SetError($iResult[0], 0, $iResult[0] = $ERROR_SUCCESS)
EndFunc ;==>_LsaLocalUserSetModals

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaLocalUserSetMinPasswordLength
; Description   : Sets the minimum password length on specified system.
; Parameter(s)  : $iLength      - The minimum length of the password.
;       : $sSystem  - The system on which the function to execute, default to local.
; Return values : True indicates success, false to failure.
; Author    : Pusofalse
; =======================================================================
Func _LsaLocalUserSetMinPasswordLength($iLength, $sSystem = "")
    Local $iResult

    $iResult = _LsaLocalUserSetModals(1001, $iLength, "dword*", $sSystem)
    Return SetError(@error, 0, $iResult)
EndFunc ;==>_LsaLocalUserSetMinPasswordLength

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaLocalUserSetMaxPasswordAge
; Description   : This function is used to set the maximum password age.
; Parameter(s)  : $iAge - The age value.
;       : $iSystem  - The system on which the function is to execute.
; Return values : True indicates success, false to failure.
; =======================================================================
Func _LsaLocalUserSetMaxPasswordAge($iAge, $sSystem = "")
    Local $iResult

    $iResult = _LsaLocalUserSetModals(1002, $iAge, "dword*", $sSystem)
    Return SetError(@error, 0, $iResult)
EndFunc ;==>_LsaLocalUserSeMaxPasswordAge

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaLocalUserSetMinPasswordAge
; Description   : This function sets the minimum passoword age.
; Parameter(s)  : $iAge - The minimum age value.
;       : $sSystem  - The system on which the function executes.
; Return values : If succeeds, returns true, else returns false and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _LsaLocalUserSetMinPasswordAge($iAge, $sSystem = "")
    Local $iResult

    $iResult = _LsaLocalUserSetModals(1003, $iAge, "dword*", $sSystem)
    Return SetError(@error, 0, $iResult)
EndFunc ;==>_LsaLocalUserSeMinPasswordAge

Func _LsaLocalUserSetForceLogoff($iTimeout, $sSystem = "")
    Local $iResult

    $iResult = _LsaLocalUserSetModals(1004, $iTimeout, "dword*", $sSystem)
    Return SetError(@error, 0, $iResult)
EndFunc ;==>_LsaLocalUserSetForceLogoff

Func _LsaLocalUserSetPasswordHistLength($iLength, $sSystem = "")
    Local $iResult

    $iResult = _LsaLocalUserSetModals(1005, $iLength, "dword*", $sSystem)
    Return SetError(@error, 0, $iResult)
EndFunc ;==>_LsaLocalUserSetPasswordHistLength

Func _LsaLocalUserSetLockout($iThreshold, $iDuration, $iObservation, $sSystem = "")
    Local $iResult, $tBuffer, $pBuffer

    $tBuffer = DllStructCreate($tagUSERMODALSINFO3)
    $pBuffer = DllStructGetPtr($tBuffer)
    DllStructSetData($tBuffer, "Duration", $iDuration)
    DllStructSetData($tBuffer, "ObservationWin", $iObservation)
    DllStructSetData($tBuffer, "Threshold", $iThreshold)

    $iResult = _LsaLocalUserSetModals(3, $pBuffer, "ptr", $sSystem)
    Return SetError(@error, _FreeVariable($tBuffer), $iResult)
EndFunc ;==>_LsaLocalUserSetLockout

; #### INTERNAL USED ONLY FUNCTION ####
; =======================================================================
; Free the buffer allocated by the API NetApiBufferAlloc.
; =======================================================================
Func _LsaApiBufferFree($pMem)
    Local $iResult
    $iResult = DllCall("Netapi32.dll", "dword", "NetApiBufferFree", "ptr", $pMem)
    Return SetError($iResult[0], 0, $iResult[0] = 0)
EndFunc ;==>_LsaApiBufferFree

; #### INTERNAL USED ONLY FUNCTION ####
; =======================================================================
; Query the size of the buffer allocated by the API NetApiBufferAlloc function.
; =======================================================================
Func _LsaApiBufferSize($pBuffer)
    Local $iResult
    $iResult = DllCall("Netapi32.dll", "long", "NetApiBufferSize", "ptr", $pBuffer, "dword*", 0)
    Return SetError($iResult[0], 0, $iResult[2])
EndFunc ;==>_LsaApiBufferSize

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaEnumerateLocalAccounts
; Description   : This function enumerates all the user accounts in the specified system.
; Parameter(s)  : $iFilter  - A value of USER ACCOUNT FILETER CONSTANTS.
;       : $sSystem  - The computer name from which the local user accounts are retrieved, default to local.
; Return values : If succeeds, the return value is an array contains the following format:
;       :   - $aArray[0]    - The number of user accounts returned.
;       :   - $aArray[1]    - The first user's name.
;       :   - $aArray[2]    - The second user's name.
;       :   - ... ...
; Author    : Pusofalse
; =======================================================================
Func _LsaEnumerateLocalAccounts($iFilter = $FILTER_ALL_USER_ACCOUNTS, $sSystem = "")
    Local $iResult, $tBuffer, $pBuffer, $aResult[1], $tagBuffer, $tName

    $iResult = DllCall("Netapi32.dll", "dword", "NetUserEnum", _
            "wstr", $sSystem, "dword", 0, "dword", $iFilter, _
            "ptr*", 0, "dword", -1, "int*", 0, "int*", 0, "int*", 0)
    $pBuffer = $iResult[4]
    $aResult[0] = $iResult[6]
    Redim $aResult[$iResult[6] + 1]
    For $i = 1 to $iResult[6]
        $tagBuffer &= "ptr;"
    Next
    For $i = 1 to $iResult[6]
        $tBuffer = DllStructCreate($tagBuffer, $iResult[4])
        $tName = DllStructCreate("wchar[128]", DllStructGetData($tBuffer, $i))
        $aResult[$i] = DllStructGetData($tName, 1)
        $tName = 0
    Next
    _LsaApiBufferFree($pBuffer)
    Return SetError($iResult[0], 0, $aResult)
EndFunc ;==>_LsaEnumerateLocalAccounts

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaAddLocalUser
; Description   : This function adds a local user on the specified system.
; Parameter(s)  : $sUser    - The name of the user account.
;       : $sPswd    - The password of the user.
;       : $iFlags   - A set of bits flag indicates the initial state of the newly created local user. Zero indicates normal.
;       : $sSystem  - System on which the function executes, default to local system.
; Return values : True indicates success, false indicates failure.
; Author    : Pusofalse
; Remarks   : The user account newly created does not belong any local group, use _LsaLocalGroupAddMembers function to add members.
; =======================================================================
Func _LsaAddLocalUser($sUser, $sPswd, $iFlag = 0, $sSystem = "")
    Local $iResult, $tUser, $tPswd, $tBuffer, $pBuffer, $v_Nul

    $tUser = DllStructCreate("wchar[" & StringLen($sUser) * 2 + 2 & "]")
    $tPswd = DllStructCreate("wchar[" & StringLen($sPswd) * 2 + 2 & "]")
    $tBuffer = DllStructCreate("ptr[2];dword[2];ptr[2];dword;ptr")
    $pBuffer = DllStructGetPtr($tBuffer)
    DllStructSetData($tUser, 1, $sUser)
    DllStructSetData($tPswd, 1, $sPswd)
    DllStructSetData($tBuffer, 1, DllStructGetPtr($tUser), 1)
    DllStructSetData($tBuffer, 1, DllStructGetPtr($tPswd), 2)
    DllStructSetData($tBuffer, 2, 1, 2)
    DllStructSetData($tBuffer, 4, $iFlag)

    $iResult = DllCall("Netapi32.dll", "dword", "NetUserAdd", _
            "wstr", $sSystem, "dword", 1, "ptr", $pBuffer, "long*",0)
    $v_Nul = _FreeVariable($tUser) & _FreeVariable($tPswd) & _FreeVariable($tBuffer)
    $v_Nul = _LookupAccountName($sUser)
    Return SetError($iResult[0], 0, $v_Nul)
EndFunc ;==>_LsaAddLocalUser

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaDelLocalUser
; Description   : Deletes an user account from the specified system.
; Parameter(s)  : $sUser    - The name of the user to delete.
;       : $sSystem  - System on which the function execute, blank indicates local.
; Return values : True indicates success, false to failure in the case the @error is set to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _LsaDelLocalUser($sUser, $sSystem = "")
    Local $iResult
    $iResult = DllCall("Netapi32.dll", "dword", "NetUserDel", _
            "wstr", $sSystem, "wstr", $sUser)
    Return SetError($iResult[0], 0, $iResult[0] = 0)
EndFunc ;==>_LsaDelLocalUser

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaAddLocalGroup
; Description   : This function adds a local group on the specified system.
; Parameter(s)  : $sName        - The name of the new local group.
;       : $sComment - The comment of the new local group, can be NULL.
;       : $sSystem  - System name on which the function executes, default to local.
; Return values : If success, returns true, else returns false and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _LsaAddLocalGroup($sName, $sComment, $sSystem = "")
    Local $tName, $tBuffer, $pBuffer, $iResult, $tagName

    $tagName = "wchar[" & (StringLen($sName) * 2 + 2) & "];wchar[" & (StringLen($sName) * 2 + 2) & "]"
    $tName = DllStructCreate($tagName)
    DllStructSetData($tName, 1, $sName)
    DllStructSetData($tName, 2, $sComment)

    $tBuffer = DllStructCreate("ptr;ptr")
    $pBuffer = DllStructGetPtr($tBuffer)
    DllStructSetData($tBuffer, 1, DllStructGetPtr($tName, 1))
    DllStructSetData($tBuffer, 2, DllStructGetPtr($tName, 2))

    $iResult = DllCall("Netapi32.dll", "dword", "NetLocalGroupAdd", _
            "wstr", $sSystem, "dword", 1, "ptr", $pBuffer, "int*", 0)
    _FreeVariable($tBuffer)
    _FreeVariable($tName)
    Return SetError($iResult[0], 0, $iResult[0] = 0)
EndFunc ;==>_LsaAddLocalGroup

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaDelLocalGroup
; Description   ; Deletes a local group on the specified system.
; Parameter(s)  : $sGroup   - The name of the local group to delete.
;       : $sSystem  - Specifies the system on which the local group is deleted, default to local.
; Return values : If success, returns true, else returns false and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _LsaDelLocalGroup($sGroup, $sSystem = "")
    Local $iResult
    $iResult = DllCall("Netapi32.dll", "dword", "NetLocalGroupDel", _
            "wstr", $sSystem, "wstr", $sGroup)
    Return SetError($iResult[0], 0, $iResult[0] = 0)
EndFunc ;==>_LsaDelLocalGroup

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaEnumerateLocalGroups
; Description   : This function lists the local groups exist on the specified system.
; Parameter(s)  : $sSystem  - Specifies system, the function retrieves the local groups exist on this system. Default to local.
; Return values : An array with following format:
;       :   - $aArray[0][0] - The number of entries returned.
;       :   - $aArray[1][0] - 1st local group's name.
;       :   - $aArray[1][1] - 1st local group's comments, empty indicates the local group has no comments.
;       :   - $aArray[2][0] - 2nd local group's name.
;       :   - ... ...
;       : If fails, the value of $aArray[0][0] is set to zero, @error is a system error code.
; Author    : Pusofalse
; =======================================================================
Func _LsaEnumerateLocalGroups($sSystem = "")
    Local $aResult[1][2] = [[0]], $pBuffer, $tBuffer, $iResult, $tCmt, $tName

    $iResult = DllCall("Netapi32.dll", "dword", "NetLocalGroupEnum", _
            "wstr", $sSystem, "dword", 1, "ptr*", 0, _
            "dword", -1,"int*", 0, "int*", 0, "int*", 0)
    $pBuffer = $iResult[3]
    $aResult[0][0] = $iResult[5]
    Redim $aResult[$iResult[5] + 1][2]
    For $i = 1 to $iResult[5]
        $tBuffer = DllStructCreate("ptr;ptr", $iResult[3])
        $tName = DllStructCreate("wchar[256]", DllStructGetData($tBuffer,1))
        $tCmt = DllStructCreate("wchar[256]", DllStructGetData($tBuffer, 2))
        $aResult[$i][0] = DllStructGetData($tName, 1)
        $aResult[$i][1] = DllStructGetData($tCmt, 1)
        _FreeVariable($tCmt)
        _FreeVariable($tName)
        _FreeVariable($tBuffer)
        $iResult[3] += 8
    Next
    _LsaApiBufferFree($pBuffer)
    Return SetError($iResult[0], 0, $aResult)
EndFunc ;==>_LsaEnumerateLocalGroups

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaLocalGroupGetMembers
; Description   : Lists the memebers exist in the specified local group.
; Parameter(s)  : $sGroup   - The local group name.
;       : $sSystem  - System name on which the function executes, default to local.
; Return values : An array in the form:
;       :   - $aArray[0][0] - The number of returned entries.
;       :   - $aArray[1][0] - 1st user account name in the form: Domain\UserName.
;       :   - $aArray[1][1] - 1st user account's SID, in string format.
;       :   - ... ...
;       : If fails, $aArray[0][0] is set zero, @error is a system error code.
; Author    : Pusofalse
; =======================================================================
Func _LsaLocalGroupGetMembers($sGroup, $sSystem = "")
    Local $iResult, $tBuffer, $pBuffer, $aResult[1][2] = [[0]], $pSid

    $iResult = DllCall("Netapi32.dll", "dword", "NetLocalGroupGetMembers", _
            "wstr", $sSystem, "wstr", $sGroup, "dword", 0, _
            "ptr*", 0, "dword", -1, "int*", 0, "int*", 0, "int*", 0)
    $pBuffer = $iResult[4]
    $aResult[0][0] = $iResult[6]
    Redim $aResult[$iResult[6] + 1][2]
    For $i = 1 to $iResult[6]
        $tBuffer = DllStructCreate("ptr Sid", $iResult[4])
        $pSid = _CopySid(DllStructGetData($tBuffer, "Sid"))
        $aResult[$i][0] = _LookupAccountSid($pSid)
        $aResult[$i][1] = _ConvertSidToStringSid($pSid)
        _HeapFree($pSid)
        _FreeVariable($tBuffer)
        $iResult[4] += 4
    Next
    _LsaApiBufferFree($pBuffer)
    Return SetError($iResult[0], 0, $aResult)
EndFunc ;==>_LsaLocalGroupGetMembers

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaLocalGroupAddMembers
; Description   : Add members to specified local group.
; Parameter(s)  : $sGroup   - To which the members are added.
;       : $aMember  - Either an user account name string, or an array in the following form:
;       :   - $aMember[0] - 1st user account name.
;       :   - $aMember[1] - 2nd user account name.
;       :   - ... ...
;       : $sSystem  - System on which the function executes, default to local.
; Return values : True indicates success, false indicates failure, @error is one system error code.
; Author    : Pusofalse
; =======================================================================
Func _LsaLocalGroupAddMembers($sGroup, $aMember, $sSystem = "")
    Local $iResult, $tagBuffer, $tBuffer, $pBuffer, $tagMember, $tMember, $pMember, $iNum

    If IsArray($aMember) Then
        If Ubound($aMember, 0) <> 1 Then Return SetError(13, 0, 0)
        $iNum = Ubound($aMember)
        For $i = 0 to Ubound($aMember) - 1
            $tagBuffer &= "ptr;"
            $tagMember &= "wchar[" & (StringLen($aMember[$i]) + 2) * 2 & "];"
        Next
    Else
        $iNum = 1
        $tagBuffer = "ptr"
        $tagMember = "wchar[" & (StringLen($aMember) + 2) * 2 & "]"
    EndIf
    $tBuffer = DllStructCreate($tagBuffer)
    $pBuffer = DllStructGetPtr($tBuffer)
    $tMember = DllStructCreate($tagMember)
    $pMember = DllStructGetPtr($tMember)
    If IsArray($aMember) Then
        For $i = 0 to Ubound($aMember) - 1
            DllStructSetData($tMember, $i + 1, $aMember[$i])
            DllStructSetData($tBuffer, $i + 1, DllStructGetPtr($tMember, $i + 1))
        Next
    Else
        DllStructSetData($tMember, 1, $aMember)
        DllStructSetData($tBuffer, 1, DllStructGetPtr($tMember))
    EndIf
    $iResult = DllCall("Netapi32.dll", "dword", "NetLocalGroupAddMembers", _
            "wstr", $sSystem, "wstr", $sGroup, "dword", 3, _
            "ptr", $pBuffer, "dword", $iNum)
    _FreeVariable($tBuffer)
    _FreeVariable($tMember)
    Return SetError($iResult[0], 0, $iResult[0] = 0)
EndFunc ;==>_LsaLocalGroupAddMembers

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaLocalGroupDelMembers
; Description   : Deletes memebers from the local group.
; Parameter(s)  : $sGroup   - Local group name.
;       : $aMember  - Either an user account string or an array in the form:
;       :   - $aMember[0] - 1st user account.
;       :   - $aMember[1] - 2nd user account.
;       :   - ... ...
;       : $sSystem  - System on which the function executes.
; Return values : True indicates success, false to failure, in this case to set @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _LsaLocalGroupDelMembers($sGroup, $aMember, $sSystem = "")
    Local $iResult, $tagBuffer, $tBuffer, $pBuffer, $tagMember, $tMember, $pMember, $iNum

    If IsArray($aMember) Then
        If Ubound($aMember, 0) <> 1 Then Return SetError(13, 0, 0)
        $iNum = Ubound($aMember)
        For $i = 0 to Ubound($aMember) - 1
            $tagBuffer &= "ptr;"
            $tagMember &= "wchar[" & (StringLen($aMember[$i]) + 2) * 2 & "];"
        Next
    Else
        $iNum = 1
        $tagBuffer = "ptr"
        $tagMember = "wchar[" & (StringLen($aMember) + 2) * 2 & "]"
    EndIf
    $tBuffer = DllStructCreate($tagBuffer)
    $pBuffer = DllStructGetPtr($tBuffer)
    $tMember = DllStructCreate($tagMember)
    $pMember = DllStructGetPtr($tMember)
    If IsArray($aMember) Then
        For $i = 0 to Ubound($aMember) - 1
            DllStructSetData($tMember, $i + 1, $aMember[$i])
            DllStructSetData($tBuffer, $i + 1, DllStructGetPtr($tMember, $i + 1))
        Next
    Else
        DllStructSetData($tMember, 1, $aMember)
        DllStructSetData($tBuffer, 1, DllStructGetPtr($tMember))
    EndIf
    $iResult = DllCall("Netapi32.dll", "dword", "NetLocalGroupDelMembers", _
            "wstr", $sSystem, "wstr", $sGroup, "dword", 3, _
            "ptr", $pBuffer, "dword", $iNum)
    _FreeVariable($tBuffer)
    _FreeVariable($tMember)
    Return SetError($iResult[0], 0, $iResult[0] = 0)
EndFunc ;==>_LsaLocalGroupDelMembers

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaStringIsUserAccount
; Description   : Determines one string whether is an user account's name.
; Parameter(s)  : $sUserAccount - A string to validate.
;       : $sSystem  - The system on which the function executes, default is local.
; Return values : If $sUserAccount is an user account in $sSystem, returns the system name on which the function tested. Otherwise, returns empty and sets @error to ERROR_INVALID_SID.
; Author    : Pusofalse
; =======================================================================
Func _LsaStringIsUserAccount($sUserAccount, $sSystem = "")
    Local $pSid

    If $sSystem = "" Then $sSystem = @ComputerName
    $pSid = _LookupAccountName($sUserAccount, $sSystem)
    If _IsValidSid($pSid) Then Return SetError(0, _HeapFree($pSid), $sSystem)
    Return SetError(@error, _HeapFree($pSid), "")
EndFunc ;==>_LsaStringIsUserAccount

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaLocalUserGetGroups
; Description   : This function lists the local group that the specified user belongs.
; Parameter(s)  : $sUserName    - The user account's name.
;       : $sSystem  - The system on which the function executes, default to local.
; Return values : If succeeds, returns an array with following format:
;       : $aArray[0]    - The number of returned entries.
;       : $aArray[1]    - 1st local group the $sUserName belongs.
;       : $aArray[2]    - 2nd local group the $sUserName belongs.
;       : If fails, $aArray[0] is set to zero, @error is a system error code.
; Author    : Pusofalse
; Remarks       : If the $sUserName does not belong any local group, $aArray[0] is either set to zero, but @error still is ERROR_SUCCESS (0).;
; =======================================================================
Func _LsaLocalUserGetGroups($sUserName, $sSystem = "")
    Local $iResult, $aResult[1] = [0], $tName, $pName

    $iResult = DllCall("Netapi32.dll", "dword", "NetUserGetLocalGroups", _
            "wstr", $sSystem, "wstr", $sUserName, _
            "dword", 0, "dword", 1, "ptr*", 0, _
            "dword", -1, "int*", 0, "int*", 0)
    $pName = $iResult[5]
    $aResult[0] = $iResult[7]
    Redim $aResult[$iResult[7] + 1]
    For $i = 1 to $aResult[0]
        $tBuffer = DllStructCreate("ptr", $iResult[5])
        $tName = DllStructCreate("wchar[256]", DllStructGetData($tBuffer, 1))
        $aResult[$i] = DllStructGetData($tName, 1)
        _FreeVariable($tName)
        _FreeVariable($tBuffer)
        $iResult[5] += 4
    Next
    _LsaApiBufferFree($pName)
    Return SetError($iResult[0], 0, $aResult)
EndFunc ;==>_LsaLocalUserGetGroups

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaLocalUserGetInfo
; Description   : Query the user account information on specified system.
; Parameter(s)  : $sUserName    - The user account's name.
;       : $iLevel   - Specifies the information is retrieves.
;       : $sSystem  - The system on which the function executes, default to local.
; Return values : If succeeds, returns a structure contains the user account information requires (information depends on the $iLevel parameter), in BYTE format, to decode the structure, see MSDN link. If fails, returns zero and sets @error to a system error code.
; Author    : Pusofalse
; Remarks   : If you finished with this structure, pass its pointer to _LsaApiBufferFree to free it.
; Link  : http://msdn.microsoft.com/en-us/library/aa370654(VS.85).aspx
; =======================================================================
Func _LsaLocalUserGetInfo($sUserName, $iLevel = 1, $sSystem = "")
    Local $iResult, $tBuffer, $iSizeofBuffer

    $iResult = DllCall("Netapi32.dll", "long", "NetUserGetInfo", "wstr", $sSystem, _
            "wstr", $sUserName, "dword", $iLevel, "ptr*", 0)
    $iSizeofBuffer = _LsaApiBufferSize($iResult[4])
    Return SetError($iResult[0], $iSizeofBuffer, $iResult[4])
EndFunc ;==>_LsaLocalUserGetInfo

; #### FUNCTION ####
; =======================================================================
; Name  : _CreateProcessAsUser
; Description   : Creates a new process and its primary thread. The new process runs in the security context of the user represented by the specified token.
; Parameter(s)  : $hToken       - Handle to the token, must have TOKEN_QUERY, TOKEN_DUPLICATE, TOKEN_ASSIGN_PRIMARY access rights.
;       : $sApp - The application to be executed.
;       : $sArg - Arguments passed to the $sApp, if not arguments, this parameter can be NULL.
;       : $pSecurAttr   - A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new process object and determines whether child processes can inherit the returned handle to the process.
;       : $hProcess - A variable receives the handle to the newly created process.
;       : $hThread  - A variable receives the handle to the primary thread of the newly created process.
;       : $iCreation    - The creation flag, specifies the priority class of the new process, default to NORMAL PRIORITY.
;       : $sWorkingDir  - Specifies the working directory, default to the where $sApp is in. If non-empty, the parameter just can only contains the Ansi string but cannot contain the unicode string, if $sWorkingDir does not exist, the path of the $sApp is used either.
;       : $iState   - The initial state, see @SW_* macros for a value, default to @SW_SHOWNORMAL.
; Return values : If succeeds, the return value is the identifier of the new process, $hProcess and $hThread parameters receive the process and thread handles, when you finished with these two handles, call _LsaCloseHandle to close them. If fails, the return value is set to zero, and @error is set to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _CreateProcessAsUser($hToken, $sApp, $sArg, $pSecurAttr, ByRef $hProcess, ByRef $hThread, $iCreation = 0x20, $sWorkingDir = "", $iState = -1, $fInheritHandle = 0)
    Local $tStartup, $pStartup, $iMask = 4, $tProcess, $pProcess, $iResult, $iPid, $iSysError, $tWD, $pWD

    If $iState <> -1 Then $iMask = bitOR($iMask, 1)
    $tStartup = DllStructCreate("dword;ptr[3];dword[8];short[2];ptr;hWnd[3]")
    $pStartup = DllStructGetPtr($tStartup)
    DllStructSetData($tStartup, 1, DllStructGetSize($tStartup))
    DllStructSetData($tStartup, 3, 77, 1)
    DllStructSetData($tStartup, 3, 77, 2)
    DllStructSetData($tStartup, 3, $iMask, 8)
    If $iState <> -1 Then DllStructSetData($tStartup, 4, $iState, 1)

    $tProcess = DllStructCreate("hWnd[2];dword[2]")
    $pProcess = DllStructGetPtr($tProcess)

    If $sWorkingDir <> "" And FileExists($sWorkingDir) And StringIsASCII($sWorkingDir) Then
        $tWD = DllStructCreate("char[" & StringLen($sWorkingDir) + 1 & "]")
        $pWD =DllStructGetPtr($tWD)
        DllStructSetData($tWD, 1, $sWorkingDir)
    EndIf

    $iResult = DllCall("Advapi32.dll", "int", "CreateProcessAsUser", _
            "hWnd", $hToken, "str", $sApp, "str", " " & $sArg, _
            "ptr", $pSecurAttr, "ptr", $pSecurAttr, "int", $fInheritHandle, _
            "dword", $iCreation, "ptr", 0, "ptr", $pWD, _
            "ptr", $pStartup, "ptr", $pProcess)
    $iSysError = _GetLastError()
    $hProcess = DllStructGetData($tProcess, 1, 1)
    $hThread = DllStructGetData($tProcess, 1, 2)
    $iPid = DllStructGetData($tProcess, 2, 1)
    _FreeVariable($tStartup)
    _FreeVariable($tProcess)
    Return SetError($iSysError, 0, $iPid)
EndFunc ;==>_CreateProcessAsUser

; #### FUNCTION ####
; =======================================================================
; Name  : _CreateProcessAsSystem
; Description   : Creates a system-level process, so that you can debug any other processes.
; Parameter(s)  : $sApp - The application to be created.
;       : $sArg - The arguments passed to the $sApp, if no arguments, this parameter can be NULL.
;       : $iSystemPid   - An existing system-level process, either image name or identifier, can not be NULL.
;       : $fProtect - If true, the function enables the minimum rights to everyone, default to false.
;       : $iCreation    - The creation flag, the parameter specifies the priority class, default to NORMAL PRIORITY.
;       : $sWorkingDir  - Specifies the working directory, default to the where $sApp is in. If non-empty, the parameter just can only contains the Ansi string but cannot contain the unicode string, if $sWorkingDir does not exist, the path of the $sApp is used either.
;       : $iState   - The initial state, see @SW_* macros for a value, default to @SW_SHOWNORMAL.
; Return values : If succeeds, the return value is the identifier of the newly created process, else returns zero and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _CreateProcessAsSystem($sApp, $sArg, $iSystemPid, $fProtect = False, $iCreation = 0x20, $sWorkingDir = "", $iState = -1, $fInheritHandle = 0)
    Local $aPrivilege[2][2] = [[$SE_DEBUG_NAME, 2], [$SE_RESTORE_NAME, 2]]
    Local $hToken, $hProcess, $hThread, $iPid, $iSysError, $aOrigSecur, $pOwner
    Local $pNewAcl, $iAccessMask = bitOR($READ_CONTROL, $WRITE_DAC)

    $iSystemPid = ProcessExists($iSystemPid)
    If Not ProcessExists($iSystemPid) Then Return SetError(2, 1, 0)

    $hToken = _OpenProcessToken(-1)
    _AdjustTokenPrivileges($hToken, $aPrivilege)
    _LsaCloseHandle($hToken)

    $hToken = _OpenProcessToken($iSystemPid, $iAccessMask)
    If Not $hToken Then Return SetError(@error, 0, 0)
    $aOrigSecur = _GetSecurityInfo($hToken, $SE_KERNEL_OBJECT, 4)
    $pNewAcl = _SetEntriesInAcl1("Everyone", $TOKEN_ALL_ACCESS, 1, 0)
    If Not _SetSecurityInfo($hToken, $SE_KERNEL_OBJECT, 4, 0, 0, $pNewAcl, 0) Then
        Return SetError(@error, _LsaLocalFree($pNewAcl) And _LsaCloseHandle($hToken), 0)
    EndIf
    _LsaLocalFree($pNewAcl)
    _LsaCloseHandle($hToken)
    $hToken = _OpenProcessToken($iSystemPid, $TOKEN_ALL_ACCESS)
    If Not $hToken Then Return SetError(@error, 0, 0)
    If Not _ImpersonateLoggedOnUser($hToken) Then Return SetError(@error, _LsaCloseHandle($hToken), 0)

    $iPid = _CreateProcessAsUser($hToken, $sApp, $sArg, 0, $hProcess, $hThread, $iCreation, $sWorkingDir, $iState, $fInheritHandle)
    $iSysError = @error
    _SetSecurityInfo($hToken, $SE_KERNEL_OBJECT, 4, 0, 0, $aOrigSecur[6], 0)
    _RevertToSelf()
    If $fProtect = True Then
        $pOwner = _LookupAccountName("Creator Owner")
        $pNewAcl = _SetEntriesInAcl1("Everyone", $GENERIC_ALL, 3, 0)
        _SetSecurityInfo($hProcess, $SE_KERNEL_OBJECT, 5, $pOwner, 0, $pNewAcl, 0)
        _SetSecurityInfo($hThread, $SE_KERNEL_OBJECT, 5, $pOwner, 0, $pNewAcl, 0)
        _HeapFree($pOwner)
        _LsaLocalFree($pNewAcl)
    EndIf
    _LsaCloseHandle($hToken)
    _LsaCloseHandle($hProcess)
    _LsaCloseHandle($hThread)
    Return SetError($iSysError, 0, $iPid)
EndFunc ;==>_CreateProcessAsSystem

; #### FUNCTION ####
; ================================================================================
; Name  : _LsaInitializeSecurityAttributes
; Description   : This function initializes an SECURITY_ATTRIBUTES structure.
; Parameter(s)  : ByRef $pSecurAttr - A variable receives the pointer to the SECURITY_ATTRIBUTES structure.
;       : $pSecurDesc   - A pointer to a security descriptor.
;       : $fInherit - Inherit options.
; Return values : SECURITY_ATTRIBUTES structure.
; Author    : Pusofalse
; ================================================================================
Func _LsaInitializeSecurityAttributes(ByRef $pSecurAttr, $pSecurDesc = 0, $fInherit = 0)
    Local $tSecurAttr

    $pSecurAttr = _HeapAlloc(12)
    $tSecurAttr = DllStructCreate($tagSECUR_ATTRIBUTES, $pSecurAttr)
    DllStructSetData($tSecurAttr, "Size", 12)
    DllStructSetData($tSecurAttr, "Securitydescriptor", $pSecurDesc)
    DllStructSetData($tSecurAttr, "Inherit", $fInherit)
    Return $tSecurAttr
EndFunc ;==>_LsaInitializeSecurityAttributes

Func _LsaInitializeObjectAttributes($sObject,$iAttributes =0, $pSecurDescr = 0, $hRootDir = 0)
    Local $pBuffer, $tBuffer, $pObject

    $pObject = _LsaInitializeBufferW($sObject)
    $pBuffer = _HeapAlloc(24)
    $tBuffer = DllStructCreate($tagOBJECT_ATTRIBUTES, $pBuffer)
    DllStructSetData($tBuffer, "Length", 24)
    DllStructSetData($tBuffer, "RootDir", $hRootDir)
    DllStructSetData($tBuffer, "BufferW", $pObject)
    DllStructSetData($tBuffer, "Attributes", $iAttributes)
    DllStructSetData($tBuffer, "Securitydescriptor", $pSecurDescr)
    Return $pBuffer
EndFunc ;==>_LsaInitializeObjectAttributes

; #### FUNCTION ####
; =======================================================================
; Name  : _GetSecurityDescriptorOwner
; Description   : Retrieves the owner information of a security descriptor.
; Parameter : $pSecurDesc   - A pointer a security descriptor.
; Return values : Success   - A pointer to a SID.
;       : Failure   - Zero, and set @error.
; Author        : Pusofalse
; =======================================================================
Func _GetSecurityDescriptorOwner($pSecurDesc)
    Local $pOwner
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)
    $pOwner = DllCall("Advapi32.dll", "int", "GetSecurityDescriptorOwner", _
            "ptr", $pSecurDesc, "ptr*", 0, "int*", 0)
    Return SetError(_GetLastError(), $pOwner[3], $pOwner[2])
EndFunc ;==>_GetSecurityDescriptorOwner

; #### FUNCTION ####
; =======================================================================
; Name  : _GetSecurityDescriptorDacl
; Description   : Retrieves a pointer to the discretionary access control list (DACL) in a specified security descriptor.
; Parameter : $pSecurDesc   - A pointer to the security descriptor.
; Return values : Success   - A DACL pointer, set @extended to the presence of the DACL.
;       : Failrue   - Zero, and set @error.
; Author        : Pusofalse
; =======================================================================
Func _GetSecurityDescriptorDacl($pSecurDesc)
    Local $pDacl
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)
    $pDacl = DllCall("Advapi32.dll", "int", "GetSecurityDescriptorDacl", _
            "ptr", $pSecurDesc, "int*", 0, "ptr*", 0, "int*", 0)
    Return SetError(_GetLastError(), $pDacl[2], $pDacl[3])
EndFunc ;==>_GetSecurityDescriptorDacl

; #### FUNCTION ####
; =======================================================================
; Name  : _GetSecurityDescriptorSacl
; Description   : Retrieves the system access control list (SACL) information of a security descriptor.
; Parameter : $pSecurDesc   - A pointer to a security descriptor.
; Return values : A pointer to a SACL indicates success, else returns zero and sets @error.
; Author        : Pusofalse
; =======================================================================
Func _GetSecurityDescriptorSacl($pSecurDesc)
    Local $pSacl
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)
    $pSacl = DllCall("Advapi32.dll", "int", "GetSecurityDescriptorSacl", _
            "ptr", $pSecurDesc, "int*", 0, "ptr*", 0, "int*", 0)
    Return SetError(_GetLastError(), $pSacl[4], $pSacl[3])
EndFunc ;==>_GetSecurityDescriptorSacl

; #### FUNCTION ####
; =======================================================================
; Name  : _GetSecurityDescriptorGroup
; Description   : Retrieves the primary group information from a secucity descriptor.
; Parameter : $pSecurDesc   - A pointer to the security descriptor
; Return values : Success   - A pointer to a SID identifies the primary group.
;       : Failure   - Zero, and set @error.
; Author        : Pusofalse
; =======================================================================
Func _GetSecurityDescriptorGroup($pSecurDesc)
    Local $pGroup

    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)
    $pGroup = DllCall("Advapi32.dll", "int", "GetSecurityDescriptorGroup", _
            "ptr", $pSecurDesc, "ptr*", 0, "int*", 0)
    Return SetError(_GetLastError(), $pGroup[3], $pGroup[2])
EndFunc ;==>_GetSecurityDescriptorGroup

; #### FUNCTION ####
; =======================================================================
; Name      : _SetSecurityDescriptorOwner
; Description   : The function sets the owner information of an absolute-format security descriptor. It replaces any owner information already present in the security descriptor.
; Parameters    : $pSecurDesc   - A pointer to SD structure.
;       : $pOwer        - A pointer to SID structure.
;       : $fDefault - Optional, indicates whether the owner information is derived from a default mechanism.
; Return values : True indicates success, otherwise indicates failure.
; Author        : Pusofalse
; =======================================================================
Func _SetSecurityDescriptorOwner($pSecurDesc, $pOwner, $fDefault = True)
    Local $iResult
    If $fDefault = Default Then $fDefault = True
    If $pOwner And Not _IsValidSid($pOwner) Then Return SetError(@error)
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error)
    $iResult = DllCall("Advapi32.dll", "int", "SetSecurityDescriptorOwner", _
            "ptr", $pSecurDesc, "ptr", $pOwner, "int", $fDefault)
    Return SetError(_GetLastError(), 0, $iResult[0] <> 0)
EndFunc ;==>_SetSecurityDescriptorOwner

; #### FUNCTION ####
; =======================================================================
; Name  : _SetSecurityDescriptorDacl
; Description   :  Sets information in a discretionary access control list (DACL).
; Parameters    : $pSecurDesc   - Pointer to a SD Structure.
;       : $pDacl        - Pointer to a DACL.
;       : $fPresent - A BOOL value that indicates the presence of a DACL in the security descriptor.
;       : $fDefault - A BOOL value that indicates the source of the DACL.
; Return values : True indicates success, else False.
; Author        : Pusofalse
; =======================================================================
Func _SetSecurityDescriptorDacl($pSecurDesc, $pDacl, $fPresent = True, $fDefault = True)
    Local $iResult

    If $fPresent = Default Then $fPresent = True
    If $fDefault = Default Then $fDefault = True
    If $pDacl And Not _IsValidAcl($pDacl) Then Return SetError(@error, 0, 0)
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error)
    $iResult = DllCall("Advapi32.dll", "int", "SetSecurityDescriptorDacl", _
            "ptr", $pSecurDesc, "int", $fPresent, _
            "ptr", $pDacl, "int", $fDefault)
    Return SetError(_GetLastError(), 0, $iResult[0] <> 0)
EndFunc ;==>_SetSecurityDescriptorDacl

; #### FUNCTION ####
; =======================================================================
; Name  : _SetSecurityDescriptorSacl
; Description   : Sets information in a system access control list (SACL).
; Parameters    : $pSecurDesc   - A pointer to a SD Structure.
;       : $pSacl        - A pointer to a SACL.
;       : $fPresent - A BOOL value indicates the presence of a SACL in the security descriptor.
;       : $fDefault - A BOOL value indicates the source of the SACL.
; Return values : True indicates success, else false.
; Author        : Pusofalse
; =======================================================================
Func _SetSecurityDescriptorSacl($pSecurDesc, $pSacl, $fPresent = True, $fDefault = True)
    Local $iResult
    If $fPresent = Default Then $fPresent = True
    If $fDefault = Default Then $fDefault = True
    If $pSacl And Not _IsValidAcl($pSacl) Then Return SetError(@error, 0, 0)
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)
    $iResult = DllCall("Advapi32.dll", "int", "SetSecurityDescriptorSacl", _
            "ptr", $pSecurDesc, "int", $fPresent, _
            "ptr", $pSacl, "int", $fDefault)
    Return SetError(_GetLastError(), 0, $iResult[0] <> 0)
EndFunc ;==>_SetSecurityDescriptorSacl

; #### FUNCTION
; =======================================================================
; Name  : _SetSecurityDescriptorGroup
; Description   : Sets the primary group information of an absolute-format security descriptor.
; Parameter(s)  : $pSecurDesc   - A pointer to a SD structure.
;       : $pGroup       - A pointer to a SID contains the security descriptor's new primary group.
;       : $fDefault - A BOOL value indicates whether the primary group information was derived from a default mechanism.
; Return values : True indicates success, else returns False.
; Author        : Pusofalse
; =======================================================================
Func _SetSecurityDescriptorGroup($pSecurDesc, $pGroup, $fDefault = True)
    Local $iResult

    If $pGroup And Not _IsValidSid($pGroup) Then Return SetError(@error, 0, 0)
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)
    $iResult = DllCall("Advapi32.dll", "int", "SetSecurityDescriptorGroup", _
            "ptr", $pSecurDesc, "ptr", $pGroup, "int", $fDefault)
    Return SetError(_GetLastError(), 0, $iResult[0] <> 0)
EndFunc ;==>_SetSecurityDescriptorGroup

; #### FUNCTION ####
; =======================================================================
; Name  : _GetSecurityDescriptorLength
; Description   : Retrieves the length of a security descriptor.
; Parameter(s)  : $pSecurDesc   - A pointer to a security descriptor
; Return values : If succeeds, returns the length of the $pSecurDesc, else returns zero and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _GetSecurityDescriptorLength($pSecurDesc)
    Local $iLength
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)
    $iLength = DllCall("Advapi32.dll", "dword", "GetSecurityDescriptorLength", "ptr", $pSecurDesc)
    Return SetError(_GetLastError(), 0, $iLength[0])
EndFunc ;==>_GetSecurityDescriptorLength

; #### FUNCTION ####
; =======================================================================
; Name  : _MakeSelfRelativeSD
; Description   : The function creates a security descriptor in self-relative format by using a security descriptor in absolute format as a template.
; Parameter(s)  : $pAbsoluteSd  - A pointer to a security descriptor in absoluted format. The function creates a version of this security descriptor in self-relative format without modifying the original.
; Return values : If succeeds, returns a pointer to a newly created self-relateive security descriptor, @extended is set to the length of the security descriptor. If fails, returns zero and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _MakeSelfRelativeSD($pAbsoluteSd)
    Local $iResult, $pSRSd

    If Not _IsValidSecurityDescriptor($pAbsoluteSd) Then Return SetError(@error, 0, 0)
    $iResult = DllCall("Advapi32.dll", "int", "MakeSelfRelativeSD", _
            "ptr", $pAbsoluteSd, "ptr", 0, "int*", 0)
    $pSRSd = _HeapAlloc($iResult[3])
    $iResult = DllCall("Advapi32.dll", "int", "MakeSelfRelativeSD", _
            "ptr", $pAbsoluteSd, "ptr", $pSRSd, "int*", $iResult[3])
    Return SetError(_GetLastError(), $iResult[3], $pSRSd)
EndFunc ;==>_MakeSelfRelativeSd

; #### FUNCTION ####
; =======================================================================
; Name  : _MakeAbsoluteSD
; Description   : This function creates a security descriptor in absolute format, by using a security descriptor in self-relative format as a template.
; Parameter(s)  : $pSRSd    - A pointer to a securiy descriptor in self-relative format.
; Return values : Returns an array in the following form:
;       : - $aArray[0]  - The exit code of the function, non-zero indicates success.
;       : - $aArray[1]  - The pointer to the security descriptor, same to $pSDSd parameter.
;       : - $aArray[2]  - A pointer to a security descriptor in absolute format.
;       : - $aArray[3]  - The length of the absolute-format security descriptor ($aArray[2]).
;       : - $aArray[4]  - A pointer to a discretionary access control list (DACL) of the absolute-format security descriptor.
;       : - $aArray[5]  - The length of the DACL ($aArray[4]).
;       : - $aArray[6]  - A pointer to a system access control list (SACL) of the absolute-format security descriptor.
;       : - $aArray[7]  - The length of the SACL ($aArray[6]).
;       : - $aArray[8]  - A pointer to a SID of the owner of the absolute-format security descriptor.
;       : - $aArray[9]  - The length of the SID of the owner ($aArray[8]).
;       : - $aArray[10] - A pointer to a SID of the primary group of the absolute-format security descriptor.
;       : - $aArray[11] - The length of the SID of the primary group ($aArray[10]).
; Author    : Pusofalse
; Remarks   : When you no longer use these returned pointers, call _HeapFree function to free them. It's caller's responsibility.
; =======================================================================
Func _MakeAbsoluteSD($pSRSd)
    Local $iResult, $pABSd, $pDacl, $pSacl, $pOwner, $pGroup, $aResult[11]

    If Not _IsValidSecurityDescriptor($pSRSd) Then Return SetError(@error, 0, $aResult)
    $iResult = DllCall("Advapi32.dll", "int", "MakeAbsoluteSD", "ptr", $pSRSd, _
            "ptr", 0, "int*", 0, "ptr", 0, "int*", 0, _
            "ptr", 0, "int*", 0, "ptr", 0, "int*", 0, "ptr", 0, "int*", 0)
    $pABSd = _HeapAlloc($iResult[3])
    $pDacl = _HeapAlloc($iResult[5])
    $pSacl = _HeapAlloc($iResult[7])
    $pOwner = _HeapAlloc($iResult[9])
    $pGroup = _HeapAlloc($iResult[11])
    $iResult = DllCall("Advapi32.dll", "int", "MakeAbsoluteSD", "ptr", $pSRSd, _
            "ptr", $pABSd, "int*", $iResult[3], "ptr", $pDacl, "int*", $iResult[5], _
            "ptr", $pSacl, "int*", $iResult[7], "ptr", $pOwner, "int*", $iResult[9], _
            "ptr", $pGroup, "int*", $iResult[11])
    Return SetError(_GetLastError(), $iResult[0], $iResult)
EndFunc ;==>_MakeAbsoluteSD

; #### FUNCTION ####
; =======================================================================
; Name  : _ConvertSdToStringSd
; Description   : This function converts a security descriptor to string format.
; Parameter(s)  : $pSecurDesc   - A pointer to a security descriptor.
; Return values : If succeeds, returns a string contains the security desciptor, else returns empty.
; Author    : Pusofalse
; =======================================================================
Func _ConvertSdToStringSd($pSecurDesc)
    Local $iResult, $tBuffer, $iMask = 15, $sResult
    Local $sFunction = "ConvertSecurityDescriptorToStringSecurityDescriptor"

    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, "")
    $iResult = DllCall("Advapi32.dll", "int", $sFunction, "ptr", $pSecurDesc, "dword", 1, _
            "dword", $iMask, "ptr*", 0, "int*", 0)
    $tBuffer = DllStructCreate("char[" & $iResult[5] & "]", $iResult[4])
    $sResult = DllStructGetData($tBuffer, 1)
    _LsaLocalFree($iResult[4])
    Return SetExtended(_FreeVariable($tBuffer), $sResult)
EndFunc ;==>_ConvertSdToStringSD

; #### FUNCTION ####
; =======================================================================
; Name  : _ConvertStringSdToSd
; Description   : Converts a security descriptor string to a security descriptor pointer.
; Parameter(s)  : $sSecurDesc   - A string contains the security descriptor.
;       : $fMakeAbsolute    - If true, the function makes an absolute-format security descriptor, default to false.
; Return values : If succeeds, the return value is a pointer to a security descriptor.
; Author    : Pusofalse
; Remarks   : When you finished with the pointer, call _LsaLocalFree function to free it.
; =======================================================================
Func _ConvertStringSdToSd($sSecurDesc, $fMakeAbsolute = false)
    Local $iResult, $sFunction
    $sFunction = "ConvertStringSecurityDescriptorToSecurityDescriptor"
    $iResult = DllCall("Advapi32.dll", "int", $sFunction, "str", $sSecurDesc, _
            "dword", 1, "ptr*", 0, "ulong*", 0)
    If Not $iResult[3] Then Return SetError(_GetLastError(), 0, 0)
    If $fMakeAbsolute = True Then
        $pAbSd = _MakeAbsoluteSD($iResult[3])
        Return SetError(@error, $pAbSd[3], $pAbSD[2])
    Else
        Return SetError(_GetLastError(), $iResult[4], $iResult[3])
    EndIf
EndFunc ;==>_ConvertStringSdToSD

; #### FUNCTION ####
; =======================================================================
; Name  : _QueryFileSecurity
; Description   : Retrieves the security descriptor of the specified file.
; Parameter(s)  : $sFile    - File the security descriptor from which is retrieved.
;       : $iSecurLevel  - SECURITY INFORMATION CONSTANTS value(s).
; Return values : If succeeds, the return value is a pointer to a security descriptor of the specified file.
; Author    : Pusofalse
; Remarks   : Note that the $sFile must be in NTFS file system, else, the function fails with ERROR_ACCESS_DENIED.
; =======================================================================
Func _QueryFileSecurity($sFile, $iSecurLevel = 4)
    Local $iResult, $pSecurDesc

    If Not _IsNtfs($sFile) Then Return SetError(@error, 0, 0)
    $iResult = DllCall("Advapi32.dll", "int", "GetFileSecurity", _
            "str", $sFile, "int", $iSecurLevel, "ptr", 0, _
            "dword", 0, "dword*", 0)
    $pSecurDesc = _HeapAlloc($iResult[5])
    $iResult = DllCall("Advapi32.dll", "int", "GetFileSecurity", _
            "str", $sFile, "int", $iSecurLevel, "ptr", $pSecurDesc, _
            "dword", $iResult[5], "dword*", 0)
    Return SetError(_GetLastError(), 0, $pSecurDesc)
EndFunc ;==>_QueryFileSecurity

; #### FUNCTION ####
; =======================================================================
; Name  : _QueryFileSecurityOwner
; Description   : This function retrieves the owner information of the specified file.
; Parameter(s)  : $sFile    - The file on which the owner information is retrieved.
; Return values : If succeeds, returns the owner of the file (in string format), else returns NULL.
; Author    : Pusofalse
; Remarks       : $sFile parameter must be in the NTFS file system, else returns NULL, and sets @error to ERROR_ACCESS_DENIED (5).
; =======================================================================
Func _QueryFileSecurityOwner($sFile)
    Local $pSecurDesc, $pOwner, $fDefault, $iSysError, $sOwner

    If Not _IsNtfs($sFile) Then Return SetError(@error, 0, "")
    $pSecurDesc = _QueryFileSecurity($sFile, 1)
    $pOwner = _GetSecurityDescriptorOwner($pSecurDesc)
    $fDefault = @Extended
    $iSysError = @ERROR
    $sOwner = _LookupAccountSid($pOwner)
    _HeapFree($pSecurDesc)
    Return SetError($iSysError, $fDefault, $sOwner)
EndFunc ;==>_QueryFileSecurityOwner

; #### FUNCTION ####
; =======================================================================
; Name  : _QueryFileSecurityDacl
; Description   : Retrieves the DACL information for a file object.
; Parameter(s)  : $sFile    - File name.
; Return values : The return value is same to the result of the _GetExplicitEntriesFromAcl function.
; Author    : Pusofalse
; =======================================================================
Func _QueryFileSecurityDacl($sFile)
    Local $pSecurDesc, $pDacl, $fDefault, $aResult[1][5] = [[0]], $aAceList, $iSysError

    If Not _IsNtfs($sFile) Then Return SetError(@error, 0, $aResult)
    $pSecurDesc = _QueryFileSecurity($sFile, 4)
    $iSysError = @error
    $pDacl = _GetSecurityDescriptorDacl($pSecurDesc)
    $fDefault = @Extended
    $aAceList = _GetExplicitEntriesFromAcl($pDacl)
    _HeapFree($pSecurDesc)
    Return SetError($iSysError, $fDefault, $aAceList)
EndFunc ;==>_QueryFileSecurityDacl

; #### FUNCTION ####
; =======================================================================
; Name  : _SetFileSecurity
; Description   : This function sets the security information for a file object.
; Parameter(s)  : $sFile    - File the security information to be set.
;       : $iSecurLevel  - SECURITY INFORMATION CONSTANTS.
;       : $pSecurDesc   - A pointer to a security descriptor contains the security information.
; Return values : True indicates success, false to failure.
; Author    : Pusofalse
; =======================================================================
Func _SetFileSecurity($sFile, $iSecurLevel, $pSecurDesc)
    Local $iResult

    If Not _IsNtfs($sFile) Then Return SetError(@error, 0, 0)
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)

    $iResult = DllCall("Advapi32.dll", "int", "SetFileSecurity", _
            "str", $sFile, "int", $iSecurLevel, "ptr", $pSecurDesc)
    Return SetError(_GetLastError(), 0, $iResult[0] <> 0)
EndFunc ;==>_SetFileSecurity

; #### FUNCTION ####
; =======================================================================
; Name  : _SetFileSecurityOwner
; Description   : This function sets the owner for a file.
; Parameter(s)  : $sFile    - File name.
;       : $sOwner   - Owner name, in string format.
; Return values : True indicates success, false to failure, in which case, @error is set to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _SetFileSecurityOwner($sFile, $sOwner)
    Local $iResult, $iSysError, $pOwner, $pSecurDesc

    If Not _IsNtfs($sFile) Then Return SetError(@error, 0, 0)
    If $sOwner Then
        $pOwner = _LookupAccountName($sOwner)
        If Not _IsValidSid($pOwner) Then Return SetError(@error, 0, 0)
    EndIf
    $pSecurDesc = _InitializeSecurityDescriptor()
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)
    If Not _SetSecurityDescriptorOwner($pSecurDesc, $pOwner) Then Return SetError(@error, 0, 0)
    $iResult = _SetFileSecurity($sFile, 1, $pSecurDesc)
    $iSysError = @error
    If $sOwner Then _HeapFree($pOwner)
    Return SetError($iSysError, _HeapFree($pSecurDesc), $iResult)
EndFunc ;==>_SetFileSecurityOwner

; #### FUNCTION ####
; =======================================================================
; Name  : _SetFileSecurityDacl
; Description   : This function is used to set the DACL security information for a file object.
; Parameter(s)  : $sFile    - The file name which the security information is set.
;       : $aAccess  - See _SetEntriesInAcl function for details.
;       : $pOldDacl - A existing pointer to an access control list, can be zero.
; Return values : If succeeds, returns true, else returns false.
; Author    : Pusofalse
; =======================================================================
Func _SetFileSecurityDacl($sFile, $aAccess, $pOldDacl = 0)
    Local $iResult, $pDacl, $pSecurDesc, $iSysError

    If Not _IsNtfs($sFile) Then Return SetError(@error, 0, 0)
    If IsArray($aAccess) Then
        $pDacl = _SetEntriesInAcl($aAccess, $pOldDacl)
    EndIf
    If $pDacl = 0 Then Return SetError(@error, 0, 0)
    If Not _IsValidAcl($pDacl) Then Return SetError(@error, 0, 0)
    $pSecurDesc = _InitializeSecurityDescriptor()
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)
    If Not _SetSecurityDescriptorDacl($pSecurDesc, $pDacl) Then Return SetError(@error, 0, 0)
    $iResult = _SetFileSecurity($sFile, 4, $pSecurDesc)
    $iSysError = @error
    _LsaLocalFree($pDacl)
    Return SetError($iSysError, _HeapFree($pSecurDesc), $iResult)
EndFunc ;==>_SetFileSecurityDacl

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaMakeLong64
; Description   : Makes an int64 value by using double 4 bytes values.
; Parameter(s)  : $iLow - The low order double word.
;       : $iHigh    - The high order double word.
; Return values : An int64 value (8 bytes).
; Author    : Pusofalse
; =======================================================================
Func _LsaMakeLong64($iLow, $iHigh)
    Local $tDword, $pDword, $tLong64

    $tDword = DllStructCreate("dword;dword")
    $pDword = DllStructGetPtr($tDword)
    $tLong64 = DllStructCreate("int64", $pDword)
    DllStructSetData($tDword, 1, $iLow)
    DllStructSetData($tDword, 2, $iHigh)
    Return DllStructGetData($tLong64, 1)
EndFunc ;==>_LsaMakeLong64

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaLoLong64
; Description   : Obtains the low order double words from an int64 value.
; Parameters    : $iLong64  - An value with 8 bytes.
; Return values : The value with double words, in lower order.
; Author    : Pusofalse
; =======================================================================
Func _LsaLoLong64($iLong64)
    Local $tLong64, $pLong64, $tDword

    $tLong64 = DllStructCreate("int64")
    $pLong64 = DllStructGetPtr($tLong64)
    $tDword = DllStructCreate("dword;dword", $pLong64)
    DllStructSetData($tLong64, 1, $iLong64)
    Return DllStructGetData($tDword, 1)
EndFunc ;==>_LsaLoLong64

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaHiLong64
; Description   : Obtains the high order double words from an 8 bytes (int64) value.
; Parameter(s)  : $iLong64 - A value with 8 bytes.
; Return values : A DWORD value (4 bytes), in high order, of the $iLong64.
; Author    : Pusofalse
; =======================================================================
Func _LsaHiLong64($iLong64)
    Local $tLong64, $pLong64, $tDword

    $tLong64 = DllStructCreate("int64")
    $pLong64 = DllStructGetPtr($tLong64)
    $tDword = DllStructCreate("dword;dword", $pLong64)
    DllStructSetData($tLong64, 1, $iLong64)
    Return DllStructGetData($tDword, 2)
EndFunc ;==>_LsaHiLong64

; #### FUNCTION ####
; =======================================================================
; Name  : _CreateProcessWithLogon
; Description   : Creates a new process and its primary thread. Then the new process runs the specified executable file in the security context of the specified credentials (user, domain, and password). It can optionally load the user profile for a specified user.
; Parameter(s)  : $sUser    - The name of the user. This is the name of the user account to log on to. If you use the UPN format, user@DNS_domain_name, the $sDomain parameter must be NULL. The user account must have the Log On Locally permission on the local computer. This permission is granted to all users on workstations and servers, but only to administrators on domain controllers.
;       : $sPswd    - The clear-text password for the $sUser user account.
;       : $sDomain  - The name of the domain or server whose account database contains the $sUser account. If this parameter is NULL, the user name must be specified in UPN format.
;       : $sApp - The application to execute.
;       : $sArg - Arguments pass to the $sApp, if no arguments, this parameter can be NULL.
;       : $hProcess - A variable receives the handle to the new process.
;       : $hThread  - A variable receives the handle to the new process's primary thread.
;       : $iCreation    - A value indicates the priority class of the new process, default to NORMAL PRIORITY.
;       : $sWorkingDir  - Specifies the working directory, if NULL or empty string specified, the path of $sApp is used.
;       : $iState   - Initial state, see @SW_* macros for a possible value, default to @SW_SHOWNORMAL.
; Return values : If succeeds, the return value is the identifier of the newly created process, $hProcess and $hThread parameters receive the handle to the new process and its primary thread, when you no longer use the handle, call _LsaCloseHandle function to close them. If fails, the return value is set to zero, and @error is set to a system error code, pass to FormatMessage API function for an explicit error message.
; Author    : Pusofalse
; =======================================================================
Func _CreateProcessWithLogon($sUser, $sPswd, $sDomain, $sApp, $sArg, ByRef $hProcess, ByRef $hThread, $iCreation = 0x20, $sWorkingDir = "", $iState = -1)
    Local $tStp, $pStp, $tWD, $pWD, $tPS, $pPS, $iResult, $iSysError, $iPid, $vNul, $iMask = 4

    $tPS = DllStructCreate("hWnd[2];dword[2]")
    $pPS = DllStructGetPtr($tPS)

    If $iState <> -1 Then $iMask = bitOR($iMask, 1)
    $tStp = DllStructCreate("dword;ptr[3];dword[8];short[2];ptr;hWnd[3]")
    $pStp = DllStructGetPtr($tStp)
    DllStructSetData($tStp, 1, DllStructGetSize($tStp))
    DllStructSetData($tStp, 3, $iMask, 8)
    DllStructSetData($tStp, 3, 77, 1)
    DllStructSetData($tStp, 3, 77, 2)
    If $iState <> -1 Then DllStructSetData($tStp, 4, $iState, 1)

    If $sWorkingDir And FileExists($sWorkingDir) Then
        $tWD = DllStructCreate("wchar[" & StringLen($sWorkingDir) + 2 & "]")
        $pWD = DllStructGetPtr($tWD)
        DllStructSetData($tWD, 1, $sWorkingDir)
    EndIf

    If $iCreation = Default Then $iCreation = 0x20
    $iResult = DllCall("Advapi32.dll", "int", "CreateProcessWithLogonW", _
            "wstr", $sUser, "wstr", $sDomain, "wstr", $sPswd, _
            "dword", 1, "wstr", $sApp, "wstr", $sArg, _
            "dword", $iCreation, "ptr", 0, "ptr", $pWD, _
            "ptr", $pStp, "ptr", $pPS)
    $iSysError = _GetLastError()
    $iPid = DllStructGetData($tPS, 2, 1)
    $hProcess = DllStructGetData($tPS, 1, 1)
    $hThread = DllStructGetData($tPS, 1, 2)
    $vNul = _FreeVariable($tPS) And _FreeVariable($tSTP) And _FreeVariable($tWD)
    Return SetError($iSysError, $iResult[0], $iPid)
EndFunc ;==>_CreateProcessWithLogon

; #### FUNCTION ####
; =======================================================================
; This function is internal used to open a registry key and retrieve its handle.
; =======================================================================
Func _RegOpenKeyEx($hMainKey, $sSubKey, $iAccess)
    Local $iResult
    $iResult = DllCall("Advapi32.dll", "long", "RegOpenKeyEx", _
            "hWnd", $hMainKey, "str", $sSubKey, _
            "dword", 0, "long", $iAccess, "hWnd*", 0)
    Return SetError($iResult[0], 0, $iResult[5])
EndFunc ;==>_RegOpenKeyEx

; #### FUNCTION ####
; =======================================================================
; The function is internal used to close the handle to the registry key opened by the previous call to _RegOpenKeyEx function.
; =======================================================================
Func _RegCloseKey($hKey)
    Local $iResult = DllCall("Advapi32.dll", "long", "RegCloseKey", "hWnd", $hKey)
    Return SetError($iResult[0], 0, $iResult[0] = 0)
EndFunc ;==>_RegCloseKey

; #### FUNCTION ####
; =======================================================================
; Name  : _RegSetKeySecurity
; Description   : This function sets the security of the registry key.
; Parameter(s)  : $hMainKey - A handle to the predefined key, see HKEY_* constants (defined in constants.au3) for a value.
;       : $sSubKey  - A string contains the sub-key name.
;       : $iSecurLevel  - Security information to be set.
;       : $pSecurDesc   - A pointer to a security descriptor.
; Return values : True indicates success, false to failure, in which case, @error is set to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _RegSetKeySecurity($hMainKey, $sSubKey, $iSecurLevel, $pSecurDesc)
    Local $hKey, $hToken, $iAccessMask = $WRITE_DAC, $v_Null

    If bitAND($iSecurLevel, $OWNER_SECURITY_INFORMATION) Then
        Local $aPriv[1][2] = [[$SE_RESTORE_NAME, 2]]
        $hToken = _OpenProcessToken(-1)
        _AdjustTokenPrivileges($hToken, $aPriv)
        $v_Null = _FreeVariable($aPriv) And _LsaCloseHandle($hToken)
        $iAccessMask = bitOR($iAccessMask, $WRITE_OWNER)
    EndIf
    If bitAND($iSecurLevel, $SACL_SECURITY_INFORMATION) Then
        Local $aPriv[1][2] = [[$SE_SECURITY_NAME, 2]]
        $hToken = _OpenProcessToken(-1)
        _AdjustTokenPrivileges($hToken, $aPriv)
        $v_Null = _FreeVariable($aPriv) And _LsaCloseHandle($hToken)
        $iAccessMask = bitOR($iAccessMask, $ACCESS_SYSTEM_SECURITY)
    EndIf
    $hKey = _RegOpenKeyEx($hMainKey, $sSubKey, $iAccessMask)
    If Not $hKey Then Return SetError(@error, 0, 0)

    $iResult = DllCall("Advapi32.dll", "long", "RegSetKeySecurity", _
            "hWnd", $hKey, "int", $iSecurLevel, "ptr", $pSecurDesc)
    _RegCloseKey($hKey)
    Return SetError($iResult[0], 0, $iResult[0] = 0)
EndFunc ;==>_RegSetKeySecurity

; #### FUNCTION ####
; =======================================================================
; Name  : _RegSetKeySecurityDacl
; Description   : The function sets the access rights for a registry key.
; Parameter(s)  : $hMainKey - A handle to the registry main key, predefined in constants.au3, prefix - HKEY_*.
;       : $sSubKey  - A string contains the sub key name.
;       : $aAccess  - See _SetEntriesInAcl function for the format of the this parameter.
;       : $pOldAcl  - A pointer to an existing access control list, can be zero.
; Return values : If succeeds, returns true, else returns false, and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _RegSetKeySecurityDacl($hMainKey, $sSubKey, $aAccess, $pOldAcl = 0)
    Local $pNewAcl, $iResult, $iSysError

    $pNewAcl = _SetEntriesInAcl($aAccess, $pOldAcl)
    If Not _IsValidAcl($pNewAcl) Then Return SetError(@error, 0, 0)
    $pSecurDesc = _InitializeSecurityDescriptor()
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)
    If Not _SetSecurityDescriptorDacl($pSecurDesc, $pNewAcl) Then
        Return SetError(@error, 0, 0)
    EndIf
    $iResult = _RegSetKeySecurity($hMainKey, $sSubKey, 4, $pSecurDesc)
    $iSysError = @error
    _HeapFree($pSecurDesc)
    _LsaLocalFree($pNewAcl)
    Return SetError($iSysError, 0, $iSysError = 0)
EndFunc ;==>_RegSetKeySecurityDacl

; #### FUNCTION ####
; =======================================================================
; Name  : _RegSetKeySecurityOwner
; Description   : This function is used to set the owner of the registry key.
; Parameter(s)  : $hMainKey - A handle to the registry main key.
;       : $sSubKey  - Sub key name.
;       : $sOwner       - Owner of the registry key, in string format.
; Return values : Returns true if succeeds, else returns false and sets @error.
; Author    : Pusofalse
; =======================================================================
Func _RegSetKeySecurityOwner($hMainKey, $sSubKey, $sOwner)
    Local $pSid, $iResult, $iSysError, $pSecurDesc, $v_Null

    $pSid = _LookupAccountName($sOwner)
    If Not _IsValidSid($pSid) Then Return SetError(@error, 0, 0)
    $pSecurDesc = _InitializeSecurityDescriptor()
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, 0)
    If Not _SetSecurityDescriptorOwner($pSecurDesc, $pSid) Then
        Return SetError(@error, 0, 0)
    EndIf
    $iResult = _RegSetKeySecurity($hMainKey, $sSubKey, 1, $pSecurDesc)
    $iSysError = @error
    $v_Null = _HeapFree($pSid) And _HeapFree($pSecurDesc)
    Return SetError($iSysError, 0, $iSysError = 0)
EndFunc ;==>_RegSetKeySecurityOwner

; #### FUNCTION ####
; =======================================================================
; Name  : _RegGetKeySecurity
; Description   : This function retrieves the security information of the registry key.
; Parameter(s)  : $hMainKey - The handle to the registry main key.
;       : $sSubKey  - A string contains the sub key name.
;       : $iSecurLevel  - Security information to retrieve.
; Return values : If succeeds, returns a pointer to a security descriptor, if fails, @error is set to non-zero.
; Author    : Pusofalse
; =======================================================================
Func _RegGetKeySecurity($hMainKey, $sSubKey, $iSecurLevel = 4)
    Local $iResult, $iAccessMask = $READ_CONTROL, $v_Null, $pSecurDesc, $hKey

    If bitAND($iSecurLevel, $SACL_SECURITY_INFORMATION) Then
        Local $aPriv[1][2] = [[$SE_SECURITY_NAME, 2]], $hToken
        $iAccessMask = bitOR($iAccessMask, $ACCESS_SYSTEM_SECURITY)
        $hToken = _OpenProcessToken(-1)
        _AdjustTokenPrivileges($hToken, $aPriv)
        $v_Null = _LsaCloseHandle($hToken) And _FreeVariable($aPriv)
    EndIf

    $hKey = _RegOpenKeyEx($hMainKey, $sSubKey, $iAccessMask)
    If Not $hKey Then Return SetError(@error, 0, 0)

    $iResult = DllCall("Advapi32.dll", "long", "RegGetKeySecurity", _
            "hWnd", $hKey, "int", $iSecurLevel, _
            "ptr", 0, "dword*", 0)
    $pSecurDesc = _HeapAlloc($iResult[4])
    $iResult = DllCall("Advapi32.dll", "long", "RegGetKeySecurity", _
            "hWnd", $hKey, "int", $iSecurLevel, _
            "ptr", $pSecurDesc, "dword*", $iResult[4])
    _RegCloseKey($hKey)
    Return SetError($iResult[0], 0, $pSecurDesc)
EndFunc ;==>_RegGetKeySecurity

; #### FUNCTION ####
; =======================================================================
; Name  : _RegGetKeySecurityOwner
; Description   : Retrieves the owner of the registry key.
; Parameter(s)  : $hMainKey - A value of predefined handle to the registry key.
;       : $sSubKey  - Sub key name.
; Return values : If succeeds, returns a string contains the owner name, if fails, @error is set to a system error code.
; =======================================================================
Func _RegGetKeySecurityOwner($hMainKey, $sSubKey)
    Local $pOwner, $sOwner, $pSecurDesc, $iSysError

    $pSecurDesc = _RegGetKeySecurity($hMainKey, $sSubKey, $OWNER_SECURITY_INFORMATION)
    $iSysError = @error
    $pOwner = _GetSecurityDescriptorOwner($pSecurDesc)
    $sOwner = _LookupAccountSid($pOwner)
    Return SetError($iSysError, _HeapFree($pSecurDesc), $sOwner)
EndFunc ;==>_RegGetKeySecurityOwner

; <-- inheritance question
; #### FUNCTION ####
; =======================================================================
; Name  : _RegGetKeySecurityDacl
; Description   : This function retrieves an array that contains the access rights of the specified registry key.
; Parameter(s)  : $hMainKey - A predefined value to the registry main key.
;       : $fRecur   - A BOOL value indicates the recursion of the registry keys, see Remarks section for defails.
; Return values : If succeeds, the format of the return value is same to _GetExplicitEntriesFromAcl's.
; Author    : Pusofalse
; Remarks   : If $fRecur parameter is false, the function does not work on keys which have the inheritance options, for example: _RegGetKeySecurityDacl(0x80000002, "SOFTWARE\Test", false), If the access rights of the key of Test is inherited from SOFTWARE parent key, the function does not work well, in this case, set $fRecur to True, then re-call the function, the function retrieves the access rights of the SOFTWARE key, @extended is a pointer to a NULL-terminated string that contains the actual key name from which the access rights is retrieved. _GetNamedSecurityInfo and _GetSecurityInfo functions can also return the access rights of a registry key object, but does not return the access rights of the key has inheritance options too.
; =======================================================================
Func _RegGetKeySecurityDacl($hMainKey, $sSubKey, $fRecur = false)
    Local $iResult, $pSecurDesc, $iSysError, $aAceList[1][5] = [[0]], $aSubKey

    $sSubKey = StringRegExpReplace($sSubKey, "\\+", "\\")
    $pSecurDesc = _RegGetKeySecurity($hMainKey, $sSubKey, 4)
    $iSysError = @error
    If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError($iSysError, 0, $aAceList)
    $pDacl = _GetSecurityDescriptorDacl($pSecurDesc)
    $iSysError = @error
    If Not _IsValidAcl($pDacl) Then Return SetError($iSysError, 0, $aAceList)
    $aAceList = _GetExplicitEntriesFromAcl($pDacl)
    _HeapFree($pSecurDesc)
    If $aAceList[0][0] = 0 AND $fRecur = True Then
        $aSubKey = StringSplit($sSubKey, "\")
        $sSubKey = ""
        For $i = 1 to $aSubKey[0] - 1
            $sSubKey &= $aSubKey[$i] & "\"
        Next
        $sSubKey = StringTrimRight($sSubKey, 1)
        $aAceList = _RegGetKeySecurityDacl($hMainKey, $sSubKey, 1)
        Return SetExtended(@extended, $aAceList)
    Else
        Local $pKeyAddr, $tKeyAddr
        $pKeyAddr = _HeapAlloc(StringLen($sSubKey) * 2 + 2)
        $tKeyAddr = DllStructCreate("wchar[" & StringLen($sSubKey) * 2 + 2 & "]", $pKeyAddr)
        DllStructSetData($tKeyAddr, 1, $sSubKey)
        Return SetExtended($pKeyAddr, $aAceList)
    EndIf
EndFunc ;==>_RegGetKeySecurityDacl

; #### FUNCTION ####
; =======================================================================
; Name  : _RegGetKeySecuritySacl
; Description   : Retrieves the system access control list (SACL) of the specified registry key.
; Parameter(s)  : $hMainKey - A value of predefined handle to the registry main key.
;       : $sSubKey  - Sub key name.
; Return values : The format of the return value is same to the _RegGetKeySecurityDacl function.
; Author    : Pusofalse
; =======================================================================
Func _RegGetKeySecuritySacl($hMainKey, $sSubKey)
    Local $aAceList, $pSecurDesc, $pSACL, $iSysError

    $pSecurDesc = _RegGetKeySecurity($hMainKey, $sSubKey, $SACL_SECURITY_INFORMATION)
    $iSysError = @error
    $pSACL = _GetSecurityDescriptorSacl($pSecurDesc)
    $aAceList = _GetExplicitEntriesFromAcl($pSACL)
    Return SetError($iSysError, _HeapFree($pSecurDesc), $aAceList)
EndFunc ;==>_RegGetKeySecuritySacl

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaEnumerateLogonSessions
; Description   : This function lists the logon sessions.
; Parameter(s)  : This function has no parameters.
; Return values : If succeeds, returns an array in the following format:
;       : - $aArray[0][0] - The number of returned logon sessions.
;       : - $aArray[1][0] - The low part of the first LUID (locally unique identifier).
;       : - $aArray[1][1] - The high part of the first LUID.
;       : - $aArray[n][0] - The low part of the Nth LUID.
;       : - ... ...
; Author    : Pusofalse
; =======================================================================
Func _LsaEnumerateLogonSessions()
    Local $tLuid, $iResult, $aResult[1][9], $aTemp

    $iResult = DllCall("Secur32.dll", "long", "LsaEnumerateLogonSessions", "ulong*", 0, "ptr*", 0)
    If $iResult[0] Then Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $aResult)

    Redim $aResult[$iResult[1] + 1][9]
    $aResult[0][0] = $iResult[1]
    For $i = 1 to $iResult[1]
        $tLuid = DllStructCreate($tagLUID, $iResult[2])
        $aResult[$i][0] = DllStructGetData($tLuid, "Low")
        $aTemp = _LsaGetLogonSessionData($aResult[$i][0])
        For $g = 1 to 8
            $aResult[$i][$g] = $aTemp[$g]
        Next
        $iResult[2] += 8
    Next
    Return SetExtended(_LsaFreeReturnBuffer($iResult[2]), $aResult)
EndFunc ;==>_LsaEnumerateLogonSessions

Func _LsaGetLogonSessionData($iLuidLow, $iLuidHigh = 0)
    Local $pLuid, $iResult, $tSession, $pSession, $aResult[9], $tagSession, $pSid

    $pLuid = _InitializeLuid($iLuidLow, $iLuidHigh)
    $iResult = DllCall("Secur32.dll", "long", "LsaGetLogonSessionData", "ptr", $pLuid, "ptr*", 0)
    If $iResult[0] Then Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $aResult)

    $tagSession = "ulong;dword;long;ushort[2];ptr;ushort[2];ptr;ushort[2];ptr;ulong[2];" & _
                "ptr;int64;ushort[2];ptr;ushort[2];ptr;ushort[2];ptr"

    $tSession = DllStructCreate($tagSession, $iResult[2])
    $aResult[0] = DllStructGetData($tSession, 2)    ; LogonID
    $aResult[1] = _LsaInitializeBufferW(DllStructGetPtr($tSession, 4), 1)   ; UserName
    $aResult[2] = _LsaInitializeBufferW(DllStructGetPtr($tSession, 6), 1)   ; Domain
    If $aResult[2] Then $aResult[1] = $aResult[2] & "\" & $aResult[1]   ;
    $aResult[2] = _LsaInitializeBufferW(DllStructGetPtr($tSession, 8), 1)   ; Package
    $aResult[3] = _LsaInitializeBufferW(DllStructGetPtr($tSession, 13), 1)  ; Server
    $aResult[4] = _LsaInitializeBufferW(DllStructGetPtr($tSession, 15), 1)  ; DnsDomain
    $aResult[5] = _LsaInitializeBufferW(DllStructGetPtr($tSession, 17), 1)  ; Upn
    $aResult[6] = DllStructGetData($tSession, 10, 1)        ; LogonType
    $pSid = _CopySid(DllStructGetData($tSession, 11))   ;
    $aResult[7] = _ConvertSidToStringSid($pSid) ; SID
    $aResult[8] = DllStructGetData($tSession, 12)   ; LogonTime
    _LsaFreeReturnBuffer($iResult[2])
    Return SetExtended(_HeapFree($pSid)*_HeapFree($pLuid), $aResult)
EndFunc ;==>_LsaGetLogonSessionData

Func _LsaEnumerateLogonSessionProcesses($iLuidLow, $iLuidHigh = 0)
    Local $iLuid, $iResult, $tBuffer, $pBuffer, $aProcess, $aProc[1][2]
    Local $hToken, $aPriv[1][2] = [[$SE_DEBUG_NAME, 2]], $aStatic

    $hToken = _OpenProcessToken(-1)
    If Not _IsPrivilegeEnabled($hToken, $SE_DEBUG_NAME) Then
        _AdjustTokenPrivileges($hToken, $aPriv)
    EndIf
    _LsaCloseHandle($hToken)
    $iLuid = _LsaMakeLong64($iLuidLow, $iLuidHigh)

    $aProcess = ProcessList()
    For $i = 1 to $aProcess[0][0]
        $hToken = _OpenProcessToken($aProcess[$i][1], $TOKEN_QUERY)
        If $hToken = 0 Then ContinueLoop
        $aStatic = _GetTokenStatistics($hToken)
        _LsaCloseHandle($hToken)
        If $aStatic[1] = $iLuid Then
            $aProc[0][0] += 1
            Redim $aProc[$aProc[0][0] + 1][2]
            $aProc[$aProc[0][0]][0] = $aProcess[$i][1]
            $aProc[$aProc[0][0]][1] = $aProcess[$i][0]
        EndIf
    Next
    Return SetExtended(_FreeVariable($aProcess), $aProc)
EndFunc ;==>_LsaEnumerateLogonSessionProcesses

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaGetUserName
; Description   : Retrieves the name of the currently logged user account or user associated to the current thread.
; Parameter(s)  : $fThread  - If true, the function returns the user name associated to the current thread. If false, the function returns the name of the currently logged user account. Default to True.
; Return values : If succeeds, returns user name requested, else returns NULL and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _LsaGetUserName($fThread = True)
    Local $aPriv[1][2] = [[$SE_DEBUG_NAME, 2]]
    Local $iPid, $hToken, $pSid, $sUserName, $hProcess

    If $fThread = True or Not ProcessExists("explorer.exe") Then
        $pSid = _LookupAccountName(@UserName)
        $sUserName = _LookupAccountSid($pSid)
        Return SetError(@error, _HeapFree($pSid), $sUserName)
    Else
        $iPid  = ProcessExists("explorer.exe")
        If $iPid = 0 Then
            $pSid = _LookupAccountName(@UserName)
            $sUserName = _LookupAccountSid($pSid)
            Return SetError(@error, _HeapFree($pSid), $sUserName)
        EndIf
        $hToken = _OpenProcessToken(-1)
        _AdjustTokenPrivileges($hToken, $aPriv)
        _LsaCloseHandle($hToken)
        $hProcess = _OpenProcess("explorer.exe", $READ_CONTROL)
        $sUserName = _QueryKernelObjectSecurityOwner($hProcess)
        Return SetError(@error, _LsaCloseHandle($hProcess), $sUserName)
    EndIf
EndFunc ;==>_LsaGetUserName

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaGetUserRelativeID
; Description   : Retrieves a string that indicates the user right, such as an administrator or guest or other.
; Parameter(s)  : $sUserName    - The name of the user account to determine.
;       : $sSystem  - The system where the $sUserName lies in, default is local.
; Return values : If succeeds, return value is one of the following string:
;       :   NONE$ - indicates an user that has no any user rights.
;       :   ADMINISTRATOR$ - indicates an administrator.
;       :   USER$ - indicates an user.
;       :   GUEST$ - indicates a guest.
;       :   POWER_USER$ - indicates a power user.
;       :   ACCOUNT_OPS$ - indicates an user that permits control over nonadministrator accounts.
;       :   SYSTEM_OPS$ - indicates an user that can perform system administrative functions, not including security functions. It establishes network shares, controls printers, unlocks workstations, and performs other operations.
;       :   PRINT_OPS$ - indicates an user that can control printers and print queues.
;       :   BACKUP_OPS$ - indicates an user used for controlling assignment of file backup-and-restore privileges.
;       :   REPLICATOR$ - indicates an user used only by the system.
;       :   UDR_*_*...$ - indicates an user that has the user-defined user rights, see Remarks section for details.
;       : If fails, returns a character $, if $sUserName does not exist in $sSystem, the function returns this value.
; Author    : Pusofalse
; Remarks   : If the $sUserName belongs 2 or more user groups, such as ADMINISTRATORS and POWER USERS, this function returns ADMINISTRATOR$ but not POWER_USER$, because the user rights of ADMINISTRATORS is higher than POWER USERS's, this is the case that the $sUserName belongs a well-known user group. Another case, if $sUserName belongs an user-created user group, the prefix of return value is set to UDR, in which case, the middle part of the return value is _*_*..., where * is the user group name the $sUserName belongs, the number of _* depends on the number of the user groups the $sUserName belongs. If $sUserName does not belong any user group, the return value is set to NONE$, you can determine the user rights by calling the _LsaEnumerateAccountRights function. The suffix of the return value of this function is always set to $.
; =======================================================================
Func _LsaGetUserRelativeID($sUserName, $sSystem = "")
    Local $iResult, $pSid, $iType, $aGroup, $iRid, $iHiRid = 0x1F0FFF

    $pSid = _LookupAccountName($sUserName, $sSystem)
    $iType = @extended
    If Not _IsValidSid($pSid) Then Return SetError(@error, 0, "$")
    If $iType <> $SID_IS_USER Then Return SetError(87, 0, "$")
    If _GetSidSubAuthorityCount($pSid) <> 5 Then Return SetError(87, 0, "$")
    $aGroup = _LsaLocalUserGetGroups($sUserName, $sSystem)
    If $aGroup[0] = 0 Then Return SetError(@error, 1, "NONE$")

    For $i = 1 to $aGroup[0]
        $pSid = _LookupAccountName($aGroup[$i], $sSystem)
        $iRid = _GetSidSubAuthority($pSid, 1)
        If $iRid < $iHiRid Then $iHiRid = $iRid
        _HeapFree($pSid)
    Next
    Switch $iHiRid
    Case $DOMAIN_ALIAS_RID_ADMINS
        Return "ADMINISTRATOR$"
    Case $DOMAIN_ALIAS_RID_USERS
        Return "USER$"
    Case $DOMAIN_ALIAS_RID_GUESTS
        Return "GUEST$"
    Case $DOMAIN_ALIAS_RID_POWER_USERS
        Return "POWER_USER$"
    Case $DOMAIN_ALIAS_RID_ACCOUNT_OPS
        Return "ACCOUNT_OPS$"
    Case $DOMAIN_ALIAS_RID_SYSTEM_OPS
        Return "SYSTEM_OPS$"
    Case $DOMAIN_ALIAS_RID_PRINT_OPS
        Return "PRINT_OPS$"
    Case $DOMAIN_ALIAS_RID_BACKUP_OPS
        Return "BACKUP_OPS$"
    Case $DOMAIN_ALIAS_RID_REPLICATOR
        Return "REPLICATOR$"
    Case Else
        Local $sGroup
        For $i = 1 to $aGroup[0]
            $sGroup &= "_" & $aGroup[$i]
        Next
        Return "UDR" & StringUpper($sGroup) & "$"
    EndSwitch
EndFunc ;==>_LsaGetUserRelativeID

; #### FUNCTION ####
; =======================================================================
; This function is internal used, used to release the memory allocated by the other functions.
; =======================================================================
Func _LsaFreeReturnBuffer($pBuffer)
    Local $iResult
    $iResult = DllCall("Secur32.dll", "int", "LsaFreeReturnBuffer", "ptr", $pBuffer)
    Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $iResult[0] = 0)
EndFunc ;==>_LsaFreeReturnBuffer

; #### FUNCTION ####
; =======================================================================
; Name  : _DuplicateTokenEx
; Description   : This function creates a new access token that duplicates an existing token.
; Parameter(s)  : $hToken   - A handle to the access token to be duplicated.
;       : $iAccess  - A value of access mask value, zero indicates the same access rights.
;       : $pSecurAttr   - A pointer to a SECURITY_ATTRIBUTES that specifies a security descriptor for the new access token and determines whether child processes can inherit the token. This parameter can be zero.
;       : $iLevel   - Indicates the impersonation level of the new token, or -1 indicates the same level.
;       : $iType    - Specifies the type of the new access token, a value of 1 indicates the token is a primary token, a value of 2 indicates the token is a impersonation token. Use -1 to use the same type.
; Return values : If succeeds, returns an handle to the duplicated access token, else returns zero and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _DuplicateTokenEx($hToken, $iAccess = 0, $pSecurAttr = 0, $iLevel = -1, $iType = -1)
    Local $iResult

    If $iType = -1 Then $iType = _GetTokenType($hToken)
    If $iLevel = -1 Then $iLevel = _GetTokenImpersonationLevel($hToken)

    $iResult = DllCall("Advapi32.dll", "int", "DuplicateTokenEx", _
            "hWnd", $hToken, "dword", $iAccess, _
            "ptr", $pSecurAttr, "int", $iLevel, _
            "dword", $iType, "hWnd*", 0)
    Return SetError(_GetLastError(), 0, $iResult[6])
EndFunc ;==>_DuplicateTokenEx

; #### FUNCTION ####
; =======================================================================
; Name  : _CheckTokenMembership
; Description   : Determines whether a specified security identifier (SID) is enabled in an access token.
; Parameter(s)  : $hToken   - A handle to the access token.
;       : $pSid - A pointer to a SID or an user account name string.
; Return values : If the SID is present and has the SE_GROUP_ENABLED attribute, returns TRUE; otherwise, it returns FALSE.
; Author    : Pusofalse
; =======================================================================
Func _CheckTokenMembership($hToken, $pSid)
    Local $iResult

    If Not _IsValidSid($pSid) Then
        $pSid = _LookupAccountName($pSid)
        If Not _IsValidSid($pSid) Then Return SetError(@error, 0, 0)
    EndIf
    If _GetTokenType($hToken) = 1 Then $hToken = _DuplicateTokenEx($hToken, 0, 0, 1, 2)
    If Not $hToken Then Return SetError(@error, 0, 0)

    $iResult = DllCall("Advapi32.dll", "int", "CheckTokenMembership", _
            "hWnd", $hToken, "ptr", $pSid, "int*", 0)
    Return SetError(_GetLastError(), 0, $iResult[3])
EndFunc ;==>_CheckTokenMembership

; #### FUNCTION ####
; =======================================================================
; Name  : _LookupPrivilegeOnLoggedUser
; Description   : Check 2 access control lists (ACLs), and retrieve one of them which contains higher user rights for currently logged user account.
; Parameters    : $pAcl1    - The first pointer to an access control list.
;       : $pAcl2    - The second pointer to an access control list.
; Return values : If $pAcl1 is an invalid ACL pointer, the function sets @error to ERROR_INVALID_ACL, @extended is set to 1. -
;       : + If $pAcl2 is an invalid ACL pointer, the function sets @error to ERROR_INVALID_ACL either, @extend is set to 2. -
;       : + If either $pAcl1 or $pAcl2 is not a valid ACL pointer, the return value is zero. -
;       : + If the function cannot obtain the SID of the current user account, sets @error to ERROR_INVALID_SID. -
;       : + If neither $pAcl1 nor $pAcl2 enables any user right (or enables DENY- USER RIGHT), the return value is set to zero, -
;       : + @error is set to 0, and @extended is set to -1. -
;       : + If both $pAcl1 and $pAcl2 are enables user right (GRANT - USER RIGHT), @error is set to zero, -
;       : + If $pAcl1 enables higher user rights for currently logged user than $pAcl2, the return value is same to $pAcl1, -
;       : + and sets @extended to 1. -
;       : + If the user rights enabled in $pAcl1 are same to user rights enabled in $pAcl2, the return value is same to $pAcl1 either, -
;       : + @extended is set to 2. -
;       : + If $pAcl2 obtains higher user rights for current user account than $pAcl1 obtains, the return values is same to $pAcl2, -
;       : + @extended is set to 3.
; Remarks       : Note that the $pAcl1 and $pAcl2 must both point a same security object, else, the function has no meaning.
; Author        : Pusofalse
; =======================================================================
Func _LookupPrivilegeOnLoggedUser($pAcl1, $pAcl2)
    Local $iAccessMask1, $iAccessMask2, $pSid

    If Not _IsValidAcl($pAcl1) Then Return SetError(@error, 1, 0)
    If Not _IsValidAcl($pAcl2) Then Return SetError(@error, 2, 0)

    $pSid = _LookupAccountName(@UserName)
    $iAccessMask1 = _GetEffectiveRightsFromAcl($pAcl1, $pSid)
    $iAccessMask2 = _GetEffectiveRightsFromAcl($pAcl2, $pSid)

    If ($iAccessMask1 = 0 And $iAccessMask2 = 0) Then Return SetError(0, -1, -1)

    _HeapFree($pSid)
    If $iAccessMask1 > $iAccessMask2 Then
        Return SetError(0, 1, $pAcl1)
    ElseIf $iAccessMask1 < $iAccessMask2 Then
        Return SetError(0, 2, $pAcl2)
    Else
        Return SetError(0, 3, $pAcl2)
    EndIf
EndFunc ;==>_LookupPrivilegeOnLoggedUser

; #### FUNCTION ####
; =======================================================================
; Name  : _CheckAclMemberShip
; Description   : The function checks whether the specified user account is assigned allowed access rights in specified access control list (ACL).
; Parameters    : $pAcl - A pointer to an access control list (ACL).
;       : $pSid - Either a SID pointer or a string contains the user name, in which case, $pSid must be an user on local system.
; Return values : If $pAcl is not a valid ACL pointer, sets @error to ERROR_INVALID_ACL, returns zero. -
;       : + If $pSid is not a valid SID pointer or a non-existing user account, sets @error to ERROR_INVALID_SID, and returns zero either.
;       : + If both $pAcl and $pSid are valid pointer, and user account ($pSid) is assigned allowed access rights, the return value is non-zero, otherwise returns zero.
; Author        : Pusofalse
; =======================================================================
Func _CheckAclMemberShip($pAcl, $pSid)
    Local $iResult, $iAccessMask

    If Not _IsValidAcl($pAcl) Then Return SetError(@error, 1, 0)
    If Not _IsValidSid($pSid) Then
        Local $tSid = _LookupAccountName($pSid)
        $pSid = DllStructGetPtr($tSid)
        If Not _IsValidSid($pSid) Then Return SetError(@error,2, 0)
    EndIf
    Return _GetEffectiveRightsFromAcl($pAcl, $pSid)
EndFunc ;==>_CheckAclMemberShip

Func _LogonUser($sUser, $sPswd, $sDomain, $iType, $iProvider = $LOGON32_PROVIDER_DEFAULT)
    Local $iResult
    $iResult = DllCall("Advapi32.dll", "int", "LogonUser", _
            "str", $sUser, "str", $sDomain, _
            "str", $sPswd, "dword", $iType, _
            "dword", $iProvider, "hWnd*", 0)
    Return SetError(_GetLastError(), 0, $iResult[6])
EndFunc ;==>_LogonUser

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaCreateSecret
; Description   : This function creates a secret.
; Parameter(s)  : $sSecret  - The name of the secret.
;       : $sData    - The data of the secret.
; Return values : If succeeds, returns true, else returns false and sets @error to a system error code.
; Author    : Pusofalse
; Remarks   : This function requires a call to _LsaCreateSecretManager function, else, the function fails and @error is set ERROR_FILE_NOT_FOUND (2).
; =======================================================================
Func _LsaCreateSecret($sSecret, $sData)
    Local $sGUID, $iResult, $aOrgSecur, $pNewDacl, $iLUID
    Local $iAccess = bitOR($READ_CONTROL, $WRITE_DAC)
    Local $aNewAce[1][4] = [["Everyone", 0xF003F, 1, 3]], $bCrypted, $bSid1, $bSid2
    Local $aAccess[2][4] = [["SYSTEM", 0xF003F, 1, 3], ["Administrators", $iAccess, 1, 0]]

    If _RegSetKeySecurityDacl(0x80000002, "SYSTEM\SECRET", $aNewAce) = 0 Then
        Return SetError(@error, 0, 0)
    EndIf

    $pGUID = _AllocateGUID()
    $sGUID = _StringFromGUID($pGUID)
    _HeapFree($pGUID)

    $bSid1 = _CryptProtectData(_LsaGetUserName(0))
    $bSid2 = _CryptProtectData(_LsaGetUserName(1))
    $bCrypted = _CryptProtectData($sData)
    $bSecret = _CryptProtectData($sSecret)
    If RegWrite("HKLM\SYSTEM\SECRET\" & $sGUID, "Name", "Reg_Binary", $bSecret) = 0 Then Return SetError(@error, -1, 0)
    If RegWrite("HKLM\SYSTEM\SECRET\" & $sGUID, "Data", "Reg_Binary", $bCrypted) = 0 Then Return SetError(@error, 1, 0)
    If RegWrite("HKLM\SYSTEM\SECRET\" & $sGUID, "SID1", "Reg_Binary", $bSid1) = 0 Then Return SetError(@error, 2, 0)
    If RegWrite("HKLM\SYSTEM\SECRET\" & $sGUID, "SID2", "Reg_Binary", $bSid2) = 0 Then Return SetError(@error, 3, 0)

    If _RegSetKeySecurityDacl(0x80000002, "SYSTEM\SECRET", $aAccess) = 0 Then
        Return SetError(@error, 0, 0)
    EndIf
    Return 1
EndFunc ;==>_LsaCreateSecret

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaCreateSecretManager
; Description   : Creates a secrets database.
; Parameter(s)  : The function has no parameters.
; Return values : A BOOLEAN value indicates success or failure.
; Author    : Pusofalse
; =======================================================================
Func _LsaCreateSecretManager()
    Local $iResult, $iAccess = bitOR($READ_CONTROL, $WRITE_DAC), $hKey
    Local $aAccess[2][4] = [["SYSTEM", 0xF003F, 1, 3], ["Administrators", $iAccess, 1, 0]]

    $hKey = _RegOpenKeyEx(0x80000002, "SYSTEM\SECRET", $READ_CONTROL)
    If $hKey = 0 Then
        If RegWrite("HKEY_LOCAL_MACHINE\SYSTEM\SECRET") = 0 Then
            Return SetError(@error, 0, 0)
        EndIf
    Else
        _RegCloseKey($hKey)
    EndIf
    If _RegSetKeySecurityDacl(0x80000002, "SYSTEM\SECRET", $aAccess) = 0 Then
        Return SetError(@error, 0, 0)
    EndIf
    Return 1
EndFunc ;==>_LsaCreateSecretManager

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaDeleteSecret
; Description   : This function deletes a secret from secrets database.
; Parameter(s)  : $sSecret  - The name of the secret.
;       : $fAll - If true, the function deletes all secrets that has the name of the $sSecret.
; Return values : Returns true if succeeds, else returns false.
; Author    : Pusofalse
; =======================================================================
Func _LsaDeleteSecret($sSecret, $fAll = True)
    Local $sGUID, $sKey, $iResult = True
    Local $aNewAce[1][4] = [["Everyone", 0xF003F, 1, 3]]
    Local $iAccess = bitOR($READ_CONTROL, $WRITE_DAC)
    Local $aAccess[2][4] = [["SYSTEM", 0xF003F, 1, 3], ["Administrators", $iAccess, 1, 0]]

    If _RegSetKeySecurityDacl(0x80000002, "SYSTEM\SECRET", $aNewAce) = 0 Then
        Return SetError(@error, 5, "")
    EndIf

    $sGUID = _LsaGetSecret($sSecret)
    If $sGUID = "" Then Return SetError(@error, 0, 0)
    $sKey = "HKLM\SYSTEM\SECRET\" & $sGUID

    If $fAll = True Then
        Do
            $iResult = RegDelete($sKey) And $iResult
            $sGUID = _LsaGetSecret($sSecret)
            $sKey = "HKLM\SYSTEM\SECRET\" & $sGUID
        Until   $sGUID = ""
    Else
        $iResult = RegDelete($sKey)
    EndIf
    If _RegSetKeySecurityDacl(0x80000002, "SYSTEM\SECRET", $aAccess) = 0 Then
        Return SetError(@error, 5, "")
    EndIf
    Return $iResult
EndFunc ;==>_LsaDeleteSecret

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaEnumerateSecrets
; Description   : This function lists all secrets in secrets database.
; Parameter(s)  : This function has no parameters.
; Return values : Returns an array with following format:
;       : - $aArray[0] - The number of returned secrets.
;       : - $aArray[1] - The name of the first secret.
;       : - $aArray[2] - The name of the 2nd secret.
;       : - ... ...
; Author    : Pusofalse
; =======================================================================
Func _LsaEnumerateSecrets()
    Local $aResult[1] = [0], $sSecret, $iIndex
    Local $aNewAce[1][4] = [["Everyone", 0xF003F, 1, 3]]
    Local $iAccess = bitOR($READ_CONTROL, $WRITE_DAC)
    Local $aAccess[2][4] = [["SYSTEM", 0xF003F, 1, 3], ["Administrators", $iAccess, 1, 0]]

    If _RegSetKeySecurityDacl(0x80000002, "SYSTEM\SECRET", $aNewAce) = 0 Then
        Return SetError(@error, 5, $aResult)
    EndIf

    While 1
        $iIndex += 1
        $sSecret = RegEnumKey("HKLM\SYSTEM\SECRET", $iIndex)
        If @error Then ExitLoop
        $sSecret = RegRead("HKLM\SYSTEM\SECRET\" & $sSecret, "Name")
        $aResult[0] = $iIndex
        Redim $aResult[$iIndex + 1]
        $aResult[$iIndex] = _CryptUnprotectData("0x" & $sSecret)
    WEnd
    If _RegSetKeySecurityDacl(0x80000002, "SYSTEM\SECRET", $aAccess) = 0 Then
        Return SetError(@error, 5, $aResult)
    EndIf
    Return $aResult
EndFunc ;==>_LsaEnumerateSecrets

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaGetSecret
; Description   : This function retrieves the GUID of the specified secret.
; Parameter(s)  : $sSecret  - The name of the secret.
; Return values : If succeeds, returns a GUID string.
; Author    : Pusofalse
; =======================================================================
Func _LsaGetSecret($sSecret)
    Local $iResult, $iIndex = 1, $sKey = "HKLM\SYSTEM\SECRET", $iFlag = 0
    Local $aNewAce[1][4] = [["Everyone", 0xF003F, 1, 3]], $s_key, $s_name
    Local $iAccess = bitOR($READ_CONTROL, $WRITE_DAC)
    Local $aAccess[2][4] = [["SYSTEM", 0xF003F, 1, 3], ["Administrators", $iAccess, 1, 0]]

    If _RegSetKeySecurityDacl(0x80000002, "SYSTEM\SECRET", $aNewAce) = 0 Then
        Return SetError(@error, 5, "")
    EndIf

    While 1
        $s_key = RegEnumKey($sKey, $iIndex)
        If @error Then ExitLoop
        $s_name = RegRead($sKey & "\" & $s_key, "Name")
        $s_name = _CryptUnprotectData("0x" & $s_name)
        If $s_name = $sSecret Then
            $iFlag = 1
            ExitLoop
        EndIf
        $iIndex += 1
    WEnd
    If _RegSetKeySecurityDacl(0x80000002, "SYSTEM\SECRET", $aAccess) = 0 Then
        Return SetError(@error, 5, "")
    EndIf
    If $iFlag = 0 Then Return SetError(2, 0, "")
    Return SetError(0, 0, $s_key)
EndFunc ;==>_LsaGetSecret

Func _LsaQuerySecret($sSecret, $iSecretLevel = 3)
    Local $sGUID, $sName, $sData, $sUser1, $sUser2, $tSecret, $tGUID
    Local $aNewAce[1][4] = [[@UserName, 0xF003F, 1, 3]]
    Local $iAccess = bitOR($READ_CONTROL, $WRITE_DAC), $_tagSECRET
    Local $aAccess[2][4] = [["SYSTEM", 0xF003F, 1, 3], ["Administrators", $iAccess, 1, 0]]

    $sGUID = _LsaGetSecret($sSecret)
    If $sGUID = "" or @error Then Return SetError(@error, 0, 0)
    $sKey = "HKLM\SYSTEM\SECRET\" & $sGUID

    If _RegSetKeySecurityDacl(0x80000002, "SYSTEM\SECRET", $aNewAce) = 0 Then
        Return SetError(@error, 5, "")
    EndIf

    $sName = _CryptUnprotectData("0x" & RegRead($sKey, "Name"))
    $sData = _CryptUnprotectData("0x" & RegRead($sKey, "Data"))
    $sUser1 = _CryptUnprotectData("0x" & RegRead($sKey, "SID1"))
    $sUser2 = _CryptUnprotectData("0x" & RegRead($sKey, "SID2"))

    If _RegSetKeySecurityDacl(0x80000002, "SYSTEM\SECRET", $aAccess) = 0 Then
        Return SetError(@error, 5, "")
    EndIf

    $_tagSECRET = $tagSECRET & ";wchar Name[" & StringLen($sName) * 2 + 2 & "];wchar Data[" & StringLen($sData) * 2 + 2 & "]"
    $tSecret = DllStructCreate($_tagSECRET)
    DllStructSetData($tSecret, "Name", $sName)
    DllStructSetData($tSecret, "SecretName", DllStructGetPtr($tSecret, "Name"))
    If bitAND($iSecretLevel, $SECRET_INFORMATION_SID1) Then DllStructSetData($tSecret, "SID1", _LookupAccountName($sUser1))
    If bitAND($iSecretLevel, $SECRET_INFORMATION_SID2) Then DllStructSetData($tSecret, "SID2", _LookupAccountName($sUser2))
    If bitAND($iSecretLevel, $SECRET_INFORMATION_DATA) Then
        DllStructSetData($tSecret, "Data", $sData)
        DllStructSetData($tSecret, "SecretData", DllStructGetPtr($tSecret, "Data"))
    EndIf
    If bitAND($iSecretLevel, $SECRET_INFORMATION_GUID) Then
        $tGUID = _GUIDFromString($sGUID)
        DllStructSetData($tSecret, "Guid", DllStructGetData($tGUID, "Guid"))
        _FreeVariable($tGUID)
    EndIf
    Return $tSecret
EndFunc ;==>_LsaQuerySecret

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaEnumeratePrivateData
; Description   : This function lists all the private data stored in private database.
; Parameter(s)  : This function has no parameters.
; Return values : If succeeds, returns an array with following format:
;       :   - $aArray[0] - The number of entry returned.
;       :   - $aArray[1] - The name of the first private data.
;       :   - $aArray[2] - The second private data's name.
;       :   - ... ...
;       : If fails, the value of $aArray[0] is set to zero and @error is set to a system error code.
; Author    : Pusofalse
; Remarks       : This function requires the administration user right.
; =======================================================================
Func _LsaEnumeratePrivateData()
    Local $aResult[1][2] = [[0]], $aSecur, $iIndex, $sKey
    Local $aAceList[1][4] = [[@UserName, 0xF003F, 1, 3]]

    $aSecur = _GetNamedSecurityInfo("Machine\SECURITY\Policy\Secrets", 4, 4)
    If $aSecur[0] or Not _IsValidAcl($aSecur[6]) Then Return SetError($aSecur[0], 0, $aResult)
    If Not _RegSetKeySecurityDacl(0x80000002, "SECURITY\Policy\Secrets", $aAceList) Then
        Return SetError(@error, 0, $aResult)
    EndIf

    While 1
        $iIndex += 1
        $sKey = RegEnumKey("HKLM\SECURITY\Policy\Secrets", $iIndex)
        If @error Then ExitLoop
        $aResult[0][0] += 1
        Redim $aResult[$aResult[0][0] + 1][2]
        $aResult[$aResult[0][0]][0] = $sKey
        $aResult[$aResult[0][0]][1] = _LsaRetrievePrivateData($sKey, "", 1)
    WEnd
    If Not _SetNamedSecurityInfo("Machine\SECURITY\Policy\Secrets", 4, 4, 0, 0, $aSecur[6], 0) Then
        Local $a_result[1] = [0]
        Return SetError(@error, 0, $a_result)
    EndIf
    Return $aResult
EndFunc ;==>_LsaEnumeratePrivateData

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaRetrievePrivateData
; Description   : This function retrieves the private data.
; Parameter(s)  : $sKeyName - Specify the key name under which the private data is stored.
;       : $sSystem  - Specify the system on which the function executes, default to local system.
; Return values : If succeeds, the return value is the private data in binary format. If fails, returns NULL and sets @error to a system error code.
; Author    : Pusofalse
; =======================================================================
Func _LsaRetrievePrivateData($sKeyName, $sSystem = "", $fLengthOnly = 0)
    Local $hPolicy, $iResult, $pKeyName, $bData, $iSize, $tBuffer

    $hPolicy = _LsaOpenPolicy($POLICY_GET_PRIVATE_INFORMATION, $sSystem)
    If $hPolicy = 0 Then Return SetError(@error, 0, 0)

    $pKeyName = _LsaInitializeBufferW($sKeyName)
    $iResult = DllCall("Advapi32.dll", "dword", "LsaRetrievePrivateData", _
            "hWnd", $hPolicy, "ptr", $pKeyName, "ptr*", 0)
    $iSize = _LsaLocalSize($iResult[3]) - 12
    If $fLengthOnly = 1 Then
        Return SetError(_LsaNtStatusToWinError($iResult[0]), _LsaFreeMemory($iResult[3]), $iSize)
    EndIf
    $tBuffer = DllStructCreate("byte[" & $iSize & "]", $iResult[3] + 12)
    $bData = DllStructGetData($tBuffer, 1)
    _LsaClose($hPolicy)
    _FreeVariable($tBuffer)
    _HeapFree($pKeyName)
    _LsaFreeMemory($iResult[3])
    Return SetError(_LsaNtStatusToWinError($iResult[0]), $iSize, $bData)
EndFunc ;==>_LsaRetrievePrivateData

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaStorePrivateData
; Description   : This function creates the private data in private database.
; Parameter(s)  : $sKeyName - Specify the name of the private data, add one of the following prefixes to the key name:
;       :   - L$    - For local object.
;       :   - G$    - For global object.
;       :   - M$    - For computer object.
;       : $sData        - Specify the private data to store.
;       : $fProtect - If true, the function encrypts the data by using the LSA_DEDICATED_KEY1.
;       : $sSystem  - Specify the system name on which the function executes, default to local system.
; Return values : True indicates success, false indicates failure.
; Author    : Pusofalse
; =======================================================================
Func _LsaStorePrivateData($sKeyName, $sData, $fProtect = False, $sSystem = "")
    Local $hPolicy, $pKeyName, $pData, $sEntropy, $pSid

    $hPolicy = _LsaOpenPolicy($POLICY_CREATE_SECRET, $sSystem)
    If $hPolicy = 0 Then Return SetError(@error, 0, 0)

    If $fProtect = True AND IsString($sData) Then
        $sEntropy = $LSA_DEDICATED_KEY1
        $sData = _CryptProtectData($sData, $sEntropy, 0)
    EndIf

    $pKeyName = _LsaInitializeBufferW($sKeyName)
    If IsPtr($sData) = 1 Then
        $pData = $sData
    Else
        $pData = _LsaInitializeBufferW(String($sData))
    EndIf
    $iResult = DllCall("Advapi32.dll", "long", "LsaStorePrivateData", "hWnd", $hPolicy, _
            "ptr", $pKeyName, "ptr", $pData)
    $pSid = _HeapFree($pData) AND _HeapFree($pKeyName)
    Return SetError(_LsaNtStatusToWinError($iResult[0]), _LsaClose($hPolicy), $iResult[0] = 0)
EndFunc ;==>_LsaStorePrivateData

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaDestroyPrivateData
; Description   : This function is used to destroy the private data.
; Parameter(s)  : $vPrimitiveData   - Specifies the primitive data to be destroied.
; Return values : Returns the dump data if success.
; Author    : Pusofalse
; =======================================================================
Func _LsaDestroyPrivateData($vPrimitiveData)
    Local $bRandom, $hContext, $bMixEntropy

    $hContext = _CryptAcquireContext(1, 0xF0000000)
    $bRandom = _CryptGenRandom($hContext, 0x10)
    $bMixEntropy = _CryptProtectData($vPrimitiveData, $bRandom)
    Return SetError(@error, _CryptReleaseContext($hContext), $bMixEntropy)
EndFunc ;==>_LsaDestroyPrivateData

; #### FUNCTION ####
; =======================================================================
; Name  : _LsaRetrieveFingerprint
; Description   : Retrieves the global unique fingerprint of the local system.
; Parameter(s)  : This function has no parameters.
; Return values : If succeeds, returns the global unique fingerprint, otherwise returns NULL.
; Author    : Pusofalse
; Remarks   : The return value is protected by the private database, to decode the fingerprint, call _CryptUnprotectData function, the entropy that used to protect the data is 2nd LSA dedicated key.
; =======================================================================
Func _LsaRetrieveFingerprint()
    Local $iResult, $hDisk, $bData, $tSCIP, $tSCOP, $tQuery, $tDescr, $sGUID

    $bData = _LsaRetrievePrivateData("G$GlobalUniqueID")
    If $bData Then Return Binary(BinaryToString(StringReplace($bData, "00", "")))

    $hDisk = DllCall("Kernel32.dll", "long", "CreateFile", "str", "\\.\PhysicalDrive0", _
            "dword", bitOR($GENERIC_READ, $GENERIC_WRITE), "dword", 3, _
            "ptr", 0, "dword", 3, "dword", 0, "long", 0)
    If $hDisk[0] < 1 Then Return SetError(_GetLastError(), 0, "")
    $hDisk = $hDisk[0]

    $tSCIP = DllStructCreate("dword;byte[8];byte[4];dword[4];byte")
    $tSCOP = DllStructCreate("dword;byte[4];dword[2];char[1024]")
    $tQuery = DllStructCreate("int;int;byte")
    $tDescr = DllStructCreate("ulong[2];byte[4];ulong[4];int;ulong;byte")
    DllStructSetData($tQuery, 1, 0)
    DllStructSetData($tQuery, 2, 0)
    $iResult = DllCall("Kernel32.dll", "int", "DeviceIoControl", "long", $hDisk, "dword", 0x2D1400, _
            "ptr", DllStructGetPtr($tQuery), "dword", DllStructGetSize($tQuery), _
            "ptr", DllStructGetPtr($tDescr), "dword", DllStructGetSize($tDescr), _
            "dword*", 0, "ptr", 0)
    If $iResult[0] = 0 Then
        $iResult = _GetLastError()
        _LsaCloseHandle($hDisk)
        $bData = _FreeVariable($tQuery) AND _FreeVariable($tDescr)
        Return SetError($iResult, _FreeVariable($tSCOP) AND _FreeVariable($tSCIP), "")
    EndIf
    If DllStructGetData($tDescr, 4) = 2 Then
        DllStructSetData($tSCIP, 2, 0xA1, 7)
    Else
        DllStructSetData($tSCIP, 2, 0xEC, 7)
    EndIf
    $iResult = DllCall("Kernel32.dll", "int", "DeviceIoControl", "long", $hDisk, "dword", 0x7C088, _
            "ptr", DllStructGetPtr($tSCIP), "dword", DllStructGetSize($tSCIP), _
            "ptr", DllStructGetPtr($tSCOP), "dword", DllStructGetSize($tSCOP), _
            "dword*", 0, "ptr", 0)
    If $iResult[0] = 0 Then
        $iResult = _GetLastError()
        _LsaCloseHandle($hDisk)
        $bData = _FreeVariable($tQuery) AND _FreeVariable($tDescr)
        Return SetError($iResult, _FreeVariable($tSCOP) AND _FreeVariable($tSCIP), "")
    EndIf
    _FreeVariable($tSCIP)
    _FreeVariable($tDescr)
    _FreeVariable($tQuery)
    _LsaCloseHandle($hDisk)
    For $i = 21 To 40
        $sGUID &= DllStructGetData($tSCOP, 4, $i)
    Next
    $bData = _CryptProtectData($sGUID, $LSA_DEDICATED_KEY2, 4)
    If _LsaStorePrivateData("G$GlobalUniqueID", $bData) = 0 Then Return SetError(@error, 0, "")
    Return SetError(_FreeVariable($tSCOP), 0, $bData)
EndFunc ;==>_LsaRetrieveFingerprint

; #### FUNCTION ####
; ======================================================================================
; Name  : _CryptProtectData
; Description   : This function performs encryption on the data. Typically, only a user with the same logon credential as the encrypter can decrypt the data. In addition, the encryption and decryption usually must be done on the same computer.
; Parameter(s)  : $vDataToProtect   - Data to be encrypted.
;       : $vEntropy - A password or other additional entropy used to encrypt the data, can be NULL.
;       : $iFlags   - This parameter can be zero or one of the following values:
;       :   - CRYPTPROTECT_LOCAL_MACHINE (4) : When this flag is set, it associates the data encrypted with the current computer instead of with an individual user. Any user on the computer on which this function is called can use _CryptUnprotectData to decryped the data.
;       :   - CRYPTPROTECT_UI_FORBIDDEN (1) : This flag is used for remote situations where presenting a user interface (UI) is not an option. When this flag is set and a UI is specified for either the protect or unprotect operation, the operation fails and _GetLastError returns the ERROR_PASSWORD_RESTRICTION.
;       :   - CRYPTPROTECT_AUDIT (16) : This flag generates an audit on protect and unprotect operations.
;       : $sDescr   - Specifies a readable description string of the data to be encryped. This description string is included with the encrypted data. This parameter is optional, if NULL is specified, the current user's SID is used.
;       : $iPromptFlags - Specifies the flags about where and when prompts are to be displayed and what the content of those prompts should be. If zero is specified, no prompts are displayed. This parameter can be one or more of the following values:
;       :   - 0 : No prompts.
;       :   - CRYPTPROTECT_PROMPT_ON_PROTECT (2) : This flag is used to provide the prompt for the protect phase.
;       :   - CRYPTPROTECT_PROMPT_ON_UNPROTECT (1) : This flag can be combined with CRYPTPROTECT_PROMPT_ON_PROTECT to enforce the UI (user interface) policy of the caller. When _CryptUnprotectData is called, the $iPromptFlags specified in the _CryptProtectData call are enforced.
;       : $sPrompt  - Specifies the prompt information, if $iPromptFlags is zero, this parameter is ignored.
;       : $hWndOwner    - Window handle to the parent window that owns the prompt dialog, if $iPromptFlags parameter is zero, this value should be NULL.
; Return values : If succeeds, returns the encrypted data, else returns NULL and sets @error to the reason code of the failure.
; Author    : Pusofalse
; ======================================================================================
Func _CryptProtectData($vDataToProtect, $vEntropy = "", $iFlags = 0, $sDescr = Default, $iPromptFlags = 0, $sPrompt = "", $hWndPrompt = 0)
    Local $tEntropy, $pEntropy, $iLength, $bResult, $tResult
    Local $iResult, $tDataIn, $pDataIn, $tPrompt, $pPrompt, $iSysError

    If $sDescr = Default OR $sDescr = "" Then $sDescr = $LSA_DEDICATED_KEY0

    $iLength = StringLen($vDataToProtect) * 2 + 2
    $tDataIn = DllStructCreate($tagDATABLOB & ";wchar Data[" & $iLength & "]")
    $pDataIn = DllStructGetPtr($tDataIn)
    DllStructSetData($tDataIn, "Length", $iLength)
    DllStructSetData($tDataIn, "Buffer", DllStructGetPtr($tDataIn, "Data"))
    DllStructSetData($tDataIn, "Data", $vDataToProtect)

    Select
    Case $vEntropy <> ""
        $iLength = StringLen($vEntropy) * 2 + 2
        $tEntropy = DllStructCreate($tagDATABLOB & ";wchar Data[" & $iLength & "]")
        $pEntropy = DllStructGetPtr($tEntropy)
        DllStructSetData($tEntropy, "Length", $iLength)
        DllStructSetData($tEntropy, "Buffer", DllStructGetPtr($tEntropy, "Data"))
        DllStructSetData($tEntropy, "Data", $vEntropy)
        ContinueCase
    Case $iPromptFlags <> 0
        If $sPrompt = "" Then $sPrompt = $sDescr
        $iLength = StringLen($sPrompt) * 2 + 2
        $tPrompt = DllStructCreate($tagCRYPTPROTECT_PROMPT & ";wchar Data[" & $iLength & "]")
        $pPrompt = DllStructGetPtr($tPrompt)
        DllStructSetData($tPrompt, "Size", 16)
        DllStructSetData($tPrompt, "Flags", $iPromptFlags)
        DllStructSetData($tPrompt, "hWndOwner", $hWndPrompt)
        DllStructSetData($tPrompt, "Prompt", DllStructGetPtr($tPrompt, "Data"))
        DllStructSetData($tPrompt, "Data", $sPrompt)
    EndSelect

    $iResult = DllCall("Crypt32.dll", "int", "CryptProtectData", "ptr", $pDataIn, _
            "wstr", $sDescr, "ptr", $pEntropy, "ptr", 0, "ptr", $pPrompt, _
            "dword", $iFlags, "int64*", 0)
    If $iResult[0] = 0 Then $iSysError = _GetLastError()
    $iLength = _LsaLoLong64($iResult[7])
    $tResult = DllStructCreate("ubyte[" & $iLength & "]", _LsaHiLong64($iResult[7]))
    $bResult = DllStructGetData($tResult, 1)
    $iLength = _FreeVariable($tResult) & _FreeVariable($tPrompt)
    $iLength = _FreeVariable($tDataIn) & _FreeVariable($tEntropy)
    Return SetError($iSysError, _LsaLocalFree(_LsaHiLong64($iResult[7])), $bResult)
EndFunc ;==>_CryptProtectData

; #### FUNCTION ####
; ======================================================================================
; Name  : _CryptUnprotectData
; Description   : This function is used to decrypt the data encryped by the call to _CryptProtectData function.
; Parameter(s)  : $bProtectedData   - Encryped data, typically returned by the _CryptProtectData function.
;       : $vEntropy - A password or other additional entropy used when the data was encryped. This parameter can be set to NULL; however, if an optional entropy was used in the encryption phase, that same entropy must be used for the decryption phase.
;       : $iFlags   - This parameter can be zero, in which case no option is set, or the following flag:
;       :   - CRYPTPROTECT_UI_FORBIDDEN (1) : This flag is used for remote situations where the user interface (UI) is not an option. When this flag is set and UI is specified for either the protect or unprotect operation, the operation fails and GetLastError returns the ERROR_PASSWORD_RESTRICTION code.
;       :   - CRYPTPROTECT_VERIFY_PROTECTION (64) : This flag verifies the protection of a protected BLOB. If the default protection level configured of the host is higher than the current protection level for the BLOB, the function returns CRYPT_I_NEW_PROTECTION_REQUIRED to advise the caller to again protect the plaintext contained in the BLOB.
;       : $iPromptFlags - Specifies the flags about where and when prompts are to be displayed and what the content of those prompts should be. If zero is specified, no prompts are displayed. This parameter can be one or more of the following values:
;       :   - 0 : No prompts.
;       :   - CRYPTPROTECT_PROMPT_ON_PROTECT (2) : This flag is used to provide the prompt for the protect phase.
;       :   - CRYPTPROTECT_PROMPT_ON_UNPROTECT (1) : This flag can be combined with CRYPTPROTECT_PROMPT_ON_PROTECT to enforce the UI (user interface) policy of the caller. When _CryptUnprotectData is called, the $iPromptFlags specified in the _CryptProtectData call are enforced.
;       : $sPrompt  - Specifies the prompt information displayed to the user, can be set to NULL; if $iPromptFlags is zero, this parameter is ignored.
;       : $hWndPrompt   - Window handle to the parent window, if $iPromptFlags is zero, this parameter is ignored.
; Return values : Returns the decrypted data if succeeds, else returns NULL or zero, @error is set to a system error code.
; Author    : Pusofalse
; ======================================================================================
Func _CryptUnprotectData($bProtectedData, $vEntropy = "", $iFlags = 0, $iPromptFlags = 0, $sPrompt = "", $hWndPrompt = 0)
    Local $iResult, $tDataIn, $pDataIn, $tPrompt, $pPrompt
    Local $tEntropy, $pEntropy, $iLength, $sResult, $tResult, $iSysError

    $iLength = BinaryLen($bProtectedData)
    $tDataIn = DllStructCreate($tagDATABLOB & ";ubyte Data[" & $iLength & "]")
    $pDataIn = DllStructGetPtr($tDataIn)
    DllStructSetData($tDataIn, "Length", $iLength)
    DllStructSetData($tDataIn, "Buffer", DllStructGetPtr($tDataIn, "Data"))
    DllStructSetData($tDataIn, "Data", $bProtectedData)

    Select
    Case $vEntropy <> ""
        $iLength = StringLen($vEntropy) * 2 + 2
        $tEntropy = DllStructCreate($tagDATABLOB & ";wchar Data[" & $iLength & "]")
        $pEntropy = DllStructGetPtr($tEntropy)
        DllStructSetData($tEntropy, "Length", $iLength)
        DllStructSetData($tEntropy, "Buffer", DllStructGetPtr($tEntropy, "Data"))
        DllStructSetData($tEntropy, "Data", $vEntropy)
        ContinueCase
    Case $iPromptFlags <> 0
        If $sPrompt = "" Then $sPrompt = $LSA_DEDICATED_KEY0
        $iLength = StringLen($sPrompt) * 2 + 2
        $tPrompt = DllStructCreate($tagCRYPTPROTECT_PROMPT & ";wchar Data[" & $iLength & "]")
        $pPrompt = DllStructGetPtr($tPrompt)
        DllStructSetData($tPrompt, "Size", 16)
        DllStructSetData($tPrompt, "Flags", $iPromptFlags)
        DllStructSetData($tPrompt, "hWndOwner", $hWndPrompt)
        DllStructSetData($tPrompt, "Prompt", DllStructGetPtr($tPrompt, "Data"))
        DllStructSetData($tPrompt, "Data", $sPrompt)
    EndSelect
    $iResult = DllCall("Crypt32.dll", "int", "CryptUnprotectData", "ptr", $pDataIn, _
            "ptr", 0, "ptr", $pEntropy, "ptr", 0, "ptr", $pPrompt, _
            "dword", $iFlags, "int64*", 0)
    If $iResult[0] = 0 Then $iSysError = _GetLastError()
    $iLength = _LsaLoLong64($iResult[7])
    $tResult = DllStructCreate("wchar[" & $iLength & "]", _LsaHiLong64($iResult[7]))
    $sResult = DllStructGetData($tResult, 1)
    $iLength = _FreeVariable($tResult) & _FreeVariable($tPrompt)
    $iLength = _FreeVariable($tDataIn) & _FreeVariable($tEntropy)
    Return SetError($iSysError, _LsaLocalFree(_LsaHiLong64($iResult[7])), $sResult)
EndFunc ;==>_CryptUnprotectData

Func _CryptAcquireContext($iType, $iFlag, $sContainer = "", $sProvider = "")
    Local $hContext

    $hContext = DllCall("Advapi32.dll", "int", "CryptAcquireContext", _
            "hWnd*", 0, "str", $sContainer, "str", $sProvider, _
            "dword", $iType, "dword", $iFlag)
    Return $hContext[1]
EndFunc ;==>_CryptAcquireContext

Func _CryptCreateHash($hContext, $iAlgID, $hKey = 0)
    Local $hHash
    $hHash = DllCall("Advapi32.dll", "int", "CryptCreateHash", "hWnd", $hContext, _
            "int", $iAlgID, "dword", $hKey, "dword", 0, "hWnd*", 0)
    Return $hHash[5]
EndFunc ;==>_CryptCreateHash

Func _CryptGenKey($hContext, $iAlgID, $iFlag = 0)
    Local $iResult
    $iResult = DllCall("Advapi32.dll", "int", "CryptGenKey", "hWnd", $hContext, _
            "int", $iAlgID, "dword", $iFlag, "hWnd*", 0)
    Return $iResult[4]
EndFunc ;==>_CryptGenKey

Func _CryptGenRandom($hContext, $iLength = 8)
    Local $bRandom, $tBuffer, $pBuffer, $iResult

    $iLength = Int($iLength)
    If $iLength < 1 Then $iLength = 8
    $tBuffer = DllStructCreate("ubyte Random[" & $iLength & "]")
    $iResult = DllCall("Advapi32.dll", "int", "CryptGenRandom", "hWnd", $hContext, _
            "dword", $iLength, "ptr", DllStructGetPtr($tBuffer))
    If $iResult[0] = 0 Then Return SetError(_GetLastError(), _FreeVariable($tBuffer), "")
    $bData = DllStructGetData($tBuffer, "Random")
    Return SetExtended(_FreeVariable($tBuffer), $bData)
EndFunc ;==>_CryptGenRandom

Func _CryptHashData($hHash, $pBuffer, $iSizeofBuffer, $iFlag)
    Local $iResult

    If IsDllStruct($pBuffer) Then $pBuffer = DllStructGetPtr($pBuffer)
    $iResult = DllCall("Advapi32.dll", "int", "CryptHashData", "hWnd", $hHash, _
            "ptr", $pBuffer, "dword", $iSizeofBuffer, "dword", $iFlag)
    Return $iResult[0] <> 0
EndFunc ;==>_CryptHashData

Func _CryptGetHashParam($hHash, $iParamRequires)
    Local $iResult, $tBuffer, $pBuffer, $bData

    $iResult = DllCall("Advapi32.dll", "int", "CryptGetHashParam", "hWnd", $hHash, _
            "dword", $iParamRequires, "ptr", 0, "int*", 0, "int", 0)
    $tBuffer = DllStructCreate("byte[" & $iResult[4] & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iResult = DllCall("Advapi32.dll", "int", "CryptGetHashParam", "hWnd", $hHash, _
            "dword", $iParamRequires, "ptr", $pBuffer, _
            "int*", $iResult[4], "int", 0)
    $bData = DllStructGetData($tBuffer, 1)
    _FreeVariable($tBuffer)
    Return SetExtended($iResult[0], $bData)
EndFunc ;==>_CryptGetHashParam

Func _CryptReleaseContext($hContext)
    Local $iResult = DllCall("Advapi32.dll", "int", "CryptReleaseContext", "hWnd", $hContext, "dword", 0)
    Return $iResult[0] <> 0
EndFunc ;==>_CryptReleaseContext

Func _CryptDestroyHash($hHash)
    Local $iResult = DllCall("Advapi32.dll", "int", "CryptDestroyHash", "hWnd", $hHash)
    Return $iResult[0] <> 0
EndFunc ;==>_CryptDestroyHash

Func _CryptDestroyKey($hKey)
    Local $iResult = DllCall("Advapi32.dll", "int", "CryptDestroyKey", "hWnd", $hKey)
    Return $iResult[0] <> 0
EndFunc ;==>_CryptDestroyKey

Func _CryptDuplicateHash($hHash)
    Local $iResult
    $iResult = DllCall("Advapi32.dll", "int", "CryptDuplicateHash", "hWnd", $hHash, _
            "ptr", 0, "dword", 0, "hWnd*", 0)
    Return SetExtended($iResult[0], $iResult[4])
EndFunc ;==>_CryptDuplicateHash

Func _CryptDuplicateKey($hKey)
    Local $iResult
    $iResult = DllCall("Advapi32.dll", "int", "CryptDuplicateKey", "hWnd", $hKey, _
            "ptr", 0, "dword", 0, "hWnd*", 0)
    Return SetExtended($iResult[0], $iResult[4])
EndFunc ;==>_CryptDuplicateKey

Func _CryptEncrypt($sBinData, $hKey, $hHash = 0)
    Local $iResult, $iLength, $tBuffer, $pBuffer, $tEncode, $pEncode, $bData

    $iLength = BinaryLen($sBinData)
    $tBuffer = DllStructCreate("byte[" & $iLength & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    DllStructSetData($tBuffer, 1, $sBinData)

    $iResult = DllCall("Advapi32.dll", "int", "CryptEncrypt", "hWnd", $hKey, _
            "hWnd", $hHash, "int", 1, "dword", 0, "ptr", $pBuffer, _
            "dword*", $iLength, "dword", $iLength)
    _FreeVariable($tBuffer)
    $tEncode = DllStructCreate("byte[" & $iResult[6] & "]")
    $pEncode = DllStructGetPtr($tEncode)
    DllStructSetData($tEncode, 1, $sBinData)
    $iResult = DllCall("Advapi32.dll", "int", "CryptEncrypt", "hWnd", $hKey, _
            "hWnd", $hHash, "int", 1, "dword", 0, "ptr", $pEncode, _
            "dword*", $iLength, "dword", $iResult[6])
    $bData = DllStructGetData($tEncode, 1)
    _FreeVariable($tEncode)
    Return SetExtended($iResult[0], $bData)
EndFunc ;==>_CryptEncrypt

Func _CryptDecrypt($bBinData, $hKey, $hHash = 0)
    Local $iResult, $iLength, $tDecode, $pDecode, $tBuffer, $pBuffer, $bData

    $iLength = BinaryLen($bBinData)
    $tBuffer = DllStructCreate("byte[" & $iLength & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    DllStructSetData($tBuffer, 1, $bBinData)

    $iResult = DllCall("Advapi32.dll", "int", "CryptDecrypt", "hWnd", $hKey, _
            "hWnd", $hHash, "int", 1, "dword", 0, _
            "ptr", $pBuffer, "dword*", $iLength)
    $tDecode = DllStructCreate("byte[" & $iResult[6] & "]", $pBuffer)
    $bData = DllStructGetData($tDecode, 1)
    _FreeVariable($tBuffer)
    _FreeVariable($tDecode)
    Return SetExtended($iResult[0], $bData)
EndFunc ;==>_CryptDecrypt

Func _CryptHashCertificate($sBinData, $iAlgID = $CALG_MD5)
    Local $tBuffer, $pBuffer, $iLength, $iResult, $tHash, $pHash, $bData

    $iLength = BinaryLen($sBinData)
    $tBuffer = DllStructCreate("byte[" & $iLength & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    DllStructSetData($tBuffer, 1, $sBinData)

    $iResult = DllCall("Crypt32.dll", "int", "CryptHashCertificate", "hWnd", 0, _
            "dword", $iAlgID, "dword", 0, "ptr", $pBuffer, _
            "dword", $iLength, "ptr", 0, "dword*", 0)
;           MsgBox(1, '', @error)
    $tHash = DllStructCreate("byte[" & $iResult[7] & "]")
    $pHash = DllStructGetPtr($tHash)
    $iResult = DllCall("Crypt32.dll", "int", "CryptHashCertificate", "hWnd", 0, _
            "dword", $iAlgID, "dword", 0, "ptr", $pBuffer, _
            "dword", $iLength, "ptr", $pHash, "dword*", $iResult[7])
    $bData = StringTrimLeft(DllStructGetData($tHash, 1), 2)
    _FreeVariable($tHash)
    _FreeVariable($tBuffer)
    Return SetExtended($iResult[0], StringLower($bData))
EndFunc ;==>_CryptHashCertificate

Func _CryptStringToBinary($sString, $iFlag = 1)
    Local $iResult, $tBuffer, $pBuffer, $iLength, $bData

    $iLength = BinaryLen($sString)
    $iResult = DllCall("Crypt32.dll", "int", "CryptStringToBinary", _
            "str", $sString, "dword", $iLength, "dword", $iFlag, _
            "ptr", 0, "dword*", 0, "dword*", 0, "dword*", 0)
    $tBuffer = DllStructCreate("char[" & $iResult[5] & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iResult = DllCall("Crypt32.dll", "int", "CryptStringToBinary", _
            "str", $sString, "dword", $iLength, "dword", $iFlag, _
            "ptr", $pBuffer, "dword*", $iResult[5], "dword*", 0, "dword*", 0)
    $bData = DllStructGetData($tBuffer, 1)
    _FreeVariable($tBuffer)
    Return SetExtended($iResult[0], $bData)
EndFunc ;==>_CryptStringToBinary

Func _CryptBinaryToString($sBinData, $iFlag = 1)
    Local $iResult, $iLength

    $iLength = BinaryLen($sBinData)
    $iResult = DllCall("Crypt32.dll", "int", "CryptBinaryToString", "str", $sBinData, _
            "dword", $iLength, "dword", $iFlag, "ptr", 0, "dword*", 0)
    $iResult = DllCall("Crypt32.dll", "int", "CryptBinaryToString", "str", $sBinData, _
            "dword", $iLength, "dword", $iFlag, _
            "str", "", "dword*", $iResult[5])
    Return SetExtended($iResult[0], $iResult[4])
EndFunc ;==>_CryptBinaryToString

; #### FUNCTION ####
; =======================================================================
; Name  : _CryptVerifyObjectHashValue
; Description   : Verify the hash value of the specified object.
; Parameter(s)  : $sObject      - Specify the name of the object to verify.
;       : $iType        - Specify the object type, see CRYPT_OBJECT_* GLOBAL CONSTants for a value.
;       : $iMask        - Specify a bit of verifier flag, see CRYPT_MASK_* GLOBAL CONSTants for a value.
;       : $iAlgID       - The verifier method, default to verify using MD5.
; Return values : If succeeds, returns the hash value in string format. Otherwise, returns NULL and sets @error.
; Author    : Pusofalse
; Remarks       : $iType - $CRYPT_OBJECT_FILE.
;       :   $CRYPT_MASK_OWNER, if the owner of the file has changed, the hash value is changed too.
;       :   $CRYPT_MASK_GROUP, if the primary group of the file has changed, the hash value is changed too.
;       :   $CRYPT_MASK_DACL, if the DACL security information has changed, the hash value is changed too.
;       :   $CRYPT_MASK_DATA, if the data of the file has changed, the hash value is changed too.
;       :   $CRYPT_MASK_TIME, if the modification time of the file has changed, the hash value is changed too.
;       :   $CRYPT_MASK_ATTRIBUTES, if the attribute of the file has changed, the hash value is changed too.
;       :   $CRYPT_MASK_SYNCHRONIZE, if the type, display name or system icon index have changed, the hash value is changed too.
;       : $iType - $CRYPT_OBJECT_SERVICE
;       :   $CRYPT_MASK_OWNER, if the owner of the service has changed, the hash value is changed too.
;       :   $CRYPT_MASK_GROUP, if the primary group of the service has changed, the hash value is changed too.
;       :   $CRYPT_MASK_DACL, if the DACL security information of the service has changed, the hash value is changed too.
;       :   $CRYPT_MASK_SACL, if the SACL security information of the service has changed, the hash value is changed too.
;       :   $CRYPT_MASK_TYPE, if the type or start type have changed, the hash value is changed too.
;       :   $CRYPT_MASK_STATE, if the state of the service has changed, the hash value is changed too.
;       :   $CRYPT_MASK_SYNCHRONIZE, if the type, start type, error control code, binary path, load order group, tag ID, dependent services, start name, or display name of the service have changed, the hash value is changed too.
;       :   $CRYPT_MASK_PARAM, if the status of the service has changed, the hash value is changed.
;       :   $CRYPT_MASK_DATA, if the data in the binary file of the service has changed, the hash is chenged, if the service does not have  an executable command, this flag is ignored.
;       :   $CRYPT_MASK_TIME, if the time of the binary file of the service has changed, the hash is chenged, if the service does not have  a executable command, this flag is ignored.
;       :   $CRYPT_MASK_ATTRIBUTES, if the attribute of the binary file of the service has changed, the hash is changed, if the service does not have an executable command, this flag is ignored.
;       : $iType - $CRYPT_OBJECT_REGISTRY_KEY
;           $CRYPT_MASK_OWNER, if the owner of the registry key has changed, the hash value is changed also.
;       :   $CRYPT_MASK_GROUP, if the primary group of the registry key has changed, the hash value is changed also.
;       :   $CRYPT_MASK_DACL, if the DACL security information of the registry key has changed, the hash value is changed also.
;       : $iType - $CRYPT_OBJECT_STRING
;       :   If $iType is CRYPT_OBJECT_STRING, the function ignores the $iType and $iMask parameters, returns the hash value of the string.
;       : $iType - $CRYPT_OBJECT_KERNEL
;       :   $CRYPT_MASK_OWNER, if the owner of the kernel object has changed, the hash is changed also.
;       :   $CRYPT_MASK_GROUP, if the primary group of the kernel object has changed, the hash is changed also.
;       :   $CRYPT_MASK_DACL, if the DACL security information of the kernel object has changed, the hash is changed also.
; =======================================================================
Func _CryptVerifyObjectHashValue($sObject, $iType, $iMask, $iAlgID = $CALG_MD5)
    Local $sBinData, $pSecurDesc, $hToken = _OpenProcessToken(-1), $iAccess, $bHash, $aResult
    Local $aPriv[2][2] = [[$SE_SECURITY_NAME, 2], [$SE_DEBUG_NAME, 2]], $aAceList, $tBuffer

    If bitAND($iMask, $CRYPT_MASK_DACL) OR bitAND($iMask, $CRYPT_MASK_SACL) Then
        _AdjustTokenPrivileges($hToken, $aPriv)
    EndIf
    _LsaCloseHandle($hToken)

    Switch $iType
    Case $CRYPT_OBJECT_STRING
        $iMask = $CRYPT_MASK_SELF
    Case $CRYPT_OBJECT_FILE
        If Not FileExists($sObject) Then Return SetError(2, 0, "")
        If bitAND($iMask, $CRYPT_MASK_OWNER) AND Not _IsNtfs($sObject) Then Return SetError(@error, 0, "")
        If bitAND($iMask, $CRYPT_MASK_GROUP) AND Not _IsNtfs($sObject) Then Return SetError(@error, 0, "")
        If bitAND($iMask, $CRYPT_MASK_DACL) AND Not _IsNtfs($sObject) Then Return SetError(@error, 0, "")

        If bitAND($iMask, $CRYPT_MASK_TIME) Then $sBinData &= FileGetTime($sObject, 0, 1)
        If bitAND($iMask, $CRYPT_MASK_DATA) Then $sBinData &= FileRead(FileOpen($sObject, 16))
        If bitAND($iMask, $CRYPT_MASK_ATTRIBUTES) Then $sBinData &= FileGetAttrib($sObject)

        If bitAND($iMask, $CRYPT_MASK_OWNER) Then $iAccess = bitOR($iAccess, $OWNER_SECURITY_INFORMATION)
        If bitAND($iMask, $CRYPT_MASK_GROUP) Then $iAccess = bitOR($iAccess, $GROUP_SECURITY_INFORMATION)
        If bitAND($iMask, $CRYPT_MASK_DACL) Then $iAccess = bitOR($iAccess, $DACL_SECURITY_INFORMATION)
        $pSecurDesc = _QueryFileSecurity($sObject, $iAccess)
        If bitAND($iMask, $CRYPT_MASK_OWNER) Then $sBinData &= _ConvertSidToStringSid(_GetSecurityDescriptorOwner($pSecurDesc))
        If bitAND($iMask, $CRYPT_MASK_GROUP) Then $sBinData &= _ConvertSidToStringSid(_GetSecurityDescriptorGroup($pSecurDesc))
        If bitAND($iMask, $CRYPT_MASK_DACL) Then
            $aAceList = _GetExplicitEntriesFromAcl(_GetSecurityDescriptorDacl($pSecurDesc))
            For $i = 1 to $aAceList[0][0]
                For $c = 0 to 4
                    $sBinData &= $aAceList[$i][$c]
                Next
            Next
        EndIf
        If bitAND($iMask, $CRYPT_MASK_SYNCHRONIZE) Then
            $tBuffer = _SHGetFileInfo($sObject)
            $sBinData &= DllStructGetData($tBuffer, 1)
            _FreeVariable($tBuffer)
        EndIf
    Case $CRYPT_OBJECT_REGISTRY_KEY
        Local $hMainKey, $sSubKey
        $sObject = StringSplit($sObject, "\")
        If $sObject[0] = 0 Then Return SetError($ERROR_INVALID_PARAMETER, 0, "")
        Switch $sObject
        Case "HKCR", "HKEY_CLASSES_ROOT"
            $hMainKey = 0x80000000
        Case "HKCU", "HKEY_CURRENT_USER"
            $hMainKey = 0x80000001
        Case "HKLM", "HKEY_LOCAL_MACHINE"
            $hMainKey = 0x80000002
        Case "HKU", "HKEY_USERS"
            $hMainKey = 0x80000003
        Case "HKCC", "HKEY_CURRENT_CONFIG"
            $hMainKey = 0x80000005
        Case Else
            Return SetError($ERROR_INVALID_PARAMETER,0, "")
        EndSwitch
        For $i = 2 to $sObject[0]
            $sSubKey &= $sObject[$i] & "\"
        Next

        If bitAND($iMask, $CRYPT_MASK_OWNER) Then $iAccess = bitOR($iAccess, $OWNER_SECURITY_INFORMATION)
        If bitAND($iMask, $CRYPT_MASK_GROUP) Then $iAccess = bitOR($iAccess, $GROUP_SECURITY_INFORMATION)
        If bitAND($iMask, $CRYPT_MASK_DACL) Then $iAccess = bitOR($iAccess, $DACL_SECURITY_INFORMATION)
        $pSecurDesc = _RegGetKeySecurity($sObject, $iAccess)
        If Not _IsValidSecurityDescriptor($pSecurDesc) Then Return SetError(@error, 0, "")
        If bitAND($iMask, $CRYPT_MASK_OWNER) Then $sBinData &= _ConvertSidToStringSid(_GetSecurityDescriptorOwner($pSecurDesc))
        If bitAND($iMask, $CRYPT_MASK_GROUP) Then $sBinData &= _ConvertSidToStringSid(_GetSecurityDescriptorGroup($pSecurDesc))
        If bitAND($iMask, $CRYPT_MASK_DACL) Then
            $aAceList = _GetExplicitEntriesFromAcl(_GetSecurityDescriptorDacl($pSecurDesc))
            For $i = 1 to $aAceList[0][0]
                For $c = 0 to 4
                    $sBinData &= $aAceList[$i][$c]
                Next
            Next
        EndIf
    Case $CRYPT_OBJECT_LMSHARE, $CRYPT_OBJECT_KERNEL
        If bitAND($iMask, $CRYPT_MASK_OWNER) Then $iAccess = bitOR($iAccess, $OWNER_SECURITY_INFORMATION)
        If bitAND($iMask, $CRYPT_MASK_GROUP) Then $iAccess = bitOR($iAccess, $GROUP_SECURITY_INFORMATION)
        If bitAND($iMask, $CRYPT_MASK_DACL) Then $iAccess = bitOR($iAccess, $DACL_SECURITY_INFORMATION)
        If $iType = $CRYPT_OBJECT_LMSHARE Then
            $aSecur = _GetNamedSecurityInfo($sObject, $SE_LMSHARE, $iAccess)
        Else
            $aSecur = _GetSecurityInfo($sObject, $SE_KERNEL_OBJECT, $iAccess)
        EndIf
        If $aSecur[0] Then Return SetError($aSecur[0], 0, "")
        If bitAND($iMask, $CRYPT_MASK_OWNER) Then $sBinData &= _ConvertSidToStringSid($aSecur[4])
        If bitAND($iMask, $CRYPT_MASK_GROUP) Then $sBinData &= _ConvertSidToStringSid($aSecur[5])
        If bitAND($iMask, $CRYPT_MASK_DACL) Then
            $aAceList = _GetExplicitEntriesFromAcl($aSecur[6])
            For $i = 1 to $aAceList[0][0]
                For $c = 0 to 4
                    $sBinData &= $aAceList[$i][$c]
                Next
            Next
        EndIf
    Case $CRYPT_OBJECT_SERVICE
        If bitAND($iMask, $CRYPT_MASK_OWNER) Then $iAccess = bitOR($iAccess, $OWNER_SECURITY_INFORMATION)
        If bitAND($iMask, $CRYPT_MASK_GROUP) Then $iAccess = bitOR($iAccess, $GROUP_SECURITY_INFORMATION)
        If bitAND($iMask, $CRYPT_MASK_DACL) Then $iAccess = bitOR($iAccess, $DACL_SECURITY_INFORMATION)
        If bitAND($iMask, $CRYPT_MASK_SACL) Then $iAccess = bitOR($iAccess, $SACL_SECURITY_INFORMATION)
        If bitAND($iMask, 8) or bitAND($iMask, 16) or bitAND($iMask, 32) or bitAND($iMask, 64) Then
            If IsString($sObject) Then
                $aSecur = _GetNamedSecurityInfo($sObject, $SE_SERVICE, $iAccess)
            Else
                $aSecur = _GetSecurityInfo($sObject, $SE_SERVICE, $iAccess)
            EndIf
            If $aSecur[0] Then Return SetError($aSecur[0], 0, "")
        EndIf
        If bitAND($iMask, $CRYPT_MASK_OWNER) Then $sBinData &= _ConvertSidToStringSid($aSecur[4])
        If bitAND($iMask, $CRYPT_MASK_GROUP) Then $sBinData &= _ConvertSidToStringSid($aSecur[5])
        If bitAND($iMask, $CRYPT_MASK_DACL) Then
            $aAceList = _GetExplicitEntriesFromAcl($aSecur[6])
            For $i = 1 to $aAceList[0][0]
                For $c = 0 to 4
                    $sBinData &= $aAceList[$i][$c]
                Next
            Next
        EndIf
        If bitAND($iMask, $CRYPT_MASK_SACL) Then
            $aAceList = _GetExplicitEntriesFromAcl($aSecur[7])
            For $i = 1 to $aAceList[0][0]
                For $c = 0 to 4
                    $sBinData &= $aAceList[$i][$c]
                Next
            Next
        EndIf
        If bitAND($iMask, $CRYPT_MASK_TYPE) Then
            $aResult = _LsaSvcCertificate_QueryServiceConfig($sObject)
            $sBinData &= $aResult[0]
            $sBinData &= $aResult[1]
            _FreeVariable($aResult)
        EndIf
        If bitAND($iMask, $CRYPT_MASK_SYNCHRONIZE) Then
            $aResult = _LsaSvcCertificate_QueryServiceConfig($sObject)
            For $i = 0 to 5
                $sBinData &= $aResult[$i]
            Next
            _FreeVariable($aResult)
            $sBinData &= _LsaSvcCertificate_QueryDependenices($sObject)
            $sBinData &= _LsaSvcCertificate_GetDisplayName($sObject)
            $sBinData &= _LsaSvcCertificate_QueryStartName($sObject)
        EndIf
        If bitAND($iMask, $CRYPT_MASK_STATE) Then
            $aResult = _LsaSvcCertificate_QueryServiceStatusEx($sObject)
            $sBinData &= $aResult[1]
            _FreeVariable($aResult)
        EndIf
        If bitAND($iMask, $CRYPT_MASK_PARAM) Then
            $aResult = _LsaSvcCertificate_QueryServiceStatusEx($sObject)
            For $i = 0 to 8
                If $i = 7 Then ContinueLoop
                $sBinData &= $aResult[$i]
            Next
            $sBinData &= _LsaSvcCertificate_QueryDescription($sObject)
            _FreeVariable($aResult)
        EndIf
        $aResult = _LsaSvcCertificate_QueryBinPath($sObject)
        If bitAND($iMask, $CRYPT_MASK_DATA) Then $sBinData &= FileRead(FileOpen($aResult, 16))
        If bitAND($iMask, $CRYPT_MASK_TIME) Then $sBinData &= FileGetTime($aResult, 0, 1)
        If bitAND($iMask, $CRYPT_MASK_ATTRIBUTES) Then $sBinData &= FileGetAttrib($aResult)
    EndSwitch

    If bitAND($iMask, $CRYPT_MASK_SELF) Then $sBinData &= $sObject
    $bHash = _CryptHashCertificate($sBinData, $iAlgID)
    Return SetExtended(_FreeVariable($sBinData), $bHash)
EndFunc ;==>_CryptVerifyObjectHashValue

#### INTERNAL USED ONLY FUNCTIONS REGION START ####
; =======================================================================
; For Authentication of services only.
; =======================================================================
Func _LsaSvcCertificate_QueryServiceConfig($sService, $sSystem = "")
    Local $hService, $aResult[9], $pBuffer, $iResult, $iSysError

    $hService = _LsaOpenService($sService, 1, $sSystem)
    If Not $hService Then Return SetError(@error, 0, $aResult)

    $iResult = DllCall("Advapi32.dll", "int", "QueryServiceConfig", _
            "hWnd", $hService, "ptr", 0, "int", 0, "int*", 0)
    $pBuffer = _HeapAlloc($iResult[4])
    $iResult = DllCall("Advapi32.dll", "int", "QueryServiceConfig", _
            "hWnd", $hService, "ptr", $pBuffer, "int", $iResult[4], "int*", 0)
    $iSysError = _GetLastError()

    $tBuffer = DllStructCreate($tagQueryServiceCfg, $pBuffer)
    $aResult[0] = DllStructGetData($tBuffer, "Type")
    $aResult[1] = DllStructGetData($tBuffer, "StartType")
    $aResult[2] = DllStructGetData($tBuffer, "ErrorCtrl")
    $aResult[3] = DllStructGetData($tBuffer, "BinPath")
    $aResult[4] = DllStructGetData($tBuffer, "LoadOrderGroup")
    $aResult[5] = DllStructGetData($tBuffer, "TagId")
    $aResult[6] = DllStructGetData($tBuffer, "Dependence")
    $aResult[7] = DllStructGetData($tBuffer, "StartName")
    $aResult[8] = DllStructGetData($tBuffer, "DisplayName")

    _HeapFree($pBuffer)
    _FreeVariable($tBuffer)
    _LsaCloseServiceHandle($hService)
    Return SetError($iSysError, 0, $aResult)
EndFunc ;==>_LsaSvcCertificate_QueryServiceConfig

Func _LsaSvcCertificate_QueryServiceStatusEx($sService, $sSystem = "")
    Local $hService, $pBuffer, $tBuffer, $iResult, $aResult[9]

    $hService = _LsaOpenService($sService, 4, $sSystem)
    If Not $hService Then Return SetError(@error, 0, $aResult)

    $iResult = DllCall("Advapi32.dll", "int", "QueryServiceStatusEx", _
            "hWnd", $hService, "dword", 0, _
            "ptr", 0, "dword", 0, "dword*", 0)
    $pBuffer = _HeapAlloc($iResult[5])
    $iResult = DllCall("Advapi32.dll", "int", "QueryServiceStatusEx", _
            "hWnd", $hService, "dword", 0, _
            "ptr", $pBuffer, "dword", $iResult[5], "dword*", 0)
    $iSysError = _GetLastError()

    $tBuffer = DllStructCreate($tagServiceStatusProcess, $pBuffer)
    $aResult[0] = DllStructGetData($tBuffer, "ServiceType")
    $aResult[1] = DllStructGetData($tBuffer, "CurrentState")
    $aResult[2] = DllStructGetData($tBuffer, "ControlsAccepted")
    $aResult[3] = DllStructGetData($tBuffer, "Win32ExitCode")
    $aResult[4] = DllStructGetData($tBuffer, "ServiceSpecificExitCode")
    $aResult[5] = DllStructGetData($tBuffer, "CheckPoint")
    $aResult[6] = DllStructGetData($tBuffer, "WaitHint")
    $aResult[7] = DllStructGetData($tBuffer, "ProcessId")
    $aResult[8] = DllStructGetData($tBuffer, "ServiceFlags")
    _HeapFree($pBuffer)
    _FreeVariable($tBuffer)
    _LsaCloseServiceHandle($hService)
    Return SetError($iSysError, 0, $aResult)
EndFunc ;==>_LsaSvcCertificate_QueryServiceStatusEx

Func _LsaSvcCertificate_QueryStartName($sService, $sSystem = "")
    Local $aCfg, $iLength, $tBuffer, $sStartName

    $aCfg = _LsaSvcCertificate_QueryServiceConfig($sService, $sSystem)
    $iLength = Number($aCfg[8] - $aCfg[7])
    $tBuffer = DllStructCreate("char[" & $iLength & "]", $aCfg[7])
    $sStartName = DllStructGetData($tBuffer, 1)
    Return SetExtended(_FreeVariable($tBuffer) And _FreeVariable($aCfg), $sStartName)
EndFunc ;==>_LsaSvcCertificate_QueryStartName

Func _LsaSvcCertificate_GetDisplayName($sName, $sSystem = "")
    Local $iResult, $hSC

    $hSC = _LsaOpenSCManager($sSystem)
    If Not $hSC Then Return SetError(@error, 0, "")

    $iResult = DllCall("Advapi32.dll", "int", "GetServiceDisplayName", _
            "hWnd", $hSC, "str", $sName, _
            "str", "", "dword*", 255)
    Return SetError(_GetLastError(), $iResult[4], $iResult[3])
EndFunc ;==>_LsaSvcCertificate_GetDisplayName

Func _SHGetFileInfo($sFile)
    Local $iResult, $tBuffer, $pBuffer, $iMask

    $tBuffer = DllStructCreate("byte[352]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iMask = bitOR(0x200, 0x400, 0x2000, 0x4000)
    $iResult = DllCall("Shell32.dll", "dword", "SHGetFileInfo", "str", $sFile, _
            "dword", 0, "ptr", $pBuffer, "uint", 352, "dword", $iMask)
    Return $tBuffer
EndFunc ;==>_SHGetFileInfo

Func _LsaSvcCertificate_QueryDependenices($sService, $sSystem = "")
    Local $aCfg, $iLength, $iSysError, $tBuffer, $sDependence, $aResult[1] = [0]

    $aCfg = _LsaSvcCertificate_QueryServiceConfig($sService, $sSystem)
    $iSysError = @error
    $iLength = Number($aCfg[7] - $aCfg[6])
    If $iLength <= 4 Then Return SetError($iSysError, 0, $aResult)
    $tBuffer = DllStructCreate("char[" & $iLength & "]", $aCfg[6])
    For $i = 1 to $iLength
        If DllStructGetData($tBuffer, 1, $i) = Chr(0) Then
            $sDependence &= "|"
        Else
            $sDependence &= DllStructGetData($tBuffer, 1, $i)
        EndIf
    Next
    _FreeVariable($aCfg)
    _FreeVariable($tBuffer)
    $sDependence = StringRegExpReplace($sDependence, "\|+$", "")
    Return SetError($iSysError, 0, ($sDependence))
EndFunc ;==>_LsaSvcCertificate_QueryDependenices

Func _LsaSvcCertificate_QueryBinPath($sService, $sSystem = "")
    Local $aCfg, $tBuffer, $sBinPath, $iSysError

    $aCfg = _LsaSvcCertificate_QueryServiceConfig($sService, $sSystem)
    $iSysError = @error
    $tBuffer = DllStructCreate("char[" & Number($aCfg[4] - $aCfg[3]) & "]", $aCfg[3])
    $sBinPath = DllStructGetData($tBuffer, 1)
    _FreeVariable($aCfg)
    _FreeVariable($tBuffer)
    Return SetError($iSysError, 0, $sBinPath)
EndFunc ;==>_LsaSvcCertificate_QueryBinPath

Func _LsaSvcCertificate_QueryStartType($sService, $sSystem = "")
    Local $aCfg = _LsaSvcCertificate_QueryServiceConfig($sService, $sSystem)
    Return SetError(@error, 0, $aCfg[1])
EndFunc ;==>_LsaSvcCertificate_QueryStartType

Func _LsaSvcCertificate_QueryServiceConfig2($sService, $iCfgQuery, $sSystem = "")
    Local $iResult, $pBuffer, $hService, $iSysError

    $hService = _LsaOpenService($sService, 1, $sSystem)
    If Not $hService Then Return SetError(@error, 0, 0)

    $iResult = DllCall("Advapi32.dll", "int", "QueryServiceConfig2", _
            "hWnd", $hService, "int", $iCfgQuery, _
            "ptr", 0, "dword", 0, "dword*", 0)
    $pBuffer = _HeapAlloc($iResult[5])
    $iResult = DllCall("Advapi32.dll", "int", "QueryServiceConfig2", _
            "hWnd", $hService, "int", $iCfgQuery, _
            "ptr", $pBuffer, "dword", $iResult[5], "dword*", 0)
    $iSysError = _GetLastError()
    _LsaCloseServiceHandle($hService)
    Return SetError($iSysError, $iResult[4], $pBuffer)
EndFunc ;==>_LsaSvcCertificate_QueryServiceConfig2

Func _LsaSvcCertificate_QueryDescription($sService, $sSystem = "")
    Local $pBuffer, $iSysError, $tBuffer, $sDescr

    $pBuffer = _LsaSvcCertificate_QueryServiceConfig2($sService, 1, $sSystem)
    $iSysError = @error
    $tBuffer = DllStructCreate("char[" & @Extended & "]", $pBuffer + 4)
    $sDescr = DllStructGetData($tBuffer, 1)
    _HeapFree($pBuffer)
    _FreeVariable($tBuffer)
    Return SetError($iSysError, 0, $sDescr)
EndFunc ;==>_LsaSvcCertificate_QueryDescription
#### END REGION ####

Const $MAX_LSA_FUNCTIONS = 232

Func _LsaAu3LibraryIsIncluded()
    Local $hFile, $sLsaData

    $hFile = FileOpen("LocalSecurityAuthority.au3", 0)
    $sLsaData = FileRead($hFile)
    FileClose($hFile)
    $sLsaData = StringRegExp($sLsaData, "Func (_.*)\(", 3)
    If UBound($sLsaData) = $MAX_LSA_FUNCTIONS Then Return 1
EndFunc ;==>_LsaAu3LibraryIsIncluded

Func _ImpersonateSystemContext()
    Local $iProcessId, $fSuccess = 0, $pDacl, $iDesiredAccess
    Local $hToken, $aPriv[1][2] = [[$SE_DEBUG_NAME, 2]], $aSecur

    If (@UserName = "SYSTEM") Then Return SetError(85, 0, 1)
    If (_LsaGetUserRelativeID(@UserName) <> "ADMINISTRATOR$") Then
        Return SetError(5, 0, 0)
    EndIf

    $iProcessId = ProcessExists("Winlogon.exe")
    If $iProcessId < 1 Then Return SetError(127, 0, 0)

    $hToken = _OpenProcessToken(-1)
    _AdjustTokenPrivileges($hToken, $aPriv)
    _LsaCloseHandle($hToken)

    $iDesiredAccess = bitOR($READ_CONTROL, $WRITE_DAC)
    $hToken = _OpenProcessToken($iProcessId, $iDesiredAccess)
    If ($hToken < 1) Then Return SetError(@error, 0, 0)
    If (_QueryKernelObjectSecurityOwner($hToken) <> "BUILTIN\Administrators") Then
        Return SetError(@error, _FreeVariable($aSecur, 0, _LsaCloseHandle($hToken)), 0)
    EndIf

    $aSecur = _GetSecurityInfo($hToken, $SE_KERNEL_OBJECT, 4)
    If $aSecur[0] Then Return SetError(@error, _FreeVariable($aSecur, 0, _LsaCloseHandle($hToken)), 0)

    $pDacl = _SetEntriesInAcl1(@UserName, $TOKEN_ALL_ACCESS, $GRANT_ACCESS, $NO_INHERITANCE)
    If @error Then Return SetError(@error, _FreeVariable($aSecur, 0, _LsaCloseHandle($hToken)), 0)

    If _SetSecurityInfo($hToken, $SE_KERNEL_OBJECT, 4, 0, 0, $pDacl, 0) = 0 Then
        Return SetError(@error, _FreeVariable($aSecur, 0, _LsaCloseHandle($hToken)), 0)
    EndIf

    _LsaCloseHandle($hToken)
    $hToken = _OpenProcessToken($iProcessId, $TOKEN_ALL_ACCESS)
    If ($hToken < 1) Then Return SetError(@error, 0, 0)

    _SetSecurityInfo($hToken, $SE_KERNEL_OBJECT, 4, 0, 0, $aSecur[6], 0)
    If _ImpersonateLoggedOnUser($hToken) = False Then
        Return SetError(@error, _LsaCloseHandle($hToken), 0)
    EndIf
    Return SetError(@error, _FreeVariable($aSecur), $hToken)
EndFunc ;==>_ImpersonateSystemContext

Func _AllocateAndInitializeSid($iSidAuthority, $iCP0, $iCP1, $iCP2 = 0, $iCP3 = 0, _
        $iCP4 = 0, $iCP5 = 0, $iCP6 = 0, $iCP7 = 0)
    Local $iResult, $pAuthority, $tBuffer

    $tBuffer = DllStructCreate("byte Value1[5];byte Value2")
    $pAuthority = DllStructGetPtr($tBuffer)
    DllStructSetData($tBuffer, "Value2", $iSidAuthority)

    $iResult = DllCall("AdvApi32.dll", "int", "AllocateAndInitializeSid", "ptr", $pAuthority, _
            "byte", @NumParams - 1, "dword", $iCP0, "dword", $iCP1, "dword", $iCP2, "dword", $iCP3, _
            "dword", $iCP4, "dword", $iCP5, "dword", $iCP6, "dword", $iCP7, "ptr*", 0)
    $tBuffer = 0
    Return SetError(_GetLastError(), 0, $iResult[11])
EndFunc ;==>_AllocateAndInitializeSid

Func _FreeSid(ByRef $pSid)
    Local $iResult
    $iResult = DllCall("AdvApi32.dll", "ptr", "FreeSid", "ptr", $pSid)
    If ($iResult[0]) Then Return 0
    Return _FreeVariable($pSid, 0, 1)
EndFunc ;==>_FreeSid

; #### FUNCTION ####
; ==========================================================================================
; Name  : _LsaUserIsLocalAdmin
; Description   : This function determines whether an user have administrative user rights.
; Parameter(s)  : $sUserName    - User name to be determined.
; Return values : True indicates the $sUserName is an administrator, otherwise returns False.
; Author    : Pusofalse
; ==========================================================================================
Func _LsaUserIsLocalAdmin($sUserName = @UserName)
    Local $iDesired, $pDacl, $pSid

    If ($sUserName = "") Or ($sUserName = Default) Then $sUserName = @UserName

    $pSid = _AllocateAndInitializeSid(5, 32, $DOMAIN_ALIAS_RID_ADMINS)
    $pDacl = _SetEntriesInAcl1($pSid, 1, $GRANT_ACCESS, $NO_INHERITANCE)
    $iDesired = _GetEffectiveRightsFromAcl($pDacl, $sUserName)
    _FreeVariable($pSid, 0, _LsaLocalFree($pDacl), _FreeSid($pSid))
    Return Number($iDesired) <> 0
EndFunc ;==>_LsaUserIsLocalAdmin

; #### FUNCTION ####
; ==========================================================================================
; Name  : _TreeResetNamedSecurityInfo
; Description   : This function resets specified security information in the security descriptor of a specified tree of objects. This function allows a specified discretionary access control list (DACL) or system access control list (SACL) to be propagated throughout an entire tree. This function supports a callback function to track the progress of the tree operation.
; Parameter(s)  : $sObject  - Name of the root node object, supported objects are registry keys and file objects.
;       : $iObjType - A value of type identifies the object, supported values are SE_REGISTRY_KEY and SE_FILE_OBJECT.
;       : $iSecurLevel  - A set of bit flags that indicates the type of security information to reset. This value can be a combination of SECURITY_INFORMATION.
;       : $pOwner   - A pointer to a SID identifies the owner of the object, if $iSecurLevel does not contain OWNER_SECURITY_INFORMATION, this value can be zero.
;       : $pGroup   - A pointer to a SID identifies the primary group of the object, if $iSecurLevel does not contain GROUP_SECURITY_INFORMATION, this value can be zero.
;       : $pDacl    - A pointer to a DACL for the object being reset. The $iSecurLeval must include the DACL_SECURITY_INFORMATION bit flag. The caller must have the READ_CONTROL and WRITE_DAC access to each object, including the root object. If you are not setting the DACL, can parameter can be zero.
;       : $pSacl    - A pointer to a SACL for the object, caller must have SE_SECURITY_NAME privilege enabled, if SACL_SECURITY_INFORMATION is not specified in $iSecurLevel, this parameter can be zero.
;       : $fKeepExplicit    - Boolean value that defines whether explicitly defined ACEs are kept or deleted for the sub-tree. If $fKeepExplicit is TRUE, then explicitly defined ACEs are kept for each subtree DACL and SACL, and inherited ACEs are replaced by the inherited ACEs from $pDacl and $pSacl. If $fKeepExplicit is FALSE, then explicitly defined ACEs for each subtree DACL and SACL are deleted before the inherited ACEs are replaced by the inherited ACEs from $pDacl and $pSacl.
;       : $sCallback    - A callback function used to track the progress, define as the following:
;       Func _TreeResetNamedSecurityInfoCallback( _
;           $sObject, _ ; Name of the object just processed.
;           $iStatus, _ ; Status of operation on object, zero indicates success, otherwise this value will set to a system error code.
;           $fSecuritySet)  ; True indicates the security was set.
;       EndFunc
;       : If the callback function returns 1, the operation will abort; if returns 2, the procedure will retry the tree operation; for any other values, the procedure processes the tree operation until all objects were set.
;       : This parameter is optional, if you need not the progress of the tree operation.
; Return values : True indicates success, False otherwise. If the callback function returns a value of 1, the function aborts the operation and set @error to ERROR_ACCESS_DENIED (5).
; Author    : Pusofalse
; ==========================================================================================
Func _TreeResetNamedSecurityInfo($sObject, $iObjType, $iSecurLevel, $pOwner, $pGroup, _
            $pDacl, $pSacl, $fKeepExplicit = True, $sCallback = "")

    Local $iResult, $hProgress, $pProgress
    If ($iObjType <> $SE_FILE_OBJECT) And ($iObjType <> $SE_REGISTRY_KEY) Then
        Return SetError(87, 0, 0)
    EndIf

    $hProgress = DllCallbackRegister("___fnProgress", "none", "wstr;dword;ptr;wstr;int")
    $pProgress = DllCallbackGetPtr($hProgress)

    $iResult = DllCall("AdvApi32.dll", "dword", "TreeResetNamedSecurityInfoW", "wstr", $sObject, _
            "dword", $iObjType, "dword", $iSecurLevel, "ptr", $pOwner, "ptr", $pGroup, _
            "ptr", $pDacl, "ptr", $pSacl, "int", $fKeepExplicit, "ptr", $pProgress, _
            "int", 2, "wstr", $sCallback)
    If (@error) Then Return SetError(1, DllCallbackFree($hProgress), 0)
    DllCallbackFree($hProgress)
    Return SetError($iResult[0], 0, $iResult[0] = 0)
EndFunc ;==>_TreeResetNamedSecurityInfo

; #### FUNCTION (INTERNAL-USED-ONLY) ####
; ==========================================================================================
Func ___fnProgress($sObject, $iStatus, $pInvokeSetting, $sCallback, $fSecuritySet)
    Local $iAbort, $tInvoke

    If ($sCallback <> "") Then
        $iAbort = Call($sCallback, $sObject, $iStatus, $fSecuritySet)
        If ($iAbort = 1) OR ($iAbort = 2) Then
            $tInvoke = DllStructCreate("int", $pInvokeSetting)
            DllStructSetData($tInvoke, 1, $iAbort + 3)
        EndIf
    EndIf
EndFunc ;==>___fnProgress


The problem im having now is when printing to more than one printer at the same time, the usb2.exe send data in a sync fashon one file to printer after printer. My problem is when sending data un chunks of 4096Bytes (just as the usb2) one printer recieves proper data and the other starts printing text strings of the data.

I have found that when sending data in small chunks, i run into issues with more data being sent than expected, but when sending small amounts of data all in one go...everything works fine. Which is good and all but i also need to be able to send a firmware file...which locks up becuase the data is too big.

Any suggestions? Thanks

What is what? What is what.

Link to comment
Share on other sites

  • 3 weeks later...

Just now that I saw this topic, I have to analyze all the codes and do some tests ok?

What kind of file is this: USB2.apmx86(NOT AU3.).AU3

JS

Edited by JScript

http://forum.autoitbrasil.com/ (AutoIt v3 Brazil!!!)

Somewhere Out ThereJames Ingram

somewh10.png

dropbo10.pngDownload Dropbox - Simplify your life!
Your virtual HD wherever you go, anywhere!

Link to comment
Share on other sites

Its a program called API Monitor. Useful for debugging and analyzing.how windows apis are called.

I am attempting to achieve Async writing to the device. Autoit cant due that threaded, so i chose the method of sending the data to the devices in 4096 byte chunks. So, printer1 would get the first chunk then printer2 would get the same chunk, afterwards printer1 would get the next chunk and loop until the file is sent.

Usb2 runs in sync mode one file one printer at a time, im attempting one file multiple printers at a time. The issue i have is with splitting the data into chunks, which causes corruption. I have the script working exactly like usb2 excet for chunking the data into 4096 byte chunks...autoit is doing more and interrupting the transfer.

What is what? What is what.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...