Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Win32 'DwmSetIconicThumbnail' from C# or VB.Net?

I would like to use the DwmSetIconicThumbnail function to set a static image for the thumbnail preview of my app.

As pointed in the reference link above, firstly is necessary to call DwmSetWindowAttribute to enable DWMWA_FORCE_ICONIC_REPRESENTATION and DWMWA_HAS_ICONIC_BITMAP attributes.

I already did all that. I've taken all the definitions from WindowsAPICodePack source code here, and I'm following the same steps (or I think so).

The problem is that when I try to adapt the example for my WinForms Window, I get a E_INVALIDARG HRESULT code when calling DwmSetIconicThumbnail function at the end of the code below, I'm not sure whether the problematic argument is the hwnd, or the hBitmap.

What I'm doing wrong?.


C#:

Bitmap bmp;
IntPtr hBitmap;
IntPtr hwnd;
int hresult;

const int DisplayThumbnailFrame = 0x1;
public enum DwmWindowAttribute : uint
{
    NcRenderingEnabled = 1,
    NcRenderingPolicy,
    TransitionsForceDisabled,
    AllowNcPaint,
    CaptionButtonBounds,
    NonClientRtlLayout,
    ForceIconicRepresentation,
    Flip3DPolicy,
    ExtendedFrameBounds,
    HasIconicBitmap,
    DisallowPeek,
    ExcludedFromPeek,
    Cloak,
    Cloaked,
    FreezeRepresentation,
    Last
}

[DllImport("dwmapi.dll", PreserveSig = true)]
static internal extern int DwmSetWindowAttribute(IntPtr hwnd, 
                                                 DwmWindowAttribute dwAttributeToSet, 
                                                 IntPtr pvAttributeValue, 
                                                 uint cbAttribute);

[DllImport("Dwmapi.dll")]
public static extern int DwmSetIconicThumbnail(IntPtr hwnd, 
                                               IntPtr hBitmap, 
                                               int flags);

private void Form1_Shown() {

    bmp = (Bitmap)Bitmap.FromFile("C:\\Image.jpg");
    hBitmap = bmp.GetHbitmap();
    hwnd = Process.GetCurrentProcess.MainWindowHandle;

    IntPtr block = Marshal.AllocHGlobal(4);
    int value = Math.Abs(Convert.ToInt32(true)); // or 1
    Marshal.WriteInt32(block, value);

    try {
        hresult = DwmSetWindowAttribute(hwnd, DwmWindowAttribute.HasIconicBitmap, block, 4);
        if ((hresult != 0)) {
            throw Marshal.GetExceptionForHR(hresult);
        }

        hresult = DwmSetWindowAttribute(hwnd, DwmWindowAttribute.ForceIconicRepresentation, block, 4);
        if ((hresult != 0)) {
            throw Marshal.GetExceptionForHR(hresult);
        }

    } finally {
        Marshal.FreeHGlobal(block);
    }

    hresult = DwmSetIconicThumbnail(hwnd, hBitmap, DisplayThumbnailFrame);
    if ((hresult != 0)) {
        throw Marshal.GetExceptionForHR(hresult);
    }

}

VB.NET:

Dim bmp As Bitmap
Dim hBitmap As IntPtr
Dim hwnd As IntPtr
Dim hresult As Integer

Const DisplayThumbnailFrame As Integer = &H1

Enum DwmWindowAttribute As UInteger
    NcRenderingEnabled = 1
    NcRenderingPolicy
    TransitionsForceDisabled
    AllowNcPaint
    CaptionButtonBounds
    NonClientRtlLayout
    ForceIconicRepresentation
    Flip3DPolicy
    ExtendedFrameBounds
    HasIconicBitmap
    DisallowPeek
    ExcludedFromPeek
    Cloak
    Cloaked
    FreezeRepresentation
    Last
End Enum

<DllImport("dwmapi.dll", PreserveSig:=True)>
Friend Shared Function DwmSetWindowAttribute(hwnd As IntPtr,
                                             dwAttributeToSet As DwmWindowAttribute,
                                             pvAttributeValue As IntPtr,
                                             cbAttribute As UInteger
) As Integer
End Function

<DllImport("Dwmapi.dll")>
Public Shared Function DwmSetIconicThumbnail(ByVal hwnd As IntPtr,
                                             ByVal hBitmap As IntPtr,
                                             ByVal flags As Integer
) As Integer
End Function

Private Sub Form1_Shown() Handles MyBase.Shown

    bmp = DirectCast(Bitmap.FromFile("C:\Image.jpg"), Bitmap)
    hBitmap = bmp.GetHbitmap()
    hwnd = Process.GetCurrentProcess.MainWindowHandle

    Dim block As IntPtr = Marshal.AllocHGlobal(4)
    Dim value As Integer = Math.Abs(CInt(True)) ' or 1
    Marshal.WriteInt32(block, value)

    Try
        hresult = DwmSetWindowAttribute(hwnd, DwmWindowAttribute.HasIconicBitmap, block, 4)
        If (hresult <> 0) Then
            Throw Marshal.GetExceptionForHR(hresult)
        End If

        hresult = DwmSetWindowAttribute(hwnd, DwmWindowAttribute.ForceIconicRepresentation, block, 4)
        If (hresult <> 0) Then
            Throw Marshal.GetExceptionForHR(hresult)
        End If

    Finally
        Marshal.FreeHGlobal(block)

    End Try

    hresult = DwmSetIconicThumbnail(hwnd, hBitmap, DisplayThumbnailFrame)
    If (hresult <> 0) Then
        Throw Marshal.GetExceptionForHR(hresult)
    End If

End Sub
like image 441
ElektroStudios Avatar asked Oct 29 '25 02:10

ElektroStudios


2 Answers

