如何实现 IServiceProvider?
How can I implement IServiceProvider?
如何在 class 中实现 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;
您查询的是服务 ID (rsid),而不是接口 ID (iid)。
QueryService
的目的与 QueryInterface
的目的不同。
QueryService
应该 return 一个指向对象本身的指针,如果它提供请求的服务(通过 rsid
),否则它应该委托给其他一些对象,通常是容器和另一个 QueryService
电话。
如果没有找到这样的服务,应该return E_NOTIMPL
.
returned接口指针将是请求的类型(通过iid
),所以如果找到服务,实现服务的对象最终是QueryInterface
d .
如果对象没有实现这样的接口,它应该return 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.
所以,分析你的 :
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
)。
如何在 class 中实现 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;
您查询的是服务 ID (rsid),而不是接口 ID (iid)。
QueryService
的目的与 QueryInterface
的目的不同。
QueryService
应该 return 一个指向对象本身的指针,如果它提供请求的服务(通过 rsid
),否则它应该委托给其他一些对象,通常是容器和另一个 QueryService
电话。
如果没有找到这样的服务,应该return E_NOTIMPL
.
returned接口指针将是请求的类型(通过iid
),所以如果找到服务,实现服务的对象最终是QueryInterface
d .
如果对象没有实现这样的接口,它应该return 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.
所以,分析你的
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
)。