4

I have a simple executable that i would like to use a dll in. I have added it as a reference. I created an app.config file because the dll is not ultimately going to be in the executable directory. If i execute my program from the IDE, everything is fine, b/c the the dll is copied it locally, but as soon as i pull my executable out it crashes. My fusion log implies it that it cannot find the file specified.

My Main() method:

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);

Assembly assembly = null;
String dllLocation = @"C:\BMS_ACTD\bin\DX\Tools\BmsReplayAnalysis.dll";
IToolsInterface myProgram = null; //from ToolsInterface.dll
try
{
    assembly = Assembly.LoadFrom(dllLocation);
}
catch
{
}

foreach (Type myType in assembly.GetTypes())
{
    if (myType.GetInterface(typeof(IToolsInterface).FullName) != null)
    {
        myProgram = (IToolsInterface)assembly.CreateInstance(myType.Namespace + "." + myType.Name, true);
        myProgram.RunTool();
        break;
    }
}

Here is my config file:

<runtime>
    <dependentAssembly>
        <assemblyIdentity name="ToolsInterface" publicKeyToken="null" culture="neutral" />
        <codeBase version="1.0.0.0" href="file://C:/BMS_ACTD/bin/DX/Globals/ToolsInterface.dll"/>
    </dependentAssembly>
</runtime>

I don't want to have to worry about doing strong naming. I will only have 1 version of that dll, and that is all i care about.

here is the excerpt from the fusionlog:

The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable  C:\Users\greenj\Desktop\BmsReplayLauncher.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: User = BMS-JMGREEN\greenj
LOG: DisplayName = ToolsInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
 (Fully-specified)
LOG: Appbase = file:///C:/Users/greenj/Desktop/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = BmsReplayLauncher.exe
Calling assembly : BmsReplayLauncher, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Users\greenj\Desktop\BmsReplayLauncher.exe.Config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Users/greenj/Desktop/ToolsInterface.DLL.
LOG: Attempting download of new URL file:///C:/Users/greenj/Desktop/ToolsInterface/ToolsInterface.DLL.
LOG: Attempting download of new URL file:///C:/Users/greenj/Desktop/ToolsInterface.EXE.
LOG: Attempting download of new URL file:///C:/Users/greenj/Desktop/ToolsInterface/ToolsInterface.EXE.
LOG: All probing URLs attempted and failed.
abatishchev
  • 98,240
  • 88
  • 296
  • 433
Jason
  • 2,147
  • 6
  • 32
  • 40
  • 1
    Is your .config file renamed to match your program? Eg: `program.exe` => `program.exe.config`? – Julien Lebosquain Jul 27 '11 at 18:22
  • @Julien - I need to rephrase the original question based on your response. I had the wrong config (had the vshost version). Having said that, my question is will be updated in a second. – Jason Jul 27 '11 at 18:26

2 Answers2

2

Things always get tricky with .net if you are attempting to load from a folder that isn't within the folder or subfolders of your application EXE's location. I would recommend trying two things. First, use the <probing> attribute to specify where to look for your custom DLL, see this answer, rather than the way you are doing it.

Second, if that doesn't work, try using the AppDomain.AssemblyResolve event instead of using your config file. You can then load your assembly from wherever you want.

Edit: Jay Walker points out that ..NET 4 and higher have changed AssemblyResolve so that if the DLL is one that isn't in the custom folder, return null to retain the default behavior.

Here's some sample code for AssemblyResolve:

Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    if (args.Name == THE_DLL_I_WANT_TO_CUSTOM_LOAD)
    {
        // Load from our custom path
        string finalPath = null;
        try
        {
            finalPath = MyCustomPath + args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
            return Assembly.LoadFrom(finalPath);
        }
        catch ()
        {
        }
    }

    // Not our custom, use the default loading
    return null;
}
Community
  • 1
  • 1
