【问题标题】:TBPF_INDETERMINATE status has no effect in .NET 2.0 on Windows 7 taskbarTBPF_INDETERMINATE 状态在 Windows 7 任务栏上的 .NET 2.0 中无效
【发布时间】:2011-08-06 03:45:59
【问题描述】:

我有一个非常奇怪的问题。我有一个 VB.NET 2.0 应用程序,它利用了 Windows 7 任务栏按钮进度功能,即在 Win7 任务栏按钮中显示某些进度和应用程序状态。

一切正常 - 我可以设置和更新进度,我可以将按钮设置为暂停或错误状态,我可以将其设置为无进度。一切正常,除了 MARQUE(不确定)模式。这完全是个谜,每当我将状态设置为 TBPF_INDETERMINATE(值为 0x1)时,它都会简单地更改回 NOPROGRESS 类型,即它会从任务栏按钮中删除所有进度指示并将其设置回默认状态 - 从来没有动画标记显示!

我已阅读 MSDN 上的文档 - http://msdn.microsoft.com/en-us/library/dd391697(v=vs.85).aspx ;尝试了各种组合,例如将进度设置为 0,然后调用 set state 来不确定;或者喜欢先将其设置为正常,然后再设置为不确定 - 没有任何效果。这完全是个谜——文档中没有任何线索说明它失败的原因......

代码如下: API 实现:

    <StructLayout(LayoutKind.Sequential)> _
Public Structure RECT
    Public left As Integer
    Public top As Integer
    Public right As Integer
    Public bottom As Integer

    Public Sub New(left As Integer, top As Integer, right As Integer, bottom As Integer)
        Me.left = left
        Me.top = top
        Me.right = right
        Me.bottom = bottom
    End Sub
End Structure

Public Enum TBPFLAG
    TBPF_NOPROGRESS = 0
    TBPF_INDETERMINATE = &H1
    TBPF_NORMAL = &H2
    TBPF_ERROR = &H4
    TBPF_PAUSED = &H8
End Enum

Public Enum TBATFLAG
    TBATF_USEMDITHUMBNAIL = &H1
    TBATF_USEMDILIVEPREVIEW = &H2
End Enum

Public Enum THBMASK
    THB_BITMAP = &H1
    THB_ICON = &H2
    THB_TOOLTIP = &H4
    THB_FLAGS = &H8
End Enum

Public Enum THBFLAGS
    THBF_ENABLED = 0
    THBF_DISABLED = &H1
    THBF_DISMISSONCLICK = &H2
    THBF_NOBACKGROUND = &H4
    THBF_HIDDEN = &H8
End Enum

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
Public Structure THUMBBUTTON
    <MarshalAs(UnmanagedType.U4)> _
    Public dwMask As THBMASK
    Public iId As UInteger
    Public iBitmap As UInteger
    Public hIcon As IntPtr
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
    Public szTip As String
    <MarshalAs(UnmanagedType.U4)> _
    Public dwFlags As THBFLAGS
End Structure

<ComImportAttribute()> _
<GuidAttribute("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")> _
<InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface ITaskbarList3
    ' ITaskbarList
    <PreserveSig()> _
    Sub HrInit()
    <PreserveSig()> _
    Sub AddTab(hwnd As IntPtr)
    <PreserveSig()> _
    Sub DeleteTab(hwnd As IntPtr)
    <PreserveSig()> _
    Sub ActivateTab(hwnd As IntPtr)
    <PreserveSig()> _
    Sub SetActiveAlt(hwnd As IntPtr)

    ' ITaskbarList2
    <PreserveSig()> _
    Sub MarkFullscreenWindow(hwnd As IntPtr, <MarshalAs(UnmanagedType.Bool)> fFullscreen As Boolean)

    ' ITaskbarList3
    Sub SetProgressValue(<[In]()> ByVal hwnd As IntPtr, <[In]()> ByVal ullCompleted As UInt64, <[In]()> ByVal ullTotal As UInt64) 'hwnd As IntPtr, ullCompleted As UInt64, ullTotal As UInt64)
    Sub SetProgressState(<[In]()> ByVal hwnd As IntPtr, <[In]()> ByVal tbpFlags As TBPFLAG) 'hwnd As IntPtr, tbpFlags As TBPFLAG) 'As Integer
    Sub RegisterTab(hwndTab As IntPtr, hwndMDI As IntPtr)
    Sub UnregisterTab(hwndTab As IntPtr)
    Sub SetTabOrder(hwndTab As IntPtr, hwndInsertBefore As IntPtr)
    Sub SetTabActive(hwndTab As IntPtr, hwndMDI As IntPtr, tbatFlags As TBATFLAG)
    Sub ThumbBarAddButtons(hwnd As IntPtr, cButtons As UInteger, <MarshalAs(UnmanagedType.LPArray)> pButtons As THUMBBUTTON())
    Sub ThumbBarUpdateButtons(hwnd As IntPtr, cButtons As UInteger, <MarshalAs(UnmanagedType.LPArray)> pButtons As THUMBBUTTON())
    Sub ThumbBarSetImageList(hwnd As IntPtr, himl As IntPtr)
    Sub SetOverlayIcon(hwnd As IntPtr, hIcon As IntPtr, <MarshalAs(UnmanagedType.LPWStr)> pszDescription As String)
    Sub SetThumbnailTooltip(hwnd As IntPtr, <MarshalAs(UnmanagedType.LPWStr)> pszTip As String)
    '[MarshalAs(UnmanagedType.LPStruct)]
    Sub SetThumbnailClip(hwnd As IntPtr, ByRef prcClip As RECT)
