C#异步可插入协议包装默认HTTP协议抛出InvalidCastException的协议、抛出、HTTP、InvalidCastException

2023-09-03 05:33:33 作者:被泪淋湿的记忆

在为了能够过滤的URL中取出(包括JS,图像等)与C#web浏览器(的WinForms),唯一的可收纳的选择似乎是一个异步插入协议包裹的HTTP(以及后来的其他人也)。不幸的是这未能与 InvalidCastException的抛出原厂原协议执行几个电话后,< - 这也是怪异的一部分,它似乎成功前几次失败的。

In order to be able to filter URLs fetched (including JS, images, etc.) with the C# web browser (WinForms), the only containable option seems to be a Asynchronous Pluggable Protocol wrapping HTTP (and later on others as well). Unfortunately this fails with an InvalidCastException thrown by the original original protocol implementation after several calls <- this is also the weird part, it seems to succeed several times before failing.

现在一些code:

第一工厂的协议注册并附:

Firstly the factory for the protocol is registered and attached:

  var ep = new FilteredHttpProtocolFactory();
  Guid id = Guid.Parse ("E00957BD-D0E1-4eb9-A025-7743FDC8B27B");
  session.RegisterNameSpace (ep, ref id, "http", 0, null, 0);

(工厂:)

[Guid ("EF474615-8079-4CFA-B114-6D1D28634DD8")]
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.None)]
public class FilteredHttpProtocolFactory : IClassFactory
{
  public void CreateInstance (object pUnkOuter, Guid riid, out object ppvObject)
  {
    ppvObject = new FilteredHttpProtocol();
  }

  public void LockServer (bool fLock)
  {
  }
}

这是IE使用原来的HTTP协议,在使用,而不是包装它只是正常工作:

This is the original HTTP protocol used by IE, when using it instead of the wrapper it works just fine:

[ComImport]
[Guid ("79eac9e2-baf9-11ce-8c82-00aa004ba90b")]
public class OriginalHttpHandler
{
}

这是包装本身:

[Guid ("E00957BD-D0E1-4eb9-A025-7743FDC8B27B")]
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.None)]
[AsyncProtocol (Name = "http2", Description = "blah")]
public class FilteredHttpProtocol : IInternetProtocol, IInternetProtocolRoot
{ 
private readonly IInternetProtocol _wrapped;

public FilteredHttpProtocol ()
{
  var originalHttpHandler = new OriginalHttpHandler();
  _wrapped = (IInternetProtocol) originalHttpHandler;
}

public void Start (string szURL, IInternetProtocolSink Sink, IInternetBindInfo pOIBindInfo, uint grfPI, uint dwReserved)
{
  _wrapped.Start (szURL, Sink, pOIBindInfo, grfPI, dwReserved);
}

public void Continue (ref _tagPROTOCOLDATA pProtocolData)
{
  _wrapped.Continue (ref pProtocolData);      // <- FAILS HERE
}
   // .... other methods from IInternetProtocol
    public uint Read (IntPtr pv, uint cb, out uint pcbRead)
    {
      return _wrapped.Read (pv, cb, out pcbRead); // <- OR HERE
    }
}

因此​​,怪异的部分是,调用构造函数,开始()被调用,即使阅读()继续(),直到整个事情失败多次调用(无论是与阅读()继续())时,页面的某些部分已经初见端倪(),但似乎主要是一个特定的图像丢失(主要是)!

So, the weird part is, that the constructor is called, Start() is called, even Read() and Continue() is called several times until the whole thing fails (either with Read() or Continue()) when parts of the page are already visible (!), but it seems that mostly one specific image is missing (mostly!) :

Unable to cast COM object of type 'Clients.Windows.Protocol.OriginalHttpHandler' 
to interface type 'Clients.Windows.Protocol.IInternetProtocol'. This operation 
failed because the QueryInterface call on the COM component for the interface 
with IID '{79EAC9E4-BAF9-11CE-8C82-00AA004BA90B}' failed due to the following 
error: No such interface supported (Exception from HRESULT: 0x80004002 E_NOINTERFACE)).

