Jump to content

Issue with DllCall


FreeFry
 Share

Recommended Posts

Hi, I'm having problem with a script I was trying in response to solve another problem.

I'm trying to add an entry to a window's system/control menu(the menu that appears when you right click the title bar of a window)

This is the code that I currently have, which causes the crash:

Opt("WinTitleMatchMode", 4)

Global Const $MIIM_TYPE = 0x00000010
Global Const $MIIM_STRING = 0x00000040
Global Const $MIIM_FTYPE = 0x00000100

Global Const $MFT_STRING = 0x0000

Dim $ItemDataString = "Test"
Dim $WindowHandle = WinGetHandle("[CLASS:SciTEWindow]")

If @error Then
    MsgBox(0, "Error", "There was an error getting the window handle!")
    Exit
EndIf

$HMENU = DllCall("user32.dll", "hwnd", "GetSystemMenu", "hwnd", $WindowHandle, "int", False)
If @error Then
    MsgBox(0, "Error", "An error occured when getting the SystemMenu: " & @error)
    Exit
EndIf

$MENUITEMINFO = DllStructCreate("uint;uint;uint;uint;uint;hwnd;hwnd;hwnd;ulong_ptr;char;uint;hwnd")
If @error Then
    MsgBox(0, "Error", "There was an error while creating the structure: " & @error)
    Exit
EndIf

DllStructSetData($MENUITEMINFO, 2, $MIIM_STRING+$MIIM_FTYPE)
DllStructSetData($MENUITEMINFO, 3, $MFT_STRING)
DllStructSetData($MENUITEMINFO, 10, $ItemDataString)
DllStructSetData($MENUITEMINFO, 11, StringLen($ItemDataString))
DllStructSetData($MENUITEMINFO, 1, DllStructGetSize($MENUITEMINFO)) ; not sure if this needs to be set after all the data is set, or if it can be set before..

DllCall("user32.dll", "int", "InsertMenuItem", "hwnd", $HMENU[0], "uint", 1, "byte", 1, "ptr", DllStructGetPtr($MENUITEMINFO))
If @error Then
    MsgBox(0, "Error", "There was an error in the InsertMenuItem function: " & @error)
EndIf

$MENUITEMINFO = 0

Reference pages with the functions:

GetSystemMenu: http://msdn2.microsoft.com/en-us/library/ms647985.aspx

InsertMenuItem: http://msdn2.microsoft.com/en-us/library/m...988(VS.85).aspx

I'm not sure wheter it's a syntax error on my part(probably), or if it might be some kind of bug/incompability(less likely) between autoit and this/these function(s).

I hope someone can shine some light at this, as I'm trying to understand how the DllCall syntax translates between the Windows API and AutoIt. :D

Edited by FreeFry
Link to comment
Share on other sites

If you say AutoIt crashes, you need to include the error messages generated when it does so.

Dale

Free Internet Tools: DebugBar, AutoIt IE Builder, HTTP UDF, MODIV2, IE Developer Toolbar, IEDocMon, Fiddler, HTML Validator, WGet, curl

MSDN docs: InternetExplorer Object, Document Object, Overviews and Tutorials, DHTML Objects, DHTML Events, WinHttpRequest, XmlHttpRequest, Cross-Frame Scripting, Office object model

Automate input type=file (Related)

Alternative to _IECreateEmbedded? better: _IECreatePseudoEmbedded  Better Better?

IE.au3 issues with Vista - Workarounds

SciTe Debug mode - it's magic: #AutoIt3Wrapper_run_debug_mode=Y

Doesn't work needs to be ripped out of the troubleshooting lexicon. It means that what you tried did not produce the results you expected. It begs the questions 1) what did you try?, 2) what did you expect? and 3) what happened instead?

Reproducer: a small (the smallest?) piece of stand-alone code that demonstrates your trouble

Link to comment
Share on other sites

Long File :D

Yea, well it's part of a crashlog, what do you expect?

