【问题标题】:Stop ActiveX control from giving focus to its form on MouseMove阻止 ActiveX 控件将焦点放在 MouseMove 上的窗体上
【发布时间】:2013-11-21 00:44:58
【问题描述】:

我在表单上使用了许多第 3 方 ActiveX 控件。我的应用程序有多个表单,并且说这些 ActiveX 控件位于 myAxHostingForm 上。将鼠标移到某些控件上会获得 myAxHostingForm 焦点。我想阻止它。

我尝试了一个空的事件处理程序

For Each c In Me.ChildControls(Of AxHost)() ' custom extension method returning controls of type provided
    AddHandler c.MouseMove,
        Sub(s As Object, m As MouseEventArgs)
        End Sub
Next

我得到以下异常:

System.NotSupportedException was caught
  HResult=-2146233067
  Message=Event MouseMove is not valid on this ActiveX control.
  Source=System.Windows.Forms
  StackTrace:
       at System.Windows.Forms.AxHost.add_MouseMove(MouseEventHandler value)
       at <my source code file>

我希望对 .NET 中的 ActiveX 托管有所了解的人可以帮助理解这个错误,并可能解决这个烦人的问题。

编辑: 尝试@Hans 方法,

<ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000114-0000-0000-C000-000000000046")>
Interface IOleWindow
    <PreserveSig>
    Function GetWindow(ByRef hwnd As IntPtr) As Int32
    Sub ContextSensitiveHelp(ByVal fEnterMode As Int32)
End Interface

Class ActiveXWindow
    Inherits NativeWindow
    Protected Overrides Sub WndProc(ByRef m As Message)
        System.Diagnostics.Debug.WriteLine(m)
        If (m.Msg = &H200) Then Return
        MyBase.WndProc(m)
    End Sub
End Class

这是在我的表单加载中:

Dim itf = CType(CCDimage1.GetOcx, IOleWindow)
Dim hWnd As IntPtr
Dim hr As Integer = itf.GetWindow(hWnd)
If hr <> 0 Or hWnd = IntPtr.Zero Then Throw New Exception("Could not find handle for DataRay window")
Dim wrapper = New ActiveXWindow()
wrapper.AssignHandle(hWnd)

我在第一行得到了异常:

System.InvalidCastException was caught
  HResult=-2147467262
  Message=Unable to cast COM object of type 'System.__ComObject' to interface type 'Instruments.IOleWindow'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{00000114-0000-0000-C000-000000000046}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
  Source=Instruments

