阻止ActiveX控件在MouseMove上将焦点放在其窗体上

时间:2013-11-18 16:57:57

标签: .net vb.net winforms activex

我在表单上使用了许多第三方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

3 个答案:

答案 0 :(得分:3)

这是您可以尝试使用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;
    }
}

答案 1 :(得分:1)

是的,那不起作用。 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);
    }

答案 2 :(得分:1)

本机窗口上的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