我如何实现IServiceProvider?

时间:2015-01-22 14:14:05

标签: delphi com bho delphi-xe7 urlmon

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

    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; 

2 个答案:

答案 0 :(得分:9)

QueryService的目的与QueryInterface的目的不同。

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

如果找不到此类服务,则应返回E_NOTIMPL

返回的接口指针将是请求的类型(通过iid),因此如果找到服务,则实现该服务的对象最终为QueryInterface d。

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

碰巧许多服务ID(SID)与通常实现的接口ID(IID)是相同的GUID,例如SID_SInternetSecurityManagerIID_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(QueryService)将QueryInterface转换为rsid,这与接口ID类似但不一定相关({{1} })。因此,如果两者不同,调用者可能会得到一个不兼容的接口指针。

然后,changed code

iid

这就是说该对象实现每个可以想象的服务,因此它只委托给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; 是您要覆盖的服务ID,SID_OverrideServiceN是您要回退的服务ID。请注意,我已将SID_FallbackServiceN更改为Succeeded(Result),因为如果实际找到某个服务但我发生了其他错误,我不想继续寻找服务,例如:请求的界面未实现(Result <> E_NOTIMPL)。

答案 1 :(得分:3)

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