如何使用C#或VB.Net中的Win32'DwmSetIconicThumbnail'?

Ele*_*ios 3 .net c# vb.net winapi dwm

我想使用DwmSetIconicThumbnail函数为我的应用程序的缩略图预览设置静态图像.

如上面的参考链接所指出的,首先需要调用DwmSetWindowAttribute来启用DWMWA_FORCE_ICONIC_REPRESENTATIONDWMWA_HAS_ICONIC_BITMAP属性.

我已经完成了所有这些.我已经采取了所有的定义从WindowsAPICodePack源代码在这里,而且我按照相同的步骤(或我是这么认为的).

问题是,当我尝试去适应的例子为我的WinForms窗口,我得到一个E_INVALIDARG打电话时HRESULT代码DwmSetIconicThumbnail下面的代码的函数结束,我不知道有问题的说法是否是HWND,或HBITMAP.

我做错了什么?


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);
    }

}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

jsa*_*ics 5

根据MSDN文档:

应用程序通常在收到其窗口的WM_DWMSENDICONICTHUMBNAIL消息后调用DwmSetIconicThumbnail函数.缩略图不应超过该消息中指定的最大x坐标和y坐标.缩略图还必须具有32位颜色深度.

因此,使用以下32×32位图,32位颜色深度,它工作:

在此输入图像描述

例外消失了.但是,它并没有完全取代应用程序图标缩略图,而是附加了它.

这就是使用ALT + TAB的样子:

在此输入图像描述

当它盘旋在它上面时:

在此输入图像描述

请注意,我根本没有修改您的代码并完全按原样运行,而只是使用合适的代码Bitmap.这些是Windows 10计算机的结果.


UPDATE

因为原因DwmSetIconicThumbnail函数返回一个错误是因为图像超过缩略图的最大尺寸,仅此而已,调整大小不是由Windows本身处理,所以我们需要做更多一点的工作.

我不确定哪个因素决定了我们可以为我们的图像建立的最大可能缩略图大小,这是推测,但我认为这取决于Windows注册表值,它决定了缩略图预览的宽度和高度(我完全没有记住该值的注册表位置,对不起,但它可以很容易谷歌搜索).

好吧,如上所述,我们需要处理WM_DWMSENDICONICTHUMBNAIL(0x323)消息,因此我们需要覆盖基本的Window过程,即Win32窗口的WNDPROC(一个Form),最后我们可以检索缩略图创建的最大宽度和高度.消息参数.

这是一个有效的代码示例:

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
Run Code Online (Sandbox Code Playgroud)

作为最后的评论,如果将来我需要记住这一点,我将分享我在MSDN上找到的这个问题,这对于有WM_DWMSENDICONICTHUMBNAIL消息问题的人有帮助: