2013-12-16 2 views
0

런타임에 사용자 지정 컨트롤에서 wndproc 주소를 성공적으로 변경했습니다. 하지만 내 문제는 디자인 타임에 이것을 구현하려고 할 때 발생합니다. 컨트롤을 Visual Studio (2008) 양식에 놓으면 충돌이 발생합니다. (주춤 5.0 CF 2.0)wndproc의 디자인 타임 변경으로 인해 Visual Studio가 손상됩니다.

의 Win32 공간

(실시 예 만)

Friend Class NativeMethods 

    Friend Shared ReadOnly Property IsDesignTime() As Boolean 

    Friend Shared ReadOnly Property IsRunTime() As Boolean 

    Friend Shared Function GetWindowLong(ByVal hWnd As IntPtr, ByVal nIndex As Integer) As Integer 
     If (NativeMethods.IsDesignTime) Then 
      Return user32.GetWindowLong(hWnd, nIndex) 
     ElseIf (NativeMethods.IsRunTime) Then 
      Return coredll.GetWindowLong(hWnd, nIndex) 
     Else 
      Throw New Exception() 
     End If 
    End Function 

    Private Class coredll 
     <DllImport(DLLNAME, EntryPoint:="GetWindowLongW", CharSet:=CharSet.Auto, SetLastError:=True)> _ 
     Friend Shared Function GetWindowLong(ByVal hWnd As IntPtr, ByVal nIndex As Integer) As Integer 
     End Function 
     Friend Const DLLNAME As String = "coredll.dll" 
    End Class 

    Private Class user32 
     <DllImport(DLLNAME, CharSet:=CharSet.Auto, SetLastError:=True)> _ 
     Friend Shared Function GetWindowLong(ByVal hWnd As IntPtr, ByVal nIndex As Integer) As Integer 
     End Function 
     Friend Const DLLNAME As String = "user32.dll" 
    End Class 

End Class 

IsDesignTime IsRunTime 및 복귀 모두 올바른 값.

