I am dealing with a somewhat related issue, basically creating an AD employee search directory app in asp.net. I want to be able to show the users' presence next to their names.
We are using LCS2005, and I am trying to develop using RTC 1.3 in C#.
So far I am pretty much stuck with getting COMException errors with some weird HRESULT codes. Interestingly a winforms app using the same code works just fine. It's the RTPresence app in the SDK samples.
Have you been able to write something for the web using RTC 1.3? I'd appreciate if you could guide me in the right direction.
Thanks so much,
Hassan.
Hey Hassan--
Yeah that's kinda what I'm talking about--the non-zero HRESULTS come flyin' back and it's just poopy.
You've gotta pay attention to which thread you created the original instance of IRTCClient2 on; never create or pass outside that thread.
Technically the team says they've fixed the marshalling but I can't make it work in C#, and I don't want to drop into C++ just for a wrapper.
I created the Interop assembly with the RTCCOreLib.dll; just use the RTCCORE.IDL file in the SDK IDL directory. DON'T use the one in System32; remember this is a redirected DLL so the actual 1.3 dll is in some weird SxS directory.
Also, be sure the .MANIFEST file is correct and available in your BIN dir.
Hope this helps!
I do hope it helps; I spent quite a few hours getting it all set up. The basic steps to using RTCClient API v1.3 from C# are:
1) generate an interop file from the RTCCORE.IDL file in "C:\Program Files\RTC Client API v1.3 SDK\SDK\IDL", call it a good name like "Interop.RtcCore.dll"
-
how to do step one:
-
use "midl rtccore.idl" to get a tlb
-
use "tlbimp rtccore.tlb /out:Interop.RtcCore.dll" to get the interop assembly; optionally, sign it at this time too
2) reference said interop assembly from a C# project
3) Now the delicious part; you must create this from an STA thread, ALWAYS, and you must never pass references to the objects you create outside that thread's context. This means no delegates on background threads, no putting references in a collection that can be hit by other threads, etc etc. It's quite painful. I created and managed said thread as follows:
m_staThread = new Thread( new ThreadStart( this.InitializeInternal ) );
m_staThread.ApartmentState = ApartmentState.STA;
m_staThread.IsBackground = true;
m_staThread.Name = String.Format( "RTCClient_STAThread_{0}", g_instanceCount );
m_staThread.Start();
4) on that thread you need to initialize an instance of IRTCClient2 as follows:
// Run the required message loop to pump messages from the RTC RtcWrapper
// COM object to this thread.
m_context = new ApplicationContext();
// trap messages with our filter, looking for WM_PUMPRTCMESSAGE--which
// we'll redirect to invoke a delegate
Application.AddMessageFilter( this );
// create the RTCClient instance and initialize it
RTCClientClassInitializeEx( INITIALIZEEX_PARAMS_BITMASK );
SetEventFilter( EVENT_FILTER_BITMASK );
//MSTUART: moved here from before new context above 04132005 to mitigate race when
// outside, initializing thread gets signalled we're ready too soon.
// OK all done, signal to whomever's waiting in Initialize()
m_completedWaitHandle.Set();
// start message loop on this STA thread
Application.Run( m_context );
All that goo above is done _on the STA thread_! And the initializeEx is called as follows:
m_client = new RTCClientClass();
m_client.InitializeEx( rtcifConstantsBitMask );
// snag events
m_client.IRTCEventNotification_Event_Event += new IRTCEventNotification_EventEventHandler( ClientEventHandler );
// set up to listen
m_client.ListenForIncomingSessions = RTC_LISTEN_MODE.RTCLM_BOTH;
// media types
//m_client.SetPreferredMediaTypes( RTCConstants.RTCMT_ALL, true );
5) Now if you want to call this from many threads, you'll have to create the plumbing for marshalling calls onto your brand-new nifty Windows message loop you created above. Since you're an IFilter, you get first chance at those messages; look for ones that pertain to you, and do only them.
It's a little painful, really. I'm looking forward to nice friendly wrappers or rewrites for this. Not having them, I had to write my own. When it came time to do the server, I had to think a bit about limiting the number of sessions per RTCClient instance--sending is O(n). So I have many instances of my wrapper (which encapsulates the STA and serializes access from outside threads), each of which is limited to 20 sessions.
It all works but it feels unnatural.
At the end of the day, our web service polling implementation offers so many advantages--scalability, ease of development, ease of maintenance, traverses Firewalls and NAT, etc. etc. etc. that it's probably the way we'll go.
We had already implemented a Heartbeat-Sequence type 'reliability layer' to give some guarantee of message delivery, and we'd designed for a plug-in model; substituting any technology is easy with this design.
Frankly I think most solutions that need to be WAN-friendly--that is, NAT, firewall, no incoming connections, etc--will be better off with HTTP(s) polling type implementations. Inside the LAN it's a totally different picture; but I hate "asymmetric" solutions, I'd much rather double up my engineering effort on one solid solution than have one for inside and one for outside to gain some fractional speed advantage inside.
Given how monstrously scalable web servers have become, and given the great gobs of bandwidth available--remember all that dark fiber we laid in the 90's?--it Just Works.
It's not quite "YAGNI"--Ya Aint' Gonna Need It--but it's close.