【问题标题】:Make portable these P/Invoke calls使这些 P/Invoke 调用可移植
【发布时间】:2014-09-25 16:16:39
【问题描述】:

我正在开发一个AnyCPU 应用程序,我正在调用CallNextHookExSetWindowsHookEx 函数,但是VisualStudio 代码分析给我一个P/Invoke 声明应该是可移植的 两个函数声明的警告。

我理解此警告的含义,但我将所有参数设置为 IntPtr,使其在 32 位系统中为 4 个字节,在 64 位系统中为 8 个字节,但我仍然收到相同的警告。

我做错了什么?

    <DllImport("user32.dll", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Auto)>
    Friend Shared Function CallNextHookEx(
           ByVal idHook As IntPtr,
           ByVal nCode As Integer,
           ByVal wParam As IntPtr,
           ByVal lParam As MSLLHOOKSTRUCT
    ) As Integer
    End Function

    <DllImport("user32.dll", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Auto)>
    Friend Shared Function SetWindowsHookEx(
           ByVal idHook As HookType,
           ByVal lpfn As MouseProcDelegate,
           ByVal hInstance As IntPtr,
           ByVal threadId As IntPtr
    ) As IntPtr
    End Function

    <Description("Enum used in 'idHook' parameter of 'SetWindowsHookEx' function")>
    Friend Enum HookType As UInteger

        WH_MOUSE_LL = 14UI

    End Enum

    <Description("Structure used in 'lParam' parameter of 'CallNextHookEx' function")>
    Friend Structure MSLLHOOKSTRUCT

        Friend pt As Point
        Friend mouseData As Integer
        Friend flags As Integer
        Friend time As Integer
        Friend dwExtraInfo As IntPtr

    End Structure

Friend Delegate Function MouseProcDelegate(
        ByVal nCode As IntPtr,
        ByVal wParam As IntPtr,
        ByRef lParam As NativeMethods.MSLLHOOKSTRUCT
) As IntPtr

更新:

再次阅读SetWindowsHookEx 的文档后,我解决了此函数中的警告,将返回数据类型修复为这些:

    <DllImport("user32.dll", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Auto)>
    Friend Shared Function SetWindowsHookEx(
           ByVal idHook As HookType,
           ByVal lpfn As MouseProcDelegate,
           ByVal hInstance As IntPtr,
           ByVal threadId As UInteger
    ) As IntPtr
    End Function

    Friend Enum HookType As Integer
        WH_MOUSE_LL = 14I
    End Enum

但我仍然坚持使用CallNextHookEx 函数。

【问题讨论】:

  • 您对 IntPtr 的大部分使用都是错误的。只有指针大小的东西应该是 IntPtr。您需要返回文档。
  • @David Heffernan 感谢您的评论,我已经修复了一个函数的声明,但我无法修复另一个函数(请参阅我的更新)。
  • 您确定MSLLHOOKSTRUCT 中的pt 是正确的吗?我希望 POINT 改为。
  • @γηράσκω δ' αεί πολλά διδασκόμε 执行您建议的更改我收到更多警告 (5)
  • @ElektroStudios 将返回类型从Integer 更改为IntPtr

标签: .net vb.net winforms winapi pinvoke


【解决方案1】:

这没有 CA 错误,是 MSDN 示例的混合体,一些 PInvoke refs 和 cmets。我不需要鼠标钩,所以它没有经过测试,CA 错误就消失了:

<DllImport("user32.dll", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Auto)>
Friend Shared Function CallNextHookEx(
        idHook As IntPtr,
        nCode As Int32,
        wParam As IntPtr,
        lParam As IntPtr
) As IntPtr
End Function

<DllImport("user32.dll", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Auto)>
Friend Shared Function SetWindowsHookEx(
        idHook As HookType,
        lpfn As MouseProcDelegate,
        hInstance As IntPtr,
        threadId As UInteger
) As IntPtr
End Function

<Description("Enum used in 'idHook' parameter of 'SetWindowsHookEx' function")>
Friend Enum HookType As UInteger

    WH_MOUSE_LL = 14

End Enum

<Flags()>
Public Enum MSLLHOOKSTRUCTFlags As Int32
    LLMHF_INJECTED = 1
End Enum

<System.Runtime.InteropServices.StructLayout(Runtime.InteropServices.LayoutKind.Sequential)> _
Public Structure POINT
    Public X As Int32
    Public Y As Int32
