Jump to content

IOCTL_DISK_SET_CACHE_INFORMATION


Recommended Posts

I have been working on a script to query and change write cache settings on various hard drives, both SATA and SAS, as part of a larger automation suite.

The following functions work correctly but if run enough times in a row, sometimes as little as 2, will crash Autoit with a rc -1073741819.

It tends to crash sooner if I enclude more functions, even the standard functions such as array.au3. (Note: I'm not saying Array.au3 causes the crash only that by including those functions my script crashes more often.)

I based these functions on the information provided on the MSDN IOCTL_DISK_SET_CACHE_INFORMATION Control Code.

#AutoIt3Wrapper_au3check_parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6
#RequireAdmin
#include-once
;~ #include<Array.au3>
#include<Constants.au3>
#include<String.au3>
Global Const $Method_Buffered = 0
Global Const $File_Any_Access = 0
Global Const $File_Read_Access = 1
Global Const $File_Write_Access = 2

Global Const $IOCTL_DISK_BASE =0x00000007
Global Const $IOCTL_DISK_GET_CACHE_INFORMATION = '0x' & CTL_CODE($IOCTL_DISK_BASE, 0x0035, $Method_Buffered, $FILE_READ_ACCESS)
Global Const $IOCTL_DISK_SET_CACHE_INFORMATION = '0x' & CTL_CODE($IOCTL_DISK_BASE, 0x0036, $Method_Buffered, BitXOR($FILE_READ_ACCESS, $FILE_WRITE_ACCESS))

Global $DriveNum = 1  ;Must be a valid drive number as shown in Disk Management
for $icc = 1 to 1000
ConsoleWrite('-------------------------------' & @CR)
ConsoleWrite('Loop#:'&$icc&@CRLF)
ConsoleWrite('Write Cache: '&_IOCTL_DISK_GET_CACHE_INFORMATION($DriveNum)&@CRLF)
_IOCTL_DISK_SET_CACHE_INFORMATION($DriveNum,1) ;1 enables, 0 disables
Next

Func _IOCTL_DISK_GET_CACHE_INFORMATION($DriveNum)
Local $WriteCacheEnabled, $vDrive
Local $BlocksPrefetch = DllStructCreate('dword Minimum;dword Maximum;')
Local $ScalarPrefetch = DllStructCreate('dword Minimum;dword Maximum;dword MaximumBlocks;', DllStructGetPtr($BlocksPrefetch))
Local $DiskInformation = 'BOOLEAN ParametersSavable; BOOLEAN ReadCacheEnabled; BOOLEAN WriteCacheEnabled; dword ReadRetentionPriority[3];dword WriteRetentionPriority[3]; word DisablePrefetchTransferLength; BOOLEAN PrefetchScaler;'
Local $Disk_Cache_Information = DllStructCreate($DiskInformation, DllStructGetPtr($ScalarPrefetch))
If $Disk_Cache_Information = 0 Then
  ConsoleWrite('Error in Disk_Cache_Information: ' & @error & @CR)
  Exit
EndIf
$vDrive = OpenDrive($DriveNum)

If $vDrive = 0 Then Exit
  DllCall("kernel32.dll", "int", _
   "DeviceIoControl", _
   "ptr", $vDrive, _
   "dword", $IOCTL_DISK_GET_CACHE_INFORMATION, _
   "ptr", 0, _
   "dword", 0, _
   "ptr", DllStructGetPtr($Disk_Cache_Information), _
   "dword", DllStructGetSize($Disk_Cache_Information), _
   "dword*", 0, _
   "ptr", 0 _
   )

If @error <> 0 Then
  _GetLastErrorMessage("Error in DeviceIoControl call to IOCTL_DISK_GET_CACHE_INFORMATION:" & String(@error) & "!")
   SetError(-1,0,-1)
   return -1
  EndIf

$WriteCacheEnabled = DllStructGetData($Disk_Cache_Information, "WriteCacheEnabled")

CloseDrive($vDrive)

$vDrive = 0
$Disk_Cache_Information = 0
$DiskInformation = 0
$ScalarPrefetch = 0
$BlocksPrefetch = 0
Return $WriteCacheEnabled
EndFunc   ;==>_IOCTL_DISK_GET_CACHE_INFORMATION

Func _IOCTL_DISK_SET_CACHE_INFORMATION($DriveNum,$Switch= 1)
Local $vDrive
Local $BlocksPrefetch = DllStructCreate('dword Minimum;dword Maximum;')
Local $ScalarPrefetch = DllStructCreate('dword Minimum;dword Maximum;dword MaximumBlocks;', DllStructGetPtr($BlocksPrefetch))
Local $DiskInformation = 'BOOLEAN ParametersSavable; BOOLEAN ReadCacheEnabled; BOOLEAN WriteCacheEnabled; dword ReadRetentionPriority[3];dword WriteRetentionPriority[3]; word DisablePrefetchTransferLength; BOOLEAN PrefetchScaler;'

Local $Disk_Cache_Information = DllStructCreate($DiskInformation, DllStructGetPtr($ScalarPrefetch))
If $Disk_Cache_Information = 0 Then
  ConsoleWrite('Error in Disk_Cache_Information: ' & @error & @CR)
  Exit
EndIf
local $returned = DllStructCreate('int;')
DllStructSetData($Disk_Cache_Information,'WriteCacheEnabled',$Switch)
If @error <> 0 Then
  _GetLastErrorMessage("Error in DeviceIoControl call to IOCTL_DISK_GET_CACHE_INFORMATION:" & String(@error) & "!")
   SetError(-1,0,-1)
   return -1
EndIf
$vDrive = OpenDrive($DriveNum)
If $vDrive = 0 Then Exit
  DllCall("kernel32.dll", "int", _
   "DeviceIoControl", _
   "ptr", $vDrive, _
   "dword", $IOCTL_DISK_SET_CACHE_INFORMATION, _
   "ptr", DllStructGetPtr($Disk_Cache_Information), _
   "dword", DllStructGetSize($Disk_Cache_Information), _
   "ptr", 0, _
   "dword", 0, _
   "dword*", DllStructGetPtr($returned), _
   "ptr", 0 _
   )

If @error <> 0 Then
  _GetLastErrorMessage("Error in DeviceIoControl call to IOCTL_DISK_GET_CACHE_INFORMATION:" & String(@error) & "!")
   SetError(-1,0,-1)
   return -1
EndIf
CloseDrive($vDrive)
$vDrive = 0
$Disk_Cache_Information = 0
$DiskInformation = 0
$ScalarPrefetch = 0
$BlocksPrefetch = 0
EndFunc   ;==>_IOCTL_DISK_SET_CACHE_INFORMATION

Func CTL_CODE($DeviceType, $Function, $Method, $Access)
;~  Converted to autoit from the MSDN msdn.microsoft.com/en-us/library/ms904001.aspx
;~ #define CTL_CODE(DeviceType, Function, Method, Access) (
;~   ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)
;~ )
Return Hex(BitXOR(BitShift($DeviceType, -16), BitShift($Access, -14), BitShift($Function, -2), $Method))
EndFunc   ;==>CTL_CODE


