views:

852

answers:

4

I have an exe file generated with py2exe. In the setup.py I specify an icon to be embedded in the exe:

windows=[{'script': 'my_script.py','icon_resources': [(0, 'my_icon.ico')], ...

I tried loading the icon using:

hinst = win32api.GetModuleHandle(None)
hicon = win32gui.LoadImage(hinst, 0, win32con.IMAGE_ICON, 0, 0, win32con.LR_DEFAULTSIZE)

But this produces an (very unspecific) error:
pywintypes.error: (0, 'LoadImage', 'No error message is available')

If I try specifying 0 as a string

hicon = win32gui.LoadImage(hinst, '0', win32con.IMAGE_ICON, 0, 0, win32con.LR_DEFAULTSIZE)

then I get the error:
pywintypes.error: (1813, 'LoadImage', 'The specified resource type cannot be found in the image file.')

So, what's the correct method/syntax to load the icon?
Also please notice that I don't use any GUI toolkit - just the Windows API via PyWin32.

A: 

You should set the icon ID to something other than 0:

'icon_resources': [(42, 'my_icon.ico')]

Windows resource IDs must be between 1 and 32767.

efotinis
Sadly a higher ID doesn't work either.
Andreas Thomas
+1  A: 

If you're using wxPython, you can use the following simple code:

wx.Icon(sys.argv[0], wx.BITMAP_TYPE_ICO)

I usually have code that checks whether it's running from an EXE or not, and acts accordingly:

def get_app_icon():
    if hasattr(sys, "frozen") and getattr(sys, "frozen") == "windows_exe":
        return wx.Icon(sys.argv[0], wx.BITMAP_TYPE_ICO)
    else:
        return wx.Icon("gfx/myapp.ico", wx.BITMAP_TYPE_ICO)
gooli
+1  A: 

Well, well... I installed py2exe and I think it's a bug. In py2exe_util.c they should init rt_icon_id to 1 instead of 0. The way it is now, it's impossible to load the first format of the first icon using LoadIcon/LoadImage.

I'll notify the developers about this if it's not already a known issue.

A workaround, in the meantime, would be to include the same icon twice in your setup.py:

'icon_resources': [(1, 'my_icon.ico'), (2, 'my_icon.ico')]

You can load the second one, while Windows will use the first one as the shell icon. Remember to use non-zero IDs though. :)

efotinis
+2  A: 

@efotinis: You're right.

Here is a workaround until py2exe gets fixed and you don't want to include the same icon twice:

hicon = win32gui.CreateIconFromResource(win32api.LoadResource(None, win32con.RT_ICON, 1), True)

Be aware that 1 is not the ID you gave the icon in setup.py (which is the icon group ID), but the resource ID automatically assigned by py2exe to each icon in each icon group. At least that's how I understand it.

If you want to create an icon with a specified size (as CreateIconFromResource uses the system default icon size), you need to use CreateIconFromResourceEx, which isn't available via PyWin32:

icon_res = win32api.LoadResource(None, win32con.RT_ICON, 1)
hicon = ctypes.windll.user32.CreateIconFromResourceEx(icon_res, len(icon_res), True,
    0x00030000, 16, 16, win32con.LR_DEFAULTCOLOR)
Andreas Thomas
Ah, yes. I forgot about that. Nice catch.
efotinis