【问题标题】:How can I implement IServiceProvider?如何实现 IServiceProvider?
【发布时间】:2015-03-21 08:51:56
【问题描述】:

如何在继承其他接口的类中实现IServiceProvider,以便实际调用它们的方法?现在我总是从QueryInterface 得到E_NOINTERFACE。

    TPassthrough = class(TComObject, IInternetProtocolRoot, IInternetProtocolSink, IInternetProtocol, IServiceProvider)
    private
      FDefaultSink: IInternetProtocol;
      FProtSink: IInternetProtocolSink;
      FBindInfo: IInternetBindInfo;
    public
    { IServiceProvider }
    function QueryService(const rsid, iid: TGuid; out Obj): HResult; stdcall;

    { IInternetProtocolSink }
    function Switch(const ProtocolData: TProtocolData): HResult; stdcall;
    function ReportProgress(ulStatusCode: ULONG; szStatusText: LPCWSTR): HResult; stdcall;
    function ReportData(grfBSCF: DWORD; ulProgress, ulProgressMax: ULONG): HResult; stdcall;
    function ReportResult(hrResult: HResult; dwError: DWORD; szResult: LPCWSTR): HResult; stdcall;

    { IInternetProtocolRoot }
    function Start(szUrl: LPCWSTR; OIProtSink: IInternetProtocolSink; OIBindInfo: IInternetBindInfo; grfPI, dwReserved: DWORD): HResult; stdcall;
    function Continue(const ProtocolData: TProtocolData): HResult; overload; stdcall;
    function Abort(hrReason: HResult; dwOptions: DWORD): HResult; stdcall;
    function Terminate(dwOptions: DWORD): HResult; stdcall;
    function Suspend: HResult; stdcall;
    function Resume: HResult; stdcall;

    { IInternetProtocol }
    function Read(pv: Pointer; cb: ULONG; out cbRead: ULONG): HResult; stdcall;
    function Seek(dlibMove: LARGE_INTEGER; dwOrigin: DWORD; out libNewPosition: ULARGE_INTEGER): HResult; stdcall;
    function LockRequest(dwOptions: DWORD): HResult; stdcall;
    function UnlockRequest: HResult; stdcall;
    end;

...

function TPassthrough.Start(szUrl: LPCWSTR; OIProtSink: IInternetProtocolSink; OIBindInfo: IInternetBindInfo; grfPI, dwReserved: DWORD): HResult; stdcall;
begin
  if (FDefaultSink = nil) then
  OleCheck(CoCreateInstance(CLSID_HttpProtocol, nil, CLSCTX_INPROC_SERVER, IUnknown, FDefaultSink));

  FBindInfo := OIBindInfo;
  FProtSink := OIProtSink;

  if (Assigned(FDefaultSink)) then
    Result := (FDefaultSink as IInternetProtocolRoot).Start(szUrl, Self, Self, grfPI, dwReserved)
  else
    Result := E_NOTIMPL;
end; 

function TPassthrough.QueryService(const rsid, iid: TGuid; out Obj): HResult; stdcall;
begin
  Result := E_NOINTERFACE;
  Pointer(Obj) := nil;

  if Succeeded(QueryInterface(rsid, Obj)) and
    Assigned(Pointer(Obj))
  then
    Result := S_OK;
end; 

【问题讨论】:

  • 很难看出TPassthrough.QueryInterface 无法返回接口。您确定在正确的对象上调用了 QI 吗?
  • 谁调用QueryService?你的其他部分代码还是外部代码?愚蠢的问题,但你确定 QueryService 的 rsid (iid) 参数等于 IID_IInternetProtocolRoot 或 IID_IInternetProtocolSink 或 IID_IInternetProtocol?
  • 您没有显示相关代码。我认为我们可以假设QI 有效。要么您在错误的对象上调用 QI,要么传递了错误的 IID。

标签: delphi com bho delphi-xe7 urlmon


【解决方案1】:

QueryService 的用途与QueryInterface 的用途不同。

