One of the .NET 4.0 (actually CLR4) enhancements is a feature called “No-PIA” which greatly enhances COM interoperability. This fits in a larger picture in CLR4 which is Type Equivalence.
First let’s review what PIA is…
Primary Interop Assemblies (PIAs)
MSDN best described PIA as follows:
“Like any other managed assembly, an interop assembly is a collection of types that are deployed, versioned, and configured as a single unit. However, unlike other managed assemblies, an interop assembly contains type definitions (not implementation) of types that have already been defined in COM. These type definitions allow managed applications to bind to the COM types at compile time and provide information to the common language runtime about how the types should be marshaled at run time.
While any number of interop assemblies may exist that describe a given COM type, only one interop assembly is labeled the PIA. The PIA contains the official description of the types as defined by the publisher of those types. The PIA may contain certain customizations that make the types easier to use from managed code. The PIA is always signed by the publisher of the original COM type.
The Tlbimp (Type Library Importer) tool provided in the Microsoft® .NET Framework SDK is capable of creating an interop assembly from an existing type library.”
Importance of PIA
Again best explained via MSDN:
“Not using a PIA will lead to type incompatibilities for customers. For example, Company A uses the COM Widget object provided by the Acme Company within its managed application. Company A decides to produce another interop assembly with definitions of Acme’s Widget library rather than use the PIA provided by the Acme Company.
Company B obtains the PIA for the Widget Library directly from the Acme Company and uses it within its managed application. Both applications work fine on their own until a developer from one of Company A and Company B’s common customers calls to find out why he keeps getting a type mismatch whenever he tries to pass a Widget from Company A to Company B.
The reason for the type mismatch is because the Widget type used by Company A is completely different from the Widget type used by Company B. Even though they both have the same name, each interop assembly was signed by a different publisher, thereby making the types incompatible. The simple solution is for both companies to use the PIA provided by the Acme Company.”
Umm…so why the hell are you bragging about no-PIA?!
Actually no-PIA does not mean that PIAs are not there anymore. It simply means that you do not have to carry them with you anymore! Ok so let’s elaborate…
Say you have a .NET 3.x application which “talks” to Microsoft Excel. When deploying your application on client machine A you have to include the PIA of Excel in your deployment. Why? Simply because Microsoft Office does not require the installation of .NET framework (it is a COM application right?), so it is very likely that by the time Office was installed on machine A, the Excel PIA was not installed with it due to the fact that .NET framework is missing. PIAs are managed assemblies that live in the GAC so they need .NET framework.
So now when you want to deploy your application, you will want to include the PIA with it.
What is so bad about deploying PIAs with your application you might say? It’s size. Office PIAs for example are multi MBs of size which you will have to deploy when your application is probably few KBs of size. This extra size is an overhead especially when you know you are using a very small number of interfaces available in that PIA and you do not need to deploy and load the whole thing.
No-PIA to the rescue
Now in .NET 4.0 and CLR4 (or CLR3 if you keep counting from the previous version CLR2), you can instruct your assembly to include inside it only the needed types of the PIA and thus the PIA won’t have to be deployed along with your application.
Let’s see an example.
Below is a simple VS 2010/C# 4.0 (currently beta2) console application code which simply creates an Excel document.
x1.Visible = true;
x1.get_Range(“A1”).Value2 = “Name”;
x1.get_Range(“B1”).Value2 = “Gender”;
x1.get_Range(“C1”).Value2 = “Age”;
x1.get_Range(“A2”).Value2 = “Mohd”;
x1.get_Range(“B2”).Value2 = “Male”;
x1.get_Range(“C2”).Value2 = “27”;
x1.get_Range(“A3”).Value2 = “Sara”;
x1.get_Range(“B3”).Value2 = “Female”;
x1.get_Range(“C3”).Value2 = “25”;
The below screen shot shows this:
The Excel PIA is added as a reference for the project. It is through this PIA that the managed code interacts with Excel. The PIA contains all the managed equivalent of interfaces, structs, enums, delegates, etc…defined in the COM with the required marshalling data…see the below image:
Now if you use .NET reflector, you will find that our managed assembly (ExcelAddIn.exe) actually has the Excel PIA as a reference; which is predictable of course. So in this case when we deploy the application we have to deploy the PIA with it. See the below screen shot:
However, what you can do is open the VS2010 properties box of the referenced PIA and change the property “Embed Interop Types” to true. This will instruct the compiler to embed the portions (types) of the PIA that your application use inside the application’s assembly itself. This way the PIA is not needed anymore and your application is using the required types implicitly. Check the below image:
As we will see in more details shortly, CLR4 will make sure that the types embedded inside the application’s assembly are equivalent with the types defined in the PIA; this is known as CLR4 “Type Equivalence”.
Ok, build your application. Refresh .NET reflector and now you can see that the PIA is no longer a reference. Instead the types used by the application are now embedded as namespaces inside the assembly itself. This way the application is not depending on the PIA anymore; hence no-PIA! See the below screen shot:
CLR4 Type Equivalence
You have seen how VS 2010 enables you to change a property and the compiler will embed the needed types inside the assembly. However, let’s dive into a little more details.
Locate the Excel PIA physical path on the machine and add it to the .NET reflector. Now you have the applications assembly (with no-PIA) and the PIA itself both loaded inside the reflector. Below is the view:
Now expand the Microsoft.Office.Interop.Excel namespace inside ExcelAddIn.exe and disassemble interface _Application as shown below:
Notice the new 4.0 keyword: TypeIdentifier. This tells the CLR that this type will participate in type equivalency. But how is this type equivalency actually happens? It is through the Guid. This Guid will be matched up by the CLR with the PIA type equivalent Guid such that when the application passes the interop type to the native code (Excel), this certifies that the application is using the same version of the type expected by Excel.
To witness this, expand the dll Microsoft.Office.Interop.Excel and again disassemble interface _Application. You will notice that it has the same Guid of the type embedded inside the assembly. This is how the CLR did the type equivalence matching:
Of course it should be clear by now, but still worth mentioning: notice how the PIA has tons of types defined but only the ones needed by the application have been imported to the assembly.