End Interface

<GuidAttribute("56FDF344-FD6D-11d0-958A-006097C9A090")> _
<ClassInterfaceAttribute(ClassInterfaceType.None)> _
<ComImportAttribute()> _
Public Class CTaskbarList
End Class

以下是使用代码的实际过程:

 Friend Sub SetWindows7Progress(ByVal aValue As Integer)
        If Not IsWin7orLater Then Exit Sub
            If w7tb Is Nothing Then
                w7tb = CType(New CTaskbarList, ITaskbarList3)
            End If
            CType(w7tb, ITaskbarList3).SetProgressValue(My.Forms.Form1.Handle, Math.Min(Math.Max(1, aValue), 1000), 1000)
 End Sub

    Friend Sub ResetWindows7Progress()
        If Not IsWin7orLater Then Exit Sub
            If w7tb Is Nothing Then
                w7tb = CType(New CTaskbarList, ITaskbarList3)
            End If
            CType(w7tb, ITaskbarList3).SetProgressState(My.Forms.Form1.Handle, TBPFLAG.TBPF_NOPROGRESS)
    End Sub

    Friend Sub SetWindows7ProgressMon()
        If Not IsWin7orLater Then Exit Sub
            If w7tb Is Nothing Then
                w7tb = CType(New CTaskbarList, ITaskbarList3)
            End If
            CType(w7tb, ITaskbarList3).SetProgressState(My.Forms.Form1.Handle, TBPF_INDETERMINATE)
    End Sub

我什至尝试从 SetProgressState 获取 HRESULT 代码并检查以确保没有抛出异常,但无济于事:SetProgressState 始终返回 0(一切正常);并且不会抛出异常!

任何帮助解决问题将不胜感激!我简直不敢相信除了 MARQUE/INDETERMINATE 状态之外的一切都正常!

谢谢。

【问题讨论】:

  • 代码是否来自 Windows API 代码包?还是您自己根据 MSDN 上的文档编写了 p/invoke 定义?
  • 两者 - 我采用了 Windows API 代码包,基本上只保留了 ITaskbarList3 以及几个额外的结构/枚举(由 ITaskbarList3 使用);然后我将那个小代码从 C# 转换为 VB.NET - 非常简单 - 我在这篇文章中包含了所有这些代码。
  • 顺便说一句,这样做的原因是:1)我的应用程序是.NET 2.0,而API代码包需要.net 3.5; 2) 我真的只需要进度按钮功能 - 不需要任何其他功能。
  • 非常有意义。我主要是好奇,这样我就知道需要多少代码来检查错误。我决定启动 Visual Studio 并自己进行测试会更容易。我想我现在已经发现了问题。

标签: vb.net winapi windows-7 .net-2.0 taskbar


【解决方案1】:

首先,很奇怪,您的工作函数正在使用以下代码访问您的表单实例:

 My.Forms.Form1.Handle

这意味着这些函数没有定义在与您的表单相同的类中(因为如果是,编译器会提示您使用Me,而不是)。如果是这样的话,你真的应该将表单的句柄作为参数传递给函数。

