【问题标题】:Implementing nsIProtocolHandler with Delphi用 Delphi 实现 nsIProtocolHandler
【发布时间】:2009-05-14 16:43:31
【问题描述】:

我正在尝试在 Delphi 中构建一个 nsIProtocolHandler 实现。 (我之前成功地完成了一个 IInternetProtocol,并希望在 FireFox 中拥有我在 Internet Explorer 中所拥有的东西。) 感谢the d-gecko 项目,它以某种方式将 TInterfacedObject 魔法与 nsISupports 魔法联系起来,我能够制作一个 DLL,它在被问到时提供 nsIModule,在被问到时提供 nsIFactory,在被问到时提供我的 nsIProtocolHandler 之一,它询问时提供我的 nsIChannel/nsIHttpChannel 之一。

当使用 firefox.exe 作为宿主进程进行调试时,我可以看到我的库被加载,NewURI 被调用了 3 次,NewChannel 被调用,并且我传递了一个实现 nsIChannel 和 nsIHttpChannel 的对象。

这就是我困扰的地方。在我从 AsyncOpen 返回控制权之前,我不应该在我得到的 nsIStreamListener 上调用 OnStartRequest 和 OnDataAvailable,但我似乎没有在调用 AsyncOpen 的线程中重新获得控制权。

我尝试使用自制的包装器围绕默认 http 处理程序进行调试(使用CreateInstanceByContractID('@mozilla.org/network/protocol;1?name=http',...)。我也把监听器包裹过去了。奇怪的是,我看到 OnStartRequest 和 OnDataAvailable 在同一个线程中被调用 after 我的频道包装器死了。但谁在打电话?如果它是我试图包装的 http 通道,它如何生存(在同一个线程中)以及它如何控制调用侦听器?我很困惑。并且卡住了。

我曾尝试联系d-gecko项目的主要开发者,但没有得到回复。

(另外,有人注意到我在talk page on MDC on nsIProtocolHandler 底部的简介吗?)

(哦,还有一件事,是的,我知道如果我只从 C++ 中的 nsBaseChannel 继承“生活会更简单”。但重点是向现有的 Delphi 项目核心添加一个 FireFox 协议处理程序。)

更新:我做了更多的阅读,也提到了here:“流监听器的方法是 在调用 asyncOpen [...] 的线程上调用,但是我不清楚如果不先从“托管应用程序”调用,这怎么可能。这是 XPCOM 技巧吗?我想我必须阅读在我得到它之前(很多)更多的 Firefox 源。

【问题讨论】:

标签: delphi firefox delphi-7 gecko


【解决方案1】:

我对 Mozilla 编码一无所知,但它就是这样。

根据nsIChannel::asyncOpen()

异步打开这个频道。数据 被馈送到指定的流 监听器,因为它变得可用。这 调用流侦听器的方法 在调用 asyncOpen 的线程上和 直到 asyncOpen 之后才被调用 返回。如果 asyncOpen 返回 成功,该频道承诺 至少调用 onStartRequest 和 onStopRequest。

因此,作为协议处理程序,您自己实现一个通道对象或将其重定向到一个通道对象,通道的消费者使用asyncOpen() 调用您的通道。由于它是异步调用,因此想法是立即将控件返回给使用者,并且假设在加载数据时调用回调。

我不确定我是否理解您所说的“但我似乎无法在调用 AsyncOpen 的线程中重新获得控制权”。线程由协议的使用者创建,并打开通道。

同样来自nsIChannel::asyncOpen()

如果 asyncOpen 成功返回,则 渠道负责保持 自己活着,直到它调用 aListener 上的 onStopRequest 或调用 onChannelRedirect。

由于 asyncOpen 会立即返回控制权,因此通道本身需要在某处保持活动状态。

如果您正在寻找示例代码,我发现 codease 非常有用。请参阅nsIProtocolHandlernsIChannel。使用它我遇到了view-source protocol(这个实现可能更旧,但没关系)。

nsViewSourceHandler 实现自定义频道。

nsViewSourceHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
{
    nsresult rv;

    nsViewSourceChannel* channel;
    rv = nsViewSourceChannel::Create(nsnull, NS_GET_IID(nsIChannel), (void**)&channel);
    if (NS_FAILED(rv)) return rv;

    rv = channel->Init(uri);
    if (NS_FAILED(rv)) {
        NS_RELEASE(channel);
        return rv;
    }

    *result = NS_STATIC_CAST(nsIViewSourceChannel*, channel);
    return NS_OK;
}

这是nsViewSourceChannel 的 AsyncOpen:

nsViewSourceChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt)
{
    NS_ENSURE_TRUE(mChannel, NS_ERROR_FAILURE);

    mListener = aListener;

    /*
     * We want to add ourselves to the loadgroup before opening
     * mChannel, since we want to make sure we're in the loadgroup
     * when mChannel finishes and fires OnStopRequest()
     */

    nsCOMPtr<nsILoadGroup> loadGroup;
    mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
    if (loadGroup)
        loadGroup->AddRequest(NS_STATIC_CAST(nsIViewSourceChannel*,
                                             this), nsnull);

    nsresult rv = mChannel->AsyncOpen(this, ctxt);

    if (NS_FAILED(rv) && loadGroup)
        loadGroup->RemoveRequest(NS_STATIC_CAST(nsIViewSourceChannel*,
                                                this),
                                 nsnull, rv);

    if (NS_SUCCEEDED(rv)) {
        mOpened = PR_TRUE;
    }

    return rv;
}

无论如何,这是一个漫长而曲折的询问方式,您是如何创建频道的?

【讨论】:

  • 恭喜,您已获得 300 分。我默默地希望没有人会回答,当赏金到期时我会拿回积分,但是,嘿,谢谢你的回答。这是澄清。我了解了 codease.com(我之前使用的是 www.google.com/codesearch 和 mxr.mozilla.org/firefox)。在发布这个问题的同时,我了解了 NS_DispatchToMainThread 这回答了我的问题。我在 nsACString 转换中也做错了,所以我不再卡住了......如果你有兴趣:xxm.svn.sourceforge.net/viewvc/xxm/trunk/Delphi/gecko
  • 我在这方面做了更多工作,所以对于任何感兴趣的人:现在我在调用 AsyncOpen 时捕获“当前线程”,并将事件分派到该线程以调用侦听器。由于这是唯一的答案并且有一些帮助,我会接受它。
  • 您可以发布自己的答案,我很乐意放弃接受状态。
猜你喜欢
  • 2013-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多