Anyways, I've looked in how this is done in the GuiMenu UDF, and there's a few things that I don't understand(actually, the way it's done in the UDF should be wrong, but it works :S)

The way the structure is done in the UDF uses element names(is this perhaps required?),

but if you compare that structure to the one in the documentation about the structure(http://msdn2.microsoft.com/en-us/library/ms647578(VS.85).aspx) it is all wrong. :S

I'm confused..

From what I could gather from the documentation about the structure, this is the correct structure:

"uint;uint;uint;uint;uint;hwnd;hwnd;hwnd;ulong_ptr;char;uint;hwnd"

But in the UDF, this is how it's done:

"int Size;int Mask;int Type;int State;int ID;int SubMenu;int BmpChecked;int BmpUnchecked;int ItemData;ptr TypeData;int CCH;int BmpItem"

It's weird as all of the first 5 elements in the structure is clearly supposed to be uint's but still in the UDF they're not ... :S

Someone please explain this. :P

Edited by FreeFry
Link to comment
Share on other sites

Yea, well it's part of a crashlog, what do you expect?

Anyways, I've looked in how this is done in the GuiMenu UDF, and there's a few things that I don't understand(actually, the way it's done in the UDF should be wrong, but it works :S)

The way the structure is done in the UDF uses element names(is this perhaps required?),

but if you compare that structure to the one in the documentation about the structure(http://msdn2.microsoft.com/en-us/library/ms647578(VS.85).aspx) it is all wrong. :S

I'm confused..

From what I could gather from the documentation about the structure, this is the correct structure:

"uint;uint;uint;uint;uint;hwnd;hwnd;hwnd;ulong_ptr;char;uint;hwnd"

But in the UDF, this is how it's done:

"int Size;int Mask;int Type;int State;int ID;int SubMenu;int BmpChecked;int BmpUnchecked;int ItemData;ptr TypeData;int CCH;int BmpItem"

It's weird as all of the first 5 elements in the structure is clearly supposed to be uint's but still in the UDF they're not ... :S

Someone please explain this. :D

In the new version 10.0 long_ptr don't exist and wa replaced by long* maybe the problem comes from that.
Link to comment
Share on other sites

Well, the helpfile doesn't say anything about that..

Edit:

Well actually it does say something about that, but that's related to DllCall, and not DllStructCreate.

Anyways, I've got it to a working state now(though, sometimes it doesn't work for some reason :S):

Opt("WinTitleMatchMode", 4)

Global Const $MIIM_STRING = 0x00000040

Run("notepad.exe")
WinWait("[CLASS:Notepad]")

Dim $WindowHandle = WinGetHandle("[CLASS:Notepad]")

Dim $TextStr = "Test"

$Text = DllStructCreate("char Text[" & StringLen($TextStr)+1 & "]")
If @error Then
    MsgBox(0, "Error", "There was an error while creating the Text structure: " & @error)
    Exit
EndIf
DllStructSetData($Text, "Text", $TextStr)

If @error Then
    MsgBox(0, "Error", "There was an error getting the window handle!")
    Exit
EndIf

$HMENU = DllCall("user32.dll", "hwnd", "GetSystemMenu", "hwnd", $WindowHandle, "int", False)
If @error Then
    MsgBox(0, "Error", "An error occured when getting the SystemMenu: " & @error)
    Exit
EndIf

$MENUITEMINFO = DllStructCreate("uint Size;uint Mask;uint Type;uint State;uint ID;hwnd SubMenu;hwnd BmpChecked;hwnd BmpUnchecked;int ItemData;ptr TypeData;uint CCH;hwnd BmpItem")
If @error Then
    MsgBox(0, "Error", "There was an error while creating the MENUITEMINFO structure: " & @error)
    Exit
EndIf

DllStructSetData($MENUITEMINFO, "Size", DllStructGetSize($MENUITEMINFO))                    ; cbSize
DllStructSetData($MENUITEMINFO, "Mask", $MIIM_STRING)                                       ; fMask
DllStructSetData($MENUITEMINFO, "TypeData", DllStructGetPtr($Text))                         ; dwTypeData

