Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

[SOLVED]Extract programs icon in systray


  • Please log in to reply
16 replies to this topic
kenn
  • Members
  • 407 posts
  • Last active: Jan 14 2015 08:16 PM
  • Joined: 11 Oct 2010
Hi coders,
I owe much AHK, I am still learning and I can't solve some simple problems.
Well, this time I want to extract a program's tray icon in systray. I tried a few scripts to do that such as Sean's TrayIcon.ahk, Lexicos' MI.ahk, Maul.Esel's SaveHImage2File.ahk, Majkinetor's W i n library. I read through many topics but I didn't find a working example. I also tried a script posted by Tic here <!-- m -->http://www.autohotke...topic31642.html<!-- m -->, I didn't achieve it to work. For a noob like me problems look impossible to overcome. Would somebody help me about it?

  • Guests
  • Last active:
  • Joined: --
Which program's icon do you want to extract? Have you succeeded to identify either PID, program path, or window handle that owns the icon? And after extracting it, what do you want to do with it? Just displaying it on GUI or save it? Please be more specific.

kenn
  • Members
  • 407 posts
  • Last active: Jan 14 2015 08:16 PM
  • Joined: 11 Oct 2010
It's a dictionary called Lingoes. I want to automate it using its icon but at first I need to extract its icon in order to use it in image search. <!-- m -->http://www.autohotke.../topic7905.html<!-- m -->
<!-- m -->http://www.autohotke...ic.php?p=448190<!-- m -->
<!-- m -->http://www.autohotke...pic.php?t=47856<!-- m -->
<!-- m -->http://www.autohotke...topic21991.html<!-- m -->
<!-- m -->http://www.autohotke...topic26042.html<!-- m -->
In these topics it's mentioned. I also asked it as a reference to newbies here.

  • Guests
  • Last active:
  • Joined: --

I need to extract its icon in order to use it in image search

Just take a screenshot and crop the icon.

Also it sounds like you want to execute menu items of the system tray icon. In that case, I suggest to learn manipulating window messages. Here is a good tutorial. [pictorial] Find WM_COMMAND parameter with Winspector

kenn
  • Members
  • 407 posts
  • Last active: Jan 14 2015 08:16 PM
  • Joined: 11 Oct 2010
Thanks. I found this here <!-- m -->http://www.autohotke...ic33955-60.html<!-- m -->
I can extract associated icon from exe file with it, it fulfilled my aim to some extent but what I want to do is to extract systray icon as it is. I ll keep pushing it.
ptr := A_PtrSize =8 ? "ptr" : "uint"   ;for AHK Basic
FileName := "C:\Program Files\Lingoes\Translator2\lingoes.exe"
hIcon := DllCall("Shell32\ExtractAssociatedIcon" (A_IsUnicode ? "W" : "A"), ptr, DllCall("GetModuleHandle", ptr, 0, ptr), str, FileName, "ushort*", lpiIcon, ptr)   ;only supports 32x32

Gui, Margin, 20, 20
Gui, Add, Text, w32 h32 hwndmypic1 0x3 ; 0x3 = SS_ICON
SendMessage, STM_SETICON := 0x0170, hIcon, 0,, Ahk_ID %mypic1%
Gui, Show
Return

GuiClose:
GuiEscape:
ExitApp


  • Guests
  • Last active:
  • Joined: --
may i ask why you want that?
you already mentioned the TrayIcon.ahk, which seems to be all you need to automate icon interaction but without unreliable ImageSearchs.

kenn
  • Members
  • 407 posts
  • Last active: Jan 14 2015 08:16 PM
  • Joined: 11 Oct 2010

may i ask why you want that?


