[英]How can I implement IServiceProvider?
How can I implement IServiceProvider
in the class which I inherit other interfaces from so their methods actually get called? 如何在继承其他接口的类中实现
IServiceProvider
,以便实际调用它们的方法? Right now I always get E_NOINTERFACE
back from QueryInterface
. 现在我总是从
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;
The purpose of QueryService
is different from the purpose of QueryInterface
. QueryService
的目的与QueryInterface
的目的不同。
QueryService
should return a pointer to the object itself if it provides the requested service (through rsid
), otherwise it should delegate to some other object, typically a container and with another QueryService
call. QueryService
提供所请求的服务(通过rsid
), QueryService
应返回指向对象本身的指针,否则它应该委托给其他对象,通常是容器和另一个QueryService
调用。
If no such service is found, it should return E_NOTIMPL
. 如果未找到此类服务,则应返回
E_NOTIMPL
。
The returned interface pointer will be of the requested type (through iid
), so if the service is found, the object that implements the service is finally QueryInterface
d. 返回的接口指针将是所请求的类型(通过
iid
),因此如果找到该服务,则实现该服务的对象最终是QueryInterface
d。
If no such interface is implemented by the object, it should return E_NOINTERFACE
. 如果对象没有实现这样的接口,它应该返回
E_NOINTERFACE
。
It so happens that many service IDs (SID) are the same GUID as the typically implemented interface ID (IID), for instance, SID_SInternetSecurityManager
and IID_IInternetSecurityManager
. 碰巧许多服务ID(SID)与通常实现的接口ID(IID)是相同的GUID,例如,
SID_SInternetSecurityManager
和IID_IInternetSecurityManager
。 Although this is a widely used convention, it is not a rule. 虽然这是一种广泛使用的惯例,但它不是一个规则。 For instance, you can ask for the
SID_SWebBrowserApp
service as a IID_IWebBrowser2
interface pointer . 例如, 您可以将
SID_SWebBrowserApp
服务作为IID_IWebBrowser2
接口指针 。
So, analysing your 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;
You're kind of just transforming QueryService
into QueryInterface
using a service ID ( rsid
), which is similar but not necessarily related to interface IDs ( iid
). 您只是使用服务ID(
rsid
)将QueryService
转换为QueryInterface
,这类似于但不一定与接口ID( iid
)相关。 As such, if the two differ, the caller may get an incompatible interface pointer. 因此,如果两者不同,则调用者可能会获得不兼容的接口指针。
Then, the 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;
This is saying that the object implements every imaginable service, so it merely delegates to QueryInterface
, but at least correctly with iid
. 这就是说该对象实现了所有可以想象的服务,因此它只是委托给
QueryInterface
,但至少正确地使用了iid
。
This is what you should do: 这是你应该做的:
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;
Should you want to provide your own services, you can optionally use the following code: 如果您想提供自己的服务,可以选择使用以下代码:
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;
Where SID_OverrideServiceN
are the service IDs you want to override and SID_FallbackServiceN
are the service IDs you want to fallback to. 其中
SID_OverrideServiceN
是您要覆盖的服务ID, SID_FallbackServiceN
是您要回退的服务ID。 Note that I changed Succeeded(Result)
to Result <> E_NOTIMPL
, because I don't want to keep looking for services if one was actually found but some other error happened, eg the requested interface was not implemented ( E_NOINTERFACE
). 请注意,我将
Succeeded(Result)
更改为Result <> E_NOTIMPL
,因为我不想继续查找服务(如果实际找到了一个但发生了其他错误,例如请求的接口未实现( E_NOINTERFACE
))。
您正在查询服务ID(rsid),而不是接口ID(iid)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.