HowTo: dynamically load an assembly from the GAC

  • Thread starter Thomas W. Brown
  • Start date
T

Thomas W. Brown

I need to dynamically addref a system assembly at runtime (for dynamically
generated code), but Assembly.Load is failing (I'm just specifying the
assembly name but no other components of the full name).

In particular,
Assembly.Load("System.Runtime.Remoting")

fails for me with an "Could not load file or assembly
'System.Runtime.Remoting' or one of its dependencies. The system cannot find
the file specified." error.

I suspect this is because, according to the Assembly.Load documentation,

"You can also make a dynamic reference to an assembly by providing the
calling method with only partial information about the assembly, such as
specifying only the assembly name. In this case, only the application
directory is searched for the assembly, and no other checking occurs. You
make a partial reference using any of the various methods for loading
assemblies such as System.Reflection.Assembly.Load or System.AppDomain.Load."

So, I need the long name of the assembly in order to load from the GAC.
Which brings me to the operative question...

What is the best (easiest) way to determine the proper long name for an
assembly in the GAC so that I can dynamically load said assembly?

Regards,
-- TB
 
T

Thomas W. Brown

Unfortunately this doesn't quite do the trick... I have a string name,
"System.Runtime.Remoting" that I need to use to load the underlying assembly.
I cannot rely on always knowing a type that would reside in that assembly
(in this case, it's a namespace, so the supplied code in that article fails).

Regards,
-- TB
 
T

Thomas W. Brown

Let me make this a bit clearer...

I'm working on a dynamic code generator; based on some data driven values,
I'm creating C# source code in memory and need to compile, instantiate, and
invoke that code. Part of this process involves having assembly references
specified in data and I need to "do the right thing" in my dynamic
compilation.

So, I'm going through the list of these references, and trying to add them
to the ReferencedAssemblies collection of my CompilerParameters object.

The problem, for GAC based system assemblies, is that whenever I try to add
the reference just using the name of the dll (e.g.
"System.Runtime.Remoting.dll"), the code fails to compile (assembly not found
error). I have to specify the full path to the assembly within the GAC.

The easiest way to get this path is via the Assembly.Location property, but
that requires loading the assembly.

So, given only the short assembly filename, how can I either load the
assembly from the GAC (and then obtain the location) or otherwise directly
obtain the location of the assembly?

I know that I could drop down to the file system level and start looking for
the assembly name via a recursive search through the assembly folder, with
some code to extract the version from the parent folder and use the highest
available version perhaps, but this seems very baroque and kludgey and I'm
hoping there is a more elegant approach.

Regards,
-- TB
 
P

parez

Unfortunately this doesn't quite do the trick... I have a string name,
"System.Runtime.Remoting" that I need to use to load the underlying assembly.
I cannot rely on always knowing a type that would reside in that assembly
(in this case, it's a namespace, so the supplied code in that article fails).

Regards,
-- TB

I am not really sure but try using Assembly.LoadWithPartialName.
It mite have worked for me...
 
T

Thomas W. Brown

parez said:
Unfortunately this doesn't quite do the trick... I have a string name,
"System.Runtime.Remoting" that I need to use to load the underlying assembly.
I cannot rely on always knowing a type that would reside in that assembly
(in this case, it's a namespace, so the supplied code in that article fails).

Regards,
-- TB
[snip]

I am not really sure but try using Assembly.LoadWithPartialName.
It mite have worked for me...

Assembly.LoadWithPartialName does indeed work!!

Now, why is this incredibly useful routine deprecated and we are instructed
to use Assembly.Load instead which does not provide the same functionality?!?

I'll check now, but maybe someone knows... Is this deprecated routine still
available in Visual Studio 2008 and .NET 3.5?

Regards,
-- TB
 
Q

qglyirnyfgfo

Hi,

So where are you getting the “"System.Runtime.Remoting.dll" name from
in the first place? Is it stored somewhere in you code?

If so, if you are already storing part of the name of the assembly why
don’ you store the full qualifying name of the assembly (mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)?

I am obviously missing something here, just throwing something out
there to see if it help.
 
T

Thomas W. Brown

Again, this is data driven code generation, I have a list of assemblies that
are required by the dynamically generated module, but these are by assembly
filename only -- no version, culture, public key token, etc. The list is
coming from outside of my application (SQL in this case), so it's not a
question of embedding all the proper information in my application. Hell, if
it were all in my app it wouldn't be a problem as I'd simply addref them from
the start.

Anyway, the deprecated LoadWithPartialName() method works like a charm. I'm
just uneasy now about relying on a deprecated routine.

Regards,
-- TB
 
Q

qglyirnyfgfo

Assembly.LoadWithPartialName does indeed work!!
Now, why is this incredibly useful routine deprecated and we are instructed
to use Assembly.Load instead which does not provide the same functionality?!?

It was deprecated because it not a good idea to use it. This is
because it is critical that you load exactly the assembly version that
you need rather than just throw an assembly name and hope that the
right assembly version gets loaded. This was known as dll hell (and
you can still run into that problem even now in certain
circumstances).

For example, what would you expect the framework to load if you
specify assembly name “System.Runtime.Remoting”? Would you expect to
load version 1.0? 1.1? 2.0?.

You are just asking for trouble by using this method.
I'll check now, but maybe someone knows... Is this deprecated routine still
available in Visual Studio 2008 and .NET 3.5?

Yes the method will be there because .Net 3.5 is .Net 2.0 and .Net 3.0
wrapped into one so those dlls weren’t changed….. It may however not
be there in future versions.
 
T

Thomas W. Brown

It was deprecated because it not a good idea to use it. This is
because it is critical that you load exactly the assembly version that
you need rather than just throw an assembly name and hope that the
right assembly version gets loaded. This was known as dll hell (and
you can still run into that problem even now in certain
circumstances).

Yeah, well, now I'm in GAC hell -- hardly better.
For example, what would you expect the framework to load if you
specify assembly name “System.Runtime.Remoting� Would you expect to
load version 1.0? 1.1? 2.0?.

Most recent, of course, that's what LoadWithPartialName did.
You are just asking for trouble by using this method.


Yes the method will be there because .Net 3.5 is .Net 2.0 and .Net 3.0
wrapped into one so those dlls weren’t changed….. It may however not
be there in future versions.

But what about a situation where I want exactly the functionality that used
to exist... Someone has "told" me that they want to reference
System.Runtime.Remoting but that someone isn't in Visual Studio and doesn't
necessary have any access to gacutil or other means to determine the other
components of an AssemblyName. They are simply creating a list (in SQL, but
that's irrelevent) that I need to process at runtime.

I want to find the most recent version of the assembly in the GAC. This is
exactly what LoadWithPartialName did. Why should I have to roll my own
implementation, examining the file system to recursively descend through all
subfolders underneath %WINDOWS%\Assembly looking for the desired assembly
filename, extract the version from the folder name, track the highest
version, and then assemble my location? That seems like a WOT when the
functionality was already provided.

Perhaps the information is in the registry somewhere???

Regards,
-- TB
 
R

Rene

For example, what would you expect the framework to load if you
Most recent, of course, that's what LoadWithPartialName did.

And where in the documentation does it specify that the version being loaded
is guaranteed to the most recent version? **and** what if company XYZ
creates an assembly called “System.Runtime.Remoting†and drops it in the GAC
too? Now what? What do you expect the .Net framework to load? The Microsoft
one or the XYZ one?
Someone has "told" me that they want to reference
System.Runtime.Remoting but that someone isn't in Visual Studio and
doesn't
necessary have any access to gacutil or other means to determine the other
components of an AssemblyName. They are simply creating a list (in SQL,
but
that's irrelevent) that I need to process at runtime.

This “someone†needs to tell you *exactly* the assembly that needs to be
loaded (name/version/culture/token/social security number etc). *specially*
if it’s an assembly located in the GAC.
 
T

Thomas W. Brown

Rene said:
And where in the documentation does it specify that the version being loaded
is guaranteed to the most recent version? **and** what if company XYZ
creates an assembly called “System.Runtime.Remoting†and drops it in the GAC
too? Now what? What do you expect the .Net framework to load? The Microsoft
one or the XYZ one?

Ummm... in the MSDN docs for LoadWithPartialName. :)

Then company XYZ should be taken out and shot, of course. I'm willing to
cross that bridge when I come to it. Keep in mind that I'm writing internal
applications, not shrink wrap for mass distribution -- I'm willing to make
certain assumptions that may not hold true in general.
This “someone†needs to tell you *exactly* the assembly that needs to be
loaded (name/version/culture/token/social security number etc). *specially*
if it’s an assembly located in the GAC.

Unrealistic, especially since the dynamic generation happens on multiple
workstations and, hypothetically, some may have .NET 2.0 installed, and some
only .NET 1.1. It would be a mistake to effectively bind the yet-to-be
generated and compiled code to a particular version (at least a mistake in my
mind). It would prevent the dynamic creation of code that was able to
examine the system the app was running on and generating code to use the most
recent version of what was there.

Regards,
-- TB
 
A

Andrus

Thomas,
I'm willing to cross that bridge when I come to it. Keep in mind that I'm
writing internal
applications, not shrink wrap for mass distribution -- I'm willing to make
certain assumptions that may not hold true in general.

You have 2 additional possibilities:

1. Add references to all assemblies referenced by your generating
application.
Newer CSScripting implements this. So you can call this engine to run your
scripts.

http://www.members.optusnet.com.au/~olegshilo
http://www.codeproject.com/KB/cs/cs-script_for_cp.aspx

2. Use the code below to resolve.

// http://www.thescripts.com/forum/thread496844.html

// static string FullReference(string relativeReference) {
// // First, get the path for this executing assembly.
// Assembly a = Assembly.GetExecutingAssembly();
// string path = Path.GetDirectoryName(a.Location);

// // if the file exists in this Path - prepend the path
// string fullReference = Path.Combine(path, relativeReference);
// if (File.Exists(fullReference))
// return fullReference;
// else {
// // Strip off any trailing ".dll" if present.
// if
(string.Compare(relativeReference.Substring(relativeReference.Length - 4),
".dll", true) == 0)
// fullReference = relativeReference.Substring(0,
relativeReference.Length - 4);
// else
// fullReference = relativeReference;

// // See if the required assembly is already present in our
currentAppDomain
// foreach (Assembly currAssembly in
AppDomain.CurrentDomain.GetAssemblies()) {
// if (string.Compare(currAssembly.GetName().Name,
fullReference, true) == 0) {
// // Found it, return the location as the full reference.
// return currAssembly.Location;
// }
// }

// // The assembly isn't present in our current application, so
attempt to
// // load it from the GAC, using the partial name.
// try {
// Assembly tempAssembly = Assembly.Load(fullReference);
//.LoadWithPartialName(fullReference);
// return tempAssembly.Location;
// } catch {
// // If we cannot load or otherwise access the assembly from
the GAC then just
// // return the relative reference and hope for the best.
// return relativeReference;
// }
// }
// }

Andrus.
 
T

Thomas W. Brown

The first is possible, but my generating application may not reference all
assemblies that generated code may need or want to references.

As to the second..., well..., this is my code :) From back when I was
using LoadWithPartialName and dropped it when migrating to .NET 2.0 (after
getting the warning about it being deprecated). As you can see, this has
been a nagging thorn in my side for some time now :-D

Regards,
-- TB
 
A

Andrus

The first is possible, but my generating application may not reference all
assemblies that generated code may need or want to references.

You can you //css_reference or using command in script code.
CScript uses fusion.dll assembly to resolve partial references in this case.

http://www.members.optusnet.com.au/~olegshilo/help/Using_.NET_assemblies.html

public static string QueryAssemblyInfo(string assemblyName)
{

AssemblyInfo aInfo = new AssemblyInfo();
aInfo.cchBuf = 1024;
aInfo.currentAssemblyPath = "Path".PadLeft(aInfo.cchBuf);

IAssemblyCache ac = null;
COM.CheckHR(CreateAssemblyCache(out ac, 0));
COM.CheckHR(ac.QueryAssemblyInfo(0, assemblyName, ref aInfo));

return aInfo.currentAssemblyPath;
}

[DllImport("fusion.dll")]
internal static extern int CreateAssemblyCache(out IAssemblyCache
ppAsmCache, int reserved);
}


