views:

1639

answers:

3

I have created a DLL that will gather information from the AssemblyInfo.cs. In the class constructor I am using Reflection to get the top-most application that is running.

public class AppInfo()
{
    public AppInfo()
    {
        System.Reflection.Assembly assembly =
            System.Reflection.Assembly.GetEntryAssembly();
        if (assembly == null)
            assembly = System.Reflection.Assembly.GetCallingAssembly();


        //code to gather needed information
    }
}

How this is intended to be used is if I call this from any DLL in a given application, MyApp, that lets say the name will always be 'MyApp'. Retrieving that information is not a problem and it works great in Windows Services and Windows Applications. My question is this: How do I get the Assembly of the top-most Website?

I have found a few articles and I can get the information in the Global.asax.cs by moving the AssemblyInfo.cs for the Website out of the App_Code folder and into the root of the Website. Then by adding a compilerOption to the physical path of the AssemblyInfo.cs

<compiler
language="c#;cs;csharp"
extension=".cs"
compilerOptions="C:\Sandbox\MyWebSite\AssemblyInfo.cs"
type="Microsoft.CSharp.CSharpCodeProvider,System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" warningLevel="4">

Using that I am able to retrieve information in the AssemblyInfo.cs for the Website through System.Reflection.Assembly.GetExecutingAssembly(). Now I can overload the constructor of my AppInfo class to accept an Assembly and retrieve information that way, but if another DLL that is used by MyWebSite creates a new AppInfo() I will get the assembly information of that DLL instead of the parent Website.

I know that if I was working with Web Apps instead of Web Sites I wouldn't have this issue, but for reasons I won't go into I am not able to use Web Apps. Any suggestions on how I can read information from the AssemblyInfo.cs of the Website I'm running in no matter what DLL I'm in?

EDIT: I need this to work for Web Sites, Windows Apps and Windows Services

+1  A: 

If I understand you properly, the problem is that Assembly.GetEntryAssembly() returns null in a Website and Assembly.GetCallingAssembly() is returning the wrong thing because you've got a chain of calls resulting in the website not being the immediate caller. If that's the case, you could find the "Entry Assembly" using the stack trace & walking back up the calling frames. The stack will be full of references from System.Web, etc as the call will have originated from deep within IIS somewhere, but you should be able to pick out the assembly you're interested in by grabbing the lowest frame that you can positively identify as belonging to you. Note that this is pretty hacky, but I think it'll get you what you want...

var trace = new StackTrace();
Assembly entryAssembly = null;
foreach (var frame in trace.GetFrames())
{
   var assembly = frame.GetMethod().DeclaringType.Assembly;
   //check if the assembly is one you own & therefore could be your logical
   //"entry assembly". You could do this by checking the prefix of the
   //Assembly Name if you use some standardised naming convention, or perhaps
   //looking at the AssemblyCompanyAttribute, etc
   if ("assembly is one of mine")
   {
      entryAssembly = assembly;
   }
}

Hopefully someone else will be able to come up with a less nasty way of doing it... but if you're really stuck, this might help.

Alconja
This is the best answer given as it works for not just web sites, but web apps, win apps and win services. The one thing I did was instead of if ("assembly is one of mine") I did if (!company.ToUpper().Contains("MICROSOFT")) entryAssembly = assembly. Since you suggested a stack trace the first entry in the stack, and the last one to be retrieved that is non-microsoft, will always be the calling application. Well done and thanks!
Alexander Kahoun
+1  A: 

AppDomains are created per website and then the bin*.dll files are loaded into that Application's AppDomain. There is not a "Master Assembly" of the site, at least any that has any special meaning.

It is a process. System.Web.Hosting.ProcessHost which creates a System.Web.ApplicationHost through a combination of the System.Web.Hosting.ApplicationManger and System.Web.Hosting.Environment which then creates a System.AppDomain for the application.

AppDomain is secured pretty well so unless you know how to get into an AppDomain, you're out of luck.

If you can update your question, someone may be able to help more.

You MAY be able to do this if security is not setup properly"

String binDir = Server.MapPath("/bin/");
Response.Write(String.Format("Bin dir:{0}<br/><br/>",binDir));

foreach (string file in Directory.GetFiles(binDir, "*.dll"))
{
    Response.Write(String.Format("File:{0}<br/>", file));
    try
    {
        Assembly assembly = Assembly.LoadFile(file);
        object[] attrinutes = assembly.GetCustomAttributes(true);
        foreach (var o in attrinutes)
        {
            //AssemblyCompanyAttribute is set in the AssemblyInfo.cs
            if (o is AssemblyCompanyAttribute)
            {
                Response.Write(String.Format("Company Attribute: Company = {0}<br/>", ((AssemblyCompanyAttribute)o).Company));
                continue;   
            }
            Response.Write(String.Format("Attribute: {0}<br/>", o));
        }
    }
    catch(Exception ex)
    {
        Response.Write(String.Format("Exception Reading File: {0} Exception: {1}",file,ex));
    }
}

You are assuming alot here that the Master site is not compiled (has a App_Code directory) and resides in the same Application Pool as your child site. Do you have needed access to do this? Is the master site a shared hosting site or something?

Chad Grant
This works for Web Sites, but not for Windows Applications or Windows Services. I need it to work for all of them.
Alexander Kahoun
+1  A: 

Deviant is right. But if you can you should use Assembly.Load instead of Assembly.LoadFile.

Here is some nice reference:

http://gisresearch.blogspot.com/2007/09/assembly-load-loadfrom-loadfile.html

Lucas