According to MSDN Documentation:

An application typically calls the DwmSetIconicThumbnail function after it receives a WM_DWMSENDICONICTHUMBNAIL message for its window. The thumbnail should not exceed the maximum x-coordinate and y-coordinate that are specified in that message. The thumbnail must also have a 32-bit color depth.

So, using the following 32-by-32 bitmap, with 32-bit color depth, it worked:

enter image description here

The exception was gone. However, it didn't quite replace the application icon thumbnail, but appended it.

This is what it looks like using ALT+TAB:

enter image description here

And this when hovering over it:

enter image description here

Note that I did not modify your code at all and ran it exactly as it is, but just using a suitable Bitmap. These are results for a Windows 10 machine.


UPDATE

The reason because the DwmSetIconicThumbnail function returns an error is because the image exceeds the maximum size for the thumbnail, that's it, a resize is not handled by Windows itself, so we need to do a little bit more of work.

I'm not sure about which factor determines the maximum possible thumbnail size that we can establish for our image, this is speculation but I think it depends on a Windows registry value that determines the width and height for thumbnail previews (I exactlly don't remember the registry location of that value, sorry, but it can be Googled easy).

Well, as pointed above, we need to handle the WM_DWMSENDICONICTHUMBNAIL (0x323) message, so we need to override the base Window procedure a.k.a WNDPROC of our Win32 window (a Form), then finally we can retrieve the maximum width and height for the thumbnail creation from the message parameters.

This is a working code sample:

Private Const WM_DWMSENDICONICTHUMBNAIL As Integer = &H323

Protected Overrides Sub WndProc(ByRef m As Windows.Forms.Message)

    Select Case m.Msg

        Case WM_DWMSENDICONICTHUMBNAIL

            Dim hwnd As IntPtr = Process.GetCurrentProcess().MainWindowHandle
            Dim dWord As Integer = m.LParam.ToInt32()
            Dim maxWidth As Short = BitConverter.ToInt16(BitConverter.GetBytes(dWord), 2)
            Dim maxHeight As Short = BitConverter.ToInt16(BitConverter.GetBytes(dWord), 0)

            Using img As Image = Bitmap.FromFile("C:\Image.jpg")

                Using thumb As Bitmap = CType(img.GetThumbnailImage(maxWidth, maxHeight, Nothing, Nothing), Bitmap)

                    Dim hBitmap As IntPtr = thumb.GetHbitmap()

                    Dim hresult As Integer = NativeMethods.DwmSetIconicThumbnail(hwnd, hBitmap, 0)
                    If (hresult <> 0) Then
                        ' Handle error...
                        ' Throw Marshal.GetExceptionForHR(hresult)
                    End If

                    NativeMethods.DeleteObject(hBitmap)

                End Using

            End Using

    End Select

    ' Return Message to base message handler.
    MyBase.WndProc(m)

End Sub

As last comment, and if in the future I need to remember this, I will share this question that I found on MSDN, which can be helpful for someone having problems with WM_DWMSENDICONICTHUMBNAIL message:

  • Why doesn’t my program receive the WM_DWMSENDICONICTHUMBNAIL message when I ask for an iconic representation?
like image 75
jsanalytics Avatar answered Oct 30 '25 16:10

jsanalytics


Using slightly-modified declarations from Microsoft Reference Source (to provide a return value), I'm able to get this to function as expected.

internal static class NativeMethods
{
    [DllImport("dwmapi.dll")]
    public static extern int DwmSetIconicThumbnail(IntPtr hwnd, IntPtr hbmp, DWM_SIT dwSITFlags);

    [DllImport("dwmapi.dll")]
    public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWA dwAttribute, ref int pvAttribute, int cbAttribute);

    public enum DWM_SIT
    {
        None,
        DISPLAYFRAME = 1
    }

    public enum DWMWA
    {
        NCRENDERING_ENABLED = 1,
        NCRENDERING_POLICY,
        TRANSITIONS_FORCEDISABLED,
        ALLOW_NCPAINT,
        CAPTION_BUTTON_BOUNDS,
        NONCLIENT_RTL_LAYOUT,
        FORCE_ICONIC_REPRESENTATION,
        FLIP3D_POLICY,
        EXTENDED_FRAME_BOUNDS,
        // New to Windows 7:
        HAS_ICONIC_BITMAP,
        DISALLOW_PEEK,
        EXCLUDED_FROM_PEEK
        // LAST
    }

    public const uint TRUE = 1;
}

Then, I just modified your existing C# code to fit these signatures.

    private void Form1_Shown(object sender, EventArgs e)
    {
        bmp = (Bitmap)Bitmap.FromFile("C:\\Image.jpg");
        hBitmap = bmp.GetHbitmap();
        hwnd = Process.GetCurrentProcess().MainWindowHandle;

        int attributeTrue = (int)NativeMethods.TRUE;
        hresult = NativeMethods.DwmSetWindowAttribute(hwnd, NativeMethods.DWMWA.HAS_ICONIC_BITMAP, ref attributeTrue, sizeof(int));
        if ((hresult != 0))
            throw Marshal.GetExceptionForHR(hresult);

        hresult = NativeMethods.DwmSetWindowAttribute(hwnd, NativeMethods.DWMWA.FORCE_ICONIC_REPRESENTATION, ref attributeTrue, sizeof(int));
        if ((hresult != 0))
            throw Marshal.GetExceptionForHR(hresult);

        hresult = NativeMethods.DwmSetIconicThumbnail(hwnd, hBitmap, NativeMethods.DWM_SIT.DISPLAYFRAME);
        if ((hresult != 0))
            throw Marshal.GetExceptionForHR(hresult);
    }
like image 25
cokeman19 Avatar answered Oct 30 '25 17:10

cokeman19



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!