Jump to content

Implementing window procedure (message handler) in compiled code


LarsJ
 Share

Recommended Posts

In Fast subclassing through compiled code a WM_NOTIFY message handler is implemented in C/C++. Is it possible to implement the entire window procedure (main message handler) in compiled code and thus avoid the overhead associated with the subclassing technique?

 

AutoIt code
This is a slightly modified version of _WinAPI_RegisterClassEx example, and a listview is created in the window (ListView0.au3):

#include <WinAPIRes.au3>
#include <WinAPISys.au3>
#include <GuiListView.au3>
#include <WindowsConstants.au3>

Global $bExit = False
Global $iRows = 100, $iCols = 8

Example()

Func Example()
  Local Const $sClass = "MyWindowClass"
  Local Const $sName = "Ordinary ListView"

  ; 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
  Local $hWnd = _WinAPI_CreateWindowEx( 0, $sClass, $sName, BitOR( $WS_CAPTION, $WS_POPUPWINDOW, $WS_VISIBLE ), ( @DesktopWidth - 826 ) / 2, ( @DesktopHeight - 584 ) / 2, 826, 584, 0 )

  ; Create listview
  Local $hListView = _GUICtrlListView_Create( $hWnd, "", 10, 10, 800, 538, $LVS_DEFAULT-$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
    _GUICtrlListView_AddItem( $hListView, "Row " & $i )
    For $j = 1 To $iCols - 1
      _GUICtrlListView_AddSubItem( $hListView, $i, "Col " & $j, $j )
    Next
  Next

  ; Main loop
  While Sleep(10)
    If $bExit Then ExitLoop
  WEnd

  ; Unregister window class and release unnecessary resources
  _WinAPI_UnregisterClass( $sClass, $hInstance )
  _WinAPI_DestroyCursor( $hCursor )
  _WinAPI_DestroyIcon( $hIcon )
  _WinAPI_DestroyIcon( $hIconSm )
EndFunc

Func WinProc( $hWnd, $iMsg, $wParam, $lParam )
  Switch $iMsg
    Case $WM_CLOSE
      $bExit = True
  EndSwitch
  Return _WinAPI_DefWindowProcW( $hWnd, $iMsg, $wParam, $lParam )
EndFunc

It's left for Windows to handle all listview messages. No new code is added to WinProc() function.

In the next example the listview is replaced by a virtual and custom drawn listview. Now it's necessary to handle LVN_GETDISPINFO and NM_CUSTOMDRAW notifications in WinProc() function (ListView1.au3):

; 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 )

WinProc() function:

Func WinProc( $hWnd, $iMsg, $wParam, $lParam )
  Local Static $tText = DllStructCreate( "wchar[100]" ), $pText = DllStructGetPtr( $tText )
  Local Static $tRect = DllStructCreate( $tagRECT ), $pRect = DllStructGetPtr( $tRect )

  Switch $iMsg
    Case $WM_NOTIFY
      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 DllCall( "user32.dll", "lresult", "SendMessageW", "hwnd", $hListView, "uint", $LVM_GETITEMSTATE, "wparam", $iItem, "lparam", $LVIS_SELECTED )[0] 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 )
              DllCall( "user32.dll", "lresult", "SendMessageW", "hwnd", $hListView, "uint", $LVM_GETSUBITEMRECT, "wparam", $iItem, "lparam", $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

    Case $WM_CLOSE
      $bExit = True
  EndSwitch

  Return _WinAPI_DefWindowProcW( $hWnd, $iMsg, $wParam, $lParam )
EndFunc

Also in this example there is a problem with the speed of the WM_NOTIFY code. You can provoke an 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.

Note that since the window isn't created with GUICreate(), it's not possible to use any of the native functions in the GUI Management section of the help file. But all the UDFs in the GUI Reference section can be used.

 

C/C++ code
The window procedure above is defined this way:

; Create DLL callback function (window procedure)
Local $pWinProc = DllCallbackGetPtr( DllCallbackRegister( "WinProc", "lresult", "hwnd;uint;wparam;lparam" ) )
DllStructSetData( $tWCEX, "hWndProc", $pWinProc )

The C/C++ definition of a window procedure in a dll-file looks like this:

LRESULT CALLBACK __stdcall WinProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )

We can get a pointer to the window procedure this way:

Local $hModule = _WinAPI_LoadLibrary( "WinProc.dll" )
Local $pWinProc = _WinAPI_GetProcAddress( $hModule, "WinProc" )

Now we can use this pointer in $tWCEX struct.

We also need to pass $aItems and $aColors arrays to the compiled code in the dll-file. But we already know how to do that.

Note that because $pWinProc is used directly in $tWCEX struct, we can avoid both the DllCall and DllCallAddress commands. Thus, we avoid a severe overhead in relation to these commands (that the AutoIt code interpreter should interpret a code line for each message (for a huge number of messages), and that DllCall and DllCallAddress are complicated and time-consuming commands).

AutoIt code in ListView2.au3:

#include <WinAPIRes.au3>
#include <WinAPISys.au3>
#include <GuiListView.au3>
#include <WindowsConstants.au3>
#include "AccArrays.au3"

Global $aData[3] ; $psaItems, $psaColors, $pExit

Example()

Func Example()
  Local Const $sClass = "MyWindowClass", $sName = "Virtual and Custom Drawn ListView"
  Local $iRows = 10000, $iCols = 8, $aItems[$iRows][$iCols], $aColors[$iRows][$iCols]

  ; 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 $hModule = _WinAPI_LoadLibrary( @AutoItX64 ? "WinProc_x64.dll" : "WinProc.dll" )
  Local $pWinProc = _WinAPI_GetProcAddress( $hModule, "WinProc" )

  ; 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
  Local $hWnd = _WinAPI_CreateWindowEx( 0, $sClass, $sName, BitOR( $WS_CAPTION, $WS_POPUPWINDOW, $WS_VISIBLE ), ( @DesktopWidth - 826 ) / 2, ( @DesktopHeight - 584 ) / 2, 826, 584, 0 )

  ; Create listview
  Local $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

  ; Load dll-file
  Local $hDll = DllOpen( @AutoItX64 ? "WinProc_x64.dll" : "WinProc.dll" )

  ; $aItems, $aColors and $bExit
  Local $psaItemsData, $psaColorsData, $bExit = False
  $aData[2] = DllStructGetPtr( DllStructCreate( $tagVARIANT ) )
  AccArrays03( Copy, $aItems, $aColors, $bExit )
  SafeArrayAccessData( $aData[0], $psaItemsData )
  SafeArrayAccessData( $aData[1], $psaColorsData )

  ; Pass data to compiled code
  DllCall( $hDll, "none", "PassData", "hwnd", $hListView, "int", $iCols, "ptr", $psaItemsData, "ptr", $psaColorsData, "ptr", $aData[2] )

  ; Set number of rows in virtual ListView
  DllCall( "user32.dll", "lresult", "SendMessageW", "hwnd", $hListView, "uint", $LVM_SETITEMCOUNT, "wparam", $iRows, "lparam", 0 )

  ; Main loop
  While Sleep(10)
    If DllStructGetData( DllStructCreate( "short", $aData[2] + 8 ), 1 ) Then ExitLoop
  WEnd

  ; Unregister window class and release unnecessary resources
  _WinAPI_UnregisterClass( $sClass, $hInstance )
  _WinAPI_DestroyCursor( $hCursor )
  _WinAPI_DestroyIcon( $hIcon )
  _WinAPI_DestroyIcon( $hIconSm )
  SafeArrayUnaccessData( $aData[0] )
  SafeArrayUnaccessData( $aData[1] )
  _WinAPI_FreeLibrary( $hModule )
  DllClose( $hDLL )
EndFunc