【问题讨论】:

    标签: .net vb.net winforms activex


    【解决方案1】:

    这是您可以尝试使用 IMessageFilter 的另一种方法:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.Load += Form1_Load;
        }
    
        void Form1_Load(object sender, EventArgs e)
        {
            // list out your 3rd party ActiveX controls here:
            Control[] controls = new Control[] { 
                this.axWindowsMediaPlayer1, 
                this.axWindowsMediaPlayer2 
            };
            Application.AddMessageFilter(new MouseMoveFilter(controls));
        }
    }
    
    public class MouseMoveFilter : IMessageFilter
    {
        private const int WM_MOUSEMOVE = 0x200;
    
        private List<IntPtr> ControlHandles = new List<IntPtr>();
    
        public MouseMoveFilter(Control[] controls)
        {
            foreach(Control ctl in controls)
            {
                this.ControlHandles.Add(ctl.Handle);
            }
        }
    
        public bool PreFilterMessage(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_MOUSEMOVE:
                    if (this.ControlHandles.Contains(m.HWnd))
                    {
                        return true;
                    }
                    break;
            }
            return false;
        }
    }
    

    【讨论】:

    • 我试过这个方法,传递给 PreFilterMessage 的句柄与从 FormLoad 传递给构造函数的句柄不同
    • 嗯..你可以尝试切换它并自己传递控件,然后在 PreFilterMessage() 中获取当前的 Handle()?
    • 嗯,m.HWnd 和我之前存储的控件的句柄还是不一样的。也许这与 Hans 关于本机窗口与 AxHost 包装器窗口上的控件句柄的说法有关
    • 这很有可能。抱歉,它不适用于您的情况。
    【解决方案2】:

    是的,这行不通。 ActiveX 控件创建自己的窗口,并且可以在他们认为合适的时候参与其中。与你自己的应用程序混在一起,它是一个很好的恶意软件注入载体。他们本应表现得很好,并与主人一起工作,但经常偷工减料。

    您唯一能做的就是对他们的窗口进行子类化,这样您就可以先了解他们的消息。您需要的第一件事是获取他们的窗口句柄,这需要使用 IOleWindow::GetWindow() 方法。你需要这个声明:

        [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000114-0000-0000-C000-000000000046")]
        interface IOleWindow {
            [PreserveSig]
            int GetWindow(out IntPtr hwnd);
            void ContextSensitiveHelp(int fEnterMode);
        }
    

    然后您需要从 NativeWindow 派生您自己的类,以便您可以覆盖 WndProc() 方法并检测消息。一个简单的过滤 WM_MOUSEMOVE:

        class ActiveXWindow : NativeWindow {
            protected override void WndProc(ref Message m) {
                System.Diagnostics.Debug.WriteLine(m);
                if (m.Msg == 0x200) return;
                base.WndProc(ref m);
            }
        }
    

    那么你需要把它放在适当的位置。您必须等到创建本机窗口,表单的 Load 事件通常是正确的时间。我使用 Windows Media Player 测试了代码:

        private void Form1_Load(object sender, EventArgs e) {
            var itf = (IOleWindow)axWindowsMediaPlayer1.GetOcx();
            IntPtr hWnd;
            int hr = itf.GetWindow(out hWnd);
            if (hr != 0 || hWnd == IntPtr.Zero) throw new Exception("Oh, no");
            var wrapper = new ActiveXWindow();
            wrapper.AssignHandle(hWnd);
        }
    

    【讨论】:

    • 嘿汉斯(我不是最初的提问者),为什么我们不能使用axWindowsMediaPlayer1.Handle?它似乎对我使用 IMessageFilter 捕获和抑制 WM_MOUSEMOVE 有用。
    • 因为那是 AxHost 包装器窗口句柄,而不是它内部的本机窗口,它是由 ActiveX 控件创建的。 IMessageFilter 确实适用于消息队列中的消息,例如 WM_MOUSEMOVE,因为它仍然是分配消息的 Winforms 消息循环。它不适用于已发送的消息,只有子类可以做到这一点。
    • 明白了!...有道理。感谢您的澄清。
    • 当我创建一个带有 Ax 控件的表单的新实例时,我得到了这个异常:(请参阅我编辑的问题)
    • 这是表单构造函数中引发的异常。距离我建议您放置代码的 Load 事件一百万英里。和这个答案没有任何关系。嘎嘎喜欢剪切/粘贴问题。首先使表单在没有代码的情况下工作。然后添加它。
    【解决方案3】:

    本机窗口上的 ActiveX 控件具有与在 Form_Load 中看到的不同的句柄。所以我无法明确地忽略鼠标在它们上的移动。但是,它们是我想在我的应用程序中忽略鼠标移动的唯一控件,它们都具有Control.FromHandle(m.HWnd) Is Nothing = true 的特征。感谢@Idle_Mind 引导我采用这种方法。

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Application.AddMessageFilter(New MouseMoveFilter())
    End Sub
    

    ...

    Public Class MouseMoveFilter
        Implements IMessageFilter
        Private Const WM_MOUSEMOVE As Int32 = &H200
        Public Function PreFilterMessage(ByRef m As Message) As Boolean Implements IMessageFilter.PreFilterMessage
            Select Case m.Msg
                Case WM_MOUSEMOVE
                    Return Control.FromHandle(m.HWnd) Is Nothing 
            End Select
            Return False
        End Function
    End Class
    

    【讨论】:

      猜你喜欢
      • 2012-11-02
      • 1970-01-01
      • 1970-01-01
      • 2014-11-22
      • 2011-02-12
      • 2013-05-31
      • 2011-09-28
      • 1970-01-01
      • 2010-10-15
      相关资源
      最近更新 更多