Leaderboard
Popular Content
Showing content with the highest reputation on 08/22/2018 in all areas
-
GUIRegisterMsg20 - Subclassing Made Easy
Professor_Bernd reacted to LarsJ for a topic
GUIRegisterMsg() subclasses an AutoIt GUI. Ie. a window created with GuiCreate(). A few issues are related to GUIRegisterMsg(): It cannot subclass a control Some messages cannot be handled Only one function for a specific message These issues cannot be described as errors. They are probably consequences of a design choice to limit the complexity of the function. A few issues are related to the subclassing technique in general: _WinAPI_SetWindowLong() The Subclassing bug GUIRegisterMsg20() addresses all these issues. 2020-02-23: It's probably not true that GUIRegisterMsg() is based on the subclassing technique. The messages handled by GUIRegisterMsg() are more likely to originate from the message loop in the window procedure (implemented in internal code) of the main GUI window. What is subclassing? Subclassing is a technique to get information from standard controls through Windows messages, and to modify the functionality of standard controls by responding to these messages. Eg. to change the background color of listview cells. GUIRegisterMsg20 GUIRegisterMsg20.au3 is a small UDF implemented in 100 code lines. This is the documentation for GUIRegisterMsg20(): ; Register a message handler function for a window or control message ; GUIRegisterMsg20( $vWinOrCtrl, _ ; Window handle or control ID/handle ; $WM_MESSAGE, _ ; Window message or control message ; $hFunction ) ; User supplied message handler func ; ; $vWinOrCtrl GUI handle as returned by GUICreate, controlID as returned by GUICtrlCreate<Control> functions ; or control handle as returned by _GUICtrl<Control>_Create UDF functions or GUICtrlGetHandle. ; ; $WM_MESSAGE A Windows message code as listed in Appendix section in help file. Or a control message code ; eg. a LVM_MESSAGE or TVM_MESSAGE as listed in ListViewConstants.au3 or TreeViewConstants.au3. ; This parameter is only checked to be within the valid range of Windows and control messages: ; 0x0000 - 0x7FFF. ; ; $hFunction The user supplied function (not the name but the FUNCTION) to call when the message appears. ; The function is defined in exactly the same way as the function for the official GUIRegisterMsg: ; Takes four input parameters ($hWnd, $iMsg, $wParam, $lParam) and returns $GUI_RUNDEFMSG to conti- ; nue with default message handling, or a specific value if the message is handled by the function. ; ; Error code in @error Return value ; 1 => Invalid window handle or control ID/handle Success => 1 ; 2 => Invalid Window or control message Failure => 0 ; 3 => Invalid function handle ; 4 => Too many subclasses And this is the very central internal function that is called directly from the code in ComCtl32.dll: Func GUIRegisterMsg20_Handler( $hWnd, $iMsg, $wParam, $lParam, $idx, $pData ) If $iMsg <> $aGUIRegisterMsg20[$idx][3] Then Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0] Local $vRetVal = $aGUIRegisterMsg20[$idx][4]( $hWnd, $iMsg, $wParam, $lParam ) ; Execute user supplied message handler function If $vRetVal == "GUI_RUNDEFMSG" Then Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0] Return $vRetVal #forceref $pData EndFunc It's rarely necessary to call GUIUnRegisterMsg20(). Message handlers are unregistered when the program exits. An exception is a situation where message handles are registered and unregistered on the fly, eg. due to user actions. Message monitor When it comes to topics like subclassing, a message monitor is required eg. Windows Message Monitor. ListView example Listview example is a custom drawn listview with colored cells. If we focus on subclass implementation, this is the difference between GUIRegisterMsg() and GUIRegisterMsg20() code. GUIRegisterMsg(): ; ... GUIRegisterMsg( $WM_NOTIFY, "WM_NOTIFY" ) ; ... GUIRegisterMsg20(): #include "..\..\Includes\GUIRegisterMsg20.au3" ; ... GUIRegisterMsg20( $hGui, $WM_NOTIFY, WM_NOTIFY ) ; ... GUIRegisterMsg20() takes $hGui as first parameter. The message handler function in third parameter is a function variable. Not a string. The message handler function, WM_NOTIFY, is exactly the same in the two examples. If you run the scripts that contains message monitor code, you will notice a remarkable difference between the two scripts. In the GUIRegisterMsg20() script, there are no NM_CUSTOMDRAW notifications in the monitor. What's the reason? All GUIRegisterMsg() examples in help file that looks like this: ; ... GUIRegisterMsg( $WM_MESSAGE, "WM_MESSAGE" ) ; ... Can be implemented with GUIRegisterMsg20() this way: ; ... GUIRegisterMsg20( $hGui, $WM_MESSAGE, WM_MESSAGE ) ; ... Input example Input example is based on this forum thread. Run Input.au3 to see the issues: You can paste a number into the control, a context menu shows up on right click, and a tooltip is displayed if you press a letter. Let's see what's going on with the message monitor. When you solve issues with a message monitor, you are generally interested in finding a particular message or a pattern of a few messages. If there are multiple instances of the message or pattern, it's easier to find. The action that generates the message or pattern should be repeated 3 - 4 times. Run "Input, wmm.au3", paste a number into the control 3 - 4 times eg. 18 (result = 18181818), right click 3 - 4 times, press a letter 3 - 4 times. Press Esc to delete Input GUI and start WMM GUI. Scroll down until you see tomato red messages. You'll see the messages WM_PASTE, WM_CONTEXTMENU and EM_SHOWBALLOONTIP. Note that all three messages are received by the Input control. This means that GUIRegisterMsg() cannot be used. It can only handle messages received by the GUI. Because all messages are received by the Input control you can register three message handlers this way in "Input, subclass.au3": ; Subclass Input GUIRegisterMsg20( $idInput, $WM_PASTE, WM_PASTE ) GUIRegisterMsg20( $idInput, $WM_CONTEXTMENU, WM_CONTEXTMENU ) GUIRegisterMsg20( $idInput, $EM_SHOWBALLOONTIP, EM_SHOWBALLOONTIP ) To implement message handler functions you have to read Microsoft documentation. Only for EM_SHOWBALLOONTIP it's completely clear that the message is suppressed by returning False or 0. For all three messages, however, you must return 0 to suppress the message: ; WM_PASTE message handler function Func WM_PASTE( $hWnd, $iMsg, $wParam, $lParam ) Return 0 #forceref $hWnd, $iMsg, $wParam, $lParam EndFunc ; WM_CONTEXTMENU message handler function Func WM_CONTEXTMENU( $hWnd, $iMsg, $wParam, $lParam ) Return 0 #forceref $hWnd, $iMsg, $wParam, $lParam EndFunc ; EM_SHOWBALLOONTIP message handler function Func EM_SHOWBALLOONTIP( $hWnd, $iMsg, $wParam, $lParam ) Return 0 #forceref $hWnd, $iMsg, $wParam, $lParam EndFunc As all three functions are the same, the code can be reduced a little bit in "Input, final.au3": ; GUICtrlCreateInput - Disable copy/ paste, right click menu and balloon pop-up ; https://www.autoitscript.com/forum/index.php?showtopic=179052 #include <GuiEdit.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include "..\..\Includes\GUIRegisterMsg20.au3" Opt( "MustDeclareVars", 1 ) Example() Func Example() ; Create GUI GUICreate( "Input (numbers only)", 300, 118 ) ; Create Input Local $idInput = GUICtrlCreateInput( "", 50, 50, 200, 18, $GUI_SS_DEFAULT_INPUT+$ES_NUMBER ) ; Create Label GUICtrlCreateLabel( "No paste, no context menu, no balloontip", 50, 70, 200, 18 ) ; Subclass Input GUIRegisterMsg20( $idInput, $WM_PASTE, InputFunc ) GUIRegisterMsg20( $idInput, $WM_CONTEXTMENU, InputFunc ) GUIRegisterMsg20( $idInput, $EM_SHOWBALLOONTIP, InputFunc ) ; Show GUI GUISetState() ; Msg loop While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch WEnd ; Cleanup GUIDelete() EndFunc ; Input message handler function Func InputFunc( $hWnd, $iMsg, $wParam, $lParam ) Return 0 #forceref $hWnd, $iMsg, $wParam, $lParam EndFunc Just by looking at the code (including UDF in top, registering and implementing message handlers in middle and bottom) it seems not to be that hard. Header example The width of listview columns can be changed by dragging header item separators with the mouse. If you want to disable this feature you have to suppress HDN_BEGINTRACK notifications from the header control. They are contained in WM_NOTIFY messages. And you have to suppress WM_SETCURSOR messages when the mouse cursor hover over header item separators. This prevents a cursor shape indicating that separators can be dragged. Run "ListView header, wmm.au3". Drag the header item separators forth and back. This generates a huge number of messages. Press Esc to delete GUI and start WMM GUI. Scroll down until you see tomato red messages. Note that HDN_BEGINTRACK notifications in WM_NOTIFY messages are sent first to the listview and then to the GUI. They can be catched by GUIRegisterMsg(). Here they'll be catched by GUIRegisterMsg20() from the listview. WM_SETCURSOR messages must be catched from the header: ; Subclass ListView and Header GUIRegisterMsg20( $idListView, $WM_NOTIFY, ListViewFunc ) GUIRegisterMsg20( $hHeader, $WM_SETCURSOR, HeaderFunc ) Handler functions: ; ListView message handler function Func ListViewFunc( $hWnd, $iMsg, $wParam, $lParam ) If $HDN_BEGINTRACKW = DllStructGetData( DllStructCreate( $tagNMHEADER, $lParam ), "Code" ) Then Return 1 Return $GUI_RUNDEFMSG #forceref $hWnd, $iMsg, $wParam EndFunc ; Header message handler function Func HeaderFunc( $hWnd, $iMsg, $wParam, $lParam ) Return 1 #forceref $hWnd, $iMsg, $wParam, $lParam EndFunc Again, just by looking at the code (including UDF in top, registering and implementing message handlers in middle and bottom) it seems not to be that hard. 2020-02-23: Note that if you want to prevent the width of the listview columns from being changed you can simply add the HDS_NOSIZING style to the header control. I wasn't aware of that when I made the example. TreeView example (2018-08-15) TreeView example is based on this forum thread. Run TreeView.au3 (copied from first post in thread) to see the issue: No WM_COMMAND messages are received on right click in treeview. "TreeView, wmm.au3" shows the problem: WM_COMMAND messages generated on right clicks are sent to the treeview and not the main GUI. This means that GUIRegisterMsg() isn't able to catch the messages. To solve the problem include GUIRegisterMsg20.au3 in top of script and use GUIRegisterMsg20() to subclass the treeview as it's done in "TreeView, subclass.au3": ;... #include "..\..\Includes\GUIRegisterMsg20.au3" ;... GUIRegisterMsg20( $g_hTreeView, $WM_COMMAND, WM_COMMAND ) The existing WM_COMMAND() message handler is reused. That was two code lines to solve the problem. It can hardly be easier. Too much code (2018-09-30) A custom drawn listview generates a lot of WM_NOTIFY messages. If custom draw code is used to draw selected items with GDI-functions in the post paint drawing stage, much code will be executed to respond to all these messages. If it's also a virtual listview, it'll double the number of messages (Examples\5) Too much code\ListView.au3): Func WM_NOTIFY( $hWnd, $iMsg, $wParam, $lParam ) Local Static $tText = DllStructCreate( "wchar[100]" ), $pText = DllStructGetPtr( $tText ) Local Static $tRect = DllStructCreate( $tagRECT ), $pRect = DllStructGetPtr( $tRect ) Switch DllStructGetData( DllStructCreate( $tagNMHDR, $lParam ), "Code" ) Case $LVN_GETDISPINFOW ; Fill virtual listview Local $tNMLVDISPINFO = DllStructCreate( $tagNMLVDISPINFO, $lParam ) If Not BitAND( DllStructGetData( $tNMLVDISPINFO, "Mask" ), $LVIF_TEXT ) Then Return Local $sItem = $aItems[DllStructGetData($tNMLVDISPINFO,"Item")][DllStructGetData($tNMLVDISPINFO,"SubItem")] DllStructSetData( $tText, 1, $sItem ) DllStructSetData( $tNMLVDISPINFO, "TextMax", StringLen( $sItem ) ) DllStructSetData( $tNMLVDISPINFO, "Text", $pText ) Return Case $NM_CUSTOMDRAW ; Draw back colors Local $tNMLVCUSTOMDRAW = DllStructCreate( $tagNMLVCUSTOMDRAW, $lParam ) Local $dwDrawStage = DllStructGetData( $tNMLVCUSTOMDRAW, "dwDrawStage" ), $iItem Switch $dwDrawStage ; Holds a value that specifies the drawing stage Case $CDDS_PREPAINT ; Before the paint cycle begins Return $CDRF_NOTIFYITEMDRAW ; Notify the parent window of any item-related drawing operations Case $CDDS_ITEMPREPAINT ; Before painting an item Return $CDRF_NOTIFYSUBITEMDRAW ; Notify the parent window of any subitem-related drawing operations Case $CDDS_ITEMPREPAINT + $CDDS_SUBITEM ; Before painting a subitem $iItem = DllStructGetData( $tNMLVCUSTOMDRAW, "dwItemSpec" ) If GUICtrlSendMsg( $idListView, $LVM_GETITEMSTATE, $iItem, $LVIS_SELECTED ) Then Return $CDRF_NOTIFYPOSTPAINT ; Selected item DllStructSetData( $tNMLVCUSTOMDRAW, "ClrTextBk", $aColors[$iItem][DllStructGetData($tNMLVCUSTOMDRAW,"iSubItem")] ) ; Normal item Return $CDRF_NEWFONT ; $CDRF_NEWFONT must be returned after changing font or colors Case $CDDS_ITEMPOSTPAINT + $CDDS_SUBITEM ; After painting a subitem Local $hDC = DllStructGetData( $tNMLVCUSTOMDRAW, "hdc" ) ; Subitem rectangle $iItem = DllStructGetData( $tNMLVCUSTOMDRAW, "dwItemSpec" ) Local $iSubItem = DllStructGetData( $tNMLVCUSTOMDRAW, "iSubItem" ) DllStructSetData( $tRect, "Left", $LVIR_LABEL ) DllStructSetData( $tRect, "Top", $iSubItem ) GUICtrlSendMsg( $idListView, $LVM_GETSUBITEMRECT, $iItem, $pRect ) DllStructSetData( $tRect, "Left", DllStructGetData( $tRect, "Left" ) + 2 ) DllStructSetData( $tRect, "Top", DllStructGetData( $tRect, "Top" ) + 1 ) DllStructSetData( $tRect, "Right", DllStructGetData( $tRect, "Right" ) - 2 ) DllStructSetData( $tRect, "Bottom", DllStructGetData( $tRect, "Bottom" ) - 1 ) ; Subitem back color Local $hBrush = DllCall( "gdi32.dll", "handle", "CreateSolidBrush", "int", $aColors[$iItem][$iSubItem] )[0] ; _WinAPI_CreateSolidBrush DllCall( "user32.dll", "int", "FillRect", "handle", $hDC, "struct*", $tRect, "handle", $hBrush ) ; _WinAPI_FillRect DllCall( "gdi32.dll", "bool", "DeleteObject", "handle", $hBrush ) ; _WinAPI_DeleteObject ; Draw subitem text DllStructSetData( $tRect, "Top", DllStructGetData( $tRect, "Top" ) + 1 ) DllCall( "gdi32.dll", "int", "SetTextColor", "handle", $hDC, "int", 0 ) ; _WinAPI_SetTextColor DllCall( "gdi32.dll", "int", "SetBkMode", "handle", $hDC, "int", $TRANSPARENT ) ; _WinAPI_SetBkMode DllCall( "user32.dll", "int", "DrawTextW", "handle", $hDC, "wstr", $aItems[$iItem][$iSubItem], "int", -1, "struct*", $tRect, "uint", $DT_CENTER ) ; _WinAPI_DrawText Return $CDRF_NEWFONT ; $CDRF_NEWFONT must be returned after changing font or colors EndSwitch EndSwitch Return $GUI_RUNDEFMSG #forceref $hWnd, $iMsg, $wParam EndFunc If there are a lot of messages and some messages perform quite a lot of code, you can end up in a situation where more code is performed than there's time for. The result is that the WM_NOTIFY messages are blocked and the code fails: You can provoke the error this way: Click a row in the listview (not one of the very top rows). Press Shift+Down arrow to select multiple rows. The problem will arise after just a few rows. Press Ctrl+Break in SciTE to stop the code. 2020-02-23: The only solution to the problem is probably to implement the WM_NOTIFY() function in compiled code as demonstrated here. Message flow (update 2018-09-30) In these examples, the message flow is examined with left and right mouse clicks. Several message handles are implemented. A standard AutoIt message loop (MessageLoop Mode) where messages are received with GUIGetMsg(). A message handler based on GUIRegisterMsg(). And a message handler created with GUIRegisterMsg20(). In addition, there is the internal AutoIt message handler (C++ code). The purpose is to determine in which order a message is received by all these different message handlers. Script 1) implements the three message handlers. By examining the message flow with the monitor we can see that the messages are received in this order: Script 2) implements an additional GUIRegisterMsg20() message handler for the same mouse click messages but with another message handler function. It seems that the last created GUIRegisterMsg20() handler, is the first to receive the mouse clicks. In script 3) mouse clicks are performed in an Edit control instead of an empty GUI. The GUIRegisterMsg() message handler does not receive the mouse clicks. At what point in the flow chart does the message monitor hook into the message flow? Since message detection is implemented through subclassing it hooks into the message flow just before the internal AutoIt message handler. Now we can answer the question from the listview example above: Why do we not see any NM_CUSTOMDRAW notifications in the message monitor from the GUIRegisterMsg20() script. Because these notifications are all answered with one of the custom draw return values and not $GUI_RUNDEFMSG. The custom draw return values goes directly back to the operating system and the messages are no longer forwarded in the message chain. The message flow stops already at the GUIRegisterMsg20() message handler function and never reaches the internal AutoIt message handler. Therefore, the messages do not reach the monitor either. The issues The listview example at the top shows that GUIRegisterMsg20() is very similar to GUIRegisterMsg(). You can do the same things with GUIRegisterMsg20() as you can do with GUIRegisterMsg(). This means that you can use GUIRegisterMsg20() in a UDF, while the user can use GUIRegisterMsg() in his own code exactly as described in the help file. From the flow chart above you know that the GUIRegisterMsg20() message handler function will receive messages before the GUIRegisterMsg() message handler function. Let's review the issues mentioned in beginning of post. Only GUIRegisterMsg20() can subclass a control and receive control messages. This is demonstrated in the input example. The mouse clicks in the edit control discussed above shows that GUIRegisterMsg() isn't receiving all WM_MESSAGEs and therefore cannot handle these messages. The mouse clicks in the section above also shows that GUIRegisterMsg20() can use several functions for the same message. Because GUIRegisterMsg20() uses SetWindowSubclass to implement subclassing, problems related to _WinAPI_SetWindowLong() are avoided. Because GUIRegisterMsg20() returns directly from the DefSubclassProc DllCall, the subclassing bug never comes into play. Conclusion GUIRegisterMsg20 UDF takes care of the tedious and cumbersome task of configuring subclassing. Ie. to set up SetWindowSubclass, DefSubclassProc and RemoveWindowSubclass functions. It uses a message handler function that is very similar to the function used in the official GUIRegisterMsg(). You can concentrate on analyzing the message flow and code the message handler function. 7z-file You need AutoIt 3.3.12 or later. Tested on Windows 7 and Windows 10. Note that the contents of the Includes folder in the Windows Message Monitor 7z-file must be installed in the WMMincl directory to use Windows Message Monitor. Comments are welcome. Let me know if there are any issues. GUIRegisterMsg20.7z1 point -
[FUNC] Bitlocker Drive Info
ERCEduTech reacted to colombeen for a topic
Hi everyone, I created a function to gather bitlocker information. It can tell you whether or not a drive is protected, which encryption method is being used, ... I tried to cover all the details in the function description The function (and 3 "internal" functions) : ; #FUNCTION# ==================================================================================================================== ; Name...........: _BitlockerDriveInfo ; Description ...: Get Bitlocker information for one or multiple drives ; Syntax.........: _BitlockerDriveInfo([$sDrive[, $sComputer = @ComputerName[, $bDebug = False]]]) ; Parameters ....: $sDrive - Optional: The drive. Allowed values are: ; |"" - Get the info for all available drives ; |Letter: - Get the info for the specific drive ; $sComputer - Optional: The computer from which the info should be requested ; $bDebug - Optional: Shows the hex ReturnValue from the WMI methods if set to True ; Return values .: Success - Returns a 2D array with the following information ; |[string] Drive Letter ; |[string] Drive Label ; |[string] Volume Type ; |[bool] Initialized For Protection ; |[string] Protection Status ; |[string] Lock Status ; |[bool] Auto Unlock Enabled ; |[bool] Auto Unlock Key Stored ; |[string] Conversion Status ; |[string] Encryption Method ; |[int] Encryption Percentage ; |[string] Wiping Status ; |[int] Wiping Percentage ; |[array] Key Protectors (Or [string] "None" if the drive isn't protected) ; Failure - 0, sets @error to: ; |1 - There was an issue retrieving the COM object. @extended returns error code from ObjGet ; |2 - The specified drive in $Drive doesn't exist ; |3 - There was an issue running the WMI query ; Author ........: colombeen ; Modified.......: ; Remarks .......: Requires to be run with admin elevation. Windows Vista or newer! ; A BIG THANKS to everyone from the community who contributed! ; Related .......: ; Link ..........: ; Example .......: #include <Array.au3> ; $Header = "Drive Letter|Drive Label|Volume Type|Initialized For Protection|Protection Status|" & _ ; "Lock Status|Auto Unlock Enabled|Auto Unlock Key Stored|Conversion Status|Encryption " & _ ; "Method|Encryption Percentage|Wiping Status|Wiping Percentage|Key Protectors" ; _ArrayDisplay(_BitlockerDriveInfo(), "Bitlocker Drive Info", "", 64, Default, $Header) ; =============================================================================================================================== Func _BitlockerDriveInfo($sDrive = "", $sComputer = @ComputerName, $bDebug = False) Local $aConversionStatusMsg[7] = ["Unknown", "Fully Decrypted", "Fully Encrypted", "Encryption In Progress", "Decryption In Progress", "Encryption Paused", "Decryption Paused"] Local $aEncryptionMethodMsg[9] = ["Unknown", "None", "AES_128_WITH_DIFFUSER", "AES_256_WITH_DIFFUSER", "AES_128", "AES_256", "HARDWARE_ENCRYPTION", "XTS_AES_128", "XTS_AES_256"] Local $aKeyProtectorTypeMsg[11] = ["Unknown or other protector type", "Trusted Platform Module (TPM)", "External key", "Numerical password", "TPM And PIN", "TPM And Startup Key", "TPM And PIN And Startup Key", "Public Key", "Passphrase", "TPM Certificate", "CryptoAPI Next Generation (CNG) Protector"] Local $aLockStatusMsg[3] = ["Unknown", "Unlocked", "Locked"] Local $aProtectionStatusMsg[3] = ["Unprotected", "Protected", "Unknown"] Local $aVolumeTypeMsg[3] = ["Operating System Volume", "Fixed Data Volume", "Portable Data Volume"] Local $aWipingStatusMsg[5] = ["Unknown", "Free Space Not Wiped", "Free Space Wiped", "Free Space Wiping In Progress", "Free Space Wiping Paused"] Local $iRow = 0 Local $sRunMethod, $objWMIService, $objWMIQuery, $sDriveFilter, $iProtectionStatus, $iLockStatus, $bIsAutoUnlockEnabled, $bIsAutoUnlockKeyStored, $iConversionStatus, $iEncryptionPercentage, $iEncryptionFlags, $iWipingStatus, $iWipingPercentage, $iEncryptionMethod, $aVolumeKeyProtectorID, $aVolumeKeyProtectors, $iKeyProtectorType $objWMIService = ObjGet("winmgmts:{impersonationLevel=impersonate,authenticationLevel=pktPrivacy}!\\" & $sComputer & "\root\CIMV2\Security\MicrosoftVolumeEncryption") If @error Then Return SetError(1, @error, 0) If $sDrive <> "" Then Local $iDriveType = _WMIPropertyValue("DriveType", "Win32_LogicalDisk", "WHERE DeviceID='" & $sDrive & "'", Default, $sComputer) If @error Or ($iDriveType <> 2 And $iDriveType <> 3) Then Return SetError(2, 0, 0) $sDriveFilter = " WHERE DriveLetter='" & $sDrive & "'" EndIf $objWMIQuery = $objWMIService.ExecQuery("SELECT * FROM Win32_EncryptableVolume" & $sDriveFilter, "WQL", 0) If Not IsObj($objWMIQuery) Then Return SetError(3, 0, 0) Local $aResult[$objWMIQuery.count][14] For $objDrive In $objWMIQuery If $bDebug Then ConsoleWrite(@CRLF & "+> " & $objDrive.DriveLetter & @CRLF) If _WMIMethodExists($objDrive, "GetConversionStatus") Then $sRunMethod = $objDrive.GetConversionStatus($iConversionStatus, $iEncryptionPercentage, $iEncryptionFlags, $iWipingStatus, $iWipingPercentage) If $bDebug Then ConsoleWrite("!> GetConversionStatus 0x" & Hex($sRunMethod) & @CRLF) Else $iConversionStatus = -1 $iWipingStatus = -1 $iEncryptionPercentage = 0 $iWipingPercentage = 0 EndIf If _WMIMethodExists($objDrive, "GetEncryptionMethod") Then $sRunMethod = $objDrive.GetEncryptionMethod($iEncryptionMethod) If $bDebug Then ConsoleWrite("!> GetEncryptionMethod 0x" & Hex($sRunMethod) & @CRLF) Else $iEncryptionMethod = 0 EndIf If _WMIMethodExists($objDrive, "GetKeyProtectors") Then $sRunMethod = $objDrive.GetKeyProtectors("0", $aVolumeKeyProtectorID) If $bDebug Then ConsoleWrite("!> GetKeyProtectors 0x" & Hex($sRunMethod) & @CRLF) Else $aVolumeKeyProtectorID = 0 EndIf If _WMIMethodExists($objDrive, "GetLockStatus") Then $sRunMethod = $objDrive.GetLockStatus($iLockStatus) If $bDebug Then ConsoleWrite("!> GetLockStatus 0x" & Hex($sRunMethod) & @CRLF) Else $iLockStatus = -1 EndIf If _WMIMethodExists($objDrive, "GetProtectionStatus") Then $sRunMethod = $objDrive.GetProtectionStatus($iProtectionStatus) If $bDebug Then ConsoleWrite("!> GetProtectionStatus 0x" & Hex($sRunMethod) & @CRLF) Else $iProtectionStatus = 2 EndIf If _WMIMethodExists($objDrive, "IsAutoUnlockEnabled") Then $sRunMethod = $objDrive.IsAutoUnlockEnabled($bIsAutoUnlockEnabled) If $bDebug Then ConsoleWrite("!> IsAutoUnlockEnabled 0x" & Hex($sRunMethod) & @CRLF) Else $bIsAutoUnlockEnabled = "Unknown" EndIf If _WMIMethodExists($objDrive, "IsAutoUnlockKeyStored") Then $sRunMethod = $objDrive.IsAutoUnlockKeyStored($bIsAutoUnlockKeyStored) If $bDebug Then ConsoleWrite("!> IsAutoUnlockKeyStored 0x" & Hex($sRunMethod) & @CRLF) Else $bIsAutoUnlockKeyStored = "Unknown" EndIf If IsArray($aVolumeKeyProtectorID) And UBound($aVolumeKeyProtectorID) > 0 Then Dim $aVolumeKeyProtectors[UBound($aVolumeKeyProtectorID)][2] For $i = 0 To UBound($aVolumeKeyProtectorID) - 1 $aVolumeKeyProtectors[$i][0] = $aVolumeKeyProtectorID[$i] If _WMIMethodExists($objDrive, "GetKeyProtectorType") Then If $objDrive.GetKeyProtectorType($aVolumeKeyProtectorID[$i], $iKeyProtectorType) = 0 Then $aVolumeKeyProtectors[$i][1]= $aKeyProtectorTypeMsg[$iKeyProtectorType] Else $aVolumeKeyProtectors[$i][1]= "Unknown" EndIf Else $aVolumeKeyProtectors[$i][1] = "Unknown" EndIf Next Else $aVolumeKeyProtectors = "None" EndIf ; DriveLetter $aResult[$iRow][0] = $objDrive.DriveLetter ; DriveLabel $aResult[$iRow][1] = _WMIPropertyValue("VolumeName", "Win32_LogicalDisk", "WHERE DeviceID='" & $objDrive.DriveLetter & "'", Default, $sComputer) ; VolumeType If _WMIPropertyExists($objDrive, "VolumeType") Then $aResult[$iRow][2] = $aVolumeTypeMsg[$objDrive.VolumeType] Else If $objDrive.DriveLetter = _WMIPropertyValue("SystemDrive", "Win32_OperatingSystem", "", Default, $sComputer) Then $aResult[$iRow][2]= $aVolumeTypeMsg[0] ElseIf _WMIPropertyValue("DriveType", "Win32_LogicalDisk", "WHERE DeviceID='" & $objDrive.DriveLetter & "'", Default, $sComputer) = 3 Then $aResult[$iRow][2]= $aVolumeTypeMsg[1] ElseIf _WMIPropertyValue("DriveType", "Win32_LogicalDisk", "WHERE DeviceID='" & $objDrive.DriveLetter & "'", Default, $sComputer) = 2 Then $aResult[$iRow][2]= $aVolumeTypeMsg[2] Else $aResult[$iRow][2]= "Unknown" EndIf EndIf ; IsVolumeInitializedForProtection If _WMIPropertyExists($objDrive, "IsVolumeInitializedForProtection") Then $aResult[$iRow][3] = $objDrive.IsVolumeInitializedForProtection Else $aResult[$iRow][3] = "Unkown" EndIf ; ProtectionStatus $aResult[$iRow][4] = $aProtectionStatusMsg[$iProtectionStatus] ; LockStatus $aResult[$iRow][5] = $aLockStatusMsg[$iLockStatus + 1] ; IsAutoUnlockEnabled $aResult[$iRow][6] = $bIsAutoUnlockEnabled ; IsAutoUnlockEnabled $aResult[$iRow][7] = $bIsAutoUnlockKeyStored ; ConversionStatus $aResult[$iRow][8] = $aConversionStatusMsg[$iConversionStatus + 1] ; EncryptionMethod $aResult[$iRow][9] = $aEncryptionMethodMsg[$iEncryptionMethod + 1] ; EncryptionPercentage $aResult[$iRow][10] = $iEncryptionPercentage ; WipingStatus $aResult[$iRow][11] = $aWipingStatusMsg[$iWipingStatus + 1] ; WipingPercentage $aResult[$iRow][12] = $iWipingPercentage ; KeyProtectors $aResult[$iRow][13] = $aVolumeKeyProtectors $iRow += 1 Next _ArraySort($aResult) Return $aResult EndFunc ;==>_BitlockerDriveInfo Func _WMIPropertyExists($Object, $Property) If Not IsObj($Object) Then Return False For $sProperty In $Object.Properties_ If $sProperty.Name = $Property Then Return True Next Return False EndFunc ;==>_WMIPropertyExists Func _WMIMethodExists($Object, $Method) If Not IsObj($Object) Then Return False For $sMethod In $Object.Methods_ If $sMethod.Name = $Method Then Return True Next Return False EndFunc ;==>_WMIMethodExists Func _WMIPropertyValue($sProperty = "", $sClass = "", $sFilter = "", $sNamespace = Default, $sComputer = @ComputerName) Local $objWMIService, $objWMIQuery If $sClass = "" Or $sProperty = "" Then Return SetError(1, 0, 0) If $sFilter <> "" Then $sFilter = " " & $sFilter If $sNamespace = Default Then $sNamespace = "\root\CIMV2" $objWMIService = ObjGet("winmgmts:{impersonationLevel=impersonate,authenticationLevel=pktPrivacy}!\\" & $sComputer & $sNamespace) If @error Then Return SetError(2, @error, 0) $objWMIQuery = $objWMIService.ExecQuery("SELECT * FROM " & $sClass & $sFilter, "WQL", 0x30) If Not IsObj($objWMIQuery) Then Return SetError(3, 0, 0) For $objItem In $objWMIQuery For $Property In $objItem.Properties_ If $Property.Name = $sProperty Then Return $Property.Value EndIf Next Next Return SetError(4, 0, 0) EndFunc ;==>_WMIPropertyValue Example 1: #RequireAdmin #include <array.au3> ; Get information on all available drives Global $test = _BitlockerDriveInfo() If @error Then ConsoleWrite("!> _BitlockerDriveInfo() error: " & @error & ". extended: " & @extended & @CRLF) ElseIf IsArray($test) Then _ArrayDisplay($test, "Bitlocker Drive Info", "", 64, Default, "Drive Letter|Drive Label|Volume Type|Initialized For Protection|Protection Status|Lock Status|Auto Unlock Enabled|Auto Unlock Key Stored|Conversion Status|Encryption Method|Encryption Percentage|Wiping Status|Wiping Percentage|Key Protectors") ; Display the Key Protectors for the first record If IsArray($test[0][13]) Then _ArrayDisplay($test[0][13]) EndIf Example 2: #RequireAdmin #include <array.au3> ; Get information on the C-drive of the current computer + show extra information in the console Global $test = _BitlockerDriveInfo("C:", @ComputerName, True) If @error Then ConsoleWrite("!> _BitlockerDriveInfo() error: " & @error & ". extended: " & @extended & @CRLF) ElseIf IsArray($test) Then ConsoleWrite("Bitlocker information on the " & $test[0][0] & " drive" & @CRLF) ConsoleWrite("Protection Status: " & $test[0][4] & @CRLF) EndIf Screenshot for the first example: Suggestions? Bugs? Just let me know TODO: ??? Version 1.0: Initial release Version 1.1: Fixed: Drive Label will not work when you request the information from a remote system (currently using DriveGetLabel) Fixed: The current fix for the missing VolumeType property in some Windows versions will only work locally Added: New internal function (_WMIPropertyValue()) Version 1.2: Fixed: The drive exists & drive type check only worked locally when a drive was specified in $sDrive1 point -
New Script
coffeeturtle reacted to careca for a topic
Many times i copied to clipboard some code to test and i had to create a new au3 file, open it and paste the code in, with this script that's automatic. #include <FileConstants.au3> $NewAu3Path = @DesktopDir & '\NewAu3' & '.' & @HOUR & '.' & @MIN & '.' & @SEC & '.au3' $NewAu3 = FileOpen($NewAu3Path, 2) FileWrite($NewAu3, ClipGet()) FileClose($NewAu3) Added current time to ensure the file has unique filename.1 point -
Being the completist I am, I decided to finish what I started, even though I have taken a different route now with INItoSQL DB, which this UDF will shortly follow. So all the following functions are done (some could do with more error checking etc), but essentially mimic all the INI functions plus some. So in a very real way, you could kind of update your INI based program to an SQL based one, just by replacing those functions, especially with those in my next update, which will also give you a lot more flexibility than the current lot. Perhaps use my INItoSQL DB program first (original version probably), to convert existing INI files ... or I would advise waiting really, for my improvements, which will make a more proper SQL file. Changes In This Update Added several functions, plus improved greatly the way the example (test) Job system works ... now with auto advance and dialog prompts & feedback. _SQLite_Delete() can delete a Key, Section or Table. _SQLite_WriteSection() could do with some improving to check on existing. Same with the Rename functions. I was going to add them in, but a few SQL quirks wasted a fair amount of my time today, and I am more enthused about developing the improved versions instead.1 point
-
Can't select radio when automated
FrancescoDiMuro reacted to Simpel for a topic
Hi @FrancescoDiMuro, I didn't saw that, maybe because of "reveal hidden contents" or because the discussion about "WM_COMMAND ". ;-) But if two persons found the exact same solution independently there must be some good at it.1 point -
I'm assuming you're talking about a GUI, rather than InputBox()? I would have 1 script for the actual running in the background, then have a separate GUI script which updates a file. The other script will keep checking that file for changes and change its behaviour accordingly (function call or parameter or whatever). Or you can try: https://www.autoitscript.com/autoit3/docs/functions/AdlibRegister.htm1 point
-
It's easy if you know how to do it: Local $aVolumeKeyProtectors If IsArray($aVolumeKeyProtectorID) And UBound($aVolumeKeyProtectorID) > 0 Then Dim $aVolumeKeyProtectors[UBound($aVolumeKeyProtectorID)][2] Local $iKeyProtectorType ; ... Else $aVolumeKeyProtectors = "None" EndIf1 point
-
{CTRLDOWN} and {CTRLUP}
PoojaKrishna reacted to caramen for a topic
To explain with an other way; When a script is starting. If you dont send a CTRLDOWN Autoit will consider there is no reason to CTRLUP. Easy .1 point -
Here's my take on an auto completer, work in progress so don't be too harsh with me. The idea is that after a spacebar press, it learns the word that was previously written, and the next time you write, it gets a list of all words that fit into what you're writing, so if the user writes "com", a word that may appear is "complex", that is, if the user has written it anytime in the past. A popup will show up whenever the user writes with the list of words, the way to select one of the words from the list is to scroll with the mouse wheel, and middle click. The popup timeout is 2.5 secs. Best regards. PS for the next step would be nice to save words as they are, for example emails, as it is it doesn't get @ for example. Completer 1.0.exe AComp.ico Completer 1.0.au31 point
-
{CTRLDOWN} and {CTRLUP}
PoojaKrishna reacted to caramen for a topic
@Deye is true ... look : Test.exe -> Calls CTRLDOWN.exe and CTRLUP.exe #RequireAdmin run("CTRLDOWN.exe") sleep(500) run("CTRLUP.exe") CTRLDOWN.exe -> Sends a {CtrlDOWN} to press CTRL key #RequireAdmin Send('{CTRLDOWN}') CTRLUP.exe -> Sends a {CtrlUP} to release CTRL key #RequireAdmin Send('{CTRLDOWN}') Send('{CTRLUP}') That should work1 point -
Here are two methods. The first is a little esoteric in establishing the meaningful, temporary array. The second is a modified version of pixelsearch's example of post #2 using _ArraySearch() to find the appropriate index. #include <Array.au3> ; Ref: https://www.autoitscript.com/forum/topic/195350-how-to-first-four-characters-changes/ Local $sFind = "BCES, BLES, BCUS, BLUS, BCJS, BLJM, BCAS, BLAS" ; Sub-strings to be found. Local $sRepl = "NPEA, NPEB, NPUA, NPUB, NPJA, NPJB, NPHA, NPHB" ; Corresponding replacement sub-strings. Local $sID = "321BLUS12345" ; Not necessarily only the first few characters in the string are to be found. Local $sREPattern = "(" & StringRegExpReplace($sFind, "\h*,\h*", ")|(") & ")" Local $aReplace = StringRegExp($sRepl, "(.+?)(?:\h*,\h*|$)", 3) ; Will not capture spaces either side of the separator coma when those spaces are present. ; The following array shows:- ; The found sub-string - "$aTemp[UBound($aTemp) - 1]"; and, ; The corresponding replacement sub-string, "$aReplace[UBound($aTemp) - 1]". Local $aTemp = StringRegExp($sID, $sREPattern, 1) ;_ArrayDisplay($aTemp) ; Display result If Not @error Then ;$sID_New = StringRegExpReplace($sID, "(" & $aTemp[UBound($aTemp) - 1] & ")", $aReplace[UBound($aTemp) - 1]) ; or $sID_New = StringReplace($sID, $aTemp[UBound($aTemp) - 1], $aReplace[UBound($aTemp) - 1]) MsgBox(0, "StringRegExp() Found", $sID & " => " & $sID_New) Else MsgBox(0, "Not Found", "No replacement done") EndIf ; --------------- Or, another method ---------------- Global $aArray[8][2] = _ [["BCES", "NPEA"], _ ["BLES", "NPEB"], _ ["BCUS", "NPUA"], _ ["BLUS", "NPUB"], _ ["BCJS", "NPJA"], _ ["BLJM", "NPJB"], _ ["BCAS", "NPHA"], _ ["BLAS", "NPHB"]] Global $sID = "BLUS12345" Local $iIndex = _ArraySearch($aArray, StringLeft($sID, 4)) ; Display result If Not @error Then $sID_NewA = $aArray[$iIndex][1] & StringTrimLeft($sID, 4) MsgBox(0, "_ArraySearch() Found", $sID & " => " & $sID_NewA) Else MsgBox(0, "Not Found", "No replacement done") EndIf1 point
-
$aID = "BLUS12345" $aID1 = StringLeft($aID, 4) $aID2 = StringMid($aID, 5) If $aID1 = 'BCES' Then ConsoleWrite(@ScriptLineNumber & " : " & "NPEA" & $aID2 & @CRLF) ElseIf $aID1 = 'BLES' Then ConsoleWrite(@ScriptLineNumber & " : " & "NPEB" & $aID2 & @CRLF) ElseIf $aID1 = 'BCUS' Then ConsoleWrite(@ScriptLineNumber & " : " & "NPUA" & $aID2 & @CRLF) ElseIf $aID1 = 'BLUS' Then ConsoleWrite(@ScriptLineNumber & " : " & "NPUB" & $aID2 & @CRLF) ElseIf $aID1 = 'BCJS' Then ConsoleWrite(@ScriptLineNumber & " : " & "NPJA" & $aID2 & @CRLF) ElseIf $aID1 = 'BLJM' Then ConsoleWrite(@ScriptLineNumber & " : " & "NPJB" & $aID2 & @CRLF) ElseIf $aID1 = 'BCAS' Then ConsoleWrite(@ScriptLineNumber & " : " & "NPHA" & $aID2 & @CRLF) ElseIf $aID1 = 'BLAS' Then ConsoleWrite(@ScriptLineNumber & " : " & "NPHB" & $aID2 & @CRLF) EndIf1 point