The Pain of deploying Primary Interop Assemblies

The Pain of deploying Primary Interop Assemblies

  • Comments 4

Alright, first of all, what are Primary Interop Assemblies (PIAs), and why am I devoting an entire post to the pains caused by deploying these things? And secondly, how do they relate with what we’ve been talking about thus far?

The answer to the second question is simple – we’ve been talking about COM Interop in C# lately, and we’ve just gotten through how C# 4.0 makes Indexed Properties accessible in a first class manner. Primary Interop Assemblies fit to the picture because they’re the way that COM interfaces are exposed into the managed world. In essence, they’re the bridge that allows you to use native COM “stuff” from your managed programming models. They are typically large API collections that must be deployed along with client applications.

What’s wrong with deploying PIAs?

Well, nothing’s wrong with it per se, but it certainly isn’t pleasant for developers who just want to write code against a COM API to have to worry about shipping the PIAs onto the client box, worry about whether or not it already exists on the client machine and what version lives there and all that. As I’ve mentioned before, you can think of the overarching theme of C# 4.0 as an interop release, one where we attempt to make interoping with different programming models and paradigms much simpler and much more first class.

How can we ship our apps without PIAs?

Avid readers will recall another interop feature that we’ve discussed in great length – dynamic. Since dynamic pushes dispatches operations at runtime, and is designed to statically use the C# paradigm while dynamically determining the runtime paradigm, wouldn’t it be perfect for this? We could compile everything without the PIAs, and therefore avoid shipping them altogether, and have the DLR deal with dispatching to the COM APIs at runtime.

Genius!

Turns out that is a perfectly viable solution – you can make all your COM calls dispatch dynamically and can avoid mentioning the PIA altogether.

So… we’re done then?

Well, this wouldn’t be much of a post if we were now, would it?

Turns out there are some costs associated with dynamic. The first cost is that you have limited intellisense. Because you’re constantly working with dynamic variables, the IDE can’t give you any guidance and useful information because it frankly doesn’t have any to give you. This means that developers new to your code base and new to using the COM APIs will struggle with them quite a bit, and will take more time to start being productive in your organization.

Secondly, there is a performance cost associated with dynamic. Now, granted we’re working very hard to minimize that impact, you cant get something for nothing. Namely, you can’t push your static binding to runtime, and expect it to be just as performant as a call bound statically.

Lastly, there are certain scenarios that I wont go into here where the dynamic COM binder isn’t able to perform the binding without information from the PIA. My colleague Misha Sneerson’s blog has more info about this.

Because of these limitations, we’ve applied some great minds to the problem, and have come up with a solution which addresses both of these concerns, and helps you get the best (well, most of it) from both worlds.

Type Equivalence

I’ll briefly outline the general idea of the solution that we’ve implemented in the 4.0 runtime right now, and go into more detail later.

Type equivalence is the notion that two different types are considered to be equivalent at runtime – that is, they’re considered interchangeable. Even though the types themselves are not the same managed type, allowing them to be equivalent means that one can use them interchangeably and not notice any difference.

Consider this brief example.

Assembly a.dll contains the following:

public interface IFoo { }
public class A
{
    public IFoo GetFoo() { ... }
}

Assembly b.exe contains the following:

public interface IFoo { }
public class B
{
    public void UseFoo(IFoo foo) { ... }
    public static void Main()
    {
        B b = new B();
        A a = new A();
        b.UseFoo(a.GetFoo());
    }
}

Notice that today, this can’t work. Assembly a.dll’s IFoo is a different type than assembly b.dll’s IFoo, so the compiler won’t allow you to pass a result of type A.IFoo to a method that takes a B.IFoo to successfully make the call. However, if we were to provide a special mechanism by which the compiler can know that both IFoo types are intended to be equivalent, then the compiler can treat these types as equivalent, and allow the call to go through.

Here’s where we need some cooperation with the CLR. If we can establish some well-known protocol between the CLR and the .NET compilers, then we can essentially agree on a mechanism to mark and treat types as being equivalent.

This is the core of the cross-team type equivalence feature, and is the foundation of the feature known as “No PIA” on the C# team.

Stay tuned for the details on what the feature does, what that contract is, what compiler writers need to do to opt in to the mechanism, and what compiler writers need to do regardless of whether or not they want to opt-in to support the feature.

kick it on DotNetKicks.com
Leave a Comment
  • Please add 5 and 2 and type the answer here:
  • Post
  • I open an MS Project (MPP) file, like such:

           Dim projectApp As New ApplicationClass()

           projectApp.FileOpen(ofdOpen.FileName, True, Missing.Value, Missing.Value, Missing.Value,

           Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value,

           PjPoolOpen.pjDoNotOpenPool, Missing.Value, Missing.Value, Missing.Value, Missing.Value)

    And I read the nodes and populate a list , like such:

    For Each p As System.Reflection.PropertyInfo In proj.GetType().GetProperties()

               If p.CanRead Then

                   Try

                       Console.WriteLine("{0}: {1}", p.Name, p.GetValue(proj, Nothing))

                       Dim item As RadListBoxItem = New RadListBoxItem

                       item.Name = p.Name

                       item.Text = p.Name

                       item.Value = p.GetValue(proj, Nothing)

                       lbNodes.SelectedIndex = 0

                       If blnComboboxItemSelected = False Then

                           lbNodes.SelectedItem = p.Name

                           blnComboboxItemSelected = True

                       End If

                       lbNodes.Items.Add(item)

                   Catch ex As Exception

                   End Try

               End If

           Next

    This works in XP Pro having VS 2010 B2. I am able to see a node called “Tasks”.

    However, it doesn’t in Windows 7 Pro x64 having VS 2010 RC. Some nodes are missing.

  • Could this be the problem?

    http://blogs.msdn.com/brismith/archive/2008/02/15/comexception-on-x64-platforms-when-automating-the-project-client-via-the-primary-interop-assembly-pia.aspx

  • I'm very much looking forward to the continuation of this series. On the surface, type equivalence feature would enable many languages with structural typing to target .NET while preserving their semantics - for example, so far as I can see, OCaml's structural statically typed object model could be implemented in terms of equivalent interfaces.

    However, my experiments with TypeIdentifierAttribute, the description of which on MSDN seems to imply that it is what is needed for type equivalence to kick in, were unsuccessful. Whenever I try to use an interface defined with such attribute in a C# program, I get this error message:

    test.cs(7,9): error CS1755: The 'TypeIdentifierAttribute' attribute constructor for interop type 'IFoo' is invalid

    So there must be more to it than that.

    By the way, it seems that CS1755 is not documented on MSDN.

  • Am I mistaken or is this massive, recurring, industry-wide, utter pain entirely avoidable using type provider from F# ?

Page 1 of 1 (4 items)