It's kind of turned out to be challenging to me, I should solve it.
I get hwnd of tray icon with TrayIcon.ahk, and I am trying to get bitmap of the icon with gdip but it fails. Isn't it possible to get icons handle with TrayIcon.ahk? Or is it something different thing that I don't know of?
pToken := Gdip_Startup()
   pBitmap := Gdip_BitmapFromHWND()
   Gdip_SaveBitmapToFile(pBitmap, "Full sized image.png")
   Gdip_DisposeImage(pBitmap)
   Gdip_Shutdown(pToken)


  • Guests
  • Last active:
  • Joined: --
Icon and Bitmap are two different image formats.

Try translate this code into AHK. Converting Icon to Bitmap (HICON -> HBITMAP) - Windows programming - Forums at ProgrammersHeaven.com.

kenn
  • Members
  • 407 posts
  • Last active: Jan 14 2015 08:16 PM
  • Joined: 11 Oct 2010
Thanks a lot Guest :) Things are getting clearer for me. I ll try to convert it into AHK. It's my homework. I hope I won't fail.

kenn
  • Members
  • 407 posts
  • Last active: Jan 14 2015 08:16 PM
  • Joined: 11 Oct 2010

Icon and Bitmap are two different image formats.

Try translate this code into AHK. Converting Icon to Bitmap (HICON -> HBITMAP) - Windows programming - Forums at ProgrammersHeaven.com.

I struggled to convert that code into AHK, I did it, when I used it to extract icon's bitmap I got no result. While searching for forum here <!-- m -->http://www.autohotke...ic.php?p=489897<!-- m --> I found this moded code from Sean's screencapture.ahk. It's used to capture cursor in screen. I inserted the systay icon handle that I grabbed with TrayIcon.ahk into code. I tried many variations. It looks hopeless. Would somebody guide me in the right direction?
Here is the code that extracts cursor into clipboard
#z::
   VarSetCapacity(mi, 20, 0), mi := Chr(20)
   DllCall("GetCursorInfo", "Uint", &mi)
   bShow   := NumGet(mi, 4)      ;1 ==> cursor is visible (MSDN)
   hCursor := NumGet(mi, 8)      ;cursor handle
   xCursor := NumGet(mi,12)      ;not really needed for this script, but might want later
   yCursor := NumGet(mi,16)      ;same as ^

   If   bShow && hCursor:=DllCall("CopyIcon", "Uint", hCursor)
   {
   VarSetCapacity(ni, 20, 0)
   DllCall("GetIconInfo", "Uint", hCursor, "Uint", &ni)
   bIcon    := NumGet(ni, 0)      ;is cursor ==> false, is icon ==> true
   xHotspot := NumGet(ni, 4)      ;I think this is the click location
   yHotspot := NumGet(ni, 8)
   hBMMask  := NumGet(ni,12)      ;again not used in script
   hBMColor := NumGet(ni,16)      ;same as ^

;the tiny part I added starts here
   mDC := DllCall("CreateCompatibleDC", "Uint", 0)      ;make a DC compatible with screen
   hBM := CreateDIBSection(mDC, 32, 32)         ;I think this makes a blank bitmap based on mDC (except dimensions)
   oBM := DllCall("SelectObject", "Uint", mDC, "Uint", hBM)   ;put hBM into the DC but save original DC in oBM
;ends here

   DllCall("DrawIcon", "Uint", mDC, "int", 0, "int", 0, "Uint", hCursor)   ;draw the icon into the DC
   DllCall("DestroyIcon", "Uint", hCursor)            ;clear the memory of uneeded objects
   If   hBMMask
   DllCall("DeleteObject", "Uint", hBMMask)
   If   hBMColor
   DllCall("DeleteObject", "Uint", hBMColor)
   }

   DllCall("SelectObject", "Uint", mDC, "Uint", oBM)      ;switch back to original DC then delete
   DllCall("DeleteDC", "Uint", mDC)
   SetClipboardData(hBM)            ;put the image on the clipboard
return

CreateDIBSection(hDC, nW, nH, bpp = 32, ByRef pBits = "")
{
   NumPut(VarSetCapacity(bi, 40, 0), bi)
   NumPut(nW, bi, 4)
   NumPut(nH, bi, 8)
   NumPut(bpp, NumPut(1, bi, 12, "UShort"), 0, "Ushort")
   NumPut(0,  bi,16)
   Return   DllCall("gdi32\CreateDIBSection", "Uint", hDC, "Uint", &bi, "Uint", 0, "UintP", pBits, "Uint", 0, "Uint", 0)
}

SetClipboardData(hBitmap)
{
   DllCall("GetObject", "Uint", hBitmap, "int", VarSetCapacity(oi,84,0), "Uint", &oi)
   hDIB :=   DllCall("GlobalAlloc", "Uint", 2, "Uint", 40+NumGet(oi,44))
   pDIB :=   DllCall("GlobalLock", "Uint", hDIB)
   DllCall("RtlMoveMemory", "Uint", pDIB, "Uint", &oi+24, "Uint", 40)
   DllCall("RtlMoveMemory", "Uint", pDIB+40, "Uint", NumGet(oi,20), "Uint", NumGet(oi,44))
   DllCall("GlobalUnlock", "Uint", hDIB)
   DllCall("DeleteObject", "Uint", hBitmap)
   DllCall("OpenClipboard", "Uint", 0)
   DllCall("EmptyClipboard")
   DllCall("SetClipboardData", "Uint", 8, "Uint", hDIB)
   DllCall("CloseClipboard")
}


  • Guests
  • Last active:
  • Joined: --
This requires. GDI+ standard library 1.45 by tic
ptr := A_PtrSize =8 ? "ptr" : "uint"   ;for AHK Basic
FileName := A_WinDir "\notepad.exe"
hIcon := DllCall("Shell32\ExtractAssociatedIcon" (A_IsUnicode ? "W" : "A")
	, ptr, DllCall("GetModuleHandle", ptr, 0, ptr)
	, str, FileName
	, "ushort*", lpiIcon
	, ptr)   ;only supports 32x32

SavehIconAsBMP(hIcon, A_ScriptDir "\notepad.bmp")

SavehIconAsBMP(hIcon, sFile) {
	if pToken := Gdip_Startup() {
		pBitmap := Gdip_CreateBitmapFromHICON(hIcon)
		Gdip_SaveBitmapToFile(pBitmap, sFile)
		Gdip_DisposeImage(pBitmap) 
		Gdip_Shutdown(pToken)
		return true
	}
		return false
}
Also bitmap does not support transparent background. Why do you want to save icon as bitmap?

kenn
  • Members
  • 407 posts
  • Last active: Jan 14 2015 08:16 PM
  • Joined: 11 Oct 2010