마지막으로
Public Class SystemControl 
    Inherits System.Windows.Forms.Control 

    Friend Sub New() 
     Me.m_borderStyle = Windows.Forms.BorderStyle.None 
     Me.m_isDisposed = False 
     Me.m_isHandleCreated = False 
    End Sub 

    Public Property BorderStyle() As BorderStyle 
     Get 
      Return Me.m_borderStyle 
     End Get 
     Set(ByVal value As BorderStyle) 
      If (value <> Me.m_borderStyle) Then 
       Me.m_borderStyle = value 
       Me.UpdateStyles() 
      End If 
     End Set 
    End Property 

    Public ReadOnly Property DesignMode() As Boolean 
     Get 
      Return (((Not Me.Site Is Nothing) AndAlso Me.Site.DesignMode) OrElse StaticMethods.IsDesignMode(MyBase.Parent)) 
     End Get 
    End Property 

    Public ReadOnly Property IsDisposed() As Boolean 
     Get 
      Return Me.m_isDisposed 
     End Get 
    End Property 

    Public ReadOnly Property IsHandleCreated() As Boolean 
     Get 
      Return Me.m_isHandleCreated 
     End Get 
    End Property 

    Private Function Callback(ByVal hWnd As IntPtr, ByVal msg As UInteger, ByVal wparam As IntPtr, ByVal lparam As IntPtr) As IntPtr 
     Dim m As Microsoft.WindowsCE.Forms.Message = Microsoft.WindowsCE.Forms.Message.Create(hWnd, CInt(msg), wparam, lparam) 
     Me.WndProc(m) 
     Return m.Result 
    End Function 

    Public Sub DefWndProc(ByRef m As Microsoft.WindowsCE.Forms.Message) 
     If (Me.windowProcHandle = IntPtr.Zero) Then 
      m.Result = Win32.NativeMethods.DefWindowProc(m.HWnd, CUInt(m.Msg), m.WParam, m.LParam) 
     Else 
      m.Result = Win32.NativeMethods.CallWindowProc(Me.windowProcHandle, m.HWnd, CUInt(m.Msg), m.WParam, m.LParam) 
     End If 
    End Sub 

    Protected Overrides Sub Dispose(ByVal disposing As Boolean) 
     Me.UnhookWindoProc() 
     Me.m_isDisposed = True 
     MyBase.Dispose(disposing) 
    End Sub 

    Private Sub HookWindowProc() 
     Me.UnhookWindoProc() 
     Dim hWnd As IntPtr = MyBase.Handle 
     If (hWnd <> IntPtr.Zero) Then 
      Dim [error] As Exception = Nothing 
      SyncLock Me 
       Me.windowProcHandle = Win32.NativeMethods.GetWindowLongPtr(hWnd, Win32.GWL.WNDPROC) 
       If (Me.windowProcHandle <> IntPtr.Zero) Then 
        Try 
         Me.windowProcDelegate = New Win32.WindowProc(AddressOf Me.Callback) 
         Me.windowProcRoot = GCHandle.Alloc(Me.windowProcDelegate) 
         Win32.NativeMethods.SetWindowLongDlg(hWnd, Win32.GWL.WNDPROC, Me.windowProcDelegate) 
         Me.windowProcHWnd = hWnd 
         Me.windowProcHooked = True 
        Catch ex As Exception 
         Me.windowProcRoot.Free() 
         Me.windowProcDelegate = Nothing 
         Me.windowProcHandle = IntPtr.Zero 
         Me.windowProcHWnd = IntPtr.Zero 
         Me.windowProcHooked = False 
         [error] = ex 
        End Try 
       End If 
      End SyncLock 
      If (Not [error] Is Nothing) Then 
       Throw [error] 
      End If 
     End If 
    End Sub 

    Protected Overrides Sub OnHandleCreated(ByVal e As System.EventArgs) 
     Me.m_isHandleCreated = True 
     MyBase.OnHandleCreated(e) 
    End Sub 

    Protected Overrides Sub OnHandleDestroyed(ByVal e As System.EventArgs) 
     Me.m_isHandleCreated = False 
     MyBase.OnHandleDestroyed(e) 
    End Sub 

    Protected Overrides Sub OnParentChanged(ByVal e As System.EventArgs) 
     Me.UnhookWindoProc() 
     If (Me.DesignMode) Then 
      'TODO: Uncomment to make Visal Studio crash. 
      'Me.HookWindowProc() 
     Else 
      Me.HookWindowProc() 
     End If 
     Me.UpdateStyles() 
     MyBase.OnParentChanged(e) 
    End Sub 

    Private Function ShouldSerializeBorderStyle() As Boolean 
     Return (Me.m_borderStyle <> Windows.Forms.BorderStyle.None) 
    End Function 

    Public Sub UpdateStyles() 
     Dim hWnd As IntPtr = MyBase.Handle 
     If (hWnd <> IntPtr.Zero) Then 

      Dim style As Win32.WS = DirectCast(CUInt(Win32.NativeMethods.GetWindowLong(hWnd, Win32.GWL.STYLE)), Win32.WS) 
      Dim exStyle As Win32.WS_EX = DirectCast(CUInt(Win32.NativeMethods.GetWindowLong(hWnd, Win32.GWL.EXSTYLE)), Win32.WS_EX) 

      style = (style And Not Win32.WS.BORDER) 
      exStyle = (exStyle And Not Win32.WS_EX.CLIENTEDGE) 

      Select Case Me.m_borderStyle 
       Case Windows.Forms.BorderStyle.Fixed3D 
        exStyle = (exStyle Or Win32.WS_EX.CLIENTEDGE) 
        Exit Select 
       Case Windows.Forms.BorderStyle.FixedSingle 
        style = (style Or Win32.WS.BORDER) 
        Exit Select 
       Case Else 
        Exit Select 
      End Select 

      Win32.NativeMethods.SetWindowLong(hWnd, Win32.GWL.STYLE, CInt(style)) 
      Win32.NativeMethods.SetWindowLong(hWnd, Win32.GWL.EXSTYLE, CInt(exStyle)) 
      Win32.NativeMethods.SetWindowPos(hWnd, New IntPtr(Win32.HWND.TOP), 0, 0, 0, 0, (Win32.SWP.DRAWFRAME Or (Win32.SWP.NOACTIVATE Or (Win32.SWP.NOZORDER Or (Win32.SWP.NOMOVE Or Win32.SWP.NOSIZE))))) 

     End If 
    End Sub 

    Private Sub UnhookWindoProc() 
     If (Me.windowProcHooked) Then 
      Dim [error] As Exception = Nothing 
      SyncLock Me 
       Try 
        Win32.NativeMethods.SetWindowLongPtr(Me.windowProcHWnd, Win32.GWL.WNDPROC, Me.windowProcHandle) 
       Catch ex As Exception 
        [error] = ex 
       Finally 
        Me.windowProcRoot.Free() 
        Me.windowProcDelegate = Nothing 
        Me.windowProcHandle = IntPtr.Zero 
        Me.windowProcHooked = False 
       End Try 
      End SyncLock 
      If (Not [error] Is Nothing) Then 
       Throw [error] 
      End If 
     End If 
    End Sub 

    Protected Overridable Sub WndProc(ByRef m As Microsoft.WindowsCE.Forms.Message) 
     Me.DefWndProc(m) 
    End Sub 

    Private windowProcDelegate As Win32.WindowProc 
    Private windowProcHandle As IntPtr 
    Private windowProcHWnd As IntPtr 
    Private windowProcRoot As GCHandle 
    Private windowProcHooked As Boolean 

    Private m_borderStyle As BorderStyle 
    Private m_isDisposed As Boolean 
    Private m_isHandleCreated As Boolean 

