Skip to main content

Getting the icon association for any file or folder with the Python Windows API

I wanted to make a file browser which has the correct file icons for each file type, instead of just getting them from the file name. This is a bad solution, since it has to call images from disc and may be wrong if the name is different. This method uses the Windows API to get the exact icon. Tested in Python 3.6 in Windows 10.

 from win32com.shell import shell, shellcon  
 from PIL import Image, ImageTk  
 import win32api  
 import win32con  
 import win32ui  
 import win32gui  
   
 def get_icon(PATH, size):  
   SHGFI_ICON = 0x000000100  
   SHGFI_ICONLOCATION = 0x000001000  
   if size == "small":  
     SHIL_SIZE= 0x00001  
   elif size == "large":  
     SHIL_SIZE= 0x00002  
   else:  
     raise TypeError("Invalid argument for 'size'. Must be equal to 'small' or 'large'")  
   ret, info = shell.SHGetFileInfo(PATH, 0, SHGFI_ICONLOCATION | SHGFI_ICON | SHIL_SIZE)  
   hIcon, iIcon, dwAttr, name, typeName = info  
   ico_x = win32api.GetSystemMetrics(win32con.SM_CXICON)  
   hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0))  
   hbmp = win32ui.CreateBitmap()  
   hbmp.CreateCompatibleBitmap(hdc, ico_x, ico_x)  
   hdc = hdc.CreateCompatibleDC()  
   hdc.SelectObject(hbmp)  
   hdc.DrawIcon((0, 0), hIcon)  
   win32gui.DestroyIcon(hIcon)  
   
   bmpinfo = hbmp.GetInfo()  
   bmpstr = hbmp.GetBitmapBits(True)  
   img = Image.frombuffer(  
     "RGBA",  
     (bmpinfo["bmWidth"], bmpinfo["bmHeight"]),  
     bmpstr, "raw", "BGRA", 0, 1  
   )  
   
   if size == "small":  
     img = img.resize((16, 16), Image.ANTIALIAS)  
   return img  

Let's test this with a few files and folders:

The Documents folder. As you can see, it gets the two different image sizes.

The user's folder.

A standard folder.

    
It automatically gets the default application's icon. Here's VLC for an MP4 file.

Here's an example of an executable, Steam

It also works with removable media, like this SD card.

Finally, the drive with the OS installed.

I originally got the code from this SO post, but as you can see I have modified it a lot. It turns out that if the value of SHIL_SIZE is odd, then it gets the smaller image. If it's even, then it gets the larger size.

My version also returns a PIL image instead of writing to and deleting a temporary file, which uses more disc. It also returns a transparent background.

Comments

Popular posts from this blog

Opening Files with your Python application

For a long time now I couldn't find any help online for my problem. I want to make my own image viewer, since the one that comes with Windows 10 is so bad, but I want to be able to right click on any image, and select open with my Python application. . Lets take a look at my simple program: 1: import sys 2: 3: try: 4: print(sys.argv[1]) 5: except IndexError: 6: print("Being run without file selected.") 7: 8: input("\n\nPress any key to exit...") I want to just get a simple program working first, i'll build the application later. Normally argv would be used to parse command line arguments into a program but it turns out that when your program is compiled, when you use 'open with' your program, the second argument will be set to the path to which the file is located. The first argument is the name of the program. So the program prints the second argument. If there isn't one, if the program is just run, the...

FPS counter with OpenCV

Showing FPS on the screen is an important part of benchmarking your app. In this post we're going to look at how to calculate the number of frames per second and show it on screen. The theory is that we keep track of the last 5 or so timestamps and average over to get the FPS. We keep deleting the last item so the list only has 5 times in it. Lets have a look at the code: 1: import time 2: 3: times = [] 4: count = 5 5: 6: times.append(time.time()) 7: if len(times) >= count: 8: times = times[-count:] 9: fps = "%.2f FPS" % (count/(times[-1] - times[0])) 10: cv2.putText(frame, fps, (0, 20), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 1, cv2.LINE_AA) First import time, which is required for this to work. Then I define two constants which will be used in the function. Remember this code will run every time the videoloop runs, it adds the time to the list. If the list gets too long, get the last bit using indexing. Then work out the av...