$a = DllCall("user32.dll", "int", "InsertMenuItem", "hwnd", $HMENU[0], "int", 8, "int", True, "ptr", DllStructGetPtr($MENUITEMINFO))
If @error Then
    MsgBox(0, "Error", "There was an error in the InsertMenuItem function: " & @error)
ElseIf Not $a[0] Then
    MsgBox(0, "Error", "The InsertMenuItem function did not add a new menu item.")
Else
    MsgBox(0, "Success", "A new menu item called '" & $TextStr & "' was added to the System Menu")
EndIf

I think that the error that caused the crash was related to using the wrong structure type(I used a char when it really should've been a pointer to another structure with the text specified)

Edit:

Now I just need to find a way of hooking into the window and catch the message whenver the menu item is clicked. :|

And I have no clue if that's possible or if it even can be done with AutoIt...

Edited by FreeFry
Link to comment
Share on other sites

Now I just need to find a way of hooking into the window and catch the message whenver the menu item is clicked. :|

And I have no clue if that's possible or if it even can be done with AutoIt...

It's definitely possible, there are some shareware apps (WireKeys and others) that do this, and I recall reading an article outlining the process somewhere, can't find it atm..

Proper way is by the use of global hooks (SetWindowsHookEx, etc.), and of course you need a dll for that. Which may be outside of what you can or want to do.

Of course, there's always cheese like this, which would work reliably enough, I guess:

#include <GuiMenu.au3>
#Include <Misc.au3>

Global $sTitle = 'Calculator', $iOurItem, $bChecked = False
If Not WinExists($sTitle) Then
    Run('calc.exe')
    WinWait($sTitle)
EndIf
$hCalc = WinGetHandle($sTitle)
$hCalcMenu = _GUICtrlMenu_GetSystemMenu($hCalc)
_GUICtrlMenu_AppendMenu($hCalcMenu, $MF_SEPARATOR, 0, 0)
$iOurItem = _GUICtrlMenu_AddMenuItem($hCalcMenu, "Always On Top")

While WinExists($hCalc)
    Sleep(50)
    If _IsPressed(1) And WinActive($hCalc) Then
        If _GUICtrlMenu_MenuItemFromPoint($hCalc, $hCalcMenu) = $iOurItem Then
            $bChecked = Not $bChecked
            _GUICtrlMenu_SetItemChecked($hCalcMenu, $iOurItem, $bChecked)
            WinSetOnTop($hCalc, "", $bChecked)
        EndIf
    EndIf
WEnd

Exit

Doesn't work too well if menu is invoked by rightclicking button on taskbar though.

And I'm not a fan of polling approach in general.

But these are your options.

Edited by Siao

"be smart, drink your wine"

Link to comment
Share on other sites

Thanks for your response Siao.

Yes, neither I am very found of polling, I like hooks more(no chance of missing a message)..

Well, I don't know how to code a DLL so that's a problem. :/

Correct me if I'm wrong, but the only hooks autoit handle is the low level Mouse and Keyboard hook right? Atleast that's the only two I've seen that doesn't require the callback procedure to be in a DLL. :D

Link to comment
Share on other sites

FreeFry

Nice work! I like it! But where you take $MIIM_STRING = 0x00000040 and why you set data only for this:

DllStructSetData($MENUITEMINFO, "Size", DllStructGetSize($MENUITEMINFO))                    ; cbSize
DllStructSetData($MENUITEMINFO, "Mask", $MIIM_STRING)                              ; fMask
DllStructSetData($MENUITEMINFO, "TypeData", DllStructGetPtr($Text))       ; dwTypeData
?
Link to comment
Share on other sites

I'm not if I understand you correctly rasim, but I was testing, and found out that you only have to set those 3 elements in the structure to be able to add a new entry to the system menu.

Inderstand, but what about $MIIM_STRING = 0x00000040? Why exactly this value?

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