End Structure

<Description("Structure used in 'lParam' parameter of 'CallNextHookEx' function")>
Friend Structure MSLLHOOKSTRUCT

    Friend pt As POINT
    Friend mouseData As Int32
    Friend flags As MSLLHOOKSTRUCTFlags
    Friend time As Int32
    Friend dwExtraInfo As IntPtr

End Structure

Friend Delegate Function MouseProcDelegate(
        ByVal nCode As IntPtr,
        ByVal wParam As IntPtr,
        ByRef lParam As IntPtr
) As Int32

重要提示:

据称,System.Drawing.Point 和 API POINT 是可以互换的。我倾向于不喜欢仅仅因为互联网上的某个人这么说就认为这是真的。不过它似乎确实有效。

第 4 个/LParam arg 在 CallNextHookEx 中从 MSLLHOOKSTRUCT 更改为 IntPtr。可以打开许多 CA 警告以查看究竟是什么问题。在这种情况下,文本告诉LParam 是错误的,因为该结构将是 28 字节(如果我记得 CA msg),它太大而无法以这种方式传递。

MSDN 就是这样做的,将IntPtr 编组回他们示例中的结构。 CallNextHookEx 从作为IntPtr 接收结构的本地委托调用。如果您只是传递它,它已经准备好了,如果您需要使用它,请编组它:

' marshal lParm (intPtr) to structure):
MyMouseHookStruct = CType(Marshal.PtrToStructure(lParam, 
            MyMouseHookStruct.GetType()), MouseHookStruct)
...

' pass along ready to use lParam:
Return CallNextHookEx(hHook, nCode, wParam, lParam)

如果你添加一个包装器,你应该能够在NativeMethods 类中完成该部分,而不是在使用它的代码中。

相关MSDN article。请注意,CA 对象为 MSDN 文章中使用的返回类型,In32IntPtr

【讨论】:

  • 非常感谢,一切正常,只是代码有一点错误,lparam不应该被引用,否则在尝试编组时会抛出未处理的写入受保护内存的异常将 lparam 转换为结构,其余代码完美运行!
  • 我的代码设计有一个问题,我将 NativeMethods 类隐藏了(因为表明 MSDN 它不应该是可见的)并且我将 Point 结构放在 NativeMethods 类中,因为我不想要该结构是可见的,那么我不能公开 Windows POINT 结构来订阅来自其他类的事件。如果我使用drawing.point 类,这是一个非常大的问题?我只是想知道您对此的看法,也许它们在某些方面有所不同,但似乎工作平等
  • 我通过接收 NativeMethods.Point 然后获取 x,y 点并发送新的 SystemDrawing.Point(x,y) 解决了这个小问题
  • 我不知道您所说的“隐藏”类是什么意思。我认为更重要的是保持 API 调用和所有令人毛骨悚然的魔术数字等私有并通过包装器公开它们。至于 POINT 与 Point,我认为这没什么大不了的——它们都只是 x,y 结构,但将 POINT 添加到类中也非常容易。将 POINT 转换为 Point 和其他 API -> NET 转换是在类中隔离的一项好任务。
  • 对于“隐藏”,我的意思是类可见性(在我的例子中是受保护的类 NativeMethods),再次感谢 :)
【解决方案2】:

添加一个类NativeMethods

Imports System.Runtime.InteropServices
Friend NotInheritable Class NativeMethods
    Private Sub New()
    End Sub
    <DllImport("user32.dll", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Auto)>
    Friend Shared Function CallNextHookEx(
           ByVal idHook As IntPtr,
           ByVal nCode As Integer,
           ByVal wParam As IntPtr,
           ByRef lParam As KBDLLHOOKSTRUCT
    ) As IntPtr
    End Function

    Friend Structure KBDLLHOOKSTRUCT
        Friend vkCode As Integer
        Friend scanCode As Integer
        Friend flags As Integer
        Friend time As Integer
        Friend dwExtraInfo As Integer
    End Structure
End Class