End Class 
+0

다시 VS를 시작하여 진단 및 +가 프로세스에 연결 도구로 1 차 인스턴스에 연결합니다. 이것은 위험해야하며 디자이너가 창을 하위 클래스로 분류 할 가능성이 매우 높습니다. –

+0

한번 시도해 보겠습니다. 디자인 타임에 VS에 표시되는 컨트롤은 CF 버전을 사용하고 있지만 사실은 .net 2.0 컨트롤 (PublicKeyToken = b77a5c561934e089)입니다. 윈도우가 호출하는 스레드와 같은 프로세스에 속하지 않으면 "이 속성 (GWL_WNDPROC)을 변경할 수 없다"는 MSDN을 읽었습니다. 그래서 이것이 사실이라면 VS 충돌을 일으킬 것이라고 생각합니까? –

+0

다른 프로세스에서 실행되지 않습니다. 디자인 타임에 데스크톱에 대한 하드 충돌은 거의 항상 StackOverflowException btw입니다. 서브 클래 싱 된 WndProc가 서브 클래 싱 된 WndProc을 다시 호출하는 원래의 WndProc을 호출 할 때 하나를 얻습니다. 윈도우가 두 번 이상 서브 클래스 화되는 경우 드문 일이 아니며 순서가 중요합니다. 따라서 다시 서브 클래 싱을 취소하면 너무 자주 잘못됩니다. –

답변

0

사용자 지정 컨트롤! 나는 그것을 해결했다! SetWindowsHookExWH_CALLWNDPROC을 사용하여 이제 적어도 창 메시지를 추적 할 수 있습니다. 네이티브 호출을 두 개의 네임 스페이스, 즉 Win32WinCE으로 구분했습니다.

사용자 정의 제어

Public Class SystemControl 
    Inherits System.Windows.Forms.Control 

    Public Sub New() 
     If (WinCE.NativeMethods.Invokable) Then 
      'TODO: WinCE = SetWindowLong(hWnd, GWL_WNDPROC, WNDPROC) 
     ElseIf (Win32.NativeMethods.Invokable) Then 
      Me.HookCallWnd32Proc() 
     End If 
    End Sub 

    Protected Overrides Sub Dispose(ByVal disposing As Boolean) 
     Me.UnhookCallWnd32Proc() 
     MyBase.Dispose(disposing) 
    End Sub 

    Private Function CallWnd32ProcCallback(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr 
     SyncLock Me 
      If ((nCode >= 0) AndAlso (lParam <> IntPtr.Zero)) Then 
       Dim cwp As Win32.CWPSTRUCT = New Win32.CWPSTRUCT() 
       Marshal.PtrToStructure(lParam, cwp) 
       If (cwp.hWnd = Me.Handle) Then 
        Select Case cwp.message 
         Case Win32.WM.MOUSEMOVE 
          'Do something... 
          Exit Select 
         Case Win32.WM.LBUTTONDOWN 
          'Do something... 
          Exit Select 
         Case Else 
          Exit Select 
        End Select 
       End If 
       Marshal.StructureToPtr(cwp, lParam, True) 
      End If 
      Return Win32.NativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam) 
     End SyncLock 
    End Function 

    Private Sub HookCallWnd32Proc() 
     SyncLock Me 
      If (Me._callWndProcHandle = IntPtr.Zero) Then 
       Dim threadID As Integer = Win32.NativeMethods.GetCurrentThreadId() 
       Dim proc As New Win32.HookProc(AddressOf Me.CallWnd32ProcCallback) 
       Dim hHook As IntPtr = IntPtr.Zero 
       Me._callWndProcRoot = GCHandle.Alloc(proc) 
       hHook = Win32.NativeMethods.SetWindowsHookEx(Win32.WH.CALLWNDPROC, proc, IntPtr.Zero, threadID) 
       If (hHook <> IntPtr.Zero) Then 
        Me._callWndProcHandle = hHook 
        Me._callWndProcHooked = True 
       Else 
        Me._callWndProcRoot.Free() 
       End If 
      End If 
     End SyncLock 
    End Sub 

    Private Sub UnhookCallWnd32Proc() 
     SyncLock Me 
      If (Me._callWndProcHandle <> IntPtr.Zero) Then 
       Win32.NativeMethods.UnhookWindowsHookEx(Me._callWndProcHandle) 
       Me._callWndProcRoot.Free() 
       Me._callWndProcHandle = IntPtr.Zero 
       Me._callWndProcHooked = True 
      End If 
     End SyncLock 
    End Sub 

    Private _callWndProcHandle As IntPtr 
    Private _callWndProcHooked As Boolean 
    Private _callWndProcRoot As GCHandle 

