Friday, March 05, 2004

Workaround for GetSavedLicenseKey in ASP.NET

When a customer deploys an ASP.NET Web Application that uses any of our ASP.NET WebControls, the license information must be embedded in one of application’s assembly manifests. In our LicenseProvider class, we use the SetSavedLicenseKey() method of the LicenseContext object to save the license information, and then we use the GetSavedLicenseKey() to get it. But in ASP.NET the GetSavedLicenseKey DOES NOT work. The problem was written in the ASP.NET newsgroups by someone called Mark some time ago:

“I've checked out the GotDotNet article before and it doesn't clarify the
issue of using context.GetSavedLicenseKey in a licesnsed control consumed in
an ASP.NET application.

After further research here is my take in the situation :

When compiling an ASP.NET application which contains a licensed control,
VS.NET automatically calls LC.EXE to embed the "licenses" resource in the
ASP.NET project dll for all licensed controls as listed in license.licx. (
This is done for all licensed controls whose providers call
context.SetSavedLicenceKey in LicenseProvider::GetLicense in the constructor
of the control ). I have check the MANIFEST of the compiled ASP.NET assembly
and their is a "licenses" resource that appears to be there.

The problem is that at runtime, context.GetSavedLicenseKey called from the
control doesn't work. I assume this is because the Framework can't locate
the ASP.NET assembly because it has been shadow-copied somewhere...???

Thus any control developer who needs to license a control which may be
consumed in an ASP.NET web application must implement an alternative licence
storage location ( eg LIC file ) is GetSavedLicenseKEy doesn't work.

For me this is a licensing show-stopper, because I have to distribute
licence files with my web control, and these are a sinch to copy/pirate.

If anyone can show how to succesfully use context.GetSavedLicenseKey from a
control within an ASP.NET web application please do! Else could someone from
Microsoft please confirm that it is impossible to do so!

Cheers,

Mark”


Well, dear Mark, we were in the same situation :-(

So after a research over the .NET framework classes we could write a workaround for this issue. We cannot put all the LicenseProvider class we use, of course. The part of the source code we're going to put and share here, is the necessary methods to get the license information embedded in the ASP.NET application’s manifests at runtime. So in your own LicenseProvider class, call the following private method called (of course) GetSavedLicenseKey(). Please add the necessary namespaces references and then write the following:

private string GetSavedLicenseKey(Type type)
{
Hashtable licenseKeys = null;
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
string s = "";

for (int i = 0; i < assemblies.Length; i++)
{
Assembly assembly = assemblies[i];
if (!(assembly is AssemblyBuilder))
{
s = GetLocalPath(assembly.EscapedCodeBase);
s = new FileInfo(s).Name;

Stream stream = assembly.GetManifestResourceStream(s + ".licenses");
if (stream == null)
{
stream = GetManifestResourceStream(assembly, (s + ".licenses"));
}
if (stream != null)
{
licenseKeys = Deserialize(stream, s.ToUpper(CultureInfo.InvariantCulture));
stream.Close();
stream = null;
break;
}
}
}

if (licenseKeys == null)
{
return null;
}
else
{
return (string)licenseKeys[type.AssemblyQualifiedName];
}
}

private string GetLocalPath(string fileName)
{
Uri uri = new Uri(fileName, true);
return (uri.LocalPath + uri.Fragment);
}

private Stream GetManifestResourceStream(Assembly satellite, string name)
{
CompareInfo compareInfo = CultureInfo.InvariantCulture.CompareInfo;
string[] resources = satellite.GetManifestResourceNames();
for (int i = 0; i < resources.Length; i++)
{
string s = resources[i];
if (compareInfo.Compare(s, name, CompareOptions.IgnoreCase) == 0)
{
name = s;
break;
}
}
return satellite.GetManifestResourceStream(name);
}

private Hashtable Deserialize(Stream stream, string embeddedLicenseName)
{
object obj;
IFormatter iFormatter = new BinaryFormatter();
new SecurityPermission(SecurityPermissionFlag.SerializationFormatter).PermitOnly();
new SecurityPermission(SecurityPermissionFlag.SerializationFormatter).Assert();
try
{
obj = iFormatter.Deserialize(stream);
}
finally
{
CodeAccessPermission.RevertAssert();
CodeAccessPermission.RevertPermitOnly();
}
if (obj is object[])
{
object[] objs = (object[])obj;
if (objs[0] is string && (string)objs[0] == embeddedLicenseName)
{
return (Hashtable)objs[1];
}
}
return null;
}

That's it. The code we wrote above is not black magic. If you examine (via Reflector) the .NET framework classes, you will see why the LicenseContext.GetSavedLicenseKey() method fails (doesn't work) in ASP.NET applications. Enjoy the code.