Reputation: 20474
I've written a class to perform system-wide hotkeys operations and this is the only constructor:
''' <summary>
''' Creates a new system-wide hotkey.
''' </summary>
''' <param name="Modifier">The keys that must be pressed in combination with the specified key.</param>
''' <param name="Key">The key used for the hotkey.</param>
''' <exception cref="IsRegisteredException"></exception>
<DebuggerStepperBoundary()>
Public Sub New(ByVal Modifier As HotkeyModifier, ByVal Key As Keys)
Me.CreateHandle(New CreateParams)
Me.PressEventArgs.ID = GetHashCode()
Me.PressEventArgs.Key = Key
Me.PressEventArgs.Modifier = Modifier
If Not NativeMethods.RegisterHotKey(Me.Handle,
Me.PressEventArgs.ID,
Me.PressEventArgs.Modifier,
Me.PressEventArgs.Key) Then
Throw New IsRegisteredException
End If
End Sub
And a code analysis on the project says this:
CA2214 Do not call overridable methods in constructors 'GlobalHotkey.New(GlobalHotkey.HotkeyModifier, Keys)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences:
GlobalHotkey..ctor(GlobalHotkey+HotkeyModifier, Keys) NativeWindow.CreateHandle(CreateParams):Void Object.GetHashCode():Int32 Color .NET GlobalHorkey.vb 213
PS: The line 213 is this:
Public Sub New(ByVal Modifier As HotkeyModifier, ByVal Key As Keys)
What I need to do to avoid this conflict?
UPDATE
Into the NativeMethods class... :
<DllImport("user32.dll", SetLastError:=True)>
Public Shared Function RegisterHotKey(
ByVal hWnd As IntPtr,
ByVal id As Integer,
ByVal fsModifiers As UInteger,
ByVal vk As UInteger
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
UPDATE 2
This is the full Class:
#Region " Usage Examples "
'Public Class Form1
' ''' <summary>
' ''' Stores the system-wide hotkey object.
' ''' </summary>
' Private WithEvents Hotkey As GlobalHotkey = Nothing
' ''' <summary>
' ''' Initializes a new instance of the <see cref="Form1"/> class.
' ''' </summary>
' Public Sub New()
' InitializeComponent()
' ' Registers a new global hotkey on the system. (Alt + Ctrl + A)
' Hotkey = New GlobalHotkey(GlobalHotkey.HotkeyModifier.Alt Or GlobalHotkey.HotkeyModifier.Ctrl, Keys.A)
' ' Replaces the current registered hotkey with a new global hotkey on the system. (Alt + Escape)
' Hotkey = New GlobalHotkey([Enum].Parse(GetType(GlobalHotkey.HotkeyModifier), "Alt", True),
' [Enum].Parse(GetType(Keys), "Escape", True))
' End Sub
' ''' <summary>
' ''' Handles the Press event of the HotKey control.
' ''' </summary>
' Private Sub HotKey_Press(ByVal sender As Object, ByVal e As GlobalHotkey.HotKeyEventArgs) _
' Handles Hotkey.Press
' MsgBox(e.ID)
' MsgBox(e.Key.ToString)
' MsgBox(e.Modifier.ToString)
' End Sub
'End Class
#End Region
#Region " Imports "
Imports System.Runtime.InteropServices
#End Region
#Region " Global Hotkey "
''' <summary>
''' Class to perform system-wide hotkey operations.
''' </summary>
Class GlobalHotkey
Inherits NativeWindow
Implements IDisposable
#Region " API "
''' <summary>
''' Native API Methods.
''' </summary>
Public Class NativeMethods
''' <summary>
''' Defines a system-wide hotkey.
''' </summary>
''' <param name="hWnd">The hWND.</param>
''' <param name="id">The identifier of the hotkey.
''' If the hWnd parameter is NULL, then the hotkey is associated with the current thread rather than with a particular window.
''' If a hotkey already exists with the same hWnd and id parameters.</param>
''' <param name="fsModifiers">The keys that must be pressed in combination with the key specified by the uVirtKey parameter
''' in order to generate the WM_HOTKEY message.
''' The fsModifiers parameter can be a combination of the following values.</param>
''' <param name="vk">The virtual-key code of the hotkey.</param>
''' <returns>
''' <c>true</c> if the function succeeds, otherwise <c>false</c>
''' </returns>
<DllImport("user32.dll", SetLastError:=True)>
Public Shared Function RegisterHotKey(
ByVal hWnd As IntPtr,
ByVal id As Integer,
ByVal fsModifiers As UInteger,
ByVal vk As UInteger
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
''' <summary>
''' Unregisters a hotkey previously registered.
''' </summary>
''' <param name="hWnd">The hWND.</param>
''' <param name="id">The identifier of the hotkey to be unregistered.</param>
''' <returns>
''' <c>true</c> if the function succeeds, otherwise <c>false</c>
''' </returns>
<DllImport("user32.dll", SetLastError:=True)>
Public Shared Function UnregisterHotKey(
ByVal hWnd As IntPtr,
ByVal id As Integer
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
End Class
#End Region
#Region " Members "
''' <summary>
''' The hotkey modifier used in combination with the key.
''' </summary>
<Flags>
Public Enum HotkeyModifier As Integer
''' <summary>
''' The none
''' </summary>
None = &H0
''' <summary>
''' The alt key
''' </summary>
Alt = &H1
''' <summary>
''' The control key
''' </summary>
Ctrl = &H2
''' <summary>
''' The shift key
''' </summary>
Shift = &H4
''' <summary>
''' The win key
''' </summary>
Win = &H8
End Enum
''' <summary>
''' Event that is raised when a hotkey is pressed.
''' </summary>
Public Event Press As EventHandler(Of HotKeyEventArgs)
''' <summary>
''' Stores the Press Event Arguments.
''' </summary>
Protected PressEventArgs As New HotKeyEventArgs
''' <summary>
''' Event arguments for the Press event.
''' </summary>
Public Class HotKeyEventArgs : Inherits EventArgs
''' <summary>
''' The identifier of the hotkey.
''' </summary>
''' <value>The identifier.</value>
Public Property ID As Integer
''' <summary>
''' The key used for the hotkey.
''' </summary>
''' <value>The key.</value>
Public Property Key As Keys
''' <summary>
''' The key modifier used for the hotkey.
''' </summary>
''' <value>The modifier.</value>
Public Property Modifier As HotkeyModifier
End Class
''' <summary>
''' Exception that is thrown when a hotkey is already registered.
''' </summary>
<Serializable>
Public Class IsRegisteredException : Inherits Exception
''' <summary>
''' Initializes a new instance of the <see cref="IsRegisteredException"/> class.
''' </summary>
Sub New()
MyBase.New("Hotkey combination is already in use.")
End Sub
End Class
#End Region
#Region " Constructor "
''' <summary>
''' Creates a new system-wide hotkey.
''' </summary>
''' <param name="Modifier">The keys that must be pressed in combination with the specified key.</param>
''' <param name="Key">The key used for the hotkey.</param>
''' <exception cref="IsRegisteredException"></exception>
<DebuggerStepperBoundary()>
Public Sub New(ByVal Modifier As HotkeyModifier, ByVal Key As Keys)
Me.CreateHandle(New CreateParams)
Me.PressEventArgs.ID = GetHashCode()
Me.PressEventArgs.Key = Key
Me.PressEventArgs.Modifier = Modifier
If Not NativeMethods.RegisterHotKey(Me.Handle,
Me.PressEventArgs.ID,
Me.PressEventArgs.Modifier,
Me.PressEventArgs.Key) Then
Throw New IsRegisteredException
End If
End Sub
#End Region
#Region " WndProc "
''' <summary>
''' Invokes the default window procedure associated with this window.
''' </summary>
''' <param name="m">
''' A <see cref="T:System.Windows.Forms.Message" /> that is associated with the current Windows message.
''' </param>
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = 786 Then
RaiseEvent Press(Me, Me.PressEventArgs)
Else
MyBase.WndProc(m)
End If
End Sub
#End Region
#Region " IDisposable "
''' <summary>
''' To detect redundant calls when disposing.
''' </summary>
Private IsDisposed As Boolean = False
''' <summary>
''' Prevent calls to methods after disposing.
''' </summary>
''' <exception cref="System.ObjectDisposedException"></exception>
Private Sub DisposedCheck()
If Me.IsDisposed Then
Throw New ObjectDisposedException(Me.GetType().FullName)
End If
End Sub
''' <summary>
''' Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
''' </summary>
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
' IDisposable
''' <summary>
''' Releases unmanaged and - optionally - managed resources.
''' </summary>
''' <param name="IsDisposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
Protected Overridable Sub Dispose(IsDisposing As Boolean)
If Not Me.IsDisposed Then
If IsDisposing Then
NativeMethods.UnregisterHotKey(Me.Handle, Me.PressEventArgs.ID)
End If
End If
Me.IsDisposed = True
End Sub
#End Region
End Class
#End Region
Upvotes: 2
Views: 2293
Reputation: 151674
Your class inherits from NativeWindow
, and its CreateHandle()
method is virtual. This is the cause for the warning you get.
If you're sure you need to call this method and there's no other way of doing so, I think you can create a class that overrides and seals it:
public class SealedNativeWindow : NativeWindow
{
public override sealed void CreateHandle(CreateParams cp)
{
base.CreateHandle(CreateParams cp);
}
}
Then let your class inherit from this one (you'll have to convert it to VB.NET).
Or you can just ignore the warning.
Upvotes: 4
Reputation: 31443
CreateHandle
is virtual and any subclass of your own can override it, making object construction uncontrolled. See : the documentation for an example.
VB equivalent of CodeCaster's solution (which is correct) to seal the method
Public NotOverridable Overrides Sub _
CreateHandle(cp As System.Windows.Forms.CreateParams)
MyBase.CreateHandle(cp)
End Sub
Upvotes: 2