2

I have a C# program that is running as a CGI app in IIS on XP Pro SP3. It runs fine until I reference a library assembly class and use it in the program's code. If I run the CGI program by hand it loads and executes. But when it is run by IIS the referenced assembly fails to bind despite it being right in the same directory as the parent executable. Here's the binding log I get:

=== Pre-bind state information ===
LOG: User = STEVIENEW\IUSR_STEVIENEW
LOG: DisplayName = VOEvent, Version=3.0.7.0, Culture=neutral, PublicKeyToken=null
 (Fully-specified)
LOG: Appbase = file://?/C:/Documents and Settings/Robert B. Denny/My Documents/iis/vomsgtst/
LOG: Initial PrivatePath = NULL
Calling assembly : MsgChkCGI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: No application configuration file found.
LOG: Using machine configuration file from c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\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:/Documents and Settings/Robert B. Denny/My Documents/iis/vomsgtst/VOEvent.DLL.
LOG: Attempting download of new URL file://?/C:/Documents and Settings/Robert B. Denny/My Documents/iis/vomsgtst/VOEvent/VOEvent.DLL.
LOG: Attempting download of new URL file://?/C:/Documents and Settings/Robert B. Denny/My Documents/iis/vomsgtst/VOEvent.EXE.
LOG: Attempting download of new URL file://?/C:/Documents and Settings/Robert B. Denny/My Documents/iis/vomsgtst/VOEvent/VOEvent.EXE.

The Appbase is correct (though what is the '?' in the URI????? maybe that's a hint). The assembly voevent.dll is definitely right in the same directory (Appbase!) as MsgChkCGI.exe. And if I just double click the exe it stsrts up correctly (and has an error due to a missing CGI environment var, but that's expected).

Anyone know what's going on?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Bob Denny
  • 1,306
  • 1
  • 11
  • 18
  • Very strange, the fusion binder can't even find the MsgChkCGI.exe.config file I put there! I tried adding assemblyBInding info to that with the exact path/version of the assembly. It can't even find the @#$%^ app.config file! What Up? – Bob Denny Feb 19 '10 at 03:41
  • 1
    That question mark in the path is a red flag. – Bob Denny Feb 19 '10 at 03:58
  • Data point: I tried this CGI under my venerable O'Reilly WebSite Pro server, also on XP Pro, and it runs fine!!! So this is a problem that is specific to IIS and it's handling of CGI programs that run under the .NET CLR. I need to solve it because this CGI needs to run on my IIS system. – Bob Denny Feb 19 '10 at 04:47

3 Answers3

3

I'm not sure why the might happen.

As a workaround, you could handle AppDomain.CurrentDomain.AssemblyResolve

EDIT: Note that if you use types from another assembly in the same method that adds the handler, the event won't fire, because the assembly will be loaded by the JITter before the code in the method is actually executed.
Therefore, you need to put any code that uses types from other assemblies in a separate method, then call that method after handling AssemblyResolve.

2nd EDIT: Call Assembly.Load(File.ReadAllBytes(path)).
Also, can you edit your question to include all of the details of the FileNotFoundException from Assembly.LoadFile?

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • It appears that this event is never fired. The problem appears to happen before the program starts to execute, not when it first tries to create a type in that referenced assembly. Thanks for trying though! – Bob Denny Feb 19 '10 at 04:12
  • Add the handler, then put _all_ of your code in a separate function and call it after the handler. It _will_ work. – SLaks Feb 19 '10 at 04:13
  • Thanks, I'll give that a try. I appreciate your persistence :-) Er, I assume you mean to add the handler to the event in Main(), then call a function which contains the entire rest of the program right after that. – Bob Denny Feb 19 '10 at 04:48
  • It's getting hit. Now for the amazing part: I use ResolveEventArgs to identify which assembly it's trying to resolve (it is the fully qualified assy name) and construct a path to the assembly file. Then I call File.Exists() on that path, it returns true. I then use the exact same path (a string variable) in a call to Assembly.LoadFile() and it throws a System.IO.FileNotFoundException!! See above, this runs on Windows on a non-IIS webserver without jumping through these hoops. This is something being done by IIS! – Bob Denny Feb 19 '10 at 12:27
  • @bob: yeah, .net has a very "interesting" usage of the FNF exception there. This can also mean that one of the dependencies is not found. Enable the Fusion log to get details. – David Schmitt Mar 08 '11 at 13:51
1

Based on the SLaks answer, I've written the solution

Assembly assembly = Assembly.GetEntryAssembly();
Uri codeBaseUrl = new Uri(assembly.CodeBase);
// hack IIS CGI malformed url
if (codeBaseUrl.ToString().StartsWith("file:///?/"))
    codeBaseUrl = new Uri("file:///" + codeBaseUrl.ToString().Substring(10));
string dir = Uri.UnescapeDataString(codeBaseUrl.AbsolutePath);
dir = Path.GetDirectoryName(dir);

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
    var dllName = new AssemblyName(args.Name).Name + ".dll";
    dllName = Path.Combine(dir, dllName);

    return Assembly.LoadFile(dllName);
};

However, other parts of .NET fails due to the malformed URL file:///?/, so I would like to know the solution how to force not to change the URL.pass the normal URL

xmedeko
  • 7,336
  • 6
  • 55
  • 85
0

Another solution is to start the process with the EXE from another CGI, e.g. from PHP, anotehr EXE or CMD.

To use the *.cmd as CGI, type a simple CMD script which runs the EXE

@echo off
%~dp0\myprogram.exe

And configure the IIS to run CMD as CGI. Do the same as for the EXE file, just type your CMD in the proper settings. Then go to the Handler Mappings, choose CGI-exe and change the *.exe to *.cmd. Finally go to the CGI panel and change the Use new console... to True.

xmedeko
  • 7,336
  • 6
  • 55
  • 85