Func OpenDrive($driveNum)
Local $vDrive
;Get handle to drive
$vDrive = DllCall( _
   "kernel32.dll", "hwnd", _
   "CreateFile", _
   "str", "[url="file://\.PhysicalDrive"]\\.\PhysicalDrive[/url]" & String($driveNum), _
   "dword", BitOR($GENERIC_READ, $GENERIC_WRITE), _
   "dword", BitOR($FILE_SHARE_READ, $FILE_SHARE_WRITE), _
   "ptr", 0, _
   "dword", $OPEN_EXISTING, _
   "dword", 0, _
   "ptr", 0 _
   )
If @error <> 0 Then
  MsgBox(1, "EXITING...", "CreateFile DLLCall failed!")
  Exit (1)
EndIf
Return $vDrive[0]
EndFunc   ;==>OpenDrive
Func CloseDrive($vDrive)
Local $rVal = DllCall("kernel32.dll", "int", "CloseHandle", "hwnd", $vDrive)
If @error Then Return SetError(-1, -1, 0)
Return $rVal[0]
EndFunc   ;==>CloseDrive

Func _GetLastErrorMessage($DisplayMsgBox = "")
Local $ret, $s
Local $p = DllStructCreate("char[4096]")
Local Const $FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
If @error Then Return ""
$ret = DllCall("Kernel32.dll", "int", "GetLastError")
$ret = DllCall("kernel32.dll", "int", "FormatMessage", _
   "int", $FORMAT_MESSAGE_FROM_SYSTEM, _
   "ptr", 0, _
   "int", $ret[0], _
   "int", 0, _
   "ptr", DllStructGetPtr($p), _
   "int", 4096, _
   "ptr", 0)