(这样做的原因是你的函数是可重用的。如果你硬编码一个对特定表单的引用,当你重命名该表单时会发生什么,或者一次在屏幕上显示它的两个实例,或者只是想要要在任务栏中为不同的表单显示进度指示器?事情坏了。将表单实例作为参数传递是一种更简洁、更可重用的方法。)

第二,似乎进行了不必要的选角。为什么不首先将w7tb 变量声明为ITaskbarList3 类型,而不是在它和CTaskbarList 之间来回转换?

第三,我不确定这是错字还是实际问题,但您的SetWindows7ProgressMon 函数实际上并未引用TBPF_INDETERMINATE 的正确值。当它实际上在TBPFLAG 枚举中定义时,您使用对该标识符的非限定引用。

因此,考虑到以上所有情况,我将重写您发布的第二个代码块,如下所示:

Private w7tb As ITaskbarList3

Friend Sub SetWindows7Progress(ByVal frm As Form, ByVal aValue As Integer)
    If (Not IsWin7orLater()) OrElse (frm Is Nothing) Then
        Exit Sub
    End If

    If w7tb Is Nothing Then
        w7tb = CType(New CTaskbarList, ITaskbarList3)
    End If
    w7tb.SetProgressValue(frm.Handle, Math.Min(Math.Max(1, aValue), 1000), 1000)
End Sub

Friend Sub ResetWindows7Progress(ByVal frm As Form)
    If (Not IsWin7orLater()) OrElse (frm Is Nothing) Then
        Exit Sub
    End If

    If w7tb Is Nothing Then
        w7tb = CType(New CTaskbarList, ITaskbarList3)
    End If
    w7tb.SetProgressState(frm.Handle, TBPFLAG.TBPF_NOPROGRESS)
End Sub

Friend Sub SetWindows7ProgressMon(ByVal frm As Form)
    If (Not IsWin7orLater()) OrElse (frm Is Nothing) Then
       Exit Sub
    End If

    If w7tb Is Nothing Then
        w7tb = CType(New CTaskbarList, ITaskbarList3)
    End If
    w7tb.SetProgressState(frm.Handle, TBPFLAG.TBPF_INDETERMINATE)
End Sub

经过测试,它可以在 32 位 Windows 7 上完美运行。请注意,您可以通过简单地为 frm 参数指定 Me 来从表单类内部的代码中调用函数。

【讨论】:

  • @Cody 哇,你真快!非常感谢您对此进行调查。我会尝试你的 1 和 2 建议,虽然在代码方面,虽然很明显它们更好更清洁,但它们不应该等同于我的代码吗? (即它们不应该是一个问题)。至于#3 - 这是一个错字(因为我尝试在那里放置各种东西,如实际值#1或“1或2”之类的东西 - 没有任何效果 - 所以#3是一个错字(因为我更正了这个代码网站),它确实是您在我的代码中编写的 TBPFLAG.TBPF_INDETERMINATE,它不起作用:() 但我会测试您的 1 和 2 建议。
  • @George:是的,选角与最终结果完全无关。您找到对表单类的引用的方式,不一定。但这可能会使它根本不起作用,所以我假设您在应用其他样式时会注意到。我担心这可能是一个错字......老实说,我不确定还有什么问题。我展示的代码(结合您的 p/invoke 声明)在运行 Windows 7 Ultimate 32 位的 MacBook Pro 上运行良好。
  • 好吧,刚刚尝试了#1 和#2 - 现在它根本不起作用。我认为罪魁祸首是你的建议#2——你真的需要创建一个 iTaskBarList3 的实例,而这样做的方法是拥有那个中间类——CTaskbarList——毕竟它应该用它的 GUID 服务于某些目的,不是吗?
  • 顺便说一句,我的操作系统也是 Win7 Ultimate 32 位。问题是否与我在 Visual Studio 中对其进行测试有关?
  • @George:您实际上无法创建接口的实例。您只能创建实现接口的类的实例。那个班级是CTaskbarList。并且由于多态性,任何实现接口的类都可以转换为该接口或从该接口转换。 CTaskbarList 不是“中间”类,它是唯一定义的类。我所做的改变不可能有任何改变。您仍在使用 GUID 元数据,因为您仍在使用以下代码实例化 CTaskbarList:CType(New CTaskbarList, ITaskbarList3)。 New CTaskbarList 是做什么的。
猜你喜欢
  • 2023-04-02
  • 1970-01-01
  • 2011-03-24
  • 2012-02-29
  • 2020-02-21
  • 2010-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多