November 2008 - Posts

Excel RTD Servers: Minimal C++ Implementation (Part 1)

Continuing the discussion of Excel RTD servers, this time I’m going to show you how to write a minimal RTD server in native C++. There’s quite a bit more to discuss so I broke this entry into two parts.

Last time I showed you a minimal RTD server written in C# and it sure is easy to bang it together. There isn’t a whole lot of code to write thanks to the mountains of abstractions provided by the CLR and the .NET Framework. Those abstractions can however get in the way. If nothing else they get in the way of our understanding of what is actually going on under the covers and that in turn can lead to unexplained bugs. If you’ve learnt anything from reading my blog I hope it is the value of understanding how things work, not just how to get them to work. Did you catch the difference?

Although C++ affords you many more options for implementing and deploying an RTD server, here I’m focusing on the simplest implementation of an in-process apartment model COM server. Subsequent entries will explore additional scenarios that go beyond this starting point.

It’s a Visual C++ Project

Every good project starts with an empty Visual C++ project.  :)

Create a new project in Visual Studio based on the Visual C++ “Win32 Project” template. Select the Application Settings tab in the Win32 Application Wizard. Select the “DLL” application type and also check the “Empty project” option.

Add a precompiled header to the project and include the following:

#pragma once

#include <atlbase.h>
#include <atlcom.h>

#define ASSERT ATLASSERT
#define VERIFY ATLVERIFY
#define TRACE ATLTRACE
#define HR(expr) { HRESULT expr_hr = expr; if (FAILED(expr_hr)) return expr_hr; }

atlbase.h is the main ATL header that includes the main Windows SDK headers and the most common ATL functions and classes used by virtually all native C++ applications (that use ATL).

atlcom.h provides most of the code for generating all the boilerplate code required to implement a COM server.

The macros are optional but help to simplify the code and should be self-explanatory.

It’s a DLL

Since we’re limiting this implementation to an in-process COM server it will be packaged as a dynamic-link library. DLLs built with the C run-time library use an entry point function called DllMain. This should be used as an indicator of when the operating system is loading and unloading your DLL and not much else.

STDAPI_(BOOL) DllMain(HINSTANCE /*module*/,
                      DWORD /*reason*/,
                      void* /*reserved*/)
{
    return TRUE;
}

DLLs typically communicate with the outside world by providing a set of exported functions. One way to define these exports is via a module definition file. Add a file to the project called Exports.def and include the following text:

LIBRARY "RtdServer"

EXPORTS

The name in quotes must match the name of the DLL produced by the linker. Any functions you wish to export are listed under the EXPORTS header. You need to also tell the linker about your module definition file. You can set this in the linker’s “Input” properties from within the project’s property pages.

Although DllMain isn’t strictly an exported function, I tend to stick it along with any other exported functions in a C++ file called Exports.cpp just so that it’s obvious where all the code paths within the DLL originate from.

It’s a COM Server

A COM server must provide a way for a client to get the class object for a particular CLSID. COM servers hosted in a DLL achieve this by exporting a function called DllGetClassObject. The DllRegisterServer and DllUnregisterServer functions are also typically exported and called by installers or simply by the built-in regsvr32.exe tool to register and unregister the server. Finally, the DllCanUnloadNow function should also be exported to allow the COM runtime to unload your DLL promptly.

These functions are well defined in the COM specification and I won’t go into their implementation details too much. The best thing you can do is rely on ATL to provide the implementation. Start by defining an ATL module class for your DLL as follows.

class Module : public CAtlDllModuleT<Module>
{
};

Amazingly you can now implement the DLL’s entry point and all of the necessary exported functions simply by delegating to the ATL implementation as follows:

Module s_module;

STDAPI_(BOOL) DllMain(HINSTANCE, DWORD reason, LPVOID reserved)
{
    return s_module.DllMain(reason, reserved);
}

STDAPI DllRegisterServer()
{
    return s_module.DllRegisterServer(FALSE);
}

STDAPI DllUnregisterServer()
{
    return s_module.DllUnregisterServer(FALSE);
}

STDAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void** object)
{
    return s_module.DllGetClassObject(clsid, iid, object);
}

STDAPI DllCanUnloadNow()
{
    return s_module.DllCanUnloadNow();
}

Now that the exported functions are implemented you can add them to the list of exports in the module definition file:

EXPORTS
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE

The PRIVATE keyword prevents other binaries from directly linking to these functions. You can of course still call them via LoadLibrary/GetProcAddress which is exactly what COM and tools like regsvr32.exe would do.

That’s it for today. Next time we’ll conclude the minimal C++ implementation with:

  • It’s a COM Class
  • It’s an Automation Server
  • It’s an RTD Server

Stay tuned.

If you’re looking for one of my previous articles here is a complete list of them for you to browse through.

Produce the highest quality screenshots with the least amount of effort! Use Window Clippings.

