Search code examples
c++winapiproxycomstub

Access read & write exceptions when executing custom COM client & server


This questions builds upon the code linked in this question

In my research for custom Marshalling i came across this article and it gave me all the insight i needed to create my own Proxy/Stubb without the help of visual studio.

here is the link to the full project, follow the exact same instructions in this question to get the server and client up & running

So Whats the problem?

First let me tell what isn't the problem

  1. Proxy gets created client side
  2. Stub gets created server side

Everything gets set up exactly as planned on both ends until errors randomly occur. When i say random i mean it,

  1. Sometimes you get a write access violation exception enter image description here

  2. Other times an execution violation exception enter image description here

  3. And finally(i think) You get another exception over here enter image description here

I have been debugging this for over a day now and still can't pinpoint why these segments of code keep failing at random which is why I'm asking help here in debugging.

If anyone does manage to get this code working then the Com Client should communicate with COM server and you will see output like this on the client side

Click 1
Scroll 10
Key Pressed 5
Key Released 10

Basically the COM server sends these strings as char arrays from the STUB to the PROXY and the proxy finally prints the received values.


Solution

  • Note that Standard marshaling using the generated RPC proxy/stub is not a Visual Studio feature, it's provided by the Windows SDK. MIDL, the .IDL compiler is a SDK tool that generates the default P/S files (.c, .h, etc.), so it's really a Windows feature.

    Visual Studio (general tooling, often used in conjunction with ATL) just simplifies this invoking MIDL for you.

    Now, doing standard marshaling without the generated proxy/stub is still possible, but I wouln't recommend it, unless you have good reasons to do so. Reasons would include very specific optimisations in transferring arguments back & forth, that you know you can use, and you know the system can't. For example a specific type of parameter (like a string or a byte array) that the generated RPC code will transfer using big chunks of memory and you can transfer differently (like some shared handle to something internal to your specific context).

    It now works in the repo mentioned, so let's go back to some glitches that had to be fixed:

    • The first problem you had is one must use the pUnkOuter argument passed to the IPSFactoryBuffer::CreateProxy method, so AddRef it at least, Release when finished.
    • The pUnkOuter must also be delegated all QueryInterface calls for any other interface that is not IRpcProxyBuffer and that is not a proxied interface: IMouse, IKeyboard, etc.. (these must be implemented by the proxy implementor of course)
    • If a Connect call is immediatly followed by a Disconnect call (proxy or stub), it means there's some reference count issue somewhere in your code which causes the system-provided proxy manager to consider it has to terminate the proxy/stub in question. This is what happens on proxy side if pUnkOuter is not used.
    • On proxy side, when calling the IRpcChannelBuffer::GetBuffer, the iMethod field of the RPCOLEMESSAGE struct must already be set to the method's vtable index (which must be >= 3 since a vtable always has at least the 3 IUnknown methods QueryInterface, AddRef, Release). If iMethod is set after the GetBuffer call, the stub's IRpcStubBuffer::Invoke will never be reached and the proxy will get an RPC_E_INVALID_HEADER error (0x80010111) in the pStatus out argument of the SendReceive method. Note the "Inside COM+" book is wrong here (or maybe things have changed since but I doubt it).

    Other remarks:

    • It depends on context but you should make AddRef and Release implementations thread-safe, for example using InterlockedIncrement and InterlockedDecrement instead of ++ and -- operators. Good practise, makes everyone's like easier.
    • IsIIDSupported method implementation should AddRef when giving back an interface to the caller (as always with COM).