Ed Bayiates
  • 11,060
  • 4
  • 43
  • 62
  • That is correct, which is why I included the AppDomain.AssemblyResolve method (I wasn't sure where your app resided). – Ed Bayiates Jul 27 '11 at 18:41
  • I put that assemblyresolve even as the first line of my code in the Main, but i am crashing even before i get to it. – Jason Jul 27 '11 at 18:57
  • Are you referencing the DLL in a static constructor or static initializer? – Ed Bayiates Jul 27 '11 at 19:10
  • it is being referenced in the static Main. – Jason Jul 27 '11 at 19:12
  • If you have any reference to it in a field initializer, it will run before Main and crash you. Move such references to a static construcor so they get executed after Main starts, or better yet, move it out of being static. – Ed Bayiates Jul 27 '11 at 19:14
  • @AresAvatar let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/1893/discussion-between-jason-and-aresavatar) – Jason Jul 27 '11 at 19:18
  • I'm confused. The console app won't allow me to have a Main that is not static. I created a static constructor and put the code in there. – Jason Jul 27 '11 at 19:27
  • Main is static, yes. But I mean a field within the class that contains Main, referencing something within the DLL that you are failing to load. The DLL should normally not load before a static constructor in that class, or before Main() is called. – Ed Bayiates Jul 27 '11 at 19:31
  • I guess the disconnect is that the code that references the DLL is located in Main. I added an answer with my main to show you what i am doing... – Jason Jul 27 '11 at 19:52
  • Oh, I see. Didn't realize you were getting an 0x80070002. That usually means that an unmanaged DLL didn't load correctly. The dot-net framework doesn't tell you which one, unfortunately. Have you run a full Depends check on your app to see both managed and unmanaged depenencies that are missing? – Ed Bayiates Jul 27 '11 at 19:58
  • I actually don't think i need to. I have really only one dynamic dependency, and that is the ToolsInterface.dll. That itself, does not have any dependencies. it's only a simple interface. Sadly, i have to head out now. wife is schedule to deliver our baby tomorrow so i won't be back for a week or so. Thanks for the help. – Jason Jul 27 '11 at 20:06
  • Per MSDN (http://msdn.microsoft.com/en-us/library/ff527268.aspx) if you implement `AssemblyResolve`, you should only resolve specific assemblies and return null for all others. In this case, you should specifically look for `ToolsInterface.DLL` – Jay Walker May 01 '13 at 21:30
  • 1
    @Jay, that is now correct; it was a change made in the 4.0 framework, not true at the time of my answer. Thanks for pointing out the change, it makes AssemblyResolve much easier to write. – Ed Bayiates May 17 '13 at 13:53
0

Quoting MSDN for <codebase>:

If the assembly has a strong name, the codebase setting can be anywhere on the local intranet or the Internet. If the assembly is a private assembly, the codebase setting must be a path relative to the application's directory.

Your assembly doesn't have any strong name so you can't use this directive by specifying a full path. Sign your assembly (it takes three clicks using Visual Studio) or use AssemblyResolve as suggested by AresAvatar.

Julien Lebosquain
  • 40,639
  • 8
  • 105
  • 117
  • My hope/goal is to use the AssemblyResolve. I actually would prefer to not have to have the config file follow the executable at all times. As mentioned in the discussion above, even a simple dll that contains an interface is failing to load. The assembly resolve is not being reached before the program crashes. I will run the depends.exe shortly (once i get my laptop booted) to see if there is something weird that it may be missing. Do the normal system dependencies count as things that are needed? (i.e. System.Windows.Forms) – Jason Jul 27 '11 at 23:20
  • depends.exe says that there are 2 dlls it can't find. gpsvc.dll and ieshims.dll. There are a couple posts around complaining about this issue. I don't really know if this is the real problem or not, but people didn't really have any solution for it. Here is the other thing, if i change my program to copy local for the tools interface dll, and run it out of the IDE, i have no problem. So WTF? – Jason Jul 28 '11 at 00:37