DCOM uses port # 135 for all processes on the computer and this is bad. WinXp SP2 quite soundly closes this common hole and this is good. But not for developers. Yes, you can have your customers crying but configuring policies. You can even manage to auto-adjust those settings, acting like a real virus. But still there is another big problem – you are in LAN. VPN is the only way to make your DCOM project work through the internet. And this is very ugly.
Let’s think of pleasant instead: Just imagine if you could select any port on your server side. Just allow this port in firewall and that’s all… Just imagine if you could pass through proxy on your client side. No VPN – no pain. Just imagine that you are controlling your whole DCOM traffic – you can compress it, cipher it, calculate it, in two words – just manage it.
And the good news is: we CAN give DCOM another railway!
IRpcChannelBuffer is the answer. Inproc communication between com objects is based on direct calls in good case. In bad case, even in one process between two objects are acting another two objects – proxy and sub. Proxy gets direct call from the calling side and packs whole information into a buffer and then sends it to stub. Stub unpacks it and makes direct call to called object. And for this communication proxy and stub, use
In two words, if we would implement this interface and somehow pass it to stub and proxy – they start to interact using your own channel. Let’s look at the main interface functions:
First of all: we can realize our own transport:
virtual HRESULT STDMETHODCALLTYPE SendReceive(RPCOLEMESSAGE *, ULONG *) = 0;
We can send and receive this data in any imaginable way. And second: we can even manage memory and make our transport very effective avoiding useless copying.
virtual HRESULT STDMETHODCALLTYPE GetBuffer(RPCOLEMESSAGE *, REFIID riid) = 0;
virtual HRESULT STDMETHODCALLTYPE FreeBuffer(RPCOLEMESSAGE *pMessage) = 0;
Reasonable question – how to get proxy and stub working through our implementation of interface? Let’s look at two other interfaces
IRpcProxyBuffer has only two methods:
virtual HRESULT STDMETHODCALLTYPE Connect(IRpcChannelBuffer *) = 0;
virtual void STDMETHODCALLTYPE Disconnect(void) = 0;
This is how we will connect proxy to our channel.
IRpcStubBuffer has more than two methods but only one is valuable:
virtual HRESULT STDMETHODCALLTYPE Invoke(RPCOLEMESSAGE*, IRpcChannelBuffer*)=0;
So all work looks like this:
- Connect proxy to your
- When it calls
SendReceive, pass buffer to the other side.
- On the other side, just call
Invoke with received buffer.
- Send buffer filled with output data back to client.
- Return from
SendReceive with output buffer.
Yet another reasonable question: how to create stub and connect it to object on server side and how to create proxy on client side? It’s quite simple too:
GetPSFactory is the answer.
HRESULT GetPSFactory(REFIID riid, IPSFactoryBuffer ** ppProxyStubFactory);
This function creates factory by given IID. Factory interface consists of two very useful methods:
virtual HRESULT STDMETHODCALLTYPE CreateProxy
(IUnknown *pUnkOuter,REFIID riid, IRpcProxyBuffer **ppProxy, void **ppv) = 0;
virtual HRESULT STDMETHODCALLTYPE CreateStub
(REFIID riid,IUnknown *pUnkServer,IRpcStubBuffer **ppStub) = 0;
Proxy is object that will play the role of remote objects that you want to use. So creating proxy we just set IID and obtain the interface itself and
IRpcProxyBuffer suited for connecting to our
IRpcChannelBuffer. When we call the method of the desired interface we receive call to
SendReceive with buffer ready to be sent to another side. On another side, we must first create a real object and then connect a stub to it.
CreateStub gets IID and gives you
IRpcStubBuffer that you can connect to the real object calling:
virtual HRESULT STDMETHODCALLTYPE Connect(IUnknown *pUnkServer) = 0;
Too Simple to be Real
Yes the start is quite easy. What will you get having only this piece of knowledge? Just one interface that is connected to server side’ object. All calls to this interface will pass through your realization. And that’s all that is good. But there is bad news:
- If your interface has a method working with another interface – DCOM will connect it to port #135!
CreateProxy not always can create proxy in spite of proxy really existing.
- You cannot use asynchronous calls.
- You cannot even work with another interface through this proxy stub pair - you need to repeat the whole process for another interface.
- And many other things that even couldn't be explained without deep descent in the DCOM inner world…
In DCOMTransport.zip you will find source code example combining all described above. This test shows how to insert in transport chain your own functionality. In this case the functionality is cancelling of call.
- 25th May, 2009: Initial post