Posted by KennyKerr with no comments

Windows with C++: X64 Debugging With Pseudo Variables and Format Specifiers

My latest Windows with C++ column in the December 2008 issue of MSDN Magazine is now online:

Windows with C++: X64 Debugging With Pseudo Variables and Format Specifiers

For many years, Visual C++ has included a set of pseudo variables and format specifiers for use in debugging. By pseudo variables I mean terms that you can enter into a debugger watch window to have it display some value that doesn't necessarily relate to any C++ variable. Unfortunately, pseudo variables have never been well documented. Although I don't have enough insider information to document them myself, I want to share some of the pseudo variables and format specifiers that I've found most useful. I hope the discussion will inspire you to read up on the subject


Shortly after writing this column I started reading Advanced Windows Debugging, which I mentioned in my latest reading list. You should read this book for one reason: it will open your eyes to the Debugging Tools for Windows. Although I’ve used these debuggers before, mainly for kernel-mode debugging, I’d always found it quite painful and longed to return to the familiar Visual C++ IDE and debugger.

I have to now admit that the Debugging Tools for Windows provide quite a bit more debugging power compared to Visual C++ and in some ways make debugging both native and managed code quite a bit easier. As it relates to this article, the debuggers also include quite a lot more pseudo variables and format specifiers than are available with the debugger included with Visual C++.

So if you’re looking for a bit more debugging power you owe it to yourself to download the free Debugging Tools for Windows and buying a copy of Advanced Windows Debugging to get you started. Obviously this doesn’t replace Visual C++, it is after all only a set of debugging tools, but it is a great tool and skill to have at your disposal.

If you’re looking for one of my previous articles here is a complete list of them for you to browse through.

Produce the highest quality screenshots with the least amount of effort! Use Window Clippings.

 

Posted by KennyKerr with no comments

Visual Studio 2010 Mouse Wheel Zooming

I just noticed that Visual Studio 2010 has duplicated the mouse wheel zooming feature used by Windows Explorer and Word 2007. Simply hold down the Ctrl key and scroll the mouse wheel to zoom the text editor in and out.

This is great for presentations. No more digging through the Visual Studio Options window. Clearly it still needs some work (e.g. the scroll bars shouldn’t zoom) but it certainly looks promising.

Posted by KennyKerr with 1 comment(s)

Excel RTD Servers: Minimal C# Implementation

Continuing the discussion of Excel RTD servers, here is about the simplest RTD server I could come up with. I used C# for this example to allow us to focus on semantics. Next time I’ll show a minimal C++ implementation.

[
    Guid("9AA100A8-E50E-4047-9C60-E4732391063E"),
    ProgId("Kerr.Sample.RtdServer"),
]
public class RtdServer : IRtdServer
{

As you can see, the RtdServer class is attributed with a CLSID and ProgId that can be used by COM clients to locate the COM server. By default (if you don’t employ registration-free COM), COM clients will locate the COM server by looking up either the ProgId (to find the CLSID) or the CLSID directly in the registry. You can register your assembly using the RegAsm tool that ships with the .NET Framework as follows:

%SystemRoot%\Microsoft.Net\Framework\v2.0.50727\RegAsm.exe RtdServer.dll /codebase

Remember also to mark your assembly as being visible to COM as follows:

[assembly: ComVisible(true)]

Next we need to define a minimal set of member variables in order for the RTD server to function:

    private IRTDUpdateEvent m_callback;
    private Timer m_timer;
    private int m_topicId;

m_callback is needed to hold onto a reference to the callback interface provided by Excel. This interface is primarily used to let Excel know that new data is available. m_timer is a Windows.Forms timer used to periodically notify Excel via the callback interface. This timer is obviously optional and you are free to implement update notifications any way you want. I did however use this technique for a reason which I’ll outline in a moment. Finally, m_topicId is used to identify the topic that Excel is “subscribing” to. Again, this is just one approach which as you’ll see in a moment is very naïve.
    
    public int ServerStart(IRTDUpdateEvent callback)
    {
        m_callback = callback;
        m_timer = new Timer();
        m_timer.Tick += new EventHandler(TimerEventHandler);
        m_timer.Interval = 2000;
        return 1;
    }

ServerStart is the first method called by Excel and is where we prepare the RTD server. In particular we set the callback member variable and prepare the timer. Notice that the timer is not yet enabled. Returning 1 indicates that everything is fine.

    public void ServerTerminate()
    {
        if (null != m_timer)
        {
            m_timer.Dispose();
            m_timer = null;
        }
    }

ServerTerminate is called when Excel is ready to unload the RTD server. Here we simply release the timer.

    public object ConnectData(int topicId,
                              ref Array strings,
                              ref bool newValues)
    {
        m_topicId = topicId;
        m_timer.Start();
        return GetTime();
    }

ConnectData is called for each “topic” that Excel wishes to “subscribe” to. It is called once for every unique subscription. As should be obvious, this implementation assumes there will only be a single topic. In a future post I’ll talk about handling multiple topics. ConnectData also starts the timer and returns an initial value that Excel can display.

    public void DisconnectData(int topicId)
    {
        m_timer.Stop();
    }

DisconnectData is called to tell the RTD server that Excel is no longer interested in data for the particular topic. In this case, we simply stop the timer to prevent the RTD server from notifying Excel of any further updates.

    private void TimerEventHandler(object sender,
                                   EventArgs args)
    {
        m_timer.Stop();
        m_callback.UpdateNotify();
    }

TimerEventHandler is the private method that is called when the timer Tick event is raised. It stops the timer and uses the callback interface to let Excel know that updates are available. Stopping the timer is important since we don’t want to call UpdateNotify repeatedly.

    public Array RefreshData(ref int topicCount)
    {
        object[,] data = new object[2, 1];
        data[0, 0] = m_topicId;
        data[1, 0] = GetTime();

        topicCount = 1;

        m_timer.Start();
        return data;
    }

RefreshData is called when Excel is ready to retrieve any updated data for the topics that it has previously subscribed to via ConnectData. The implementation looks a bit strange. That’s mainly because Excel is expecting the data as a COM SAFEARRAY. Although it isn’t pretty, The CLR’s COM infrastructure does a commendable job of marshalling the data for you. All you need to do is populate the two-dimensional array with the topic Ids and values and set the topicCount parameter to the number of topics that are included in the update. Finally, the timer is restarted before returning the data.

    public int Heartbeat()
    {
        return 1;
    }

Heartbeat is called by Excel if it hasn’t received any updates recently in an attempt to determine whether your RTD server is still OK. Returning 1 indicates that everything is fine.

    private string GetTime()
    {
        return DateTime.Now.ToString("hh:mm:ss:ff");
    }
}

GetTime is a private method used to get a formatted time string that represents the data to display in Excel. As you can imagine, this RTD server simply updates the time in the cell roughly every two seconds.

Clearly this RTD server implementation leaves a lot to be desired but it does demonstrate enough functionality to give you an idea of how RTD servers work. To give it a try you can use the following function from within Excel:

=RTD("Kerr.Sample.RtdServer", , "topic")

The only thing left that’s worth mentioning about this implementation is the use of the Windows.Forms.Timer class and why this even works. If you look at the way the RegAsm tool (and internally the RegistrationServices class) registers the types within the assembly you may notice that it is registered with a threading model of “Both” which indicates that the COM class is able, from a threading perspective, to load into the apartment of the caller (no proxy). In the case of Excel, the apartment happens to be a single-threaded apartment which as part of its contract provides a message pump and the message pump is all the timer needs to function. Internally it creates a hidden window to handle the WM_TIMER messages and then raise the Tick event. So it happens to work because Excel creates the RTD server in a single threaded apartment and the RTD server is happy to run directly in that apartment. This is incidentally also how ActiveX controls work.

About the only remaining thing that might trip you up is the way that Excel calls the RTD server interface. In particular it uses IDispatch to invoke the methods rather than calling them directly. In order for this to work, a type library needs to be used. I’ll discuss this further when I look at the C++ implementation but for now you just need to reference the “Microsoft Excel 11.0 Object Library”, or later, for the definitions of those interfaces. 

That’s all for today. I’m really trying to keep these entries short, but there’s clearly a lot more to discuss. If you find this helpful please let me know.

Posted by KennyKerr with 10 comment(s)

Excel RTD Servers: C# Interfaces

So it turns out that there are in fact quite a few developers who care about writing RTD servers. I’ve received a few emails asking for more information so I may post a few more entries on the subject. Last time I simply showed what the RTD server interfaces look like in C++. Here’s the equivalent in C#:

[Guid("A43788C1-D91B-11D3-8F39-00C04F3651B8")]
interface IRTDUpdateEvent
{
    void UpdateNotify();

    int HeartbeatInterval { get; set; }

    void Disconnect();
}

[Guid("EC0E6191-DB51-11D3-8F3E-00C04F3651B8")]
interface IRtdServer
{
    int ServerStart(IRTDUpdateEvent callback);

    object ConnectData(int topicId,
                       ref Array strings,
                       ref bool newValues);

    Array RefreshData(ref int topicCount);

    void DisconnectData(int topicId);

    int Heartbeat();

    void ServerTerminate();
}


Of course what these interfaces look like and how they are actually called by Excel are two very different things thanks to the horrors wonders of IDispatch.

I’m including C# in the discussion as its very tempting for .NET developers to implement an RTD server in managed code. Unfortunately it’s easy to forget that you still need to follow the rules outlined by the COM specification. If you don’t, it will come back to haunt you later on.

Posted by KennyKerr with 4 comment(s)
More Posts