Jeff Griffith

I have the followinf configuration:

Visual Studio 2005 Solution #1, that I'll call "Launcher", which produces one EXE and two DLL's

Visual Studio 2005 Solution #2, that I'll call "Middleware", which produces one DLL and it references the two DLL's in "Launcher"

Visual Studio 2005 Solution #3, that I'll call "Report", which produces one DLL which references all of the DLL's in Launcher and Middleware.

All elements (EXE's and DLL's) are strongly named and have unique public keys.

If I compile all elements, and reploy all elements, everything works.

If I then compile Launcher and reploy Launcher (all three files), Report cannot be loaded. The error is:

{"Could not load type 'AxysReports.CSSI_cashEnvironmentManager' from assembly 'AxysCSSI_cash, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bb9e4fc8d6e39f2f'.":"AxysReports.CSSI_cashEnvironmentManager"}

{"Could not load file or assembly 'LauncherInterfaces, Version=1.0.2762.24379, Culture=neutral, PublicKeyToken=119602bbf1593321' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)":"LauncherInterfaces, Version=1.0.2762.24379, Culture=neutral, PublicKeyToken=119602bbf1593321"}

My problem is that Report (and various future incarnations of it) will be distributed independently of Launcher. We are very careful to insure that we maintain "backward-compatibility using the old COM and COM+ type rules, so we do and will continue to insure that changes in Launcher don't break Report or Middleware.

If I cannot build and distribute Launcher independently of Report or Middleware because strong naming ties Report to a specific compilation instance of Luncher, then I'll have to move to a platform that allows this.

Can this project be saved

Thanks,

Jeff Griffith

CSSI




Re: Common Language Runtime Versioning problem with strongly named asemblies

TaylorMichaelL

This is the preferred behavior in most cases. It is better to tie one's self to a specific version and guarantee backward compatibility then assume that you can always maintain backward compatibility. Therefore I think the current behavior is really what you want. Why do you really need the assemblies strongly named anyway

If you really want to use strongly named assemblies but you don't want to tie yourself to a specific version then you have to override the binding rules of the loader. To do this you can either add an entry to each application's config file telling it to use a newer version (bindingRedirect I believe) or you can create a publisher policy file that redirects everybody to the newer version.

Michael Taylor - 7/26/07

http://p3net.mvps.org





Re: Common Language Runtime Versioning problem with strongly named asemblies

Jeff Griffith

Mike,

Thanks for your very helpful reply.

There's several parts to this problem:

1): I'm using strong-named assemblies to provide each with an immutable ID that we can use for licensing. Strong-naming guarantees it can't be changed and makes it hard to fake. Personally, if I can dump strong-naming and still provide an immutable ID for each assembly that is part of the assembly, I would be all for it. Strong-naming has created several problems (like dictating our installation structure).

2): The Launcher and the Reports are developed and deployed independently; the Launcher provides a small number of services (like license verification) that are needed by all reports, but it's primary role is to provide a tool for managing, purchasing and running Reports. I could replicate the common source code across all of the Reports, but we expect there will be hundreds of Reports to maintain.

3): Once a given version of a Report is deployed, it cannot be changed. Our customers will be purchasing Reports over time and there is no guarantee that they will upgrade any Report, although we expect to require that they upgrade the Launcher periodically. As a result, we need to insure that any version of the Launcher can work with any version of any Report.

4): The Middleware is code common to sets of Reports. It contains the base class from which a given set of Reports are derived. There will be several types of Middleware. Changes to this will force a new release of all Reports derived from a given version of a type. I haven't worked completely through the problems in this area, yet, but strong-naming won't allow the Middleware assemblies to be loaded by the Reports unless the Middleware assemblies are installed in the same directory as the Launcher. It looks like I will have to build the version number into the file name so that the customer can purchase a Report based on version 1.x Middleware today, and purchase a second report based on version 3.x Middelware next year and still be able to run both of these Reports with either the very first Launcher they installed, or any other version of the Launcher they have purchased along the way.

5): I'm new to .NET and CSharp. I wrote C++ code from 1986 to 1998, VB code from 1998 to 2001, Java code on Websphere from 2001 to 2007 (and a lot of Javascript/ASP/HTML/XML/XSL/XSLT/Perl code along the way). If you could provide some links on how to override the binding process or to disable these versioning rules, that would be a big help.

Jeff Griffith

CSSI






Re: Common Language Runtime Versioning problem with strongly named asemblies

TaylorMichaelL

Interesting problem you have there.

Strongly named assemblies are immutable and provide a unique ID but depending on how your licensing engine works you don't necessarily need SN assemblies just to get a unique ID. The GUID associated with an assembly is effectively unique as well. However nothing prevents someone from creating a new assembly with the same ID. But is this a big deal since all they'd be doing is creating a new assembly that now is "effectively" licensed. They still wouldn't be circumventing your licensing I believe. Again, it depends on how licensing works.

You seem to be tying licensing to versioning through SN assemblies. I'm not sure this is going to be worth the effort. Have you looked into the licensing model provided by .NET. It is generally associated with components and controls but technically it can be used anywhere. I haven't done anything with it myself so I won't be much help but I'll throw out a theoretical solution anyway.

You would create a custom licensing provider that handles your specific needs. You would ship a LIC file to each customer. The LIC file would determine what reports (and perhaps what versions of what reports) the user could use. When the Launcher loads a report it uses the licensing subsystem to verify the user can use the given report. In your custom code you would validate against a report id and/or the version. For example you might have a subscription license that allows a user to use all versions of a report whereas the cheap license allows for only a specific version (and maybe any updates). This effectively locks out the Reports assemblies from general use without a license.

I don't know that you need the Launcher or the Middleware to be secure since they are useless without the reports. If you have custom code that you don't want exposed then use an obfuscator or nShroud to encrypt the code to protect it.

Someone else might be able to provide more insight into how the licensing model in .NET works. You can also look online at MSDN. Nevertheless it breaks the versioning/licensing issue you have while still allowing you to do licensing.

Michael Taylor - 7/26/07

http://p3net.mvps.org





Re: Common Language Runtime Versioning problem with strongly named asemblies

Jeff Griffith

Thanks again.

The issue with versioning is an "unintended consequence" of using strongly-named assemblies. I didn't notice the fact that each assembly has a GUID, but I do need to prohibit the exact case that you mention to insure that Reports aren't shared; that is, Customer A sends a copy of Report A to Customer B, who hacks the GUID with a binary file editor and runs Report A.

I also didn't know that there is a Licensing model in .NET. I'll have to investigate it. I would rather not have to re-invent the wheel.

Jeff Griffith

CSSI






Re: Common Language Runtime Versioning problem with strongly named asemblies

Jeff Griffith

Following up on Mike's advice, I found this reference:

How the Runtime Locates Assemblies

http://msdn2.microsoft.com/en-us/library/aa720133(VS.71,d=printer).aspx

which provides lots of useful links. We should all read this before we write a single line of CSharp code. The last line of this page is:

Note There is no version checking for assemblies without strong names, nor does the runtime check in the global assembly cache for assemblies without strong names.

I also discovered another item. Visual Studio 2005 creates applications with this default assembly version in the AssmeblyInfo.cs file:

[assembly: AssemblyVersion("1.0.*")]

which means that the version changes with every compilation. I can fix some of my problems just by changing this to:

[assembly: AssemblyVersion("1.0.0.0")]

which prevents the compiler from updating the version number without my consent. I stumbled on this when I noticed that some of my Reports had odd versions (e.g. 1.0.282.27845) but the version numbers for other reports never changed and were stuck at "1.0.0.0".

Now to find a way to allow any Report to refer to a base or newer version of the Launcher and a base or newer version of Middleware.






Re: Common Language Runtime Versioning problem with strongly named asemblies

Jeff Griffith

I've been able to resolve the versioning issues by creating an Application Config file that lists the Dependent Assemblies and assigns a CodeBase for each version. This will let me install multiple versions of the Launcher and Middleware DLL's on the customer's machine. The signficance of this is that a customer can buy a Report today and get one set of DLL's, and then come back and buy another Report next year and get a whole different set of DLL's and both sets can live on the machine. The Launcher can use whichever set it likes.

There is, as always, one caveat that makes this work: the Launcher, the Middleware and the Reports may not pass references to objects defined within the system. For example, I have a "CfgData" class in the Launcher that I was passing to the Report. This class is a wrapper on a "string[]" object and it is defined in a DLL that is part of the Launcher. I discovered that if I changed the version numbers on the Launcher executable and the DLL that contains CfgData, I could install the new DLL in a "v2" directory and use the App Config file to tell the Launcher where to find it. The Report could be instantiated because the App Config file also included the location of the "v1" version of this DLL. But, when the Launcher tried to invoke a method in the Report and pass a reference to a "v2" instance of the CfgData object, an exception was thrown. This makes sense because the "v2" and "v1" CfgData objects could be completely different objects. In my case, Iwas able to circumvent this by flattening the object into a "string[]" and passing a reference to the array.

Thanks for your suggestions,

Jeff Griffith

CSSI

PS: The App Config file has this pattern with one "dependent assembly" section for each version of each DLL:

<configuration>
<runtime>
<assemblyBinding xmlns="urnTongue Tiedchemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="LauncherBusinessObjects"
publicKeyToken="f93ed8ba7bc441f0"
culture="neutral"/>
<codeBase version="1.0.0.0" href="v1.0.0.0/LauncherBusinessObjects.dll"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>