; Copy internal AutoIt variants (variables) to real Windows
; variants, that can be accessed from both AutoIt and C/C++.
Func Copy( $pvItems, $pvColors, $pvExit )
  SafeArrayCopy( DllStructGetData( DllStructCreate( "ptr", $pvItems  + 8 ), 1 ), $aData[0] )
  SafeArrayCopy( DllStructGetData( DllStructCreate( "ptr", $pvColors + 8 ), 1 ), $aData[1] )
  VariantCopy( $aData[2], $pvExit )
EndFunc

C/C++ code:

#include <Windows.h>
#include <CommCtrl.h>
#include <OleAuto.h>

// Globals
HWND      g_hListView;
int       g_iColumns;
VARIANT*  g_psaItems;
VARIANT*  g_psaColors;
VARIANT*  g_pExit;

void __stdcall PassData( HWND hListView, int iColumns, VARIANT* psaItems, VARIANT* psaColors, VARIANT* pExit ) {
  g_hListView = hListView; g_iColumns = iColumns; g_psaItems = psaItems; g_psaColors = psaColors; g_pExit = pExit;
}

LRESULT CALLBACK __stdcall WinProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
  NMLVDISPINFOW*  plvdi;
  NMLVCUSTOMDRAW* plvcd;
  BSTR            pBstr;
  DWORD_PTR       iItem;
  RECT            r;
  HBRUSH          hBrush;

  switch ( uMsg ) {
    case WM_NOTIFY:
      switch ( ((LPNMHDR)lParam)->code ) {
        case LVN_GETDISPINFOW:
          // Fill virtual listview
          plvdi = (NMLVDISPINFOW*)lParam;
          if ( ( plvdi->item.mask & LVIF_TEXT ) == 0 ) return TRUE;
          pBstr = g_psaItems[plvdi->item.iItem*g_iColumns+plvdi->item.iSubItem].bstrVal;
          plvdi->item.pszText = pBstr; plvdi->item.cchTextMax = SysStringLen( pBstr );
          return TRUE;

        case NM_CUSTOMDRAW:
          // Draw back colors
          plvcd = (NMLVCUSTOMDRAW*)lParam;
          switch ( plvcd->nmcd.dwDrawStage ) {
            case CDDS_PREPAINT:
              return CDRF_NOTIFYITEMDRAW;
            case CDDS_ITEMPREPAINT:
              return CDRF_NOTIFYSUBITEMDRAW;
            case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
              iItem = plvcd->nmcd.dwItemSpec;
              if ( SendMessageW( g_hListView, LVM_GETITEMSTATE, iItem, LVIS_SELECTED ) ) return CDRF_NOTIFYPOSTPAINT;
              plvcd->clrTextBk = g_psaColors[iItem*g_iColumns+plvcd->iSubItem].intVal;
              return CDRF_NEWFONT;
            case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM:
              // Subitem rectangle
              iItem = plvcd->nmcd.dwItemSpec;
              r.left = LVIR_LABEL; r.top = plvcd->iSubItem;
              SendMessageW( g_hListView, LVM_GETSUBITEMRECT, iItem, (LPARAM)&r );
              r.left += 2; r.right -= 2; r.top += 1; r.bottom -= 1;
              // Subitem back color
              hBrush = CreateSolidBrush( g_psaColors[iItem*g_iColumns+plvcd->iSubItem].intVal );
              FillRect( plvcd->nmcd.hdc, &r, hBrush );
              DeleteObject( hBrush );
              // Draw subitem text
              SetTextColor( plvcd->nmcd.hdc, 0 );
              SetBkMode( plvcd->nmcd.hdc, TRANSPARENT );
              pBstr = g_psaItems[iItem*g_iColumns+plvcd->iSubItem].bstrVal; r.top += 1;
              DrawTextW( plvcd->nmcd.hdc, pBstr, SysStringLen( pBstr ), &r, DT_CENTER );
              return CDRF_NEWFONT;
          }

        default: 
          return DefWindowProcW( hWnd, uMsg, wParam, lParam ); 
      }

    case WM_CLOSE:
      g_pExit->boolVal = 1;

    default: 
      return DefWindowProcW( hWnd, uMsg, wParam, lParam ); 
  }

  return 0;
}

 

Zip-file
ListView0.au3 and ListView1.au3 is pure AutoIt code. In ListView2.au3 the window procedure is replaced with compiled code.

Note that the zip-file contains two small dll-files. If you are using Microsoft SmartScreen or Microsoft Windows Defender, there is a likelihood of fake virus detection, even if the dll-files are created with Microsoft Visual Studio. Most other antivirus products will not detect fake viruses.

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.

WinProc.7z

Edited by LarsJ
New zip-file with updated ListView2.au3
Link to comment
Share on other sites

Hello really nice, You rock LarsJ!!! :drool:

I was getting ListView2.au3 Ended without pressing close button. It was exiting from main loop because you forgot to check g_pExit value with 1.

This fixed the issue.

If DllStructGetData( DllStructCreate( "short", $aData[2] + 8 ), 1 )=1 Then ExitLoop

You could wrap "DllStructGetData( DllStructCreate( "short", $aData[2] + 8 ), 1 )" in a easier way for not advanced  users because the will get scared when they see that. :lol:


Saludos
 

Link to comment
Share on other sites

Danyfirex, thank you very much.

 

The variables used to close the program are $bExit and $aData[2]:

; $aItems, $aColors and $bExit
Local $psaItemsData, $psaColorsData, $bExit = False
$aData[2] = DllStructGetPtr( DllStructCreate( $tagVARIANT ) )
AccArrays03( Copy, $aItems, $aColors, $bExit )

The Copy() function that's executed by AccArrays03() copies $bExit to $aData[2] in this way:

VariantCopy( $aData[2], $pvExit )

Now $aData[2] is a real windows variant that can be accessed from both AutoIt and C/C++. This means that $aData[2] is a pointer to a variant structure defined by $tagVARIANT. And since it's copied from $bExit which is a boolean (False/True or 0/1), $aData[2] is a variant of type $VT_BOOL:

Global Const $tagVARIANT = _
  "word vt;" & _  ; 2 bytes, contains the value $VT_BOOL (= 11 = 0x000B) for $aData[2]
  "word r1;" & _  ; 2 bytes, reserved for Windows
  "word r2;" & _  ; 2 bytes, reserved for Windows
  "word r3;" & _  ; 2 bytes, reserved for Windows
  "ptr data; ptr" ; 8 (32 bit) or 16 (64 bit) bytes data field

Because $aData[2] is a boolean variant, only the first two bytes of the data field is used to store the boolean value (usually 0 or 1, but any non-zero value that fits into two bytes can be used instead of 1). And these two bytes are interpreted as a short. That's the Microsoft definition.

Because $aData[2] is a pointer to the start of the variant structure, $aData[2] + 8 is a pointer directly to the data field. This means that the value of the data field (the boolean value) can be read this way:

$tShort = DllStructCreate( "short", $aData[2] + 8 )
$bValue = DllStructGetData( $tShort, 1 )

Or simply:

$bValue = DllStructGetData( DllStructCreate( "short", $aData[2] + 8 ), 1 )

And since $bValue is a boolean (True/False or 1/0 or non-zero/zero) it can simply be tested this way:

If $bValue Then ...

Or:

If DllStructGetData( DllStructCreate( "short", $aData[2] + 8 ), 1 ) Then ...

You can read more about variants (and safearrays) in Accessing AutoIt Variables.

 

Now to the error. Replace this line in ListView2.au3:

$aData[2] = DllStructGetPtr( DllStructCreate( $tagVARIANT ) )

With these two lines:

Local $tVARIANT = DllStructCreate( $tagVARIANT )
$aData[2] = DllStructGetPtr( $tVARIANT )

A structure must be assigned to a variable to prevent the allocated memory to be used for something else. And that was exactly what was happening. When the two bytes given by $aData[2] + 8 were filled with a non-zero value in a very random way, the program immediately closed. If you were lucky. In worst case it would crash with some kind of memory access violation error. 0xC0000005 or something.

 

New zip-file with updated ListView2.au3 in bottom of first post.

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