Leaderboard
Popular Content
Showing content with the highest reputation on 04/19/2020 in all areas
-
Implementing a Windows message loop in a non-AutoIt GUI
pixelsearch and 2 others reacted to LarsJ for a topic
GUIs of interest here are non-AutoIt GUIs not created with GUICreate(). They are created with Windows API functions eg. _WinAPI_CreateWindowEx(). (2020-04-26) Is it possible in such a non-AutoIt GUI to implement a Windows message loop? Which functionality and techniques do and do not work in such non-AutoIt GUIs? Which internal functions can be used? Can AutoIt and non-AutoIt GUIs exist side by side in the same script? Can existing UDFs be used? This example started last week with inspiration from this thread. After several tests during the week, I've decided to make a little more of the example. This is an update of first post. Over the coming weeks I'll be adding more code in new posts. If it all works well and stable, the ideas in the project may be seen in a wider perspective. Some information has been deleted during this update. The information will be added again later. Basic functionality What is a Windows message loop In Microsoft documentation, a message loop is usually coded like this: while GetMessage(&msg, 0, 0, 0) { // Retrieve a message from the message queue // Processes accelerator keystrokes if (!TranslateAccelerator( hwndMain, // handle to receiving window haccel, // handle to active accelerator table &msg)) // message data { TranslateMessage(&msg); // Translate a virtual-key message into a character message DispatchMessage(&msg); // Send a message to the window procedure } } And the window procedure is defined like this: LRESULT CALLBACK WinProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam) // second message parameter { switch (uMsg) { case WM_CREATE: // Initialize the window. return 0; case WM_PAINT: // Paint the window's client area. return 0; case WM_SIZE: // Set the size and position of the window. return 0; case WM_DESTROY: // Clean up window-specific data objects. return 0; // // Process other messages. // default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; } Message loop The purpose of a message loop is first and foremost to keep the program running and prevent it from ending immediately. It's exactly the same in AutoIt. But why so complicated code as shown above? It's necessary for advanced message handling eg. to use keyboard accelerators that are local to the individual program and not global as hot keys. Window procedure The window procedure handles the messages sent from the DispatchMessage() function in the message loop. Even in a simpler message loop, messages are sent to the window procedure. This is done through deault code in the Windows API functions and the operating system. If a message isn't handled by the window procedure, it's forwarded to the default window procedure DefWindowProc(). 0) _WinAPI_RegisterClassEx.au3 This is the example of _WinAPI_RegisterClassEx() in the help file. _WinAPI_CreateWindowEx() is used to create the GUI window. The example here is a slightly modified version: #AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7 #AutoIt3Wrapper_UseX64=y Opt( "MustDeclareVars", 1 ) #include <WinAPIRes.au3> #include <WinAPISys.au3> #include <WindowsConstants.au3> Global $bExit = False Example() Func Example() Local Const $sClass = "MyWindowClass" Local Const $sName = "_WinAPI_RegisterClassEx" ; Get module handle for the current process Local $hInstance = _WinAPI_GetModuleHandle( 0 ) ; Create a class cursor Local $hCursor = _WinAPI_LoadCursor( 0, 32512 ) ; IDC_ARROW ; Create a class icons (large and small) Local $tIcons = DllStructCreate( "ptr;ptr" ) _WinAPI_ExtractIconEx( @SystemDir & "\shell32.dll", 130, DllStructGetPtr( $tIcons, 1 ), DllStructGetPtr( $tIcons, 2 ), 1 ) Local $hIcon = DllStructGetData( $tIcons, 1 ) Local $hIconSm = DllStructGetData( $tIcons, 2 ) ; Create DLL callback function (window procedure) Local $pWinProc = DllCallbackGetPtr( DllCallbackRegister( "WinProc", "lresult", "hwnd;uint;wparam;lparam" ) ) ; Create and fill $tagWNDCLASSEX structure Local $tWCEX = DllStructCreate( $tagWNDCLASSEX & ";wchar szClassName[" & ( StringLen( $sClass ) + 1 ) & "]" ) DllStructSetData( $tWCEX, "Size", DllStructGetPtr( $tWCEX, "szClassName" ) - DllStructGetPtr( $tWCEX ) ) DllStructSetData( $tWCEX, "Style", 0 ) DllStructSetData( $tWCEX, "hWndProc", $pWinProc ) DllStructSetData( $tWCEX, "ClsExtra", 0 ) DllStructSetData( $tWCEX, "WndExtra", 0 ) DllStructSetData( $tWCEX, "hInstance", $hInstance ) DllStructSetData( $tWCEX, "hIcon", $hIcon ) DllStructSetData( $tWCEX, "hCursor", $hCursor ) DllStructSetData( $tWCEX, "hBackground", _WinAPI_CreateSolidBrush( _WinAPI_GetSysColor( $COLOR_3DFACE ) ) ) DllStructSetData( $tWCEX, "MenuName", 0 ) DllStructSetData( $tWCEX, "ClassName", DllStructGetPtr( $tWCEX, "szClassName" ) ) DllStructSetData( $tWCEX, "hIconSm", $hIconSm ) DllStructSetData( $tWCEX, "szClassName", $sClass ) ; Register a window class _WinAPI_RegisterClassEx( $tWCEX ) ; Create a window _WinAPI_CreateWindowEx( 0, $sClass, $sName, BitOR( $WS_CAPTION, $WS_POPUPWINDOW, $WS_VISIBLE ), ( @DesktopWidth - 826 ) / 2, ( @DesktopHeight - 584 ) / 2, 826, 584, 0 ) ; Main msg loop While Sleep(10) If $bExit Then ExitLoop WEnd ; Unregister window class and release resources _WinAPI_UnregisterClass( $sClass, $hInstance ) _WinAPI_DestroyCursor( $hCursor ) _WinAPI_DestroyIcon( $hIcon ) _WinAPI_DestroyIcon( $hIconSm ) EndFunc ; Window procedure Func WinProc( $hWnd, $iMsg, $wParam, $lParam ) Switch $iMsg Case $WM_CLOSE $bExit = True EndSwitch Return _WinAPI_DefWindowProcW( $hWnd, $iMsg, $wParam, $lParam ) EndFunc Note that the Esc key, which can normally be used to close an AutoIt window, doesn't work. But you can use Alt+F4 to close the window. Alt+F4 is a hot key. In the example, the main message loop is coded this way: ; Main msg loop While Sleep(10) If $bExit Then ExitLoop WEnd And the window procedure that handles messages: Func WinProc( $hWnd, $iMsg, $wParam, $lParam ) Switch $iMsg Case $WM_CLOSE $bExit = True EndSwitch Return _WinAPI_DefWindowProcW( $hWnd, $iMsg, $wParam, $lParam ) EndFunc Esc key In AutoIt, the Esc key to exit the program is implemented as an accelerator key. But this simple message loop is unable to handle keyboard accelerators. Keyboard accelerators The Microsoft documentation for keyboard accelerators can be found here. To use keyboard accelerators in a program, the following code steps must be implemented: Create a keyboard accelerator struct to store information Fill in the accelerator key structure with information Create an accelerator table with CreateAcceleratorTable() Include TranslateAccelerator() in the message loop Include a WM_COMMAND message handler in the window procedure Includes\WinMsgLoop.au3 (2020-04-26) WinMsgLoop.au3 implements the functions to use keyboard accelerators and to create a Windows message loop: #include-once ; Message structure Global Const $tagMSG = "hwnd hwnd;uint message;wparam wParam;lparam lParam;dword time;int X;int Y" ; Keyboard accelerator structure Global Const $tagACCEL = "byte fVirt;word key;word cmd;" ; Values of the fVirt field Global Const $FVIRTKEY = TRUE Global Const $FNOINVERT = 0x02 Global Const $FSHIFT = 0x04 Global Const $FCONTROL = 0x08 Global Const $FALT = 0x10 #cs ; One accelerator key Local $tAccel = DllStructCreate( $tagACCEL ) DllStructSetData( $tAccel, "fVirt", $FVIRTKEY ) DllStructSetData( $tAccel, "key", $VK_ESCAPE ) DllStructSetData( $tAccel, "cmd", $VK_ESCAPE ) ; cmd = key to keep it simple ; Two accelerator keys Local $tAccel = DllStructCreate( $tagACCEL & $tagACCEL ) DllStructSetData( $tAccel, 1, $FVIRTKEY ) DllStructSetData( $tAccel, 2, $VK_KEY1 ) DllStructSetData( $tAccel, 3, $VK_KEY1 ) DllStructSetData( $tAccel, 4, $FVIRTKEY ) DllStructSetData( $tAccel, 5, $VK_KEY2 ) DllStructSetData( $tAccel, 6, $VK_KEY2 ) #ce ; Error handling ; @error = 0: No errors ; 1: Parameter error ; Create a keyboard accelerator table Func WinMsgLoop_CreateAcceleratorTable( $tAccel ) Local $iSize = DllStructGetSize( $tAccel ) If Mod( $iSize, 6 ) Then Return SetError(1,0,0) ; SetError ( code [, extended = 0 [, return value]] ) Return DllCall( "User32.dll", "handle", "CreateAcceleratorTableW", "struct*", $tAccel, "int", $iSize/6 )[0] EndFunc ; Retrieve a message from the message queue Func WinMsgLoop_GetMessage( ByRef $tMsg ) Return DllCall( "User32.dll", "bool", "GetMessageW", "struct*", $tMsg, "hwnd", 0, "uint", 0, "uint", 0 )[0] EndFunc ; Processes accelerator keystrokes Func WinMsgLoop_TranslateAccelerator( $hWnd, $hAccel, ByRef $tMsg ) Return DllCall( "User32.dll", "int", "TranslateAcceleratorW", "hwnd", $hWnd, "handle", $hAccel, "struct*", $tMsg )[0] EndFunc ; Processes dialog box messages Func WinMsgLoop_IsDialogMessage( $hWnd, ByRef $tMsg ) Return DllCall( "User32.dll", "bool", "IsDialogMessageW", "hwnd", $hWnd, "struct*", $tMsg )[0] EndFunc ; Translate a virtual-key message into a character message Func WinMsgLoop_TranslateMessage( ByRef $tMsg ) DllCall( "User32.dll", "bool", "TranslateMessage", "struct*", $tMsg ) EndFunc ; Send a message to the window procedure Func WinMsgLoop_DispatchMessage( ByRef $tMsg ) DllCall( "User32.dll", "lresult", "DispatchMessageW", "struct*", $tMsg ) EndFunc ; Destroy a keyboard accelerator table Func WinMsgLoop_DestroyAcceleratorTable( $hAccel ) DllCall( "User32.dll", "bool", "DestroyAcceleratorTable", "handle", $hAccel ) EndFunc ; Posts a WM_QUIT message to the message queue ; WinMsgLoop_GetMessage() returns 0 on WM_QUIT and the message loop terminates Func WinMsgLoop_PostQuitMessage( $iExitCode = 0 ) Return DllCall( "User32.dll", "none", "PostQuitMessage", "int", $iExitCode )[0] EndFunc 1) Windows message loop.au3 In this example, the Esc and End keys can be used to exit the program. It contains a complete Windows message loop. The script contains a number of ConsoleWrites so you can see what's going on in SciTE console. Keyboard accelerators: #cs ; Esc accelerator key to Exit ; Create keyboard accelerator structure Local $tAccel = DllStructCreate( $tagACCEL ) DllStructSetData( $tAccel, "fVirt", $FVIRTKEY ) DllStructSetData( $tAccel, "key", $VK_ESCAPE ) DllStructSetData( $tAccel, "cmd", $VK_ESCAPE ) ; cmd = key to keep it simple #ce ; Esc/End accelerator keys to Exit ; Create keyboard accelerator structure Local $tAccel = DllStructCreate( $tagACCEL & $tagACCEL ) DllStructSetData( $tAccel, 1, $FVIRTKEY ) DllStructSetData( $tAccel, 2, $VK_ESCAPE ) DllStructSetData( $tAccel, 3, $VK_ESCAPE ) ; cmd = key to keep it simple DllStructSetData( $tAccel, 4, $FVIRTKEY ) DllStructSetData( $tAccel, 5, $VK_END ) DllStructSetData( $tAccel, 6, $VK_END ) ; Create a keyboard accelerator table Local $hAccel = WinMsgLoop_CreateAcceleratorTable( $tAccel ) ConsoleWrite( "$hAccel = " & $hAccel & @CRLF & @CRLF ) Using dialog box keys (2020-04-26) To use dialog box keys, the message loop must contain the IsDialogMessage() function. The function identifies and processes the keys. Windows message loop: (2020-04-26) ; Windows message loop Local $tMsg = DllStructCreate( $tagMSG ) While WinMsgLoop_GetMessage( $tMsg ) ; Retrieve a message from the message queue ConsoleWrite( "0x" & Hex( DllStructGetData( $tMsg, "message" ), 4 ) & @CRLF ) If Not WinMsgLoop_TranslateAccelerator( $hWnd, $hAccel, $tMsg ) And _ ; Processes accelerator keystrokes Not WinMsgLoop_IsDialogMessage( $hWnd, $tMsg ) Then ; Processes dialog box messages WinMsgLoop_TranslateMessage( $tMsg ) ; Translate a virtual-key message into a character message WinMsgLoop_DispatchMessage( $tMsg ) ; Send a message to the window procedure EndIf If $bExit Then ExitLoop WEnd WM_COMMAND message handler: (2020-04-26) Case $WM_COMMAND ConsoleWrite( @CRLF & "$WM_COMMAND" & @CRLF ) Switch BitShift( $wParam, 16 ) ; HiWord Case 1 ; Accelerator key ConsoleWrite( "Accelerator key" & @CRLF ) Switch BitAND( $wParam, 0xFFFF ) ; LoWord Case $VK_ESCAPE ConsoleWrite( "$VK_ESCAPE" & @CRLF ) _WinAPI_DestroyWindow( $hWnd ) Return 0 ; Don't call DefWindowProc() Case $VK_END ConsoleWrite( "$VK_END" & @CRLF ) _WinAPI_DestroyWindow( $hWnd ) Return 0 EndSwitch EndSwitch Program termination code: (2020-04-26) Case $WM_CLOSE ConsoleWrite( @CRLF & "$WM_CLOSE" & @CRLF ) If MsgBox( $MB_OKCANCEL, "Really close?", "My application", 0, $hWnd ) = $IDOK Then _WinAPI_DestroyWindow( $hWnd ) Else ConsoleWrite( @CRLF ) EndIf Return 0 ; Don't call DefWindowProc() Case $WM_DESTROY ConsoleWrite( @CRLF & "$WM_DESTROY" & @CRLF ) $bExit = True Return 0 2) Windows message loop 2.au3 (2020-04-26) Optimized version of the message loop because DllCall() is used directly instead of more time-consuming functions in WinMsgLoop.au3 UDF: ; Windows message loop Local $tMsg = DllStructCreate( $tagMSG ) While DllCall( "User32.dll", "bool", "GetMessageW", "struct*", $tMsg, "hwnd", 0, "uint", 0, "uint", 0 )[0] ; Retrieve a message from the message queue ConsoleWrite( "0x" & Hex( DllStructGetData( $tMsg, "message" ), 4 ) & @CRLF ) If Not DllCall( "User32.dll", "int", "TranslateAcceleratorW", "hwnd", $hWnd, "handle", $hAccel, "struct*", $tMsg )[0] And _ ; Processes accelerator keystrokes Not DllCall( "User32.dll", "bool", "IsDialogMessageW", "hwnd", $hWnd, "struct*", $tMsg )[0] Then ; Processes dialog box messages DllCall( "User32.dll", "bool", "TranslateMessage", "struct*", $tMsg ) ; Translate a virtual-key message into a character message DllCall( "User32.dll", "lresult", "DispatchMessageW", "struct*", $tMsg ) ; Send a message to the window procedure EndIf If $bExit Then ExitLoop WEnd 3) Navigating with Tab key.au3 (2020-04-26) When IsDialogMessage() is added to the code in the message loop, dialog box keys work immediately. Eg. Tab and Shift+Tab. Demonstrated in the example with three buttons. 4) Adding virtual ListView.au3 A virtual listview with cell background colors is created in the window. As it's a virtual list view, a WM_NOTIFY message handler is needed to handle LVN_GETDISPINFO notifications. Background colors are drawn through NM_CUSTOMDRAW notifications. A virtual and custom drawn listview is very message intensive and therefore interesting to test. ; Create ListView $hListView = _GUICtrlListView_Create( $hWnd, "", 10, 10, 800, 538, $LVS_DEFAULT+$LVS_OWNERDATA-$LVS_SINGLESEL, $WS_EX_CLIENTEDGE ) _GUICtrlListView_SetExtendedListViewStyle( $hListView, $LVS_EX_DOUBLEBUFFER+$LVS_EX_FULLROWSELECT ) ; Add columns For $i = 0 To $iCols - 1 _GUICtrlListView_AddColumn( $hListView, "Col " & $i, 96, 2 ) ; 2 = Centered text Next ; ListView items For $i = 0 To $iRows - 1 For $j = 0 To $iCols - 1 $aItems[$i][$j] = $i & "/" & $j Next Next ; ListView colors Local $aLVColors = [ 0xCCCCFF, 0xCCFFFF, 0xCCFFCC, 0xFFFFCC, 0xFFCCCC, 0xFFCCFF ] ; BGR For $i = 0 To $iRows - 1 For $j = 0 To $iCols - 1 $aColors[$i][$j] = $aLVColors[Random( 0,5,1 )] Next Next ; Set number of rows in virtual ListView DllCall( "user32.dll", "lresult", "SendMessageW", "hwnd", $hListView, "uint", $LVM_SETITEMCOUNT, "wparam", $iRows, "lparam", 0 ) Case $WM_NOTIFY Switch DllStructGetData( DllStructCreate( $tagNMHDR, $lParam ), "Code" ) Case $LVN_GETDISPINFOW ; Fill virtual listview Local Static $tText = DllStructCreate( "wchar[100]" ), $pText = DllStructGetPtr( $tText ) 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 0 ; Don't call DefWindowProc() 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" ) DllStructSetData( $tNMLVCUSTOMDRAW, "ClrTextBk", $aColors[$iItem][DllStructGetData($tNMLVCUSTOMDRAW,"iSubItem")] ) Return $CDRF_NEWFONT ; $CDRF_NEWFONT must be returned after changing font or colors EndSwitch EndSwitch Usage of the code I don't think there's such a great need for code like this. At least the code shows that translating Windows API functions into AutoIt works pretty well. Implementing the code in the WinMsgLoop.au3 UDF was straightforward. There is a question about using keyboard accelerators in non-AutoIt GUIs in this post. If you use AutoIt for prototyping, you might get some ideas here. Because the examples do not contain internal AutoIt functions but only Windows API functions, they are easy to translate into C/C++ and other languages. When translating back and forth between AutoIt and other languages, the internal functions are always the problem. How to translate these functions? Recent performance issue Recently, a problem was raised in this post regarding a multiple 100% performance degradation on Windows 10 versions later than 1803 as soon as a GUI element became visible in a script. The problem persisted throughout the rest of the code, even though the GUI element was deleted. I've tested the issue under Windows 10 1809 with all the examples here. All of the examples suffer from the problem. How much of the code needs to be translated into C/C++ before the problem disappears? Only the line which contains the _WinAPI_CreateWindowEx() function that creates and displays the window? Or does the problem still exist when all code is translated? That could be interesting. I've already done examples of translating AutoIt code into C/C++ eg. in this example and this example so it's not that hard. 7z-file The 7z-file contains source code for the UDFs and examples. You need AutoIt 3.3.12 or later. Tested on Windows 7 and Windows 10. Comments are welcome. Let me know if there are any issues. WinMsgLoop.7z3 points -
GUIRegisterMsg20 - Subclassing Made Easy
Professor_Bernd and one other 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.7z2 points -
Here is a small function that lists installed applications from the registry (uninstall keys). _UninstallList() allows to search on specific string in registry values. The feature supports x86 and x64, even if the program is compiled in 32 or 64 bits. Thanks for you comments and suggestions. ! NEW VERSION ! Changes : - The function returns the installation date of each application (see remark) - $sCol parameter added : allows you to add columns in the returned array Remark : the installation date is retrieved from the InstallDate value in the registry. If this value does not exist, the InstallDate takes the last time at which the subkey was last written (great idea from JFX, thanks to him !) #include <Date.au3> ; needed for _UninstallList function ; Examples ########################################################################################################## #Include <Array.au3> ; Just for _ArrayDisplay Local $aList ; Lists all uninstall keys $aList = _UninstallList() _ArrayDisplay($aList, "All uninstall keys", Default, Default, Default, "RegistryPath|RegistrySubKey|DisplayName|Date") ; Lists all keys, where the publisher name (Publisher value) starts with "adobe" $aList = _UninstallList("Publisher", "Adobe") _ArrayDisplay($aList, "Adobe Publisher", Default, Default, Default, "RegistryPath|RegistrySubKey|DisplayName|Date") ; Lists all x86 keys only, where the name (DisplayName value) contains "flash" $aList = _UninstallList("DisplayName", "Flash", "", 1, 1) _ArrayDisplay($aList, "Flash (x86)", Default, Default, Default, "RegistryPath|RegistrySubKey|DisplayName|Date") ; Lists all keys matching a Java version (using a regular expression) $aList = _UninstallList("DisplayName", "(?i)Java \d+ Update \d+", "", 3) _ArrayDisplay($aList, "Java", Default, Default, Default, "RegistryPath|RegistrySubKey|DisplayName|Date") ; Lists all x64 keys only, where the quiet uninstall string (QuietUninstallString value) is set $aList = _UninstallList("QuietUninstallString", ".+", "QuietUninstallString", 3, 2) _ArrayDisplay($aList, "QuietUninstallString (x64)", Default, Default, Default, "RegistryPath|RegistrySubKey|DisplayName|Date|QuietUninstallString") ; List all x86 keys only, where the name (DisplayName value) start with "Autoit" and retrieve the ; UninstallString, DisplayVersion values $aList = _UninstallList("DisplayName", "Autoit", "UninstallString|DisplayVersion", 0, 1) _ArrayDisplay($aList, "Autoit (x86)", Default, Default, Default, "RegistryPath|RegistrySubKey|DisplayName|Date|UninstallString|DisplayVersion") ; ################################################################################################################### ; #FUNCTION# ==================================================================================================================== ; Name ..........: _UninstallList ; Description ...: Returns an array of matching uninstall keys from registry, with an optional filter ; Syntax ........: _UninstallList([$sValueName = ""[, $sFilter = ""[, $sCols = ""[, $iSearchMode = 0[,$ iArch = 3]]]]]]) ; Parameters ....: $sValueName - [optional] Registry value used for the filter. ; Default is all keys ($sFilter do not operates). ; $sFilter - [optional] String to search in $sValueName. Filter is not case sensitive. ; $sCols - [optional] Additional values to retrieve. Use "|" to separate each value. ; Each value adds a column in the returned array ; $iSearchMode - [optional] Search mode. Default is 0. ; 0 : Match string from the start. ; 1 : Match any substring. ; 2 : Exact string match. ; 3 : $sFilter is a regular expression ; $iArch - [optional] Registry keys to search in. Default is 3. ; 1 : x86 registry keys only ; 2 : x64 registry keys only ; 3 : both x86 and x64 registry keys ; Return values .: Returns a 2D array of registry keys and values : ; $array[0][0] : Number of keys ; $array[n][0] : Registry key path ; $array[n][1] : Registry subkey ; $array[n][2] : Display name ; $array[n][3] : Installation date (YYYYMMDD format) ; $array[n][4] : 1st additional value specified in $sCols (only if $sCols is set) ; $array[n][5] : 2nd additional value specified in $sCols (only if $sCols contains at least 2 entries) ; $array[n][x] : Nth additional value ... ; Author ........: jguinch ; =============================================================================================================================== Func _UninstallList($sValueName = "", $sFilter = "", $sCols = "", $iSearchMode = 0, $iArch = 3) Local $sHKLMx86, $sHKLM64, $sHKCU = "HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall" Local $aKeys[1] = [ $sHKCU ] Local $sDisplayName, $sSubKey, $sKeyDate, $sDate, $sValue, $iFound, $n, $aResult[1][4], $iCol Local $aCols[1] = [0] If NOT IsInt($iArch) OR $iArch < 0 OR $iArch > 3 Then Return SetError(1, 0, 0) If NOT IsInt($iSearchMode) OR $iSearchMode < 0 OR $iSearchMode > 3 Then Return SetError(1, 0, 0) $sCols = StringRegExpReplace( StringRegExpReplace($sCols, "(?i)(DisplayName|InstallDate)\|?", ""), "\|$", "") If $sCols <> "" Then $aCols = StringSplit($sCols, "|") If @OSArch = "X86" Then $iArch = 1 $sHKLMx86 = "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" Else If @AutoitX64 Then $sHKLMx86 = "HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" $sHKLM64 = "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" Else $sHKLMx86 = "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" $sHKLM64 = "HKLM64\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" EndIf EndIf If BitAND($iArch, 1) Then Redim $aKeys[ UBound($aKeys) + 1] $aKeys [ UBound($aKeys) - 1] = $sHKLMx86 EndIf If BitAND($iArch, 2) Then Redim $aKeys[ UBound($aKeys) + 1] $aKeys [ UBound($aKeys) - 1] = $sHKLM64 EndIf For $i = 0 To UBound($aKeys) - 1 $n = 1 While 1 $iFound = 1 $aSubKey = _RegEnumKeyEx($aKeys[$i], $n) If @error Then ExitLoop $sSubKey = $aSubKey[0] $sKeyDate = StringRegExpReplace($aSubKey[1], "^(\d{4})/(\d{2})/(\d{2}).+", "$1$2$3") $sDisplayName = RegRead($aKeys[$i] & "\" & $sSubKey, "DisplayName") $sDate = RegRead($aKeys[$i] & "\" & $sSubKey, "InstallDate") If $sDate = "" Then $sDate = $sKeyDate If $sDisplayName <> "" Then If $sValueName <> "" Then $iFound = 0 $sValue = RegRead( $aKeys[$i] & "\" & $sSubKey, $sValueName) If ( $iSearchMode = 0 AND StringInStr($sValue, $sFilter) = 1 ) OR _ ( $iSearchMode = 1 AND StringInStr($sValue, $sFilter) ) OR _ ( $iSearchMode = 2 AND $sValue = $sFilter ) OR _ ( $iSearchMode = 3 AND StringRegExp($sValue, $sFilter) ) Then $iFound = 1 EndIf EndIf If $iFound Then Redim $aResult[ UBound($aResult) + 1][ 4 + $aCols[0] ] $aResult[ UBound($aResult) - 1][0] = $aKeys[$i] $aResult[ UBound($aResult) - 1][1] = $sSubKey $aResult[ UBound($aResult) - 1][2] = $sDisplayName $aResult[ UBound($aResult) - 1][3] = $sDate For $iCol = 1 To $aCols[0] $aResult[ UBound($aResult) - 1][3 + $iCol] = RegRead( $aKeys[$i] & "\" & $sSubKey, $aCols[$iCol]) Next EndIf EndIf $n += 1 WEnd Next $aResult[0][0] = UBound($aResult) - 1 Return $aResult EndFunc ; #FUNCTION# ==================================================================================================================== ; Name ..........: _RegEnumKeyEx ; Description ...: Enumerates the subkeys of the specified open registry key. The function retrieves information about one subkey ; each time it is called. ; Syntax ........: _RegEnumKeyEx($sKey, $iInstance) ; Parameters ....: $sKey - The registry key to read. ; $iInstance - The 1-based key instance to retrieve. ; Return values .: Success - A 1D array : ; $aArray[0] = subkey name ; $aArray[1] = time at which the enumerated subkey was last written ; Failure - Returns 0 and set @eror to non-zero value ; Author ........: jguinch ; =============================================================================================================================== Func _RegEnumKeyEx($sKey, $iInstance) If NOT IsDeclared("KEY_WOW64_32KEY") Then Local Const $KEY_WOW64_32KEY = 0x0200 If NOT IsDeclared("KEY_WOW64_64KEY") Then Local Const $KEY_WOW64_64KEY = 0x0100 If NOT IsDeclared("KEY_ENUMERATE_SUB_KEYS") Then Local Const $KEY_ENUMERATE_SUB_KEYS = 0x0008 If NOT IsDeclared("tagFILETIME") Then Local Const $tagFILETIME = "struct;dword Lo;dword Hi;endstruct" Local $iSamDesired = $KEY_ENUMERATE_SUB_KEYS Local $iX64Key = 0, $sRootKey, $aResult[2] Local $sRoot = StringRegExpReplace($sKey, "\\.+", "") Local $sSubkey = StringRegExpReplace($sKey, "^[^\\]+\\", "") $sRoot = StringReplace($sRoot, "64", "") If @extended Then $iX64Key = 1 If NOT IsInt($iInstance) OR $iInstance < 1 Then Return SetError(2, 0, 0) Switch $sRoot Case "HKCR", "HKEY_CLASSES_ROOT" $sRootKey = 0x80000000 Case "HKLM", "HKEY_LOCAL_MACHINE" $sRootKey = 0x80000002 Case "HKCU", "HKEY_CURRENT_USER" $sRootKey = 0x80000001 Case "HKU", "HKEY_USERS" $sRootKey = 0x80000003 Case "HKCC", "HKEY_CURRENT_CONFIG" $sRootKey = 0x80000005 Case Else Return SetError(1, 0, 0) EndSwitch If StringRegExp(@OSArch, "64$") Then If @AutoItX64 OR $iX64Key Then $iSamDesired = BitOR($iSamDesired, $KEY_WOW64_64KEY) Else $iSamDesired = BitOR($iSamDesired, $KEY_WOW64_32KEY) EndIf EndIf Local $aRetOPen = DllCall('advapi32.dll', 'long', 'RegOpenKeyExW', 'handle', $sRootKey, 'wstr', $sSubKey, 'dword', 0, 'dword', $iSamDesired, 'ulong_ptr*', 0) If @error Then Return SetError(@error, @extended, 0) If $aRetOPen[0] Then Return SetError(10, $aRetOPen[0], 0) Local $hKey = $aRetOPen[5] Local $tFILETIME = DllStructCreate($tagFILETIME) Local $lpftLastWriteTime = DllStructGetPtr($tFILETIME) Local $aRetEnum = DllCall('Advapi32.dll', 'long', 'RegEnumKeyExW', 'long', $hKey, 'dword', $iInstance - 1, 'wstr', "", 'dword*', 255, 'dword', "", 'ptr', "", 'dword', "", 'ptr', $lpftLastWriteTime) If Not IsArray($aRetEnum) OR $aRetEnum[0] <> 0 Then Return SetError( 3, 0, 1) Local $tFILETIME2 = _Date_Time_FileTimeToLocalFileTime($lpftLastWriteTime) Local $localtime = _Date_Time_FileTimeToStr($tFILETIME2, 1) $aResult[0] = $aRetEnum[3] $aResult[1] = $localtime Return $aResult EndFunc1 point
-
Use C/C++ DLL in VBA
FrancescoDiMuro reacted to Danyfirex for a topic
Hello. yes You can get the length of a string from the BSTR structure But It's not the right/correct way. But using WTSQuerySessionInformationA you will get the length in lngBytes in bytes. So If you use WTSQuerySessionInformationW You will get lngBytes * 2 It's correct you can use the lngBytes for redim and subtract 2, (1 for NULL Char at the end of the string and the other because vba array bound starts from 0.) It's what lstrlenA does. in your code you could do this instead using PointerToStringA. Dim aByteArray() As Byte ReDim aByteArray(lngBytes - 2) CopyMemory aByteArray(0), ByVal lngBuffer, lngBytes - 1 Dim sStr As String sStr = StrConv(aByteArray, vbUnicode) Saludos1 point -
Try 3.3.15 it was buld for latest versions of AutoIt1 point
-
Script becomes way slower after a msgbox - (Moved)
FrancescoDiMuro reacted to Danyfirex for a topic
This is my ouput with your example: First loop is 143.64% slower after Sleep() Subsequent loops are 0.07% slower after Sleep() Complete run is 20.85% slower after Sleep() You're right, but If I have/need/put a sleep in my for It will slow down too. Check removing the sleep(1000) and add sleep(100) inside second for in your code like: For $i = 0 To $Runs - 1 Sleep(100) $times[$i][2] = Round(timeit(), 1) Next This is my ouput with the change above. First loop is 121.31% slower after Sleep() Subsequent loops are 1.06% slower after Sleep() Complete run is 107.12% slower after Sleep() @jchd nice average code 👏 PD: Sorry If I'm not using syntax highlighting My 30Kbs connection don't want to load the required resource to use the syntax highlight feature. Saludos1 point -
Program has been updated to Get Image Links v1.1, see first post. It involves a bugfix, due to a change of line endings from TheDcoder's script, which he tells me is the browser's fault for modifying what is sent to the clipboard. Previously my program was dividing on @CRLF during Import. Today that did not work for me, and I found it instead needed @LF. To cover all bases, my program now tests for more than one @CRLF first, and if not found then tests for more than one @LF, then if that not found, tests for @CR. Hopefully that solves any future issues in that regard.1 point
-
In this case I would suggest to remove the alpha channel and draw with background color. #include <GUIConstants.au3> #include <GDIPlus.au3> Global $iBgColor = 0xFFFF0000 _GDIPlus_Startup() $hMain = GUICreate('', 200, 200) $pi_Pic = _GUICtrlCreatePNG(@ScriptDir & '\png_blend.png', 50, 50, 100, 100, $iBgColor) GUISetBkColor(BitAND(0xFFFFFF, $iBgColor)) GUISetState() Do Until GUIGetMsg() = $GUI_EVENT_CLOSE _GDIPlus_Shutdown() Func _GUICtrlCreatePNG($sPath, $iX, $iY, $iW, $iH, $iBgColor) Local $idPic = GUICtrlCreatePic('', $iX, $iY, $iW, $iH) Local $hImage = _GDIPlus_ImageLoadFromFile($sPath) ;Local $hBmp = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage) Local $hBmp = _GDIPlus_Convert2HBitmap($hImage, $iBgColor) _WinAPI_DeleteObject(GUICtrlSendMsg($idPic, $STM_SETIMAGE, $IMAGE_BITMAP, $hBmp)) _GDIPlus_ImageDispose($hImage) _WinAPI_DeleteObject($hBmp) Return $idPic EndFunc Func _GDIPlus_Convert2HBitmap($hBitmap, $iBgColor) Local $iWidth = _GDIPlus_ImageGetWidth($hBitmap), $iHeight = _GDIPlus_ImageGetHeight($hBitmap) Local $hBitmap_new = _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight) Local $hCtx_new = _GDIPlus_ImageGetGraphicsContext($hBitmap_new) Local $hBrush = _GDIPlus_BrushCreateSolid($iBgColor) _GDIPlus_GraphicsFillRect($hCtx_new, 0, 0, $iWidth, $iHeight, $hBrush) _GDIPlus_GraphicsDrawImageRect($hCtx_new, $hBitmap, 0, 0, $iWidth, $iHeight) Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap_new) _GDIPlus_BrushDispose($hBrush) _GDIPlus_BitmapDispose($hBitmap_new) _GDIPlus_GraphicsDispose($hCtx_new) Return $hHBITMAP EndFunc ;==>_GDIPlus_Convert2HBitmap1 point
-
Hi everyone. first of all thank you for all the good forum post - they helped me a lot to start digging into Autoit. But I am at a point know where I am lost and still can't find the right way to use StringRegExp in my script. How can I validate if a string is a valid tld or cctld (example: domain.com or domain.co.uk) 1. Element (the name before the dot) has to be only Letters, Number or the minus sign: [A-Z0-9-]+ 2. Element has to be 1 dot: \. 3. Element the tld can only be letters: [A-Z]+ ...that would be for TLD 4. + 5. Element the cctld is optional but max once and has to be "dot followed by only letters" : \.[A-Z]? My best string result so far is: StringRegExp($domains[$i], "(?i)([A-Z0-9-]+\.[A-Z]{2,10}){1}$") But this see domain.co1.uk as valid which is wrong because only letters are allowed after the dot Can anyone help me here? Thanks Uwe1 point
-
#Include <Array.au3> $txt = "neto.net.il" & @crlf & _ "60israel.org.il" & @crlf & _ "www.tau.ac.il" & @crlf & _ "www.tau.ac.co.uk" & @crlf & _ "weizmann.ac.il" & @crlf & _ "ww2.saritlorens.co.il" & @crlf & _ "saribeauty.co.il" $ext = "uk" ;"il" $res = StringRegExp($txt, '(?m)^(?:ww.\.)?(.*?\.' & $ext & ')$', 3) _ArrayDisplay($res)1 point
-
StringRegExp - validate domain tld & cctld
Jangal reacted to FrancescoDiMuro for a topic
@Jangal Could you please post some sample strings and what results you expect?1 point -
StringRegExp - validate domain tld & cctld
Jangal reacted to FrancescoDiMuro for a topic
Hi @frobber, and welcome to the AutoIt forums Maybe something like this? (?i)^([-a-z0-9]+\.[a-z]+(?:\.[a-z]+)?)$1 point -
It certainly is, the line which creates the final big multi-line string has never changed since the first version: urls = urls.join('\n');0 points