【问题标题】:How to use Win32 'DwmSetIconicThumbnail' from C# or VB.Net?如何从 C# 或 VB.Net 使用 Win32 'DwmSetIconicThumbnail'?
【发布时间】:2016-05-19 11:49:34
【问题描述】:

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

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

我已经做到了。我已经从 WindowsAPICodePack 源代码 here 中获取了所有定义,并且我正在遵循相同的步骤(或者我认为是这样)。

问题是,当我尝试为我的 WinForms 窗口调整示例时,在下面的代码末尾调用 DwmSetIconicThumbnail 函数时,我得到一个 E_INVALIDARG HRESULT 代码,我不确定是否有问题的参数是 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);
    }

}

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

【问题讨论】:

    标签: c# .net vb.net winapi dwm


    【解决方案1】:

    根据MSDN Documentation

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

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

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

    这是使用 ALT+TAB 的样子:

    当鼠标悬停在它上面时:

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


    更新

    DwmSetIconicThumbnail函数返回错误的原因是因为图像超过了缩略图的最大尺寸,就是这样,调整大小不是由Windows自己处理的,所以我们需要做更多的工作。

    我不确定哪个因素决定了我们可以为图像建立的最大可能缩略图大小,这是推测,但我认为这取决于决定缩略图预览宽度和高度的 Windows 注册表值(我完全不记得该值的注册表位置,抱歉,但可以很容易地用 Google 搜索)。

    好吧,如上所述,我们需要处理 WM_DWMSENDICONICTHUMBNAIL (0x323) 消息,因此我们需要覆盖 Win32 窗口(一个窗体)的基本 Window 过程,也就是 WNDPROC ,最后我们可以从消息参数中获取缩略图创建的最大宽度和高度。

    这是一个工作代码示例:

    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
    

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

    【讨论】:

    • 答案指向了我错过的 MSDN 的结论性评论,然后经过进一步调查,我终于解决了它,谢谢!,但这个答案仅表明 32x32 的小图标可以工作,这不是现实生活中的解决方案。
    • 附加图像的原因是因为设置了 DISPLAYFRAME 标志,但应该是帧效果而不是“图像附加”,反正效果没有我不感兴趣,不用担心,但是您的答案中提供的信息尚无定论,我希望您可以允许我使用其他必需的详细信息来扩展您的答案,在几分钟内我会这样做并给予赏金,再次感谢!!。
    【解决方案2】:

    使用来自Microsoft Reference Source 的稍微修改的声明(以提供返回值),我可以让它按预期运行。

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

    然后,我刚刚修改了您现有的 C# 代码以适应这些签名。

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

    【讨论】:

    • 感谢您的评论,但您可能已经在我的问题中看到我也修改了 DwmSetWindowAttributeDwmSetIconicThumbnail 的声明以返回一个值。 .我们使用的是相同的。无论如何,我当然尝试验证您提供的解决方案是否有效,但我在 DwmSetIconicThumbnail 函数中获得了相同的 HRESULT 代码(使用您的 Marshal 调用的 System.ArgumentException)。对不起,它不适合我。
    • 作为参考,我关于 retval 的注释是为了表明我从 MS 的声明中改变了什么,而不是你的。但是由于我的示例不适合您,我怀疑@jstreet 的答案可能比我的更相关。可以发具体的图片吗?
    • 再次感谢您的帮助,关于图像现在不相关,但是我发现它的大小,假设我使用的是随机大分辨率图像,它会失败,因为我预计 Windows将调整它的大小以适应最大可能的缩略图大小,但我需要确定最大可能的大小,然后自己调整它的大小。问题已经解决了!。如果您有兴趣将此功能应用于您的应用程序,我在 jstreet 的答案中添加了一个代码示例!问候。
    【解决方案3】:

    也许尺寸的硬编码“4”在起作用? 看看(这里的 c# mods)是否有效:

    ...
    IntPtr block = Marshal.AllocHGlobal(sizeof(int));
    ...
    hresult = DwmSetWindowAttribute(hwnd, DwmWindowAttribute.HasIconicBitmap, block, sizeof(int));
    ...
    hresult = DwmSetWindowAttribute(hwnd, DwmWindowAttribute.ForceIconicRepresentation, block, sizeof(int));
    

    还请注意,在调用Bitmap.GetHbitmap() (https://msdn.microsoft.com/en-us/library/1dz311e4(v=vs.110).aspx#Anchor_2) 后,您可能需要释放位图使用的内存

    另外,一个不错的编组入门:http://justlikeamagic.com/2010/03/09/marshaling/

    【讨论】:

    • 那没有相关性,32 Bits (Int) = 4 Bytes。如果您看到,我使用相同的标志(ForceIconicRepresentationHasIconicBitmap)、相同的数据类型(Int32)并传递相同的正确大小值(4) 就像 WindowsCodeApiPack 的人一样,该函数也不会返回任何失败的 HRESULT 代码,正如我提到的另一个返回错误的函数,但无论如何还是谢谢你试一试。
    • 既然你是抛出异常,那么异常细节提供了什么?
    • 没有详细信息,因为它不是托管异常,它是DwmSetIconicThumbnail Win32 函数返回的HRESULT 代码,引用E_INVALIDARG (0x80070057) 正如我所解释的,这相当于System.ArgumentException .NET 异常:msdn.microsoft.com/en-us/library/9ztbc5s1%28v=vs.110%29.aspx
    猜你喜欢
    • 1970-01-01
    • 2010-09-12
    • 1970-01-01
    • 2013-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-29
    • 2011-06-21
    相关资源
    最近更新 更多