Andrus.
 
T

Thomas W. Brown

Andrus said:
The first is possible, but my generating application may not reference all
assemblies that generated code may need or want to references.

You can you //css_reference or using command in script code.
CScript uses fusion.dll assembly to resolve partial references in this case.

http://www.members.optusnet.com.au/~olegshilo/help/Using_.NET_assemblies.html

public static string QueryAssemblyInfo(string assemblyName)
{

AssemblyInfo aInfo = new AssemblyInfo();
aInfo.cchBuf = 1024;
aInfo.currentAssemblyPath = "Path".PadLeft(aInfo.cchBuf);

IAssemblyCache ac = null;
COM.CheckHR(CreateAssemblyCache(out ac, 0));
COM.CheckHR(ac.QueryAssemblyInfo(0, assemblyName, ref aInfo));

return aInfo.currentAssemblyPath;
}

[DllImport("fusion.dll")]
internal static extern int CreateAssemblyCache(out IAssemblyCache
ppAsmCache, int reserved);
}


Andrus.


Well, I'm not using CS scripting, I'm writing a full on C#/.NET application,
but I'm sure I could leverage the information here.

However, I've just rolled my own version of what is probably happening in
LoadWithPartialName as well as the AssemblyCache stuff you've referenced
here. Basically, I scan the GAC at the file system level (yes, the process
will require read access to the GAC), looking for all DLLs that are in a
folder that follows the <version>__<publickeytoken> pattern and builds a
static dictionary that maps the assembly filename to the version and
location. If multiple versions are encountered, I keep the highest version.
When I need to add a reference to "System.Runtime.Remoting", I simply look it
up in my dictionary and get the full location.

This way I'm not using a deprecated routine (gasp), and still am able to get
the functionality I require. The downside, of course, is that I am now
tightly coupled to the directory and file structure of the GAC -- something
that I'm sure will come back to haunt me in the not-too-distant future.

Regards,
-- TB
 
A

Andrus

Well, I'm not using CS scripting, I'm writing a full on C#/.NET
application,
but I'm sure I could leverage the information here.

CSScripting can also be used from C# appl just like any other class.
However, I've just rolled my own version of what is probably happening in
LoadWithPartialName as well as the AssemblyCache stuff you've referenced
here. Basically, I scan the GAC at the file system level (yes, the
process
will require read access to the GAC), looking for all DLLs that are in a
folder that follows the <version>__<publickeytoken> pattern and builds a
static dictionary that maps the assembly filename to the version and
location. If multiple versions are encountered, I keep the highest
version.
When I need to add a reference to "System.Runtime.Remoting", I simply look
it
up in my dictionary and get the full location.

For .NET assemblies better solution would be to distribute GAC assembly
table with your scripting routine.
Scripting engine can use this table to translate partial names to full
names. Or add references to all .net assemblies to your solution.

For other assemblies you can requite to put them into application directory.
In Windows you can call fusion.dll insted of requiring read access to GAC
directories.

Andrus.
 
A

Ashley

I know that this is an old thread, but it may be of interest to people.
Instead of using Assembly.Load, use:
Assembly.GetAssembly(typeof (namespace.classname)). The method just
needs the type of a class that you know is in that assembly.

Hope this helps someone!

Ash...
 
A

Ashley

I know that this is an old thread, but it may be of interest to people.
Instead of using Assembly.Load, use:
Assembly.GetAssembly(typeof (namespace.classname)). The method just
needs the type of a class that you know is in that assembly.

Hope this helps someone!

Ash...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top