Thank you for your interest and the post. I tried that kind of scripts, they work. Unfortunately it doesn't answer my aim. What I am trying to do is to reach systray icons by handle. I just noticed that it's not that easy.
First, what I get with TrayIcon.ahk is not the icon's handle in systray but the main application's handle :( So it didn't work in my case.
I found the solution in AutoIt's forum. Here it is <!-- m -->http://www.autoitscr... ... ns-solved/<!-- m -->
I hope somebody converts it into AHK

kenn
  • Members
  • 407 posts
  • Last active: Jan 14 2015 08:16 PM
  • Joined: 11 Oct 2010
At last I solved it!! Thanks to Sean and HotkeyIt and Lexikos.
I only changed return value of TrayIcon.ahk. While I was searching I met RefreshTray() function by HotkeyIt, subsequently I noticed moded version of TrayIcon.ahk by HotkeyIt. When I combine what I read for a week in the forums, I noticed I was only one step away from it. Then I tried following moded script, it gave me the correct handle of the icon.
TrayIcons() ;will remove dead icons
msgbox, % TrayIcons(sExeName = "lingoes.exe")
TrayIcons(sExeName = "lingoes.exe")
{
  DetectHiddenWindows,% DHW:=A_DetectHiddenWindows ? "On" : "On"
   WinGet,   pidTaskbar, PID, ahk_class Shell_TrayWnd
   hProc:=   DllCall("OpenProcess", "Uint", 0x38, "int", 0, "Uint", pidTaskbar)
   pProc:=   DllCall("VirtualAllocEx", "Uint", hProc, "Uint", 0, "Uint", 32, "Uint", 0x1000, "Uint", 0x4)
   idxTB:=   GetTrayBar()
  ControlGet,chWnd,HWND,,ToolbarWindow32%idxTB%, ahk_class Shell_TrayWnd
   ErrorLevel:=DllCall("SendMessage","UPTR",chWnd,"UInt", 0x418, "UPTR",0,"UPTR", 0,"UPTR")   ; TB_BUTTONCOUNT
   Loop,   %ErrorLevel%
   {
    DllCall("SendMessage","UPTR",chWnd,"UInt",0x417,"UPTR",A_Index-1,"UPTR",pProc) ; TB_GETBUTTON
      VarSetCapacity(btn,32,0), VarSetCapacity(nfo,32,0)
      DllCall("ReadProcessMemory", "UPTR", hProc, "UPTR", pProc, "UPTR", &btn, "Uint", 32, "Uint", 0)
         iBitmap   := NumGet(btn, 0)
         idn   := NumGet(btn, 4)
         Statyle := NumGet(btn, 8)
      If   dwData   := NumGet(btn,12)
         iString   := NumGet(btn,16)
      Else   dwData   := NumGet(btn,16,"int64"), iString:=NumGet(btn,24,"int64")
      DllCall("ReadProcessMemory", "UPTR", hProc, "UPTR", dwData, "UPTR", &nfo, "Uint", 32, "Uint", 0)
      If   NumGet(btn,12)
         hWnd   := NumGet(nfo, 0)
      ,   uID   := NumGet(nfo, 4)
      ,   nMsg   := NumGet(nfo, 8)
      ,   hIcon   := NumGet(nfo,20)
      Else   hWnd   := NumGet(nfo, 0,"int64"), uID:=NumGet(nfo, 8), nMsg:=NumGet(nfo,12)
    If (!DllCall("IsWindow","UPTR",hwnd))
    {
      IconsToRemove.=hwnd "." uID "."
      Continue
    }
      WinGet, pid, PID,              ahk_id %hWnd%
      WinGet, sProcess, ProcessName, ahk_id %hWnd%
      WinGetClass, sClass,           ahk_id %hWnd%
      If !sExeName || (sExeName = sProcess) || (sExeName = pid)
         VarSetCapacity(sTooltip,128), VarSetCapacity(wTooltip,128*2)
      ,   DllCall("ReadProcessMemory", "UPTR", hProc, "UPTR", iString, "UPTR", &wTooltip, "Uint", 128*2, "Uint", 0)
      ,   A_IsUnicode ? (sToolTip:=StrGet(&wToolTip)) : DllCall("WideCharToMultiByte", "Uint", 0, "Uint", 0, "str", wTooltip, "int", -1, "str", sTooltip, "int", 128, "Uint", 0, "Uint", 0)
      ,   sTrayIcons .= "idx: " . A_Index-1 . " | idn: " . idn . " | Pid: " . pid . " | uID: " . uID . " | MessageID: " . nMsg . " | hWnd: " . hWnd . " | Class: " . sClass . " | Process: " . sProcess . "`n" . "   | Tooltip: " . sTooltip . "`n"
   }
  Loop,Parse,IconsToRemove,.
    If !Mod(A_Index,2)
      RemoveTrayIcon(hwnd,A_LoopField)
    else hwnd:=A_LoopField
  DllCall("VirtualFreeEx", "UPTR", hProc, "UPTR", pProc, "Uint", 0, "Uint", 0x8000)
   DllCall("CloseHandle", "UPTR", hProc)
   DetectHiddenWindows,% DHW
  Return   hIcon ;  [color=red]<--I only added this [/color] 
}
I extract icon by inserting icon handle into Lexikos' ExtractIcon() function in Menu Icons v2 script
hIcon1 :=66528
STM_SETICON := 0x0170
Gui, Margin, 20, 20
Gui, Add, Text, w64 h64 hwndmypic1 0x3
SendMessage, STM_SETICON, hIcon1, 0,, Ahk_ID %mypic1%

Gui, Show
Return

GuiClose:
ExitApp
ExtractIcon(Filename, IconNumber, IconSize)
{
   ;most of the code is from Menu Icons v2.21
   ;written by Lexikos http://www.autohotkey.com/forum/topic21991.html
   
    static ExtractIconEx, Ptr, PtrP
   if !Ptr
      Ptr := (A_PtrSize = 8) ? "ptr" : "uint"
   If !PtrP
      PtrP := (A_PtrSize = 8) ? "uptr*" : "uint*"
    ; LoadImage is not used..
    ; ..with exe/dll files because:
    ;   it only works with modules loaded by the current process,
    ;   it needs the resource ordinal (which is not the same as an icon index), and
    ; ..with ico files because:
    ;   it can only load the first icon (of size %IconSize%) from an .ico file.
   
    ; If possible, use PrivateExtractIcons, which supports any size of icon.
    if (IconSize != 16 && IconSize != 32)
        if A_OSVersion in WIN_7,WIN_XP,WIN_VISTA,WIN_2003,WIN_2000
            if DllCall("PrivateExtractIcons", "str", Filename, "int", IconNumber-1, "int", IconSize, "int", IconSize, PtrP, h_icon, "uint*", 0, "uint",1, "uint",0,"int")
                return h_icon
   
    if !ExtractIconEx
      ExtractIconEx := DllCall("GetProcAddress"
                  , ptr, DllCall("GetModuleHandle", str, "shell32", ptr)
                  , A_IsUnicode ? "astr":"str"  ; Always an ANSI string.
                  , "ExtractIconEx" . (A_IsUnicode ? "W":"A"), ptr)   
   
    ; Use ExtractIconEx, which only returns 16x16 or 32x32 icons.
    ; if DllCall(ExtractIconEx, "str", Filename, "int", IconNumber-1, PtrP, h_icon, PtrP, h_icon_small, "uint", 1)
    if DllCall(ExtractIconEx, "str", Filename, "int", IconNumber-1, PtrP, h_icon, PtrP, h_icon_small, "uint", 1)
    {
        SysGet, SmallIconSize, 49
       
        ; Use the best-fit size; delete the other. Defaults to small icon.
        if (IconSize <= SmallIconSize) {
            DllCall("DestroyIcon", ptr, h_icon)
            h_icon := h_icon_small
        } else
            DllCall("DestroyIcon", ptr, h_icon_small)
       
        ; I think PrivateExtractIcons resizes icons automatically,
        ; so resize icons returned by ExtractIconEx for consistency.
        if (h_icon && IconSize)
            h_icon := DllCall("CopyImage", ptr, h_icon, "uint", 1, "int", IconSize, "int", IconSize, "uint", 4|8, ptr)
    }

    return h_icon ? h_icon : 0
}


tic
  • Members
  • 1934 posts
  • Last active: May 30 2018 08:13 PM
  • Joined: 22 Apr 2007
I might take a look at beginning to incorporate some of this into the gdi+ library (although not really gdi+ it could be helpful)

kenn
  • Members
  • 407 posts
  • Last active: Jan 14 2015 08:16 PM
  • Joined: 11 Oct 2010
Hi Tic, thanks a lot for your interest and kindness. It'd be very nice to have a standalone trayicon extraction script, I hope you generalize it and make a multi-functional icon script.