Is it documented somewhere how ASP.Net sets up search paths for native DLLs? I need to be able to replicate the logic in my own code.
For more background: I'm maintaining a managed library (say Managed.DLL) that wraps a native library (say Native.DLL) that in turn uses another native DLL (say Driver.DLL). So far Managed.DLL has been importing functions from Native.DLL using .Net's DllImport attribute, but now I have to change this to hand-coded calls to LoadLibrary and GetProcAddress to get more control; in particular, I need to be able to call FreeLibrary to unload Native.DLL and I can't do this when Native.DLL has been loaded via DllImport.
And here comes the problem: While just DllImport("Native.DLL") is sufficient to locate both Native.DLL and Driver.DLL, calling LoadLibrary("Native.DLL") fails with ERROR_FILE_NOT_FOUND when Managed.DLL is used in an ASP.Net application, because the directory containing Managed.DLL is not on the search path for native code DLLs.
My first thought was to use Assembly.GetExecutingAssembly().Location and then issue the LoadLibrary call with a full path, but then Native.DLL fails to find Driver.DLL, because the directory containing them both is still not on the search path.
I could work around this by using the Assembly.GetExecutingAssembly().Location value to set the native DLL search path with SetDllDirectory, but this has two major drawbacks:
1) SetDllDirectory changes global WinAPI settings and could interfere with other code within the same ASP.Net worker process that also uses native code DLLs; I have also verified that using DllImport attribute does not mess with this setting, so changing it now could indeed break something that was working before.
2) It would still not work for debugging ASP.Net applications from within Visual Studio, because VS copies the managed resources into a temporary directory, but leaves the native DLLs in the project build directory, so they end up in different locations in the debugging session (and the temporary directory is wiped for every debugging session, so copying the native DLLs into it manually does not work either; I had to copy the native DLLs into IIS's directory for the debugging session to find them and this is clearly not acceptable solution).
I really would like to do the compatible thing here, but so far haven't been able to find out what this is and after couple of days of fruitless searches any pointers would be greatly appreciated.
To answer my own question:
1) The keyword I was missing regarding the copying of Managed.DLL was "shadow copying" (James Schubert explains it much better than any official Microsoft documentation I've seen) and the trick is to use Assembly.CodeBase instead of Assembly.Location, because the former gives the the original location of Managed.DLL and the latter the location of the shadow copy (John Sibly and Sneal shared nice code snippets to extract the directory name from the URI in Assembly.CodeBase).
2) The way to make dependencies of the native DLLs available is to explicitly load them using LoadLibrary before they are needed (and since this increments their reference counts, also release them with FreeLibrary when done).
So, the loading sequence is
string dir = Assembly.GetExecutingAssembly().CodeBase;
dir = new Uri(dir).LocalPath;
dir = Path.GetDirectoryName(dir);
IntPtr driver = LoadLibrary(dir + Path.DirectorySeparatorChar + "Driver.DLL");
IntPtr managed = LoadLibrary(dir + Path.DirectorySeparatorChar + "Managed.DLL");
and the unloading sequence
FreeLibrary(managed);
FreeLibrary(driver);
(note also the order of LoadLibrary and FreeLibrary calls).
Related
EDIT: I found a way to get it to work locally, but on Azure I still get System.IO.FileNotFoundException on that assembly.
My question might seem like a duplicate to this question here. But it is slightly different, I have already tried that solution and it did not work. Here are the details.
I have an ASP.NET MVC App that has a Reference added to a third party CLR DLL. That third-party DLL requires a native DLL which it invokes. Now if I had control over where the Shadow Copying occurs and what is copied, I would be in paradise. The Shadow Copying misses copying that native DLL despite it's Build Action set as Content and Copy To Output Dir set as Copy Always.
So I searched internet and ran into this discussion on SO, which is same as what was mentioned earlier. I tried adding the code that sets the PATH Environment Variable inside Application_Init and Application_Start of Global.asax, I set the breakpoints in both the methods and to my surprise I get the ASP.NET Error Page before it even hits the breakpoint. This leads me to believe that the referenced assembly at the time of binding hits the native DLL and invokes it. What can I do? Can I delay the reference binding somehow?
EDIT: Yes we can, I opened the Referenced DLL's code which was written in Managed C++, I adjusted the linker setting to Delay Load the Native DLL and now my Application_Start executes first. Yayy! but that does not solve the same problem I am having on Azure
Here is the test solution with DLLs
Here is the source code for the Native DLL
Here is the source code for the Referenced Assembly that uses the Native DLL
To download the Native DLL distribution, Go to their distribution page, choose the windows archive with the bitness you desire (I am using 32-bit), and you will find amzi.dll inside APIs/bin directory.
Actual problem was the wrapper DLL not recognized on Azure server because of lack of support of earlier frameworks and toolsets, as well as Debug CRT.
I used XDT/Application_Start to set the PATH environment variable to include the location of my native DLL
I upgraded my Managed C++ Wrapper DLL to use Toolset 14.0 and .NET 4.6.2
Used Linker Setting of /DELAYLOAD on Managed C++ Wrapper DLL
After downloaded the DLLs and source code which you provided, I found that the native DLL depends on x64 platform. Firstly, we need to change the Platform property of our web app to x64 using Azure portal. If the platform button is disabled, you need to scale up your web app plan to Basic level plan or higher level.
In addition, the original path may end with “;”, so we need to check whether it contains “;” and append right content to it. Code below is for your reference.
string path = Environment.GetEnvironmentVariable("PATH");
Trace.TraceError(path);
string binDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Bin");
Trace.TraceError(binDir);
if (path.EndsWith(";"))
{
Environment.SetEnvironmentVariable("PATH", path + binDir);
}
else
{
Environment.SetEnvironmentVariable("PATH", path + ";" + binDir);
}
To test whether the path is set successfully, you could add a page to test it.
public ActionResult GetPath()
{
string path = Environment.GetEnvironmentVariable("PATH");
return Content(path);
}
After path is set, the native DLL can be load successfully on my side.
On my end I added a throw new ApplicationException("Test") at the beginning of Application_Start and instead of getting my test exception, I was getting the DLL load error.
It means the setting path code will not executed. To fix it, you could remove the native DLL reference from your web application. Now your application could work fine and set the path environment variable. Then you could add the native DLL reference back.
Another way to do it is that we could create a webjobs and set the path environment variable in webjobs and deploy this webjobs before deploying your web application.
I am using 32-bit distributions, my native dlls depends on x86/32-bit.
If you use 32-bit distributions and the platform targets of your CLR DLL and your web application are set to "x86 or Any CPU", you won't need to change platform to x64 in web app. Please change it back to x86.
I usually follow the unofficial Meteor FAQ on how to structure my codebase, but I can't figure out where I should put my global constants.
To give an example: I have some database entries with a constant GUID that I need to reference in many points of my app. So far I just attached the constants to the relevant collection, such that in collections/myCollectionWithGuids.coffee it would say:
#MyCollectionWithGuids = new Meteor.Collection "myCollectionWithGuids"
#MyCollectionWithGuids.CONSTANT_ID = "8e7c2fe3-6644-42ea-b114-df8c5211b842"
This approach worked fine, until I need to use it in the following snippet, located in client/views/myCollectionWithGuidsView.coffee, where it says:
Session.setDefault "selectedOption", MyCollectionWithGuids.CONSTANT_ID
...which is unavailable because the file is being loaded before the Collections are created.
So where should I put my constants then, such that they are always loaded first without hacking in a bunch of subdirectories?
You could rely on the fact that a directory names lib is always treated first when it comes to load order.
So I would probably advise you to organize your code as follow :
lib/collections/collection.js
client/views/view.js
In your particular use case this is going to be okay, but you might find cases when you have to use lib in your client directory as well and as the load order rules stack (subdirectories being loaded first), it will be loaded BEFORE the lib folder residing in your project root.
For the moment, the only way to have full control over the load order is to rely on the package API, so you would have to make your piece of code a local package of your app (living in the packages directory of your project root).
It makes sense because you seem to have a collection and a view somehow related, plus splicing your project into a bunch of collaborative local packages tends to be an elegant design pattern after all.
Creating a local package is really easy now that Meteor 0.9 provide documentation for the package.js API.
http://docs.meteor.com/#packagejs
I would put your collection definitions in a lib directory. File structure documentation explains that all files under the lib directory get loaded before any other files, which means your variable would be defined when you attempt to access it in your client-side code.
Generally speaking, you always want your collections to be defined before anything else in your application is loaded or executed, since your application will most likely heavily depend upon the use of the collection's cursor.
I am trying to trace a possible memory leak in a very large ASP.NET application. I am trying to familiarize myself with WinDBG before attempting to use this tool in the live environment.
I have followed the instructions in the following article, which I found very helpful: http://humblecoder.co.uk/uncategorized/spotting-a-memory-leak-with-windbg-in-net. I am able to create a "memory dump" file of the ASP.NET process and show that the delegate is causing the memory leak as specified in the article. I refer to the paragraph in the article that starts: "Next we need the symbols". I did not add the symbol files using File\Symbol File Path; in WinDBG and yet I still seem to be able to debug the application and follow through the remaining steps of the article. Are symbol paths not required with an ASP.NET application?
Because .NET assemblies contain metadata, including the name of every method and its parameters, symbols aren't necessary to obtain a readable stack trace of a managed thread.
One thing symbols can provide is the file name and line number of each statement, so you can more easily figure out which frames in the stack trace correspond to which lines in your source code.
As Michael says symbols are not strictly necessary for managed code as most of the relevant information is available at runtime as metadata, but if you're digging into native code it is very useful to have symbols.
For many scenarios you can just do .symfix which will tell WinDbg to use Microsoft's public symbol server. That will give you access to symbols for all the CLR and Win32 specific calls in your code. Remember to do a .reload if you set the path.
If your code includes native non-Microsoft assemblies as well, you need to append the location of the corresponding PDB files to the symbol path. Use the .sympath command for that.
To troubleshoot symbol loading use the !sym noisy command.
For more information see this.
When ngen is executed on a .NET managed application at installation time, and a crash dump is retrieved from Windows Error Reporting for the app, how can you use it to see a stack trace, variables, etc.?
Here's some background related to the question: We have a .NET app that gets ngened at installation. When it crashes due to an unhandled .NET exception, the crash is bucketted in Windows Error Reporting, and from that I was able to download the minidump.mdmp file from winqual.microsoft.com.
I put minidump.mdmp in a folder containing the .dbg files for the build of the app that crashed, and double-clicked minidump.mdmp to open it in a new instance of VS2008 SP1. My stack trace looks like this:
kernel32.dll!RaiseException() + 0x3d bytes
mscorwks.dll!RaiseTheExceptionInternalOnly() + 0x295 bytes
mscorwks.dll!JIT_Throw() + 0x130 bytes
MyApp.ni.exe!000007feee74c84c()
[Frames below may be incorrect and/or missing, no symbols loaded for MyApp.ni.exe]
0000000070000d5e()
MyApp.ni.exe!000007feee611000()
000000000300bf78()
000000000300bf60()
The Modules window indicates that symbols are loaded for the OS and .NET DLLs, but for the application modules, I get this:
MyApp.exe -> No native symbols in symbol file.
MyApp.ni.exe -> No matching binary found.
MyAppsLibrary.ni.dll -> No matching binary found.
The easiest way to debug those dumps is with the Windows Debuggers (Windbg, cdb, or ntsd) and to load the SOS debugger extension (you can search for SOS for more details).
As far as I remember, the NGEN'ed part of thing shouldn't matter for SOS as long as you have the original EXE and the symbols (Since it's your app, I'd expect you have the non ngen'ed exe and symbols).
"Debugging Tools for Windows" (specifically, WinDBG) has limited support for managed apps. Provided with PDBs, you should be able to see the call stack, including source line references. To see variable values, you'll need to use the SOS plugin, which is more difficult that just opening the call stack window.
If you're able to request the clients to run your app again, ask them to run it with the following environment variable set: COMPLUS_ZapDisable=1
This way CLR won't load the native images when running your app and you'll see your usual modules with symbols on the stack.
http://referencesource.microsoft.com/faq.aspx
Is this of any help for you:
Symbol issue when debugging C# code
The guy seems to be able to load some symbols to debug his application from the crash dump, had a problem loading the correct symbols but someone answered his question.
Since this is managed code, you may need to set the _NT_EXECUTABLE_IMAGE_PATH environment variable to point to the folders where your executables live. In this case, you'll need to locate the folder in the NativeImage cache that points to your assemblies. The debugger needs the images in order to load the assembly.
I've a simple, if not primitive, C++/CLI .NET 2.0 class library. It is used in order to wrap some C++ legacy code for the Web Service. The following facts appear to be true:
Primitive C# test program calls class library and it works.
If class library does not refer to any modules of our code base, it works as well as part of the web service. That is, I load the web service and invoke the methods and receive proper response.
The same moment I replace the copied and pasted code by the calls from our code base libraries, the Web Service stops to load. I get System.IO.FileNotFoundException message.
The problem: I cannot find any place where the file name that couldn't be found is written.
I googled it and gave some permissions to some ASP.NET user on my computer. I copied all the DLLs of our libraries into the same directory where web service is installed. I searched in IIS logs, event logs, etc - no where could I find the name of the module that prevents the web service from coming up.
Any help on the matter would be greatly appreciated.
Boris
Make sure all the dependent DLLs are in the path (Path meaning not the directory where your assembly is, because ASP.net copies your assembly away into a temporary folder, but rather a directory that's included in the System path environment variable).
What calls are you replacing? Could it be the original code gracefully handles missing files (which may not even be important) and yours does not?
Add same rights to the iusr-account that you did to the asp.net-account.