QueryService 应该返回一个指向对象本身的指针,如果它提供了请求的服务(通过rsid),否则它应该委托给其他一些对象,通常是一个容器和另一个QueryService 调用。

如果没有找到这样的服务,它应该返回E_NOTIMPL。

返回的接口指针将是请求的类型(通过iid),所以如果找到服务,实现该服务的对象最终是QueryInterfaced。

如果对象没有实现这样的接口,它应该返回E_NOINTERFACE。

碰巧许多服务 ID (SID) 与通常实现的接口 ID (IID) 具有相同的 GUID,例如 SID_SInternetSecurityManager 和 IID_IInternetSecurityManager。虽然这是一个广泛使用的约定,但它不是一个规则。例如,you can ask for the SID_SWebBrowserApp service as a IID_IWebBrowser2 interface pointer。

所以,分析你的original code:

function TPassthrough.QueryService(const rsid, iid: TGuid; out Obj): HResult; stdcall;
begin
  Result := E_NOINTERFACE;
  Pointer(Obj) := nil;

  if Succeeded(QueryInterface(rsid, Obj)) and
    Assigned(Pointer(Obj))
  then
    Result := S_OK;
end;

您只是使用服务 ID (rsid) 将 QueryService 转换为 QueryInterface,这与接口 ID (iid) 相似但不一定相关。因此,如果两者不同,调用者可能会得到一个不兼容的接口指针。

那么,changed code:

function TMonitor.QueryService(const rsid, iid: TGuid; out Obj): HResult; stdcall;
begin
  Result := E_NOINTERFACE;
  Pointer(Obj) := nil;

  if Succeeded(QueryInterface(iid, Obj)) and
    Assigned(Pointer(Obj))
  then
    Result := S_OK;
end;

这是说该对象实现了每一个可以想象的服务,所以它只是委托给QueryInterface,但至少与iid是正确的。

这是你应该做的:

function TMonitor.QueryService(const rsid, iid: TGuid; out Obj): HResult; stdcall;
begin
  if Not Assigned(@Obj) then
  begin
    Result := E_POINTER;
    Exit;
  end;

  Pointer(Obj) := nil;
  Result := E_NOTIMPL;

  if Assigned(FServiceProvider) then
    Result := FServiceProvider.QueryService(rsid, iid, Obj);
end;

如果您想提供自己的服务,您可以选择使用以下代码:

function TMonitor.QueryService(const rsid, iid: TGuid; out Obj): HResult; stdcall;
begin
  if Not Assigned(@Obj) then
  begin
    Result := E_POINTER;
    Exit;
  end;

  Pointer(Obj) := nil;
  Result := E_NOTIMPL;

  if IsEqualGUID(rsid, SID_SOverrideService1) or
     IsEqualGUID(rsid, SID_SOverrideService2)
  then
    Result := QueryInterface(iid, Obj);

  if Result = E_NOTIMPL and
     Assigned(FServiceProvider)
  then
    Result := FServiceProvider.QueryService(rsid, iid, Obj);

  if Result = E_NOTIMPL and
     (IsEqualGUID(rsid, SID_SFallbackService1) or
      IsEqualGUID(rsid, SID_SFallbackService2))
  then
    Result := QueryInterface(iid, Obj);
end;

SID_OverrideServiceN 是您要覆盖的服务 ID,SID_FallbackServiceN 是您要回退到的服务 ID。请注意,我将Succeeded(Result) 更改为Result <> E_NOTIMPL,因为如果确实找到了服务但发生了其他错误,我不想继续寻找服务,例如请求的接口未实现 (E_NOINTERFACE)。

【讨论】:

  • 我认为你应该在 Obj 上添加一个AddRef,否则会导致引用计数问题。
【解决方案2】:

您查询的是服务 ID (rsid),而不是接口 ID (iid)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-02
    • 1970-01-01
    • 1970-01-01
    • 2017-04-28
    • 2020-04-21
    • 2011-11-17
    • 2022-11-23
    • 2022-06-21
    相关资源
    最近更新 更多