$s = DllStructGetData($p, 1)
$p = 0
If $DisplayMsgBox <> "" Then MsgBox(0, "_GetLastErrorMessage", $DisplayMsgBox & @CRLF & $s)
Return $s
EndFunc   ;==>_GetLastErrorMessage

As these are functions with locally declared variables I would have guessed that the variables would clear after each run, but it seems like something is being retained after the function exits.

Can anyone see where I am going wrong with this DllCall?

Kerros===============================================================How to learn scripting: Figure out enough to be dangerous, then ask for assistance.

Link to comment
Share on other sites

This looks wrong already:

Func _IOCTL_DISK_GET_CACHE_INFORMATION($DriveNum)
    Local $WriteCacheEnabled, $vDrive
    Local $BlocksPrefetch = DllStructCreate('dword Minimum;dword Maximum;')
    Local $ScalarPrefetch = DllStructCreate('dword Minimum;dword Maximum;dword MaximumBlocks;', DllStructGetPtr($BlocksPrefetch))
    
    ; ...
    
EndFunc   ;==>_IOCTL_DISK_GET_CACHE_INFORMATION

You create a struct with two DWORDs (with an odd trailing ";" in it), then create another struct on the same address space with three DWORDs?

:(

Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law
Link to comment
Share on other sites

This looks wrong already:

Func _IOCTL_DISK_GET_CACHE_INFORMATION($DriveNum)
    Local $WriteCacheEnabled, $vDrive
    Local $BlocksPrefetch = DllStructCreate('dword Minimum;dword Maximum;')
    Local $ScalarPrefetch = DllStructCreate('dword Minimum;dword Maximum;dword MaximumBlocks;', DllStructGetPtr($BlocksPrefetch))
    
    ; ...
    
EndFunc   ;==>_IOCTL_DISK_GET_CACHE_INFORMATION

You create a struct with two DWORDs (with an odd trailing ";" in it), then create another struct on the same address space with three DWORDs?

It's more than that, in the next line he creates a third struct on the same pointer:

Local $Disk_Cache_Information = DllStructCreate($DiskInformation, DllStructGetPtr($ScalarPrefetch))

Somehow it makes no sense...

UDFS & Apps:

Spoiler

DDEML.au3 - DDE Client + Server
Localization.au3 - localize your scripts
TLI.au3 - type information on COM objects (TLBINF emulation)
TLBAutoEnum.au3 - auto-import of COM constants (enums)
AU3Automation - export AU3 scripts via COM interfaces
TypeLibInspector - OleView was yesterday

Coder's last words before final release: WE APOLOGIZE FOR INCONVENIENCEĀ 

Link to comment
Share on other sites

It should be actually:

Local $DiskInformation = "BOOLEAN ParametersSavable;BOOLEAN ReadCacheEnabled;BOOLEAN  WriteCacheEnabled;int ReadRetentionPriority;int WriteRetentionPriority;WORD DisablePrefetchTransferLength;BOOLEAN PrefetchScalar;WORD Minimum;WORD Maximum;WORD MaximumBlocks;"
Local $Disk_Cache_Information = DllStructCreate($DiskInformation)

UDFS & Apps:

Spoiler

DDEML.au3 - DDE Client + Server
Localization.au3 - localize your scripts
TLI.au3 - type information on COM objects (TLBINF emulation)
TLBAutoEnum.au3 - auto-import of COM constants (enums)
AU3Automation - export AU3 scripts via COM interfaces
TypeLibInspector - OleView was yesterday

Coder's last words before final release: WE APOLOGIZE FOR INCONVENIENCEĀ 

Link to comment
Share on other sites

It should be actually:

Local $DiskInformation = "BOOLEAN ParametersSavable;BOOLEAN ReadCacheEnabled;BOOLEAN  WriteCacheEnabled;int ReadRetentionPriority;int WriteRetentionPriority;WORD DisablePrefetchTransferLength;BOOLEAN PrefetchScalar;WORD Minimum;WORD Maximum;WORD MaximumBlocks;"
Local $Disk_Cache_Information = DllStructCreate($DiskInformation)

Thank you, that seems to have fixed the issue. As for why I did the structs that I did, reviewing the MSDN showed an overlapped structure, so I was trying to follow this structure:

union {
        struct {
            USHORT  Minimum;
            USHORT  Maximum;
            USHORT  MaximumBlocks;
        } ScalarPrefetch;
        struct {
            USHORT  Minimum;
            USHORT  Maximum;
        } BlockPrefetch;
    };

I know my understand of this is very low, but it seems like you are not using an overlapped structure there. Just trying to gain some insite so I don't have this same issue in the future.

Kerros===============================================================How to learn scripting: Figure out enough to be dangerous, then ask for assistance.

Link to comment
Share on other sites

Thank you, that seems to have fixed the issue. As for why I did the structs that I did, reviewing the MSDN showed an overlapped structure, so I was trying to follow this structure:

union {
        struct {
            USHORT  Minimum;
            USHORT  Maximum;
            USHORT  MaximumBlocks;
        } ScalarPrefetch;
        struct {
            USHORT  Minimum;
            USHORT  Maximum;
        } BlockPrefetch;
    };

I know my understand of this is very low, but it seems like you are not using an overlapped structure there. Just trying to gain some insite so I don't have this same issue in the future.

Members of a union are located at the same address and the union itself occupies the amount of memory of the size of the largest member. In this case ScalarPrefetch is the largest of the two, so you take only this in the DllStruct declaration and can then access the substructs accordingly:

Local $BlocksPrefetch = DllStructCreate('dword Minimum;dword Maximum;', DllStructGetPtr($Disk_Cache_Information, 'Minimum'))
; or
Local $ScalarPrefetch = DllStructCreate('dword Minimum;dword Maximum;dword MaximumBlocks;', DllStructGetPtr($Disk_Cache_Information, 'Minimum'))
Edited by doudou

UDFS & Apps:

Spoiler

DDEML.au3 - DDE Client + Server
Localization.au3 - localize your scripts
TLI.au3 - type information on COM objects (TLBINF emulation)
TLBAutoEnum.au3 - auto-import of COM constants (enums)
AU3Automation - export AU3 scripts via COM interfaces
TypeLibInspector - OleView was yesterday

Coder's last words before final release: WE APOLOGIZE FOR INCONVENIENCEĀ 

Link to comment
Share on other sites

Ok, so what you did was take the larger of the 2 structures and include the elements in the main structure thereby bypassing the need to combine two structures. Thank you for your help and explanation. It makes much more sense now.

Kerros

Kerros===============================================================How to learn scripting: Figure out enough to be dangerous, then ask for assistance.

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...