More COM capers in Visual Studio 2005

I recently wrote about creating early bound COM objects in .NET, the object (no pun intended) of the exercise was to persuade Visual Studio 2005 to produce an early binable COM object without any ‘jiggery pokery’ after the standard build. The exercise proved successful, I could simply ‘build’ and start using my COM object from my unmanaged code.

However I did run into some issues, the first of which was caused by .NET not initialising it’s FPU control word when being called from unmanaged code. The issue occured because the unmanaged application I was calling my component from was written in Borland Delphi, which doesn’t suppress certain floating exceptions in the same was Microsoft does. If I’d had access to the Delphi source I could have recompiled turning off the exceptions, something like this.

Here is how to turn them off in C++Builder:
#include float.h
 
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
  _control87(MCW_EM, MCW_EM);
}

Here is how to turn them off in Delphi:
const MCW_EM = DWord($133f);
  begin
    Set8087CW(MCW_EM);
  end;

Of course I’d have to produce a solution from my component, the easiest implementation I could find for this was to add something to my default constructor of my COMBase class. I started using code to explicitly set the control word, something along these lines…

[DllImport("msvcrt.dll", EntryPoint="_control87")]
public static extern UInt32 Control87(UInt32 Toggle, UInt32 Mask);
 
UInt32 OldControlWord = Control87(0, 0); // Get current value
 
Control87(0×9001f, 0xffffffff); // Set to .NET default
// Call Code Here
Control87(OldControlWord, 0xffffffff); // Restore to previous value

Although this works admirably, whilst I was working on it, a much simpler solution was offered to be from another developer, which I added to my COMBase class.

[ComVisible(false)]
    public class COMBase
    {
 
        public COMBase()
        {
            // Ensure FPU Control Word is set when called from unmanaged applications.
            try { Double.IsNaN(Double.NaN); }
            catch (ArithmeticException) { /* Intentionally empty. .NET will initialise the FPU at this point. */ }
        }
 
        /*  Snip */
    }

With this solved, the only remaining task was distribution of my COM component. For this I added a simple installer component to my code, which would be called from my setup project.

[ComVisible(false)]
    [RunInstaller(true)]
    public partial class CustomInstall : Installer
    {
        public CustomInstall()
        {
            InitializeComponent();
        }
 
        public override void Install(IDictionary stateSaver)
        {
            base.Install(stateSaver);
            RegistrationServices regSrv = new RegistrationServices();
            regSrv.RegisterAssembly(base.GetType().Assembly, AssemblyRegistrationFlags.SetCodeBase);
        }
 
        public override void Uninstall(IDictionary savedState)
        {
            base.Uninstall(savedState);
            RegistrationServices regSrv = new RegistrationServices();
            regSrv.UnregisterAssembly(base.GetType().Assembly);
        }
    }

With the project output added as ‘custom actions’ to my setup project, I could simply build my COM component, test it; then build an MSI for distribution which ensured calling of the registration functions, which in turn registered it for COM. It’s important to note that my components are not flagged for COM registration in the setup project, this is all catered for by the custom code in my installer class and COMBase class.


Recent Entries

2 Responses to “More COM capers in Visual Studio 2005”

  1. Volker Says:

    Thanks a lot!
    This made my day.

    Cheers,
    Volker

  2. walrus Says:

    I should follow this post up at some point. Just setting (and not resetting after you’re finished) the FPU control word can upset the caller application I’ve found.

    Therefore it might be prudent to revert the FPU control word to the state you discovered it in before passing control back, if that is the way your code works. That is, something along the lines of the Control87 sample code in my original post.

Leave a Reply