Last active
May 30, 2026 17:30
-
-
Save ynkdir/f1482fddbeed13961f7a9b20962ed648 to your computer and use it in GitHub Desktop.
Icon Resource Viewer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # /// script | |
| # dependencies = ["win32more"] | |
| # /// | |
| from ctypes import POINTER | |
| from win32more import FAILED, Byte, List, UInt32, WinError | |
| from win32more.Microsoft.UI.Xaml.Media.Imaging import WriteableBitmap | |
| from win32more.Microsoft.Windows.Storage.Pickers import FileOpenPicker | |
| from win32more.Windows.Win32.Graphics.Imaging import ( | |
| CLSID_WICImagingFactory, | |
| GUID_WICPixelFormat32bppBGRA, | |
| IWICBitmap, | |
| IWICFormatConverter, | |
| IWICImagingFactory, | |
| WICBitmapDitherTypeNone, | |
| WICBitmapPaletteTypeCustom, | |
| WICRect, | |
| ) | |
| from win32more.Windows.Win32.System.Com import CLSCTX_INPROC_SERVER, CoCreateInstance | |
| from win32more.Windows.Win32.System.WinRT import IBufferByteAccess | |
| from win32more.Windows.Win32.UI.Shell import ExtractIconExW | |
| from win32more.Windows.Win32.UI.WindowsAndMessaging import HICON, DestroyIcon | |
| from win32more.winui3 import XamlApplication, XamlLoader | |
| class App(XamlApplication): | |
| xaml = """ | |
| <Window | |
| xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |
| xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |
| xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | |
| xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |
| mc:Ignorable="d" | |
| xmlns:muxc="using:Microsoft.UI.Xaml.Controls"> | |
| <Grid x:Name="_grid"> | |
| <Grid.RowDefinitions> | |
| <RowDefinition Height="Auto" /> | |
| <RowDefinition Height="Auto" /> | |
| <RowDefinition Height="*" /> | |
| </Grid.RowDefinitions> | |
| <Grid.ColumnDefinitions> | |
| <ColumnDefinition Width="*" /> | |
| </Grid.ColumnDefinitions> | |
| <muxc:MenuBar Grid.Row="0"> | |
| <muxc:MenuBarItem Title="File"> | |
| <muxc:MenuFlyoutItem Text="Open" Click="_File_Open_Click" /> | |
| </muxc:MenuBarItem> | |
| </muxc:MenuBar> | |
| <TextBlock x:Name="TextBlock1" Grid.Row="1"></TextBlock> | |
| <GridView x:Name="GridView1" Grid.Row="2"> | |
| <GridView.ItemTemplate> | |
| <DataTemplate> | |
| <StackPanel Width="100" Height="100"> | |
| <TextBlock Text="{Binding id}" /> | |
| <Image Width="{Binding width}" Height="{Binding height}" Source="{Binding bitmap}" /> | |
| </StackPanel> | |
| </DataTemplate> | |
| </GridView.ItemTemplate> | |
| </GridView> | |
| </Grid> | |
| </Window> | |
| """ | |
| def OnLaunched(self, args): | |
| self._window = XamlLoader.Load(self, self.xaml) | |
| self._window.Title = "Icon Resource Viewer" | |
| self._window.Activate() | |
| self._items = List() | |
| self.GridView1.ItemsSource = self._items | |
| async def _File_Open_Click(self, sender, args): | |
| picker = FileOpenPicker(self._window.AppWindow.Id) | |
| picker.FileTypeFilter.ReplaceAll([".exe", ".dll"]) | |
| result = await picker.PickSingleFileAsync() | |
| if result is None: | |
| # canceled | |
| return | |
| self.TextBlock1.Text = result.Path | |
| self._items[:] = self._load_icon_resource(result.Path) | |
| def _load_icon_resource(self, path): | |
| icon_large, icon_small = self._extract_icon(path) | |
| items = [] | |
| for i in range(len(icon_large)): | |
| bitmap = self._create_bitmap_from_hicon(icon_large[i]) | |
| width = min(64, bitmap.PixelWidth) | |
| height = min(64, bitmap.PixelHeight) | |
| items.append({"id": i, "bitmap": bitmap, "width": width, "height": height}) | |
| DestroyIcon(icon_large[i]) | |
| DestroyIcon(icon_small[i]) | |
| return items | |
| def _extract_icon(self, path): | |
| UINT_MAX = 0xFFFFFFFF | |
| number_of_icons = ExtractIconExW(path, -1, None, None, 0) | |
| if number_of_icons == UINT_MAX: | |
| raise WinError() | |
| if number_of_icons == 0: | |
| return [], [] | |
| icon_large = (HICON * number_of_icons)() | |
| icon_small = (HICON * number_of_icons)() | |
| result = ExtractIconExW(path, 0, icon_large, icon_small, number_of_icons) | |
| if result == -1: | |
| raise WinError() | |
| return icon_large, icon_small | |
| def _create_bitmap_from_hicon(self, hicon): | |
| wic_factory = IWICImagingFactory(own=True) | |
| r = CoCreateInstance(CLSID_WICImagingFactory, None, CLSCTX_INPROC_SERVER, IWICImagingFactory._iid_, wic_factory) | |
| if FAILED(r): | |
| raise WinError(r) | |
| wic_bitmap = IWICBitmap(own=True) | |
| r = wic_factory.CreateBitmapFromHICON(hicon, wic_bitmap) | |
| if FAILED(r): | |
| raise WinError(r) | |
| converter = IWICFormatConverter(own=True) | |
| r = wic_factory.CreateFormatConverter(converter) | |
| if FAILED(r): | |
| raise WinError(r) | |
| r = converter.Initialize( | |
| wic_bitmap, GUID_WICPixelFormat32bppBGRA, WICBitmapDitherTypeNone, None, 0, WICBitmapPaletteTypeCustom | |
| ) | |
| if FAILED(r): | |
| raise WinError(r) | |
| c_width = UInt32() | |
| c_height = UInt32() | |
| r = wic_bitmap.GetSize(c_width, c_height) | |
| if FAILED(r): | |
| raise WinError(r) | |
| width = c_width.value | |
| height = c_height.value | |
| wb = WriteableBitmap(width, height) | |
| pixel_buffer = wb.PixelBuffer.as_(IBufferByteAccess) | |
| bitmap_data = POINTER(Byte)() | |
| r = pixel_buffer.Buffer(bitmap_data) | |
| if FAILED(r): | |
| raise WinError(r) | |
| rc = WICRect(0, 0, width, height) | |
| stride = width * 4 | |
| buffer_size = stride * height | |
| r = converter.CopyPixels(rc, stride, buffer_size, bitmap_data) | |
| if FAILED(r): | |
| raise WinError(r) | |
| return wb | |
| if __name__ == "__main__": | |
| XamlApplication.Start(App) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment