 |
 |

Custom Enumeration Interfaces
Writing a COM Enumeration Interface for a custom data-type should be easy. In fact, it is, once you understand the need for local and remote methods.
The problem
Many COM interfaces provide the ability to step through, or enumerate, a collection of some kind. The usual way for a COM interface to expose this kind of functionality is via an interface which conforms to the IEnumXXXX standard.
There are lots of IEnum interfaces available already, but you may need to write your own to enumerate a custom data type, or interface pointer. Once you decide you need to have your own IEnum interface you need to write some IDL, build a proxy and then code your interface object. This should be fairly straight forward, after all, you just need to open objidl.idl, do a search of an existing IEnum interface, cut and paste the idl into your own file and change some data types... Or, at least that's all you should have to do...
Unfortunately the IEnum interfaces use some rather esoteric features of IDL. Simply copying the IDL and building the proxy will fail with unresolved external symbol errors for _IEnumMyDataType_Next_Proxy and _IEnumMyDataType_Next_Stub.
Understanding [local] methods
The problem with IEnum is in the strange definition of the Next() method in the IDL.
interface IEnumChatSession : IUnknown
{
typedef [unique] IEnumChatSession *LPENUMCHATSESSION;
[local]
HRESULT Next(
[in] ULONG celt,
[out] IChatSession **rgelt,
[out] ULONG *pceltFetched);
[call_as(Next)]
HRESULT RemoteNext(
[in] ULONG celt,
[out, size_is(celt), length_is(*pceltFetched)]
IChatSession **rgelt,
[out] ULONG *pceltFetched);
HRESULT Skip(
[in] ULONG celt);
HRESULT Reset();
HRESULT Clone(
[out] IEnumChatSession **ppenum);
}
|
Next() has the [local] attribute. This means that no proxy or stub code is generated for it. This is because the semantics of the Next() method call are illegal in IDL. The method cannot be remoted because the pceltFetched value can be set to a NULL pointer if the caller passes 1 for celt. But MIDL doesn't allow an [out] parameter that's a pointer to be NULL. If you pass a NULL pointer as an out parameter then your proxy/stub code will trap the error and return a failure code.
[call_as(Next)]
The way around the problem of methods that are semantically illegal in IDL is to mark them as [local], which causes no proxy/stub code to be generated for them. Then, so that you can actually use the method, you write a second method, that can be remoted, and marked this as [call_as(illegalMethodName)]. This means that proxy/stub code is generated for the call_as method.
You now have two copies of the same method, one with proxy/stub code and one without. This allows you to write a forwarding function (or "shim") that is called when you invoke the method in the client -and which should fix up the illegal semantics of the method and then call the call as function's proxy. And another which is called by the stub code of the call_as() method which can then fix up the call before calling the real method in the server. It's these functions that the compiler is complaining about when you try to build a proxy for an Enum interface.
Writing the shim functions
The code required to make your method semantically legal will, of course, vary from method to method. The code required for the Next() method of an Enumeration interface is shown below:
// This function is called by client code, it should do
// anything it needs to do and then call the proxy...
/* [local] */
HRESULT STDMETHODCALLTYPE IEnumChatSession_Next_Proxy(
IEnumChatSession __RPC_FAR * This,
/* [in] */ ULONG celt,
/* [length_is][size_is][out] */
IChatSession __RPC_FAR *__RPC_FAR *rgelt,
/* [out] */ ULONG __RPC_FAR *pceltFetched)
{
ULONG celtFetched = 0;
HRESULT hr = E_POINTER;
if (!pceltFetched && celt != 1)
{
return hr;
}
hr = IEnumChatSession_RemoteNext_Proxy(
This, celt, rgelt, &celtFetched);
if (pceltFetched)
{
*pceltFetched = celtFetched;
}
return hr;
}
// This function is called by the stub manager, it should do
// anything it needs to do and then call the real object's
// function...
/* [call_as] */
HRESULT STDMETHODCALLTYPE IEnumChatSession_Next_Stub(
IEnumChatSession __RPC_FAR * This,
/* [in] */ ULONG celt,
/* [length_is][size_is][out] */
IChatSession __RPC_FAR *__RPC_FAR *rgelt,
/* [out] */ ULONG __RPC_FAR *pceltFetched)
{
return (This)->lpVtbl->Next(This,celt,rgelt,pceltFetched);
}
|
Most of the details for the shim functions can be worked out by looking at the proxy code that MIDL generates in the _p.c file. One thing to be aware of is that this should be compiled as C code, not C++. This is so that the function names aren't mangled by the C++ compiler. The proxy code is assuming that the functions will be mangled as C functions (i.e. just a leading _ added...)
|
 |
 |
 |