GOTCHA 71 Auto-generating GUID for your classes leads to versioning woes


GOTCHA #71 Auto-generating GUID for your classes leads to versioning woes

Let's switch focus to accessing your .NET classes from unmanaged code using COM.

How can you do that? You can use COM to achieve thisMicrosoft has done something smart here. You can associate a GUID with a .NET component and register its assembly as if it were a COM component. In the Registry, the name and location of the assembly are stored under the key

     HKEY_CLASSES_ROOT\CLSID\{...GUID...}\InprocServer32

(where ...GUID... is the GUID given to the .NET class) as shown in Figure 8-14.

The InprocServer32's Default key entry refers to the host component DLL as mscoree.dll, the primary DLL that initializes the CLR. Remember that when a

Figure 8-14. Registration of .NET class for access from COM


client activates a COM object, the component's DLL is loaded and the DllGetClassObject() method is invoked with the CLSID/GUID for the component as its parameter (again refer to [Box98]). If the CLSID/GUID is related to a .NET class, the component DLL that's loaded is the CLR's mscoree.dll. It then finds the name of the .NET class and the location of the assembly from the Class and CodeBase entries in the Registry key. The CLR then creates a COM Callable Wrapper (CCW) object as a proxy for the client to interact with. This CCW manages and properly routes the calls from the COM client to the .NET component. So.NET classes can easily pretend to be COM components.

You know that every COM component has a Class ID or CLSID that is a Globally Unique ID (GUID). In exposing a .NET class for COM interoperability, if you do not specify a GUID attribute for the class, then a new GUID is created. According to the documentation, the GUID that is automatically generated uniquely identifies the combination of the namespace name and the class name. This is to guarantee that no two classes will have the same GUID.

In practice though, the generated GUID is unique to the assembly version as well; i.e., the GUID generation scheme uses the namespace name, the class name, and the assembly version number. This may actually be good in a lot of ways. However, what if you change nothing but the revision number part of the four-part version number, and the newer version is compatible with the older version? Say your intent is for clients that use the older version to use this newer version, which has a different revision number, maybe because of a small bug fix. Consider Example 8-8.

By default, the assembly version in your project is set to 1.0.*. This ends up generating a new version number each time you build your project. That's trouble from the point of view of GUID generation. Give a proper assembly version number for your projects.


Example 8-8. Problem with automatic GUID generation

C# (GUID)

 //MyComponent.cs using System; namespace ALib {     public class MyComponent     {         public void Method1()         {             //... whatever code goes here         }     } } //AssemblyInfo.cs using System.Reflection; using System.Runtime.CompilerServices; //... (not shown) // // Version information for an assembly consists of the following four values: // //      Major Version //      Minor Version //      Build Number //      Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] 

VB.NET (GUID)

 'MyComponent.vb Public Class MyComponent     Public Sub Method1()         '... whatever code goes here     End Sub End Class 'AssemblyInfo.vb Imports System Imports System.Reflection Imports System.Runtime.InteropServices ' ... (Not shown) ' Version information for an assembly consists of the following four values: ' '      Major Version '      Minor Version '      Build Number '      Revision ' ' You can specify all the values or you can default the Build and Revision Numbers ' by using the '*' as shown below: <Assembly: AssemblyVersion("1.0.0.0")> 

For the above assembly, the version number is 1.0.0.0. You have selected Register for COM Interop in the project settings. Part of the registration information created from this assembly is shown in Example 8-9.

Example 8-9. COM Registration information for .NET component
 REGEDIT4 [HKEY_CLASSES_ROOT\ALib.MyComponent] @="ALib.MyComponent" [HKEY_CLASSES_ROOT\ALib.MyComponent\CLSID] @="{4F59B9B0-40ED-3CEE-B560-56CEECDB9F65}" [HKEY_CLASSES_ROOT\CLSID\{4F59B9B0-40ED-3CEE-B560-56CEECDB9F65}] @="ALib.MyComponent" [HKEY_CLASSES_ROOT\CLSID\{4F59B9B0-40ED-3CEE-B560-56CEECDB9F65}\InprocServer32] @="mscoree.dll" "ThreadingModel"="Both" "Class"="ALib.MyComponent" "Assembly"="ALib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" "RuntimeVersion"="v1.1.4322" ... 

The GUID created for class MyComponent is 4F59B9B0-40ED-3CEE-B560-56CEECDB9F65. Now let's modify the version number in the AssemblyInfo file from 1.0.0.0 to 1.0.0.1 (changing only the revision number).

When you recompile and regenerate the registration, you will find the information shown in Example 8-10.

Example 8-10. Effect of change in version number on generated GUID
 REGEDIT4 [HKEY_CLASSES_ROOT\ALib.MyComponent] @="ALib.MyComponent" [HKEY_CLASSES_ROOT\ALib.MyComponent\CLSID] @="{D0190E9A-AD60-3EA2-B268-2C5D96507F21}" [HKEY_CLASSES_ROOT\CLSID\{D0190E9A-AD60-3EA2-B268-2C5D96507F21}] @="ALib.MyComponent" [HKEY_CLASSES_ROOT\CLSID\{D0190E9A-AD60-3EA2-B268-2C5D96507F21}\InprocServer32] @="mscoree.dll" "ThreadingModel"="Both" "Class"="ALib.MyComponent" "Assembly"="ALib, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null" "RuntimeVersion"="v1.1.4322" ... 

The GUID value is now D0190E9A-AD60-3EA2-B268-2C5D96507F21, which is different from the one generated earlier. This might not be desirable if you have changed only the revision number and still want existing COM clients to use this updated component, because they won't be able to. The intent of changing the revision number may be a minor bug fix, or a very small change. The disadvantage of generating the GUID automatically is that it becomes harder to manage the versioning of components. This problem can be easily avoided by providing the GUID attribute for classes you want to expose for COM interoperability, as shown in Example 8-11.

Example 8-11. Exposing .NET component using GUID attribute

C# (ClassInterfaceType)

 using System; using System.Runtime.InteropServices; namespace ALib {     [Guid("53DEF193-D7A4-4ce3-938E-A7A35B5F7AB7"),         ClassInterface(ClassInterfaceType.AutoDispatch)]     public class MyComponent     {         public void Method1()         {             //...         }     } } 

VB.NET (ClassInterfaceType)

 Imports System.Runtime.InteropServices <Guid("53DEF193-D7A4-4ce3-938E-A7A35B5F7AB7"), _   ClassInterface(ClassInterfaceType.AutoDispatch)> _ Public Class MyComponent     Public Sub Method1()         '...     End Sub End Class 

By default, all public classes are exposed for COM interoperability. You must set the COMVisible attribute on the class (or the assembly to affect all classes in it) to false if you don't want to expose the class(es).

IN A NUTSHELL

Set the GUID attribute on your class if you intend to make it available for COM interoperability. To prevent COM access, use the COMVisible(false) attribute.

SEE ALSO

Gotcha #72, "All but one of the ClassInterface options are ineffective" and Gotcha #73, "Simply tur ning the switch for COM interop is dangerous."



    .NET Gotachas
    .NET Gotachas
    ISBN: N/A
    EAN: N/A
    Year: 2005
    Pages: 126

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net