Extracting IM Conversation Text using the Office Communicator SDK
The Office Communicator 2007 SDK is one of a bunch of SDKs available for the Office Communications Server 2007 product suite. This particular one is used to automate a running instance of Communicator 2007. For example, you can interact with Communicator to start text, audio, or video conversations. You can also respond to certain Communicator events in code, such as the creation of a new conversation window.
If you've tried to extract the contents of an IM conversation window using IMessengerConversationWnd::History, you might have noticed that the History property always seems to return null.
The trick here is that the conversation text isn't injected into the IM window at the exact moment that it is created; you have to use a timer to check for when the History property returns something other than null.
Here's some code that shows how to use System.Threading.Timer to poll the conversation window. This code doesn't go into how to use the CommunicatorAPI COM objects to hook into Communicator from within your application and wire up the necessary events - the SDK I linked to above has plenty of examples on how to do that.
If you have any questions about that though, hit me up and I'll try to answer them.
We'll start by declaring a Timer object in our class, and also a plain object to hold a reference to a specific IM conversation window:
private Timer timer = null;
private object convWindow;
Communicator WindowCreated Event Handler
In the Communicator WindowCreated event handler, we can tell whether this is an incoming or outgoing conversation window by comparing the window's HWND property to a local variable called windowHandle.
void communicator_OnIMWindowCreated(object pIMWindow)
{
try
{
IMessengerConversationWndAdvanced imWindow =
(IMessengerConversationWndAdvanced)pIMWindow;
if (((IMessengerConversationWndAdvanced)pIMWindow).HWND == windowHandle)
{
// outgoing
}
else
{
// incoming
windowHandle = imWindow.HWND;
HandleIncomingMessage(pIMWindow);
}
}
}
Setting up the Timer
For this example, I'll use a function called HandleIncomingMessage to handle an incoming conversation. I'll pass the conversation window as a parameter to the function.
void HandleIncomingMessage(object pIMWindow)
{
IMessengerConversationWndAdvanced imWindow =
(IMessengerConversationWndAdvanced)pIMWindow;
if (((IMessengerConversationWndAdvanced)pIMWindow).HWND == windowHandle)
{
convWindow = pIMWindow;
timer = new Timer(new TimerCallback(timer_Tick), null,
TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1));
}
}
Note that we check for matching window handles before doing anything - there could be multiple instances of Communicator windows in existence at the same time; we need to make sure that we are dealing with the right window.
Also note that we set our class variable convWindow to the pIMWindow object. This way, the Timer's tick event can access the window to check its History property.
Tick
private void timer_Tick(object state)
{
IMessengerConversationWndAdvanced imWindow =
(IMessengerConversationWndAdvanced)convWindow;
if (imWindow.History != null)
{
string history = imWindow.History; // Do what you need to do here ...
imWindow.Close();
timer.Change(Timeout.Infinite, Timeout.Infinite);
timer.Dispose();
}
}
When the window's History property is finally not null, you can can run whatever logic you need to. In my scenario, I closed the window, set the timer to never fire again, and then disposed it. This way, the next time this happens, the timer will be reinitialized cleanly.
Tick Durations
You should set up your timer to tick at an interval that's convenient for your needs; for example it shouldn't tick again while the timer_Tick handler is still executing.
Cleaning Up
Remember that these are all COM objects that need to be disposed of property. You should use Marshal.ReleaseComObject(... to clean up when you're done.