Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# compiler bug or normal COM oddity?

C# 4, to simplify COM interop, allow callers to COM interfaces to omit the ref keyword in front of arguments for by ref parameters.

I was surprised to see today that this also applies to extension methods that are extending COM interfaces. See the following, compiling, code:

using System;
using System.Runtime.InteropServices;

[ComImport, Guid ("cb4ac859-0589-483e-934d-b27845d5fe74")]
interface IFoo {
}

static class Program {

    public static void Bar (this IFoo self, ref Guid id)
    {
        id = Guid.NewGuid ();
    }

    static void Main ()
    {
        Foo (null);
    }

    static void Foo (IFoo o)
    {
        Guid g = Guid.NewGuid ();
        Console.WriteLine (g);

        // note that g is passed as is, and not as ref g    
        o.Bar (g);

        Console.WriteLine (g);
    }
}

I didn't find anything in the spec to explain this behavior.

My feeling would be that code outside of the COM interface, even if it's an extension method extending a COM interface, should follow the regular C# rules, and enforce the usage of the ref keyword. I therefore filed a bug on connect. Not that I think this will be fixed, even if it's considered as a bug, there's already code out there relying on this.

Bug? Not a bug?

like image 938
Jb Evain Avatar asked Jan 19 '12 16:01

Jb Evain


1 Answers

I don't think it's a bug; it looks more like "COM voodoo" as you say. Under the hood, the C# compiler emits something that is in fact correct, like this:

private static void Foo(IFoo o)
{
    ...
    Guid g = Guid.NewGuid();
    Guid <>r__ComRefCallLocal0 = g;
    Bar(o, ref <>r__ComRefCallLocal0);
    ...
}

C# is in fact full of tricks. If you add a method to IFoo, like this for example,

[ComImport, Guid("cb4ac859-0589-483e-934d-b27845d5fe74")]
interface IFoo
{
    void Test([Optional] ref object test);
}

you, again, will be able to declare this in C# 4:

static void Foo(IFoo o)
{
    Guid g = Guid.NewGuid();
    o.Test(g);
}

Of course, all this only works because CSC.EXE has an intimate knowledge of the ComImport attribute. These new magic Interop tricks were added to C# 4.0 to be able to easily interop with existing COM interfaces. Well, for Microsoft Office interfaces and methods mostly, and especially the armies of dreadful 'ref missing' parameters :-)

I don't think this is fully specified anywhere. This is all what the C# 4 specification has to say:

17.5 Attributes for Interoperation Note: This section is applicable only to the Microsoft .NET implementation of C#. 17.5.1 Interoperation with COM and Win32 components The .NET run-time provides a large number of attributes that enable C# programs to interoperate with components written using COM and Win32 DLLs. For example, the DllImport attribute can be used on a static extern method to indicate that the implementation of the method is to be found in a Win32 DLL. These attributes are found in the System.Runtime.InteropServices namespace, and detailed documentation for these attributes is found in the .NET runtime documentation.

And here are some pages on MSDN:

  • Named and Optional Arguments (C# Programming Guide)
  • How to: Access Office Interop Objects by Using Visual C# 2010 Features (C# Programming Guide)
like image 63
Simon Mourier Avatar answered Oct 26 '22 10:10

Simon Mourier