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
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:
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:
And this when hovering over it:
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.
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: