Jerry Orman

Adventures in SharePoint

Loading C++ Assemblies in ASP.Net

Loading C++ Assemblies in ASP.Net

Rate This
  • Comments 12

When you reference a Native C++ assembly from ASP.Net you may run into the following error:

System.IO.FileNotFoundException: The specified module could not be found.
(Exception from HRESULT: 0x8007007E)

[FileNotFoundException: The specified module could not be found. (Exception from HRESULT: 0x8007007E)]
System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) +0
System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +211
System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +141
System.Reflection.Assembly.Load(String assemblyString) +25
System.Web.Configuration.CompilationSection.LoadAssemblyHelper(String assemblyName, Boolean starDirective) +32

[ConfigurationErrorsException: The specified module could not be found. (Exception from HRESULT: 0x8007007E)]
System.Web.Configuration.CompilationSection.LoadAssemblyHelper(String assemblyName, Boolean starDirective) +596
System.Web.Configuration.CompilationSection.LoadAllAssembliesFromAppDomainBinDirectory() +3591161
System.Web.Configuration.CompilationSection.LoadAssembly(AssemblyInfo ai) +46
System.Web.Compilation.BuildManager.GetReferencedAssemblies(CompilationSection compConfig) +177
System.Web.Compilation.BuildProvidersCompiler..ctor(VirtualPath configPath, Boolean supportLocalization, String outputAssemblyName) +180
System.Web.Compilation.ApplicationBuildProvider.GetGlobalAsaxBuildResult(Boolean isPrecompiledApp) +3558605
System.Web.Compilation.BuildManager.CompileGlobalAsax() +51
System.Web.Compilation.BuildManager.EnsureTopLevelFilesCompiled() +462

[HttpException (0x80004005): The specified module could not be found. (Exception from HRESULT: 0x8007007E)]
System.Web.Compilation.BuildManager.ReportTopLevelCompilationException() +57
System.Web.Compilation.BuildManager.EnsureTopLevelFilesCompiled() +612
System.Web.Hosting.HostingEnvironment.Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters) +642

[HttpException (0x80004005): The specified module could not be found. (Exception from HRESULT: 0x8007007E)]
System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +3539851
System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +69
System.Web.HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr) +252

 

The core cause to this problem is in the way the operating system loads native DLL's at runtime. Native DLL's are loaded using the following logic which does not include the Temporary ASP.net Files nor the applications /bin folder. This problem will also occur in any .Net application if the Native DLL is not included in the /bin folder with the .EXE file or if the DLL is not in the Path Environment Variable.
 

  1. The directory from which the application loaded.  In the case of ASP.Net, this will resolve to %windir%\Microsoft.Net\Framework\v###\ or %windir%\system32\inetsrv for IIS 6.  
  2. The current directory.  In the case of ASP.Net, this will resolve to %windir%\System32\inetsrv for IIS 6.  If using the built-in web server, this resolves to a path under C:\Program Files\Microsoft Visual Studio 8.
  3. The Windows system directory.  Use the GetSystemDirectory function to get the path of this directory. 
  4. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory. 
  5. The directories that are listed in the PATH environment variable.


============
The options:
============

  1. Use DLLImport to load the dll using a relative or absolute path at runtime.
  2. Set the PATH Environment Variable so the ASP.Net process can locate the C++ DLL.  You can set this property at runtime so that it only affects the process running your code.  You can also set this globally in the System Properties (Environment Variables | PATH property). Setting this programmatically does not require a reboot and you can point the PATH to the /bin folder of the web app if you want to be able to do XCopy deployments of your ASP.Net application.  Here are the steps to set the Path programmatically from ASP.Net.

 Option 2.a - If you want your Native C++ DLL’s loaded from the /bin of the ASP.Net application. 

  1. Native C++ DLL project
    1. Use al.exe to build a NativeWrapper for the Native C++ DLL.  This allows you to bring the Native C++ DLL along with the DLL that is referencing it.  You can add this in the Post Build Script of the Native C++ DLL Project to automate this.

      al.exe /link:"$(TargetPath)" /out:"$(TargetDir)$(TargetName).NW.dll" /platform:x86
    2. Managed C++ DLL Project
      1. Reference the NativeWrapper DLL - when you build this project, the native wrapper and Native C++ DLL files are copied to the output directory along with the managed C++ DLL
      2. Set Delay Load DLLs to the Native DLL.  (C++ Project Properties, Expand Linker, select Input, Delay Loaded DLLs) - This prevents the Native DLL from loading when the process starts, giving you a chance to set the PATH Environment variable.  If you don’t set this property, moci.net and its dependencies (i.e. the Native DLL) will be loaded before any of your ASP.Net code can run and this will fail unless you have set the PATH environment variable on the machine level.
    3. ASP.Net Project
      1. Reference the managed C++ DLL - The managed C++ DLL, Native C++ DLL, and NativeWrapper DLL are moved into the applications /bin folder.
      2. Add a Global.asax with the following code.  (Right-click the Web Application, select Add, New Item, select Global Application Class).  You could also use an HTTPModule compiled into a DLL if you don’t want people to be able to change your code.  Application_Start runs one time when the application loads and this code will set the PATH environment variable for the process to include the /bin directory of the application.

        protected void Application_Start(object sender, EventArgs e){
            String _path = String.Concat(System.Environment.GetEnvironmentVariable("PATH"), ";", System.AppDomain.CurrentDomain.RelativeSearchPath);
            System.Environment.SetEnvironmentVariable("PATH", _path, EnvironmentVariableTarget.Process);
        }

 Option 2.b - If you want your Native C++ DLLs to load from an installation path outside of the web site you can avoid the AL command.  You would still need to set the Delay Load property on any Managed C++ DLL that loads the Native C++ DLL’s as well as set the Environment Variable.  If you choose to go this route, you can load the path to your Native C++ DLL’s dynamically from the web.config file at runtime:

  1. Native C++ DLL Project - don’t need to do anything in the Post Build Script
  2. Managed C++ DLL Project
    1. Configure Delay Load DLL’s and specify the Native C++ DLL
  3. ASP.Net Project
    1. Reference the Managed C++ DLL - only Managed C++ DLL is in the /bin
    2. In web.config, add the following…use a path where the Native C++ DLL is located:
         <appSettings>
          <add key="NativePath" value="C:\MyNativeDLLs"/>
         </appSettings>
    3. In global.asax, add the following:

      protected void Application_Start(object sender, EventArgs e){
          String _path = String.Concat(System.Environment.GetEnvironmentVariable("PATH"), ";", ConfigurationSettings.AppSettings["NativePath"]);
          System.Environment.SetEnvironmentVariable("PATH", _path, EnvironmentVariableTarget.Process);
      }
  • Jerry, this very complete and detailed post is awesome. It was exactly what I needed to do, and got me back to work very quickly. Thanks for sharing.

  • Fantastic post.  Excellent explanation of the issue and proposed solutions.

  • Hi Jerry -  Thank you for this post!  I am attempting to work w/ option 2b.  I am having a tough time delay signing my Managed C++ .DLL.  Here are the things hanging me up:

    1) I don't understand what you mean by "specify omuGFStandardud.dll".

    2) when I specify /DELAYSIGN it is requiring a full path to the KeyFile, is this what you would expect?

    3) When I provide a full path then I get an error saying that the signature could not be verified.  Apparently I need to fully sign it somehow (perhaps after load?)

    Best Regards and thanks for a great starting point.

  • Really, fantastic post.  Many, many thanks.

  • Really, truly fantastic post. Where have you been in the last 2 years? I am searching for this since ice age. Cheers!

  • I just got the same issue suddenly, my code was working and I had the Path variable set right to the folder which had the unmanaged dll's

    :S

  • Thanks Jerry,

    It helped my cx.

    Thanks

    Jas[Jaskis]

  • This posted helped me a ton, thank you!  I modified 2a slightly to create 2c:

    1) use delay-loading linker settings but drop native wrapper gen - works fine w/o it

    2) keep native DLL's out of "bin" dir as that is recommendation that ASP.NET Quick Response team gave me.  I use "bin_native" instead.

    3) shadow copy native DLL's to AppDomain.DynamicDirectory so automatic updating of app by ASP.NET still triggered by xcopy deployment

    4) use static Global constructor instead of Application_Start - probably no difference

    static Global()

           {

               AppDomain curDomain = AppDomain.CurrentDomain;

               String binDir = Path.Combine( curDomain.BaseDirectory, "bin_native" );

               String shadowCopyDir = curDomain.DynamicDirectory;

               // C++/CLI project needs to link with delay-loading on these DLL's for this to work

               String aafIoDllSrc = Path.Combine(binDir, "Aaf.IO.dll");

               String aafIoDllDst = Path.Combine(shadowCopyDir, Path.GetFileName(aafIoDllSrc));

    #if DEBUG

               String boostThreadDllSrc = Path.Combine(binDir, "boost_thread-vc90-mt-gd-1_37.dll");

               String boostThreadDllDst = Path.Combine(shadowCopyDir, Path.GetFileName( boostThreadDllSrc ) );

    #else

               String boostThreadDllSrc = Path.Combine(binDir, "boost_thread-vc90-mt-1_37.dll");

               String boostThreadDllDst = Path.Combine(shadowCopyDir, Path.GetFileName( boostThreadDllSrc ));

    #endif

               try

               {

                   File.Copy(aafIoDllSrc, aafIoDllDst, true);

               }

               catch (System.Exception ex)

               {

                   _log.Warn( ex.ToString() );

                _log.WarnFormat( "Unable to update {0}", aafIoDllDst );

               }

               try

               {

                   File.Copy(boostThreadDllSrc, boostThreadDllDst, true);

               }

               catch (System.Exception ex)

               {

                   _log.Warn(ex.ToString());

                   _log.WarnFormat("Unable to update {0}", boostThreadDllDst);

               }

               String path = Environment.GetEnvironmentVariable("PATH");

               path = String.Concat(path, ";", shadowCopyDir);

               Environment.SetEnvironmentVariable("PATH", path, EnvironmentVariableTarget.Process);

           }

  • Good information, got native dlls working in IIS, thanks.

  • Thanks for this post Jerry it has been very useful.

    However I have followed the instructions of 2a closely and I still can not get the system to see my native dll at the point of execution. I am working with Visual studio 2010. I upload a compiled version of my website to my host's server. On navigating to the website there is no complaint at load up of the home page (there was before I read this article). But on navigating to the page that should call the native dll the response is as if the dll can not be found. I am at a loss as to what else to do. Any update on your article would be gratefully appreciated.

  • We are having the same problem. It seems the dll works fine when in debugging ASP.NET but as soon as we publish to IIS 6.0 the dll can not be found.

  • Can you believe that after more than 7 years, this blog is still the best resource I can find to resolve my problem?!...

Page 1 of 1 (12 items)
Leave a Comment
  • Please add 5 and 5 and type the answer here:
  • Post