TimRude Posted February 2, 2023 Share Posted February 2, 2023 (edited) Here's a code sample showing how I add icon images (in this simple case just colored blocks) to the tray items. It should be noted that I used solid blocks to simplify the example, but anything can be set as the bitmap (and in any size) using the appropriate code to generate a bitmap to feed into the _GUICtrlMenu_SetItemBmp function. expandcollapse popup#NoTrayIcon #include <TrayConstants.au3> #include <ColorConstants.au3> #include <GuiMenu.au3> #include <WinAPIGdi.au3> Opt("TrayMenuMode", 3) ; The default tray menu items will not be shown and items are not checked when selected. These are options 1 and 2 for TrayMenuMode. Opt("TrayOnEventMode", 1) ; Enable TrayOnEventMode. Global $idTrayText, $idUDFText Example() Func Example() Local $iBmpSize = 32 TrayCreateItem("About") TrayItemSetOnEvent(-1, "About") _TrayItemSetBmp(-1, $iBmpSize, $COLOR_BLUE) TrayCreateItem("") ; Create a separator line. $idTrayText = TrayCreateItem("Read Text with TrayItemGetText") TrayItemSetOnEvent(-1, "TrayText") _TrayItemSetBmp(-1, $iBmpSize, $COLOR_GREEN) $idUDFText = TrayCreateItem("Read Text with _GUICtrlMenu_GetItemText") TrayItemSetOnEvent(-1, "UDFText") _TrayItemSetBmp(-1, $iBmpSize, $COLOR_ORANGE) TrayCreateItem("") ; Create a separator line. TrayCreateItem("Exit") TrayItemSetOnEvent(-1, "ExitScript") _TrayItemSetBmp(-1, $iBmpSize, $COLOR_RED) TraySetState($TRAY_ICONSTATE_SHOW) ; Show the tray menu. While 1 Sleep(100) ; An idle loop. WEnd EndFunc ;==>Example Func About() MsgBox(0, "Tray Menu Item Image Icons", "AutoIt tray menu item image icons example.") EndFunc ;==>About Func TrayText() ConsoleWrite("TrayText: " & TrayItemGetText($idTrayText) & @CRLF) EndFunc ;==>TrayText Func UDFText() Local $hMenu = TrayItemGetHandle(0) ConsoleWrite("UDFText: " & _GUICtrlMenu_GetItemText($hMenu, $idUDFText, False) & @CRLF) EndFunc ;==>UDFText Func ExitScript() Exit EndFunc ;==>ExitScript Func _TrayItemSetBmp($idControl, $iBmpSize, $iColor, $bRGB = 1) ; Adds an image (bitmap) to a tray menu item - solid block of color ; $idControl = the control ID of the menu item (or -1 for last created item) ; $iBmpSize = the size of the Bmp (height & width are the same) ; $iColor = the color of the bitmap, stated in RGB ; $bRGB [optional] = if True converts to COLOREF (0x00bbggrr) Local $hMenu = TrayItemGetHandle(0) ; handle for standard tray menu If $idControl = -1 Then $idControl = _GUICtrlMenu_GetItemID($hMenu, _GUICtrlMenu_GetItemCount($hMenu) - 1, True) ; last item in menu _GUICtrlMenu_SetItemBmp($hMenu, $idControl, _WinAPI_CreateSolidBitmap(WinGetHandle(AutoItWinGetTitle()), $iColor, $iBmpSize, $iBmpSize, $bRGB), False) EndFunc ;==>_TrayItemSetBmp This example also illustrates an odd quirk. Once you add an image to a tray menu item using the _GUICtrlMenu_SetItemBmp function, you can no longer use TrayItemGetText or TrayItemSetText on that item. Sometimes TrayItemGetText returns gibberish (like it's reading the wrong portion of memory) and sometimes it returns nothing. And TrayItemSetText doesn't change the item text. However, the functions _GUICtrlMenu_GetItemText and _GUICtrlMenuSetItemText work just fine as replacements. Any ideas from the developers (or anyone else) as to why adding an image messes up the TrayItemGetText and TrayItemSetText functions? Maybe something for the Bug Tracker? Edited February 2, 2023 by TimRude Fixed the variable name - Thanks mistersquirrle Link to comment Share on other sites More sharing options...
mistersquirrle Posted February 2, 2023 Share Posted February 2, 2023 I tried just messing around with this some, see if it was the same on my computer. First of all, your code currently returns nothing when you click on 'Read Text with TrayItemGetText' because you're using a different variable than your global variable, so you're not referencing the correct menu (or any menu). Though with that fixed, I do get the 'incorrect' text. Here's my modified script: expandcollapse popup#NoTrayIcon #include <WinAPI.au3> #include <TrayConstants.au3> #include <ColorConstants.au3> #include <GuiMenu.au3> #include <WinAPIGdi.au3> Opt("TrayMenuMode", 3) ; The default tray menu items will not be shown and items are not checked when selected. These are options 1 and 2 for TrayMenuMode. Opt("TrayOnEventMode", 1) ; Enable TrayOnEventMode. ;http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/ Local $aCodePages[] = [0, 1, 2, 3, 42, 65000, 65001, 874, 932, 936, 949, 950, 1250, 1251, 1252, 1253, 1254, 1255, 1256, 1257, 1258] Global $idTrayText, $idUDFText Example() Func Example() Local $iBmpSize = 32 Local $idAbout = TrayCreateItem("About") TrayItemSetOnEvent($idAbout, "About") _TrayItemSetBmp($idAbout, $iBmpSize, $COLOR_BLUE) TrayCreateItem("") ; Create a separator line. $idTrayText = TrayCreateItem("Read Text with TrayItemGetText") TrayItemSetOnEvent($idTrayText, "TrayText") _TrayItemSetBmp($idTrayText, $iBmpSize, $COLOR_GREEN) $idUDFText = TrayCreateItem("Read Text with _GUICtrlMenu_GetItemText") TrayItemSetOnEvent($idUDFText, "UDFText") _TrayItemSetBmp($idUDFText, $iBmpSize, $COLOR_ORANGE) TrayCreateItem("") ; Create a separator line. Local $idExit = TrayCreateItem("Exit") TrayItemSetOnEvent($idExit, "ExitScript") _TrayItemSetBmp($idExit, $iBmpSize, $COLOR_RED) TraySetState($TRAY_ICONSTATE_SHOW) ; Show the tray menu. While 1 Sleep(100) ; An idle loop. WEnd EndFunc ;==>Example Func About() MsgBox(0, "Tray Menu Item Image Icons", "AutoIt tray menu item image icons example.") EndFunc ;==>About Func TrayText() ConsoleWrite("TrayText: " & TrayItemGetText($idTrayText) & @CRLF) ConsoleWrite("TrayText VarGetType: " & VarGetType(TrayItemGetText($idTrayText)) & @CRLF) ConsoleWrite("TrayText StringLen: " & StringLen(TrayItemGetText($idTrayText)) & @CRLF) ConsoleWrite("TrayText StringToBinary: " & StringToBinary(TrayItemGetText($idTrayText)) & @CRLF) ConsoleWrite("TrayText _WinAPI_WideCharToMultiByte($string): " & _WinAPI_WideCharToMultiByte(TrayItemGetText($idTrayText)) & @CRLF) ConsoleWrite("TrayText _ANSIToUnicode($string): " & _ANSIToUnicode(TrayItemGetText($idTrayText)) & @CRLF) For $iCodePage = 0 To UBound($aCodePages) - 1 ConsoleWrite("TrayText _WinAPI_WideCharToMultiByte($string, " & $aCodePages[$iCodePage] & "): " & _WinAPI_WideCharToMultiByte(TrayItemGetText($idTrayText), $aCodePages[$iCodePage]) & @CRLF) Next EndFunc ;==>TrayText Func UDFText() Local $hMenu = TrayItemGetHandle(0) ConsoleWrite("UDFText: " & _GUICtrlMenu_GetItemText($hMenu, $idUDFText, False) & @CRLF) EndFunc ;==>UDFText Func ExitScript() Exit EndFunc ;==>ExitScript Func _TrayItemSetBmp($idControl, $iBmpSize, $iColor, $bRGB = 1) ; Adds an image (bitmap) to a tray menu item - solid block of color ; $idControl = the control ID of the menu item (or -1 for last created item) ; $iBmpSize = the size of the Bmp (height & width are the same) ; $iColor = the color of the bitmap, stated in RGB ; $bRGB [optional] = if True converts to COLOREF (0x00bbggrr) Local $hMenu = TrayItemGetHandle(0) ; handle for standard tray menu If $idControl = -1 Then $idControl = _GUICtrlMenu_GetItemID($hMenu, _GUICtrlMenu_GetItemCount($hMenu) - 1, True) ; last item in menu ConsoleWrite('Defaulting control id to latest item: ' & $idControl & @CRLF) EndIf ConsoleWrite("TrayText for " & $idControl & " before: " & TrayItemGetText($idControl) & @CRLF) Local $hWnd = WinGetHandle(AutoItWinGetTitle()) ;~ ConsoleWrite("TrayText for "&$idControl&" after 1: " & TrayItemGetText($idControl) & @CRLF) Local $hBitmap = _WinAPI_CreateSolidBitmap($hWnd, $iColor, $iBmpSize, $iBmpSize, $bRGB) ;~ ConsoleWrite("TrayText for "&$idControl&" after 2: " & TrayItemGetText($idControl) & @CRLF) _GUICtrlMenu_SetItemBmp($hMenu, $idControl, $hBitmap, False) ConsoleWrite("TrayText for " & $idControl & " after 3: " & TrayItemGetText($idControl) & @CRLF) EndFunc ;==>_TrayItemSetBmp ; https://www.autoitscript.com/forum/topic/146184-solved-convert-string-into-ansi-from-utf-8unicode/ Func _ANSIToUnicode($sString) #cs Local Const $SF_ANSI = 1 Local Const $SF_UTF16_LE = 2 Local Const $SF_UTF16_BE = 3 Local Const $SF_UTF8 = 4 #ce Local Const $SF_ANSI = 1, $SF_UTF8 = 4 Return BinaryToString(StringToBinary($sString, $SF_ANSI), $SF_UTF8) EndFunc ;==>_ANSIToUnicode And here's the output that I got: TrayText for 7 before: About TrayText for 7 after 3: O?? TrayText for 9 before: Read Text with TrayItemGetText TrayText for 9 after 3: O?? TrayText for 10 before: Read Text with _GUICtrlMenu_GetItemText TrayText for 10 after 3: O?? TrayText for 12 before: Exit TrayText for 12 after 3: O?? TrayText: ?????? TrayText VarGetType: String TrayText StringLen: 24 TrayText StringToBinary: 0x2C4D5320476F74686963 TrayText _WinAPI_WideCharToMultiByte($string): ?????????????????????????????????????? TrayText _ANSIToUnicode($string): ?o?r?v?y?}???????????????? TrayText _WinAPI_WideCharToMultiByte($string, 874): TrayText _WinAPI_WideCharToMultiByte($string, 932): ?????Á TrayText _WinAPI_WideCharToMultiByte($string, 936): ol TrayText _WinAPI_WideCharToMultiByte($string, 949): TrayText _WinAPI_WideCharToMultiByte($string, 950): ,MS Gothic TrayText _WinAPI_WideCharToMultiByte($string, 1250): TrayText _WinAPI_WideCharToMultiByte($string, 1251): TrayText _WinAPI_WideCharToMultiByte($string, 1252): TrayText _WinAPI_WideCharToMultiByte($string, 1253): thicMINGLIU.TTC,PMingLiU TrayText _WinAPI_WideCharToMultiByte($string, 1254): thicMINGLIU.TTC,PMingLiU TrayText _WinAPI_WideCharToMultiByte($string, 1255): TrayText _WinAPI_WideCharToMultiByte($string, 1256): ,MS Gothic TrayText _WinAPI_WideCharToMultiByte($string, 1257): soft YaHei UI TrayText _WinAPI_WideCharToMultiByte($string, 1258): ,MS Gothic Mostly it looks like that I'm getting information about the font (MS Gothic), instead of the text. I think the main reason that _GUICtrlMenu_GetItemText() uses "GetMenuStringW", and when the image/bitmap is set: "SetMenuItemInfoW" is used. I tried a edited version of the file using GetMenuStringA, SetMenuItemInfoA and GetMenuString, SetMenuItemInfo, and they ended up giving different results. Without a suffix (A = ANSI and W = Wide/Unicode), I was getting mostly window handles it looked like. Seems like that one of these functions is messing something up. That's about all I have, helpful or not. We ought not to misbehave, but we should look as though we could. Link to comment Share on other sites More sharing options...
TimRude Posted February 2, 2023 Author Share Posted February 2, 2023 (edited) 6 hours ago, mistersquirrle said: I tried just messing around with this some, see if it was the same on my computer. First of all, your code currently returns nothing when you click on 'Read Text with TrayItemGetText' because you're using a different variable than your global variable, so you're not referencing the correct menu (or any menu). D'oh! That's what I get for trying to knock out some quick demo code and forgetting to add Opt("MustDeclareVars", 1). I was wondering why all of a sudden I wasn't getting corrupted text but it was late and I was ready to sleep. Thanks for throwing some additional tests at it and confirming that the issue with TrayItemGetText it isn't just me. Quote Mostly it looks like that I'm getting information about the font (MS Gothic), instead of the text. What I get varies from run to run. Sometimes I get what looks like bits of code. It's starting to sound like something for the Bug Tracker. Edited February 2, 2023 by TimRude Link to comment Share on other sites More sharing options...
pixelsearch Posted February 2, 2023 Share Posted February 2, 2023 @TimRude as you noticed, with your initial script, the Console shows : TrayText: UDFText: Read Text with _GUICtrlMenu_GetItemText Now if we modify the script like this... ;;; _GUICtrlMenu_SetItemBmp($hMenu, $idControl, _WinAPI_CreateSolidBitmap(WinGetHandle(AutoItWinGetTitle()), $iColor, $iBmpSize, $iBmpSize, $bRGB), False) Local $hBmp = _WinAPI_CreateSolidBitmap(WinGetHandle(AutoItWinGetTitle()), $iColor, $iBmpSize, $iBmpSize, $bRGB) _GUICtrlMenu_SetItemBitmaps($hMenu, $idControl, $hBmp, $hBmp, False) ...then the Console shows : TrayText: Read Text with TrayItemGetText UDFText: Read Text with _GUICtrlMenu_GetItemText mistersquirrle 1 Link to comment Share on other sites More sharing options...
mistersquirrle Posted February 2, 2023 Share Posted February 2, 2023 (edited) @pixelsearch I think you just helped progress my theory, that function (_GUICtrlMenu_SetItemBitmaps) doesn't use a "W" dll call, it's using "SetMenuItemBitmaps". I think that calling something like "SetMenuItemInfoW" with the "W" (Wide/Unicode) encoding messes some things up (obviously). Seems to me like it's changing the encoding of *something somewhere* and messing up some pointers/addresses that TrayItemGetText must use. But that's all above me Also, nice job finding an alternative way of doing it Edit: Maybe it's the not the "W" part specifically, I tried editing the _GUICtrlMenu_SetItemBitmaps function to use SetMenuItemBitmapsW as the DLL call, and everything still works just fine. Edited February 2, 2023 by mistersquirrle We ought not to misbehave, but we should look as though we could. Link to comment Share on other sites More sharing options...
TimRude Posted February 3, 2023 Author Share Posted February 3, 2023 @pixelsearch I confirm that using _GUICtrlMenu_SetItemBitmaps to set both the checked and unchecked images works great. I have also tested TrayItemSetText after setting the images and it works now as well. Great catch! @mistersquirrle I agree with your later conclusion that it doesn't have anything to do with the 'A' vs 'W' function calls. I think it actually has to do with whether or not both the checked and unchecked images are set. If only one is set, it causes a problem. To test this, I tried using _GUICtrlMenu_SetItemBmpChecked and _GUICtrlMenu_SetItemBmpUnchecked to set both of the images. The interesting thing about these two functions is that they use the exact same method of setting the image as the _GUICtrlMenu_SetItemBmp function, including the same 'W' flavor of SetMenuItemInfoW. Setting both images this way works properly and doesn't mess up TrayItemGetText and TrayItemSetText. ; My original code that doesn't get along with TrayItemGetText or TrayItemSetText ; _GUICtrlMenu_SetItemBmp($hMenu, $idControl, _WinAPI_CreateSolidBitmap(WinGetHandle(AutoItWinGetTitle()), $iColor, $iBmpSize, $iBmpSize, $bRGB), False) ; Working code suggested by pixelsearch to set both checked and unchecked images at the same time ; Local $hBmp = _WinAPI_CreateSolidBitmap(WinGetHandle(AutoItWinGetTitle()), $iColor, $iBmpSize, $iBmpSize, $bRGB) ; _GUICtrlMenu_SetItemBitmaps($hMenu, $idControl, $hBmp, $hBmp, False) ; Working code for setting each image (checked and unchecked) individually _GUICtrlMenu_SetItemBmpChecked($hMenu, $idControl, _WinAPI_CreateSolidBitmap(WinGetHandle(AutoItWinGetTitle()), $iColor, $iBmpSize, $iBmpSize, $bRGB), False) _GUICtrlMenu_SetItemBmpUnchecked($hMenu, $idControl, _WinAPI_CreateSolidBitmap(WinGetHandle(AutoItWinGetTitle()), $iColor, $iBmpSize, $iBmpSize, $bRGB), False) Of course, _GUICtrlMenu_SetItemBitmaps is the easier and neater way to set both images (and what I'll stick with), but I tried it this way to test the underlying function calls. And I deliberately didn't assign the bitmap to variable in this test because I wanted to exactly duplicate the problematic line with the two lines for controlled testing. Link to comment Share on other sites More sharing options...
TimRude Posted February 3, 2023 Author Share Posted February 3, 2023 (edited) Now that the kinks have been worked out, here's the more able function that I actually use to assign icon images to tray menu items. expandcollapse popupFunc _MenuItemSetIcon($hMenu, $idControl, $sIconFile, $iIconIndex, $iResourceNum, $iWidth, $iHeight) ; $hMenu is the handle of the menu (if 0, will assume handle for standard tray menu is to be used) ; $idControl is the control ID of the menu item (or -1 for last created item) ; $sIconFile and $iIconIndex will be used if running in script mode ; $iResourceNum will be used if running in compiled mode ; Supply both in the call so it works in both script and compiled mode If $hMenu = 0 Then $hMenu = TrayItemGetHandle(0) ; default to standard tray menu if handle not supplied If $idControl = -1 Then $idControl = _GUICtrlMenu_GetItemID($hMenu, _GUICtrlMenu_GetItemCount($hMenu) - 1, True) ; last item in menu If @Compiled Then Local $hBitmap = _CreateBitmapFromIconResource(_WinAPI_GetSysColor($COLOR_MENU), $iResourceNum, $iWidth, $iHeight) _GUICtrlMenu_SetItemBitmaps($hMenu, $idControl, $hBitmap, $hBitmap, False) Else Local $hBitmap = _CreateBitmapFromIcon(_WinAPI_GetSysColor($COLOR_MENU), $sIconFile, $iIconIndex, $iWidth, $iHeight) _GUICtrlMenu_SetItemBitmaps($hMenu, $idControl, $hBitmap, $hBitmap, False) EndIf EndFunc ;==>_MenuItemSetIcon Func _CreateBitmapFromIconResource($iBackground, $iIconNum, $iWidth, $iHeight) ; Based on code by Yashied, modified by TimRude Local $hDC, $hBackDC, $hBackSv, $hIcon, $hBitmap, $hInstance $hDC = _WinAPI_GetDC(0) $hBackDC = _WinAPI_CreateCompatibleDC($hDC) $hBitmap = _WinAPI_CreateSolidBitmap(0, $iBackground, $iWidth, $iHeight) $hBackSv = _WinAPI_SelectObject($hBackDC, $hBitmap) $hInstance = _WinAPI_GetModuleHandle(Null) $hIcon = _WinAPI_LoadImage($hInstance, $iIconNum, $IMAGE_ICON, $iWidth, $iHeight, 0) If Not @error Then _WinAPI_DrawIconEx($hBackDC, 0, 0, $hIcon, 0, 0, 0, 0, $DI_NORMAL) _WinAPI_DestroyIcon($hIcon) EndIf _WinAPI_SelectObject($hBackDC, $hBackSv) _WinAPI_ReleaseDC(0, $hDC) _WinAPI_DeleteDC($hBackDC) Return $hBitmap EndFunc ;==>_CreateBitmapFromIconResource Func _CreateBitmapFromIcon($iBackground, $sIconFile, $iIndex, $iWidth, $iHeight) ; By Yashied Local $hDC, $hBackDC, $hBackSv, $hIcon, $hBitmap $hDC = _WinAPI_GetDC(0) $hBackDC = _WinAPI_CreateCompatibleDC($hDC) $hBitmap = _WinAPI_CreateSolidBitmap(0, $iBackground, $iWidth, $iHeight) $hBackSv = _WinAPI_SelectObject($hBackDC, $hBitmap) $hIcon = _WinAPI_PrivateExtractIcon($sIconFile, $iIndex, $iWidth, $iHeight) If Not @error Then _WinAPI_DrawIconEx($hBackDC, 0, 0, $hIcon, 0, 0, 0, 0, $DI_NORMAL) _WinAPI_DestroyIcon($hIcon) EndIf _WinAPI_SelectObject($hBackDC, $hBackSv) _WinAPI_ReleaseDC(0, $hDC) _WinAPI_DeleteDC($hBackDC) Return $hBitmap EndFunc ;==>_CreateBitmapFromIcon Func _WinAPI_PrivateExtractIcon($sIcon, $iIndex, $iWidth, $iHeight) ; By Yashied Local $hIcon, $tIcon = DllStructCreate('hwnd'), $tID = DllStructCreate('hwnd') Local $Ret = DllCall('user32.dll', 'int', 'PrivateExtractIcons', 'str', $sIcon, 'int', $iIndex, 'int', $iWidth, 'int', $iHeight, 'ptr', DllStructGetPtr($tIcon), 'ptr', DllStructGetPtr($tID), 'int', 1, 'int', 0) If (@error) Or ($Ret[0] = 0) Then Return SetError(1, 0, 0) EndIf $hIcon = DllStructGetData($tIcon, 1) If ($hIcon = Ptr(0)) Or (Not IsPtr($hIcon)) Then Return SetError(1, 0, 0) EndIf Return $hIcon EndFunc ;==>_WinAPI_PrivateExtractIcon The _MenuItemSetIcon function is what is called to assign the images. It works under the assumption that the icons will be compiled as resources into the executable, but that you'll want to see them also while you're working on and debugging the script. So in the call you specify both the icon filename and index number (i.e. which icon in the file if the file contains more than one) for use when scripting, as well as the resource ID number that the icon will be addressed by in the compiled version. Then it uses the appropriate method as needed to load the image. The code for actually turning the icons into bitmaps for the tray menu comes from this post by Yashied. Edited February 3, 2023 by TimRude Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now