Dynamic load assembly problem in asp.net

  • Thread starter Thread starter Johnny Hu
  • Start date Start date
J

Johnny Hu

i have some code which will dynamic load another assembly
it works fine in WinForm App but got an exception "cannot load the assembly"
when i put it in a WebService App


Loader.AssemblyLoader al = null;
object[] parms = { AssemblyName }; // string AssebmlyName
al = (Loader.AssemblyLoader)domain.CreateInstanceFromAndUnwrap(
"Loader.dll", "Loader.AssemblyLoader", true, bindings, null, parms,
null, null, null); //exception here

can anyone tell me the reason and the solution ?
 
Look at fuslogvw.exe and see why it did not load the assembly. My guess is
that the path to the dll is incorrect for the environment it is running in.
 
Hi Johnny,

Generally, if we correctly load an assembly in winform application but
failed in web app, the problem is likely caused by security issue. Is there
any detailed error info which indicate that there is securty problem?

Also, where do you put the assembly? In ASP.NET , the normal assembly
should be put in the web application's private "bin" folder , if it's
strong-named ones, you should put them in GAC.

Regards,

Steven Cheng
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)

Get Preview at ASP.NET whidbey
http://msdn.microsoft.com/asp.net/whidbey/default.aspx
 
the exception is:
"File or assembly name Loader.dll, or one of its dependencies, was not
found."

just exception above, there is no call stack message.

What does the exception say? Can you post the stack trace and the message?
i have some code which will dynamic load another assembly
it works fine in WinForm App but got an exception "cannot load the assembly"
when i put it in a WebService App


Loader.AssemblyLoader al = null;
object[] parms = { AssemblyName }; // string AssebmlyName
al = (Loader.AssemblyLoader)domain.CreateInstanceFromAndUnwrap(
"Loader.dll", "Loader.AssemblyLoader", true, bindings, null, parms,
null, null, null); //exception here

can anyone tell me the reason and the solution ?
 
i have already tested it with a physical path
if the system cannot find the dll, different exception will be got.

Look at fuslogvw.exe and see why it did not load the assembly. My guess is
that the path to the dll is incorrect for the environment it is running in.
i have some code which will dynamic load another assembly
it works fine in WinForm App but got an exception "cannot load the assembly"
when i put it in a WebService App


Loader.AssemblyLoader al = null;
object[] parms = { AssemblyName }; // string AssebmlyName
al = (Loader.AssemblyLoader)domain.CreateInstanceFromAndUnwrap(
"Loader.dll", "Loader.AssemblyLoader", true, bindings, null, parms,
null, null, null); //exception here

can anyone tell me the reason and the solution ?
 
i have tried to sign the Bin folder's full control to networkservice aspnet
everyone internetuser,
and i got the same error (windows 2003)

the Loader.dll is in Bin folder , and the Loader.dll will load another
assembly in another folder.
does it matter ?

you can track my previous post to find out what am i trying to do
http://www.developersdex.com/asp/message.asp?p=2912&ID=<[email protected]>

*****Full Code Below***********

// Loader.cs
// Compile Loader.cs into Loader.dll
using System;
using System.Reflection;
using System.Runtime.Serialization;
namespace Loader
{
// container for assembly and exposes a GetObject function
// to create a late-bound object for casting by the consumer
// this class is meant to be contained in a separate appDomain
// controlled by ObjectLoader class to allow for proper encapsulation
// which enables proper shadow-copying functionality.
public class AssemblyLoader : MarshalByRefObject, IDisposable
{

#region class-level declarations
private Assembly assembly = null;

private CAssemblyInfo assemblyInfo;
public CAssemblyInfo AssemblyInfo
{
get{return assemblyInfo;}
}
#endregion

#region constructors and destructors
public AssemblyLoader( string fullPath )
{
if( assembly == null )
{
assembly = Assembly.LoadFrom( fullPath );

assemblyInfo = new CAssemblyInfo();
assemblyInfo.fullName = assembly.FullName;
assemblyInfo.version = assembly.GetName().Version;
}
}

~AssemblyLoader()
{
dispose( false );
}

public void Dispose()
{
dispose( true );
}

private void dispose( bool disposing )
{
if( disposing )
{
assembly = null;
assemblyInfo = null;
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect( 0 );
}
}
#endregion
//object GetObject ()
//{
// i delete this function for i don't need it.
//}
}
[Serializable]
public class CAssemblyInfo
{
public CAssemblyInfo()
{
}
internal string fullName;
public string FullName
{
get{return fullName;}
}
internal Version version;
public Version Version
{
get{return version;}
}
}
}


// ObjectLoader.cs and WebService.asmx is in the same project
// ObjectLoader.cs
using System;
using System.Reflection;
using System.Collections;
namespace Loader
{
/* contains assembly loader objects, stored in a hash
* and keyed on the .dll file they represent. Each assembly loader
* object can be referenced by the original name/path and is used to
* load objects, returned as type Object. It is up to the calling class
* to cast the object to the necessary type for consumption.
* External interfaces are highly recommended!!
* */
public class AssemblyInfoHelper : IDisposable
{
public AssemblyInfoHelper() {/*...*/}

private AppDomainSetup setup;
private AppDomain domain;
private Loader.CAssemblyInfo assemblyInfo;

public Loader.CAssemblyInfo GetAssemblyinfo(string AssemblyName)
{
Loader.AssemblyLoader al = null;

setup = new AppDomainSetup();
setup.ShadowCopyFiles = "true";

domain = AppDomain.CreateDomain( AssemblyName, null, setup );

BindingFlags bindings = BindingFlags.CreateInstance |
BindingFlags.Instance | BindingFlags.Public;
object[] parms = { AssemblyName };
al = (Loader.AssemblyLoader)domain.CreateInstanceFromAndUnwrap(
"Loader.dll", "Loader.AssemblyLoader", true, bindings, null, parms,
null, null, null);

assemblyInfo = al.AssemblyInfo;
AppDomain.Unload(domain);
return assemblyInfo;
}

~AssemblyInfoHelper()
{
dispose( false );
}

public void Dispose()
{
dispose( true );
}

private void dispose( bool disposing )
{
if( disposing )
{
if (domain != null)
AppDomain.Unload(domain);
}
}
}
}

//WebService.asmx
[WebMethod]
public string GetVersion()
{
Loader.AssemblyInfoHelper ai = new Loader.AssemblyInfoHelper();
string ver = "";
try
{
// get the assembly version info with the physical path
// and the assembly will be unloaded with the separate appDomain
// so i can update the assembly anytime,otherwise i have to manully
// stop the w3p.exe process and will cause other WebApp crash
// this code works in a winform app
ver = ai.GetAssemblyinfo(@"C:\MyAssembly.exe").Version.ToString();
}
catch (Exception ex)
{
return ex.Message;
}
return ver;
}
 
i think i have to re-present the problem

1,when the file path is not right, the exception will be
"File or assembly name Loader.dll, or one of its dependencies, was not
found."

2,after using a physical path , still error
"Loader.dll cannot be loaded"
and i got stack trace in watch window :
"System.IO.FileLoadException"
Server stack trace:
at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase,
Boolean isStringized, Evidence assemblySecurity, Boolean
throwOnFileNotFound, Assembly locationHint, StackCrawlMark& stackMark)
at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Boolean
stringized, Evidence assemblySecurity, StackCrawlMark& stackMark)
at System.Reflection.Assembly.LoadFrom(String assemblyFile, Evidence
securityEvidence, Byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm)
at System.Activator.CreateInstanceFrom(String assemblyFile, String typeName,
Boolean ignoreCase, BindingFlags bindingAttr, Binder binder, Object[] args,
CultureInfo culture, Object[] activationAttributes, Evidence securityInfo)
at System.AppDomain.CreateInstanceFrom(String assemblyFile, String typeName,
Boolean ignoreCase, BindingFlags bindingAttr, Binder binder, Object[] args,
CultureInfo culture, Object[] activationAttributes, Evidence
securityAttributes)
at System.AppDomain.CreateInstanceFromAndUnwrap(String assemblyName, String
typeName, Boolean ignoreCase, BindingFlags bindingAttr, Binder binder,
Object[] args, CultureInfo culture, Object[] activationAttributes, Evidence
securityAttributes)
at
System.Runtime.Remoting.Messaging.StackBuilderSink.PrivateProcessMessage(Met
hodBase mb, Object[] args, Object server, Int32 methodPtr, Boolean
fExecuteInContext, Object[]& outArgs)
at
System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMessa
ge msg, Int32 methodPtr, Boolean fExecuteInContext)
Exception rethrown at [0]:
 
Hi Johnny,

After some test, I've repro the problem you mentioned, and from the asp.net
exception page, it seems that the runtime will always try locating the
assembly in the
{sys dir}:\windows\system32\inetsrv\

rather than the path we set in the AppDomain's AppDomainSetup object.
Anyway, I"m currently consulting some further experts on this and will
reply you when I got any new information.
Thanks for your understanding.


Regards,

Steven Cheng
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)

Get Preview at ASP.NET whidbey
http://msdn.microsoft.com/asp.net/whidbey/default.aspx
 
Hi Johnny,

After some further research, I think we've got the root cause. I'll explain
it detailed here:

In fact, the problems is that we incorrectly use the
AppDomain.CreateInstanceFromAndUnwrap function,

we should use the AppDomain.CreateInstanceAndUnwrap instead.

The difference between the two functions are just like the difference
between Assembly.Load and Assembly.LoadFrom.
Load will search the assembly obey the .net 's assembly locating rules.
From GAC to private bin path...
But the LoadFrom instead, will not. It require the caller to provide a full
path of the assembly such as
LoadFrom("c:\folder\test.dll") , if we don't provide a absolute full path,
it will use the current directory ("Environment.CurrentDirectory").

So as for our issue, we have to alternative means:
1. Use the AppDomain.CreateInstanceAndUnwrap instead of
AppDomain.CreateInstanceFromAndUnwrap , that will make the runtime to
locate the assembly in the privatebin path we specified in the appdomain's
setup info.

#note :
Another thing I'd like to point out is that the setup info's
"PrivateBinPath" is not a absolute path, it should be a subfolder name
which will be combined with the appdomain's basedirectory path so that
generate the whole runtime search path, for example:

string appbase = string appbase = Server.MapPath("~/");
setup.ApplicationBase = appbase + "Addins";
setup.PrivateBinPath = "bin";

2. Still use the AppDomain.CreateInstanceFromAndUnwrap, but we should
specify the full path of the assembly, for example:

util = (CSLib.CSUtil)appDomain.CreateInstanceFromAndUnwrap(appbase +
"Addins\\bin\\"+"CSLib.dll", "CSLib.CSUtil");

Please have a look at the above suggestions. Hope they will help. Thanks.


Regards,

Steven Cheng
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)

Get Preview at ASP.NET whidbey
http://msdn.microsoft.com/asp.net/whidbey/default.aspx
 
As another poster pointed out, CreateInstanceFromAndUnwrap() uses a full
path and CreateInstanceAndUnwrap uses the 4part assembly name. Looking in
fuslogvw at a load failure makes it instantly obvious why it could not find
the assembly in question - it might be an incorrect assembly name, or the
assembly is not in the path probed. Fuslog is a valuable tool for tracking
down the cause of these problems.

Johnny Hu said:
i have already tested it with a physical path
if the system cannot find the dll, different exception will be got.

Look at fuslogvw.exe and see why it did not load the assembly. My guess is
that the path to the dll is incorrect for the environment it is running in.
i have some code which will dynamic load another assembly
it works fine in WinForm App but got an exception "cannot load the assembly"
when i put it in a WebService App


Loader.AssemblyLoader al = null;
object[] parms = { AssemblyName }; // string AssebmlyName
al = (Loader.AssemblyLoader)domain.CreateInstanceFromAndUnwrap(
"Loader.dll", "Loader.AssemblyLoader", true, bindings, null, parms,
null, null, null); //exception here

can anyone tell me the reason and the solution ?
 
i am using another simple method, works at this time.

i wonder there is some way i can read version info from a standard .exe
file.
when using windows explorer, there is version info on status bar when the
focus is on a file.

string ver;
Byte[] asmBytes;
BinaryReader reader = new BinaryReader(new FileStream(
Server.MapPath(".\\bin\\Test.exe",
FileMode.Open, FileAccess.Read));

asmBytes = new Byte[reader.BaseStream.Length];
reader.Read(asmBytes, 0, (Int32) reader.BaseStream.Length);
reader.Close();
reader = null;

AppDomain dom = AppDomain.CreateDomain("NewDomain",
AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation);
Assembly asm = dom.Load(asmBytes);
ver = asm.GetName().Version.ToString();
AppDomain.Unload(dom);
 
Hi Johnny,

Yes, generally if we want to get an certain assembies's version info, we
can use the reflection api . Such as
Assemly.Getname().Version . Also, there are means to get the fileversion
info. Here is blog thread discussing on this:

#How to get the assembly version and file version of your own assembly?
http://blogs.msdn.com/junfeng/archive/2004/02/28/81407.aspx

Hope also helpful.

Regards,

Steven Cheng
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)
 
Back
Top