End Class 

는 Win32 네임 스페이스

Friend NotInheritable Class NativeMethods 

    Shared Sub New() 
    End Sub 

    Private Sub New() 
    End Sub 

    Public Shared ReadOnly Property Assembly() As System.Reflection.Assembly 
     Get 
      If (NativeMethods.m_assembly Is Nothing) Then 
       NativeMethods.m_assembly = GetType(Integer).Assembly 
      End If 
      Return NativeMethods.m_assembly 
     End Get 
    End Property 

    Public Shared ReadOnly Property Invokable() As Boolean 
     Get 
      If (Not NativeMethods.m_invokable.HasValue) Then 
       Dim assembly As System.Reflection.Assembly = NativeMethods.Assembly 
       NativeMethods.m_invokable = New Boolean?((Not [assembly] Is Nothing) AndAlso [assembly].FullName.ToUpper.EndsWith("B77A5C561934E089")) 
      End If 
      Return NativeMethods.m_invokable.Value 
     End Get 
    End Property 

    <DllImport(user32, CharSet:=CharSet.Auto, SetLastError:=True)> _ 
    Friend Shared Function CallNextHookEx(ByVal hhk As IntPtr, ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr 
    End Function 

    <DllImport(user32, CharSet:=CharSet.Auto, SetLastError:=True)> _ 
    Friend Shared Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, <Out()> ByRef processId As UInteger) As UInteger 
    End Function 

    <DllImport(user32, CharSet:=CharSet.Auto, SetLastError:=True)> _ 
    Friend Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal lpfn As HookProc, ByVal hmod As IntPtr, ByVal dwThreadId As Integer) As IntPtr 
    End Function 

    <DllImport(user32, CharSet:=CharSet.Auto, SetLastError:=True)> _ 
    Friend Shared Function UnhookWindowsHookEx(ByVal hhk As IntPtr) As Boolean 
    End Function 

    Private Shared m_assembly As System.Reflection.Assembly 
    Private Shared m_invokable As Boolean? 

    Public Const user32 As String = "user32.dll" 
    Public Const kernel32 As String = "kernel32.dll" 
    Public Const ComCtl32 As String = "comctl32.dll" 

End Class 

'It's important that this a `Class` and NOT a `Structure`, orelse it will cause VS to crash. 
<StructLayout(LayoutKind.Sequential)> _ 
Friend Class CWPSTRUCT 
    Public lParam As IntPtr 
    Public wParam As IntPtr 
    Public message As UInteger 
    Public hWnd As IntPtr 
End Class 

Friend Enum WH As Integer 
    MSGFILTER = -1 
    JOURNALRECORD = 0 
    JOURNALPLAYBACK = 1 
    KEYBOARD = 2 
    GETMESSAGE = 3 
    CALLWNDPROC = 4 
    CBT = 5 
    SYSMSGFILTER = 6 
    MOUSE = 7 
    DEBUG = 9 
    SHELL = 10 
    FOREGROUNDIDLE = 11 
    CALLWNDPROCRET = 12 
    KEYBOARD_LL = 13 
    MOUSE_LL = 14 
End Enum