眼看我已经把对象到所述接口数倍(这将导致一个的QueryInterface()调用每一个时间,它被称为(通过验证断点等)等前几次失败这个错误实在令人费解。通过看裁判计数我已经排除了该对象被设置得太早(没有意义呢)。

Seeing that I already cast the object to said interface several times (which should result in a QueryInterface() call every time, and that it has been called (verified through breakpoints and such) several times before it fails this error is really puzzling. By looking at the ref counts I already ruled out the object being disposed too early (wouldn't make sense anyway).

我已经试过几件事情:

在谷歌,但应用程序是相当罕见 http://msdn.microsoft.com /en-us/library/aa767916(v=vs.85).aspx 传承ComImport'ed对象 - 那么我的实现被忽略 看裁判计数 的各种铸件 检查的GUID和接口错误 问同事 Google but APPs are fairly rare http://msdn.microsoft.com/en-us/library/aa767916(v=vs.85).aspx Inheriting the ComImport'ed object - then my implementation gets ignored Look at the ref counts Casts of all sorts Check GUIDs and interfaces for mistakes Asking colleagues

基本上,我想实现的是包默认的HTTP协议实现的IE浏览器的过滤掉的网址,包括那些资源从恢复。我也满意合适的替代品,但他们必须与GPLv2许可,部署与浏览器应用程序标准,并且未更改系统(即没有代理)。

Basically what I am trying to achieve is to wrap the default http protocol implementation of the IE to filter out URLs, including those where resources are retrieved from. I would also be satisfied with suitable alternatives, but they have to be compliant with GPLv2, deployable with the browser application, and do NO changes to rest of the system (i.e. no proxies).

感谢您的帮助;)

顺便说一句,这将是我的硕士论文的一部分,在这里: HTTP://desktopgap.$c$ cplex.com

btw, this is going to be part of my Master's Thesis here: http://desktopgap.codeplex.com

推荐答案

试验COM和阅读各种事情,我找到了解决方案,在互联网上经过一天的。

After a day of experimenting COM and reading all sorts of things on the Internet I found the solution.

由于西蒙Mourier 指出,这是一个线程问题: FilteredHttpProtocol 我创建支持STA和MTA与COM(= C#默认行为),其默认的HTTP协议的对象不和时调用来自不同的线程的方法(这显然会发生),则封送处理失败(也指出出这里)抛出E_NOINTERFACE。

As Simon Mourier pointed out it was a threading issue: The FilteredHttpProtocol I created supports STA and MTA with COM (= C# default behavior), which the default Http Protocol object doesn't and upon calling a method from a different thread (which apparently happens), the Marshaling fails (also pointed out here) throwing E_NOINTERFACE.

由于改变COM线程的行为显然是有点古怪(它需要写入注册表)和我没有工作,该解决方案是创建一个自定义的STA线程的类工厂(即在同一线程被传递到每一个FilteredHttpProtocol对象)(而不是协议类本身),并调用的每次的使用方法的invoke()

Since changing the COM threading behavior is apparently a bit odd (it requires writing to the registry) and did not work for me, the solution was to create a custom STA thread in the class factory (i.e. the same thread is passed into every FilteredHttpProtocol object) (rather than the Protocol class itself) and call every method using Invoke().

有一个快速和肮脏的解决方法是:

A quick and dirty solution is:

[ComVisible (true)]
public class FilteredHttpProtocolFactory : IClassFactory
{
    private readonly Control _ctrl;

    public FilteredHttpProtocolFactory ()
    {
      _ctrl = new Control();
    }

    public void CreateInstance (object pUnkOuter, Guid riid, out object ppvObject)
    {
      ppvObject = new FilteredHttpProtocol(_ctrl);
    }

    public void LockServer (bool fLock)
    {
    }
}

和协议类本身:

[ComVisible (true)]
[AsyncProtocol (Name = "http2", Description = "blah")]
public class FilteredHttpProtocol :  IInternetProtocol, IInternetProtocolRoot
{
    private IInternetProtocol _wrapped;
    private static int s_id = 0;
    private int _id = -1;
    private int _creatingTID = -1;

    private Control _dispatcher;

    public FilteredHttpProtocol (Control ctrl)
    {
      _dispatcher = ctrl;
      _id = s_id;
      s_id++;
      _creatingTID = Thread.CurrentThread.ManagedThreadId;
      Debug.WriteLine ("#" + _id + " threadID: " + _creatingTID + " C'tor()");

      _dispatcher.Dispatcher.Invoke (
          () =>
          {
            var originalHttpHandler = new OriginalHttpHandler();
            _wrapped = (IInternetProtocol) originalHttpHandler;
          });
    }

    public void Start (string szURL, IInternetProtocolSink Sink, IInternetBindInfo pOIBindInfo, uint grfPI, uint dwReserved)
    {
      Debug.WriteLine ("#" + _id + " URL: " + "\t" + szURL);
      _dispatcher.Dispatcher.Invoke (
          () =>
          {
            Debug.WriteLine (
                "#" + _id + " original thread: " + _creatingTID + " calling thread " + Thread.CurrentThread.ManagedThreadId
                + " Start() "
                + Thread.CurrentThread.GetApartmentState());
            _wrapped.Start (szURL, Sink, pOIBindInfo, grfPI, dwReserved);
          });
    }

    public void Continue (ref _tagPROTOCOLDATA pProtocolData)
    {
      var _pProtocolData = pProtocolData;
      _dispatcher.Dispatcher.Invoke (
          () =>
          {
            Debug.WriteLine (
                "#" + _id + " original thread: " + _creatingTID + " calling thread " + Thread.CurrentThread.ManagedThreadId + " Continue() "
                + Thread.CurrentThread.GetApartmentState());
            _wrapped.Continue (ref _pProtocolData);
          });
      pProtocolData = _pProtocolData;
    }
    // ...
}

(请不要使用此code,因为它是;)) 感谢您的帮助,我希望这可以帮助别人了。

(please do not use this code as it is ;) ) Thanks for your help, I hope this also helps someone else too.

干杯,

克劳斯