【讨论】:

    【解决方案3】:

    如果有人需要,这里是该课程的完整来源:

    用法示例:

    dim withevents hook as new mousehook(install:=true)
    ' then subscribe to the desired events...
    hook.uninstall ' calls the finalizer
    

    (由于 StackOverflow 的字符长度限制,我需要删除一些有用的 XML 文档)

    ' ***********************************************************************
    ' Author           : Elektro
    ' Last Modified On : 09-26-2014
    ' ***********************************************************************
    
    #Region " Instructions "
    
    ' Go to page:
    ' Project > Properties > Debug
    '
    ' Then uncheck the option:
    ' "Enable the Visual Studio Hosting Process"
    
    #End Region
    
    #Region " Imports "
    
    Imports System.ComponentModel
    Imports System.Reflection
    Imports System.Runtime.InteropServices
    
    #End Region
    
    #Region " MouseHook "
    
    ''' <summary>
    ''' A low level mouse hook that processes mouse input events.
    ''' </summary>
    Friend NotInheritable Class MouseHook : Implements IDisposable
    
    #Region " P/Invoke "
    
        ''' <summary>
        ''' Platform Invocation methods (P/Invoke), access unmanaged code.
        ''' This class does not suppress stack walks for unmanaged code permission.
        ''' <see cref="System.Security.SuppressUnmanagedCodeSecurityAttribute"/>  must not be applied to this class.
        ''' This class is for methods that can be used anywhere because a stack walk will be performed.
        ''' MSDN Documentation: http://msdn.microsoft.com/en-us/library/ms182161.aspx
        ''' </summary>
        Protected NotInheritable Class NativeMethods
    
    #Region " Methods "
    
            <DllImport("user32.dll", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Auto)>
            Friend Shared Function CallNextHookEx(
                   ByVal idHook As IntPtr,
                   ByVal nCode As Integer,
                   ByVal wParam As IntPtr,
                   ByVal lParam As IntPtr
            ) As IntPtr
            End Function
    
            <DllImport("user32.dll", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Auto)>
            Friend Shared Function SetWindowsHookEx(
                   ByVal idHook As HookType,
                   ByVal lpfn As LowLevelMouseProcDelegate,
                   ByVal hInstance As IntPtr,
                   ByVal threadId As UInteger
            ) As IntPtr
            End Function
    
            <DllImport("user32.dll", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Auto)>
            Friend Shared Function UnhookWindowsHookEx(
                   ByVal idHook As IntPtr
            ) As Boolean
            End Function
    
            ''' <summary>
            ''' Retrieves the current double-click time for the mouse. 
            ''' A double-click is a series of two clicks of the mouse button, 
            ''' the second occurring within a specified time after the first. 
            ''' The double-click time is the maximum number of milliseconds that may occur 
            ''' between the first and second click of a double-click. 
            ''' The maximum double-click time is 5000 milliseconds.
            ''' </summary>
            ''' <returns>
            ''' The return value specifies the current double-click time, in milliseconds. 
            ''' The maximum return value is 5000 milliseconds.
            ''' </returns>
            <DllImport("user32.dll", CharSet:=CharSet.Auto)>
            Friend Shared Function GetDoubleClickTime() As Integer
            End Function
    
    #End Region
    
    #Region " Enumerations "
    
            ''' <summary>
            ''' Indicates a type of Hook procedure to be installed.
            ''' MSDN Documentation: http://msdn.microsoft.com/en-us/library/windows/desktop/ms644990%28v=vs.85%29.aspx
            ''' </summary>
            <Description("Enum used in 'idHook' parameter of 'SetWindowsHookEx' function.")>
            Friend Enum HookType As UInteger
    
                ' **************************************
                ' This enumeration is partially defined.
                ' **************************************
    
                ''' <summary>
                ''' Installs a hook procedure that monitors low-level mouse input events. 
                ''' For more information, see the LowLevelMouseProc hook procedure.
                ''' </summary>
                WH_MOUSE_LL = 14UI
    
            End Enum
    
            ''' <summary>
            ''' The event-injected flags. 
            ''' An application can use the following values to test the flags. 
            ''' Testing LLMHF_INJECTED (bit 0) will tell you whether the event was injected. 
            ''' If it was, then testing LLMHF_LOWER_IL_INJECTED (bit 1) will tell you 
            ''' whether or not the event was injected from a process running at lower integrity level.
            ''' </summary>
            <Flags()>
            <Description("Enum used in 'flags' parameter of 'MSLLHOOKSTRUCT' structure.")>
            Public Enum MSLLHOOKSTRUCTFlags As Integer
    
                ''' <summary>
                ''' Test the event-injected (from any process) flag.
                ''' </summary>
                LLMHF_INJECTED = 1
    
                ''' <summary>
                ''' Test the event-injected (from a process running at lower integrity level) flag.
                ''' </summary>
                LLMHF_LOWER_IL_INJECTED = 2
    
            End Enum
    
    #End Region
    
    #Region " Structures "
    
            ''' <summary>
            ''' The POINT structure defines the x- and y- coordinates of a point.
            ''' MSDN Documentation: http://msdn.microsoft.com/en-us/library/windows/desktop/dd162805%28v=vs.85%29.aspx
            ''' </summary>
            <Description("Structure used in 'pt' parameter of 'MSLLHOOKSTRUCT' structure.")>
            <StructLayout(LayoutKind.Sequential)>
            Friend Structure POINT
    
                ''' <summary>
                ''' The x-coordinate of the point.
                ''' </summary>
                Public X As Integer
    
                ''' <summary>
                ''' The y-coordinate of the point.
                ''' </summary>
                Public Y As Integer
    
            End Structure
    
            ''' <summary>
            ''' Contains information about a low-level mouse input event.
            ''' MSDN Documentation: http://msdn.microsoft.com/en-us/library/windows/desktop/ms644970%28v=vs.85%29.aspx
            ''' </summary>
            <Description("Structure used in 'lParam' parameter of 'CallNextHookEx' function.")>
            Friend Structure MSLLHOOKSTRUCT
    
                ''' <summary>
                ''' The ptThe x- and y-coordinates of the cursor, in screen coordinates.
                ''' </summary>
                Friend pt As NativeMethods.POINT
    
                ''' <summary>
                ''' If the message is 'WM_MOUSEWHEEL', the high-order word of this member is the wheel delta. 
                ''' The low-order word is reserved. 
                ''' A positive value indicates that the wheel was rotated forward, away from the user; 
                ''' a negative value indicates that the wheel was rotated backward, toward the user. 
                ''' One wheel click is defined as 'WHEEL_DELTA', which is '120'.
                ''' </summary>
                Friend mouseData As Integer
    
                ''' <summary>
                ''' The event-injected flag.
                ''' </summary>
                Friend flags As MSLLHOOKSTRUCTFlags
    
                ''' <summary>
                ''' The time stamp for this message. 
                ''' </summary>
                Friend time As UInteger
    
                ''' <summary>
                ''' Additional information associated with the message.
                ''' </summary>
                Friend dwExtraInfo As IntPtr
    
            End Structure
    
    #End Region
    
    #Region " Delegates "
    
            ''' <summary>
            ''' Delegate LowLevelMouseProc
            ''' MSDN Documentation: http://msdn.microsoft.com/en-us/library/windows/desktop/ms644986%28v=vs.85%29.aspx
            ''' </summary>
            ''' <returns>
            ''' If nCode is less than zero, the hook procedure must return the value returned by CallNextHookEx.
            ''' If nCode is greater than or equal to zero, and the hook procedure did not process the message, 
            ''' it is highly recommended that you call CallNextHookEx and return the value it returns; 
            ''' otherwise, other applications that have installed WH_MOUSE_LL hooks will not receive hook notifications 
            ''' and may behave incorrectly as a result.
            ''' If the hook procedure processed the message, 
            ''' it may return a nonzero value to prevent the system from passing the message to the rest of the hook chain or the target window procedure. 
            ''' </returns>
            Friend Delegate Function LowLevelMouseProcDelegate(
                    ByVal nCode As IntPtr,
                    ByVal wParam As IntPtr,
                    ByVal lParam As IntPtr
            ) As Integer
    
    #End Region
    
    #Region " Hidden Methods "
    
            ''' <summary>
            ''' Determines whether the specified System.Object instances are considered equal.
            ''' </summary>
            <EditorBrowsable(EditorBrowsableState.Never)>
            Public Shadows Sub Equals()
            End Sub
    
            ''' <summary>
            ''' Determines whether the specified System.Object instances are the same instance.
            ''' </summary>
            <EditorBrowsable(EditorBrowsableState.Never)>
            Private Shadows Sub ReferenceEquals()
            End Sub
    
    #End Region
    
        End Class
    
    #End Region
    
    #Region " Objects "
    
        ''' <summary>
        ''' Handle to the hook procedure.
        ''' </summary>
        Private MouseHook As IntPtr
    
        ''' <summary>
        ''' The mouse hook delegate.
        ''' </summary>
        Private MouseHookDelegate As NativeMethods.LowLevelMouseProcDelegate
    
    #End Region
    
    #Region " Properties "
    
        ''' <summary>
        ''' ** ONLY FOR TESTING PURPOSES **
        ''' Gets or sets a value indicating whether to suppress the last MouseUp event of 
        ''' the specified clicked button when a double-click fires.
        ''' 
        ''' If this value is set to <c>true</c>, the application will send the events in this order for a Double-Click:
        ''' MouseDown, MouseUp, MouseDown, MouseDoubleClick
        ''' 
        ''' If this value is set to <c>false</c>, the application will send the events in this order for a Double-Click:
        ''' MouseDown, MouseUp, MouseDown, MouseUp, MouseDoubleClick
        ''' 
        ''' </summary>
        ''' <value><c>true</c> if MouseUp event is suppressed; otherwise <c>false</c>.</value>
        Friend Property SuppressMouseUpEventWhenDoubleClick As Boolean = False
    
    #End Region
    
    #Region " Enumerations "
    
        ''' <summary>
        ''' Indicates a Windows Message related to a mouse events.
        ''' For more info see here:
        ''' http://msdn.microsoft.com/en-us/library/windows/desktop/ff468877%28v=vs.85%29.aspx
        ''' </summary>
        Private Enum MouseMessages As UInteger
    
            ''' <summary>
            ''' Posted to a window when the cursor moves. 
            ''' If the mouse is not captured, the message is posted to the window that contains the cursor. 
            ''' Otherwise, the message is posted to the window that has captured the mouse
            ''' </summary>
            WM_MOUSEMOVE = &H200UI
    
            ''' <summary>
            ''' Posted when the user presses the left mouse button while the cursor is in the client area of a window.
            ''' If the mouse is not captured, the message is posted to the window beneath the cursor.
            ''' Otherwise, the message is posted to the window that has captured the mouse
            ''' </summary>
            WM_LBUTTONDOWN = &H201UI
    
            ''' <summary>
            ''' Posted when the user releases the left mouse button while the cursor is in the client area of a window. 
            ''' If the mouse is not captured, the message is posted to the window beneath the cursor. 
            ''' Otherwise, the message is posted to the window that has captured the mouse
            ''' </summary>
            WM_LBUTTONUP = &H202UI
    
            ''' <summary>
            ''' Posted when the user presses the right mouse button while the cursor is in the client area of a window. 
            ''' If the mouse is not captured, the message is posted to the window beneath the cursor. 
            ''' Otherwise, the message is posted to the window that has captured the mouse
            ''' </summary>
            WM_RBUTTONDOWN = &H204UI
    
            ''' <summary>
            ''' Posted when the user releases the right mouse button while the cursor is in the client area of a window. 
            ''' If the mouse is not captured, the message is posted to the window beneath the cursor. 
            ''' Otherwise, the message is posted to the window that has captured the mouse
            ''' </summary>
            WM_RBUTTONUP = &H205UI
    
            ''' <summary>
            ''' Posted when the user presses the middle mouse button while the cursor is in the client area of a window. 
            ''' If the mouse is not captured, the message is posted to the window beneath the cursor. 
            ''' Otherwise, the message is posted to the window that has captured the mouse
            ''' </summary>
            WM_MBUTTONDOWN = &H207UI
    
            ''' <summary>
            ''' Posted when the user releases the middle mouse button while the cursor is in the client area of a window. 
            ''' If the mouse is not captured, the message is posted to the window beneath the cursor. 
            ''' Otherwise, the message is posted to the window that has captured the mouse
            ''' </summary>
            WM_MBUTTONUP = &H208UI
    
            ''' <summary>
            ''' Sent to the active window when the mouse's horizontal scroll wheel is tilted or rotated. 
            ''' The DefWindowProc function propagates the message to the window's parent. 
            ''' There should be no internal forwarding of the message, 
            ''' since DefWindowProc propagates it up the parent chain until it finds a window that processes it.
            ''' </summary>
            WM_MOUSEWHEEL = &H20AUI
    
        End Enum
    
        ''' <summary>
        ''' Indicates the whell direction of the mouse.
        ''' </summary>
        Public Enum WheelDirection As Integer
    
            ''' <summary>
            ''' The wheel is moved up.
            ''' </summary>
            WheelUp = 1
    
            ''' <summary>
            ''' The wheel is moved down.
            ''' </summary>
            WheelDown = 2
    
        End Enum
    
    #End Region
    
    #Region " Events "
    
        ''' <summary>
        ''' Occurs when the mouse moves.
        ''' </summary>
        Friend Event MouseMove(ByVal sender As Object,
                               ByVal MouseLocation As Point)
    
        ''' <summary>
        ''' Occurs when the mouse left button is pressed.
        ''' </summary>
        Friend Event MouseLeftDown(ByVal sender As Object,
                                   ByVal MouseLocation As Point)
    
        ''' <summary>
        ''' Occurs when the mouse left button is released.
        ''' </summary>
        Friend Event MouseLeftUp(ByVal sender As Object,
                                 ByVal MouseLocation As Point)
    
        ''' <summary>
        ''' Occurs when the mouse left button performs a double-click.
        ''' A double click is considered as: (MouseLeftDown + MouseLeftUp) * 2
        ''' </summary>
        Friend Event MouseLeftDoubleClick(ByVal sender As Object,
                                          ByVal MouseLocation As Point)
    
        ''' <summary>
        ''' Occurs when the mouse right button is pressed.
        ''' </summary>
        Friend Event MouseRightDown(ByVal sender As Object,
                                    ByVal MouseLocation As Point)
    
        ''' <summary>
        ''' Occurs when the mouse right button is released.
        ''' </summary>
        Friend Event MouseRightUp(ByVal sender As Object,
                                  ByVal MouseLocation As Point)
    
        ''' <summary>
        ''' Occurs when the mouse right button performs a double-click.
        ''' A double click is considered as: (MouseLeftDown + MouseLeftUp) * 2
        ''' </summary>
        Friend Event MouseRightDoubleClick(ByVal sender As Object,
                                           ByVal MouseLocation As Point)
    
        ''' <summary>
        ''' Occurs when the mouse middle button is pressed.
        ''' </summary>
        Friend Event MouseMiddleDown(ByVal sender As Object,
                                     ByVal MouseLocation As Point)
    
        ''' <summary>
        ''' Occurs when the mouse middle button is released.
        ''' </summary>
        Friend Event MouseMiddleUp(ByVal sender As Object,
                                   ByVal MouseLocation As Point)
    
        ''' <summary>
        ''' Occurs when the mouse middle button performs a double-click.
        ''' A double click is considered as: (MouseLeftDown + MouseLeftUp) * 2
        ''' </summary>
        Friend Event MouseMiddleDoubleClick(ByVal sender As Object,
                                            ByVal MouseLocation As Point)
    
        ''' <summary>
        ''' Occurs when the mouse wheel is moved up or down.
        ''' </summary>
        Friend Event MouseWheel(ByVal sender As Object,
                                ByVal MouseLocation As Point,
                                ByVal WheelDirection As WheelDirection)
    
    #End Region
    
    #Region " Constructors "
    
        ''' <summary>
        ''' Prevents a default instance of the <see cref="MouseHook"/> class from being created.
        ''' </summary>
        Private Sub New()
        End Sub
    
        ''' <summary>
        ''' Initializes a new instance of the <see cref="MouseHook"/> class.
        ''' </summary>
        ''' <param name="Install">
        ''' If set to <c>true</c>, the Hook starts initialized for this <see cref="MouseHook"/> instance.
        ''' </param>
        Friend Sub New(Optional ByVal Install As Boolean = False)
    
            If Install Then
                Me.Install()
            End If
    
        End Sub
    
    #End Region
    
    #Region " Public Methods "
    
        ''' <summary>
        ''' Installs the Mouse Hook, then start processing messages to fire events.
        ''' </summary>
        Friend Sub Install()
    
            Me.MouseHookDelegate = New NativeMethods.LowLevelMouseProcDelegate(AddressOf LowLevelMouseProc)
    
            Try
                Me.MouseHook = NativeMethods.SetWindowsHookEx(NativeMethods.HookType.WH_MOUSE_LL,
                                                              Me.MouseHookDelegate,
                                                              Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly.GetModules()(0)).ToInt32, 0)
    
            Catch ex As Exception
                Throw
    
            End Try
    
        End Sub
    
        ''' <summary>
        ''' Uninstalls the Mouse Hook and free all resources, then stop processing messages to fire events.
        ''' </summary>
        Friend Sub Uninstall()
    
            Me.Finalize()
    
        End Sub
    
    #End Region
    
    #Region " Private Methods "
    
        Private Function LowLevelMouseProc(ByVal nCode As Integer,
                                           ByVal wParam As MouseMessages,
                                           ByVal lParam As IntPtr) As Integer
    
            Static LeftClickTime As Integer = 0I ' Determines a left button double-click.
            Static RightClickTime As Integer = 0I ' Determines a right button double-click.
            Static MiddleClickTime As Integer = 0I ' Determines a middle button double-click.
    
            If nCode = 0I Then
    
                Dim x As Integer = 0
                Dim y As Integer = 0
    
                Dim MouseStruct As NativeMethods.MSLLHOOKSTRUCT
                MouseStruct = CType(Marshal.PtrToStructure(lParam, MouseStruct.GetType()), 
                                    NativeMethods.MSLLHOOKSTRUCT)
    
                ' Fix X coordinate.
                Select Case MouseStruct.pt.X
    
                    Case Is < 0I
                        x = 0I
    
                    Case Is > SystemInformation.VirtualScreen.Width
                        x = SystemInformation.VirtualScreen.Width
    
                    Case Else
                        x = MouseStruct.pt.X
    
                End Select
    
                ' Fix Y coordinate.
                Select Case MouseStruct.pt.Y
    
                    Case Is > SystemInformation.VirtualScreen.Height
                        y = SystemInformation.VirtualScreen.Height
    
                    Case Is < 0I
                        y = 0I
    
                    Case Else
                        y = MouseStruct.pt.Y
    
                End Select
    
                Select Case wParam
    
                    Case MouseMessages.WM_MOUSEMOVE
                        RaiseEvent MouseMove(Me, New Point(x, y))
    
                    Case MouseMessages.WM_LBUTTONDOWN
                        RaiseEvent MouseLeftDown(Me, New Point(x, y))
    
                    Case MouseMessages.WM_LBUTTONUP
                        If LeftClickTime <> 0 Then
                            LeftClickTime = Environment.TickCount() - LeftClickTime
                        End If
    
                        If (LeftClickTime <> 0) AndAlso (LeftClickTime < NativeMethods.GetDoubleClickTime()) Then
                            LeftClickTime = 0
                            If Not Me.SuppressMouseUpEventWhenDoubleClick Then
                                RaiseEvent MouseLeftUp(Me, New Point(x, y))
                            End If
                            RaiseEvent MouseLeftDoubleClick(Me, New Point(x, y))
    
                        Else
                            LeftClickTime = Environment.TickCount()
                            RaiseEvent MouseLeftUp(Me, New Point(x, y))
    
                        End If
    
                    Case MouseMessages.WM_RBUTTONDOWN
                        RaiseEvent MouseRightDown(Me, New Point(x, y))
    
                    Case MouseMessages.WM_RBUTTONUP
                        If RightClickTime <> 0 Then
                            RightClickTime = Environment.TickCount() - RightClickTime
                        End If
    
                        If (RightClickTime <> 0) AndAlso (RightClickTime < NativeMethods.GetDoubleClickTime()) Then
                            RightClickTime = 0
                            If Not Me.SuppressMouseUpEventWhenDoubleClick Then
                                RaiseEvent MouseRightUp(Me, New Point(x, y))
                            End If
                            RaiseEvent MouseRightDoubleClick(Me, New Point(x, y))
    
                        Else
                            RightClickTime = Environment.TickCount()
                            RaiseEvent MouseRightUp(Me, New Point(x, y))
    
                        End If
    
                    Case MouseMessages.WM_MBUTTONDOWN
                        RaiseEvent MouseMiddleDown(Me, New Point(x, y))
    
                    Case MouseMessages.WM_MBUTTONUP
                        If MiddleClickTime <> 0 Then
                            MiddleClickTime = Environment.TickCount() - MiddleClickTime
                        End If
    
                        If (MiddleClickTime <> 0) AndAlso (MiddleClickTime < NativeMethods.GetDoubleClickTime()) Then
                            MiddleClickTime = 0
                            If Not Me.SuppressMouseUpEventWhenDoubleClick Then
                                RaiseEvent MouseMiddleUp(Me, New Point(x, y))
                            End If
                            RaiseEvent MouseMiddleDoubleClick(Me, New Point(x, y))
    
                        Else
                            MiddleClickTime = Environment.TickCount()
                            RaiseEvent MouseMiddleUp(Me, New Point(x, y))
    
                        End If
    
                    Case MouseMessages.WM_MOUSEWHEEL
                        RaiseEvent MouseWheel(Me, New Point(x, y), If(MouseStruct.mouseData < 0,
                                                                     WheelDirection.WheelDown,
                                                                     WheelDirection.WheelUp))
    
                    Case Else
                        Exit Select ' Do Nothing
    
                End Select
    
                Return 0
    
            ElseIf nCode < 0I Then
                Return NativeMethods.CallNextHookEx(MouseHook, nCode, wParam, lParam)
    
            Else ' nCode > 0
                Return -1I
    
            End If
    
        End Function
    
    #End Region
    
    #Region " Hidden Methods "
    
        ''' <summary>
        ''' Serves as a hash function for a particular type.
        ''' </summary>
        <EditorBrowsable(EditorBrowsableState.Never)>
        Public Shadows Sub GetHashCode()
        End Sub
    
        ''' <summary>
        ''' Gets the System.Type of the current instance.
        ''' </summary>
        ''' <returns>The exact runtime type of the current instance.</returns>
        <EditorBrowsable(EditorBrowsableState.Never)>
        Public Shadows Function [GetType]()
            Return Me.GetType
        End Function
    
        ''' <summary>
        ''' Determines whether the specified System.Object instances are considered equal.
        ''' </summary>
        <EditorBrowsable(EditorBrowsableState.Never)>
        Public Shadows Sub Equals()
        End Sub
    
        ''' <summary>
        ''' Determines whether the specified System.Object instances are the same instance.
        ''' </summary>
        <EditorBrowsable(EditorBrowsableState.Never)>
        Private Shadows Sub ReferenceEquals()
        End Sub
    
        ''' <summary>
        ''' Returns a String that represents the current object.
        ''' </summary>
        <EditorBrowsable(EditorBrowsableState.Never)>
        Public Shadows Sub ToString()
        End Sub
    
    #End Region
    
    #Region "IDisposable Support"
    
        ''' <summary>
        ''' Flag to detect redundant calls at <see cref="Dispose"/> method.
        ''' </summary>
        Private disposedValue As Boolean
    
        ''' <summary>
        ''' Releases unmanaged and optionally managed resources.
        ''' </summary>
        ''' <param name="disposing">
        ''' <c>true</c> to release both managed and unmanaged resources; 
        ''' <c>false</c> to release only unmanaged resources.
        ''' </param>
        Protected Sub Dispose(ByVal disposing As Boolean)
    
            If Not Me.disposedValue Then
    
                If disposing Then ' Dispose managed state (managed objects).
    
                Else ' Free unmanaged resources (unmanaged objects).
                    NativeMethods.UnhookWindowsHookEx(Me.MouseHook)
    
                End If
    
            End If
    
            Me.disposedValue = True
    
        End Sub
    
        ''' <summary>
        ''' Allows an object to try to free resources
        ''' and perform other cleanup operations before it is reclaimed by garbage collection.
        ''' </summary>
        Protected Overrides Sub Finalize()
    
            ' Do not change this code. Put cleanup code in method: Dispose(ByVal disposing As Boolean)
    
            Me.Dispose(disposing:=False)
            MyBase.Finalize()
    
        End Sub
    
        ''' <summary>
        ''' Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        ''' </summary>
        Private Sub Dispose() Implements IDisposable.Dispose
    
            ' Do not change this code. Put cleanup code in method: Dispose(ByVal disposing As Boolean)
    
            Me.Dispose(disposing:=True)
            GC.SuppressFinalize(obj:=Me)
    
        End Sub
    
    #End Region
    
    End Class
    
    #End Region
    

    【讨论】:

    • 看,我不会进行 API 调用 Friend 并允许从订户调用它们(我在答案中留下了它们,因为这就是你的方式他们)——甚至作为一个内部类。 NativeMethods 类的部分目的是将所有令人讨厌的旧 API 东西隔离在一个地方。例如,请参阅this answer。尤其是 CallNextHookEx 应该/只能在本地使用。
    • 没问题,我承认我没有对其他问题的 p/invokes 执行分析代码,我会对此进行说明,谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-24
    • 2011-05-02
    • 2010-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多