piątek, 5 marca 2010

Debugging Assembly Loading Failures

Debugging Assembly Loading Failures

So...you're seeing a FileNotFoundException, FileLoadException, BadImageFormatException or you suspect an assembly loading failure? Try the steps below to start your debugging process.

First, get the Message property from the exception. If the exception's InnerException property is set, get the Message property from that. If the message is generic, also get the hresult by calling System.Runtime.InteropServices.Marshal.GetHRForException(), passing in the Exception object. If that info is not readily available, you may need to attach a debugger and set it to catch managed exceptions. Then, get the hresult by retrieving the private _HResult field of the managed Exception object.

Retrieving the Fusion log

If the exception message isn t enough for you to determine what the problem is, try getting the Fusion log. It will describe the binding failure (if this is due to an assembly binding failure, instead of a loading failure after the file is found). The exception may already include the log. If not, to get it, run fuslogvw.exe. If you don't have fuslogvw.exe already, install the Framework SDK.

  • For pre-v2.0: click on "Log Failures." If this is an ASP.NET or .NET Windows service app, select the Custom option and using regedit, set [HKLM\Software\Microsoft\Fusion\LogPath] to point to an existing directory (like c:\mylogs, not c:\). If you need to log all binds, not just failing ones, set [HKLM\Software\Microsoft\Fusion\ForceLog] as a DWORD value to 1.
  • For v2: click on "Settings," then choose "Log bind failures to disk" if you only care about the failures or "Log all binds to disk" if you want to see all binding requests.
  • To turn on failure logging during a test run instead of by hand, have your script set [HKLM\Software\Microsoft\Fusion\LogFailures] as a DWORD value to 1 using regedit.

Then, click "OK." Now, re-run the process. Next, click "Refresh" in fuslogvw, and a line should appear for each binding failure (or for each bind, if ForceLog is set). Now, double-click on the line in the "Application" column for the interesting bind, and a web page should come up with the Fusion log.

If the file was found, it was loaded from the last path probed in the log (or from the GAC, if there is no probed path shown).

Note: Unless you are explicitly debugging the failure of a resource to load, you will likely want to ignore failures to find assemblies with the ".resources" extension with the culture set to something other than "neutral". Those are expected failures when the ResourceManager is probing for satellite assemblies.

Troubleshooting Fusion logging

Keep in mind that there may not be any entries if there were no binds or if there were no binding failures (when logging failures only). (And don't forget to restart your application's process after changing logging settings.) If there are not as many entries as you expect, you may need to clear your download cache to make room for them. To do that, in Internet Explorer, choose “Tools“, “Internet Options“, “Delete Files”, “OK“, then re-run your app and finally click “Refresh“ again in fuslogvw.

There is only one Fusion log saved per display name/codebase. So, if the same exact reference is requested twice in the same process, it will only show up once in fuslogvw. To see the duplicate logs, you'll need to stop execution (for example, with breakpoints in your debugger) and then get the log at that time.

For FileNotFoundException:
At the bottom of the log will be the paths that Fusion tried probing for this assembly. If this was a load by path (as in Assembly.LoadFrom()), there will be just one path, and your assembly will need to be there to be found. Otherwise, your assembly will need to be on one of the probing paths listed or in the GAC if it's to be found.

You may also get this exception if an unmanaged dependency or internal module of the assembly failed to load. Try running depends.exe on the file to verify that unmanaged dependencies can be loaded. Note that if you re using ASP.NET, the PATH environment variable it's using may differ from the one the command line uses. If all of them could be loaded, try ildasm.exe on the file, double-click on "MANIFEST" and look for ".file" entries. Each of those files will need to be in the same directory as the manifest-containing file.

For BadImageFormatException:
Try running peverify.exe on the file. That will give a more specific description about why it s considered a bad image. Keep in mind that modules built against v2 can not be loaded by a pre-v2 CLR.

For SecurityException:
You need Execute permission for loading any assembly. Also, if a codebase was used to load this file, you would need both FileIOPermission.Read and FileIOPermission.PathDiscovery or else WebPermission to the location (depending on whether this is a local file or a URL). Try caspol.exe to check your managed security settings.

For FileLoadException:

For an "Access is denied" message (for hresult E_ACCESSDENIED, 0x80070005):
Run tlist -m on the file to see if another process has the file locked and without share-read access. If not, check the ACLs for the file and its dependencies (especially if you're using impersonation).

For a "The located assembly's manifest definition with name [yourAssembly] does not match the assembly reference" message (for hresult FUSION_E_REF_DEF_MISMATCH, 0x80131040):
The Fusion log will say which part of the assembly reference failed to match what was found. It will be the assembly name, culture, public key (or token) or version (if the found assembly was strongly-named).

For "Unverifiable image [yourAssembly] can not be run" or "Can not run executable [yourAssembly] because it contains relocations" messages (for hresult COR_E_FIXUPSINEXE, 0x80131019):
That image must be run as the process exe or else be compiled as a dll. This is because MC++ has made optimizations for you in your image, based on the assumption that it will be the process exe. If it's not the process exe, it won t be loaded at the expected location, so the assumed offsets will be incorrect. When the CLR sees such a file loaded as a non-process exe, it will reject the load.

Published Thursday, May 29, 2003 2:09 PM by Suzanne Cook Filed under:

AppDomain and Shadow Copy

AppDomain and Shadow Copy

.Net Framework has a feature called Shadow Copy. When shadow copy is enabled on an appdomain, assemblies loaded in that appdomain will be copied to a shadow copy cache directory, and will be used from there. This is great because the original file is not locked. So it can be changed at will. ASP.NET uses this extensively. Actually, shadow copy is enabled on every appdomain created by ASP.NET.

This feature is implemented by fusion.

Unfortunately the documentation for shadow copy is rather poor. MSDN has documentation on various things affecting shadow copy. But it does not discuss the detail of shadow copy feature. This post is an attempt to discuss in detail how shadow copy works.

All the discussion is based on AppDomainSetup class unless specified.

There are three things affecting shadow copy:

  1. How to enable shadow copy?
  2. What will be shadow copied?
  3. Where do shadow copied bits go?

1. How to enable shadow copy?

MSDN says it all. Set AppDomainSetup.ShadowCopyFiles to true will enable shadow copy.

2. What will be shadow copied?

AppDomainSetup.ShadowCopyDirectories controls what will be shadow copied. It is a list of directory names, separated by a semicolon. Each directory name should be absolute directory path. Assemblies loaded from those directories will be shadow copied. If ShadowCopyDirectories is null, all assemblies will be shadow copied. (An interesting corner case is what if ShadowCopyDirectories is empty string. I’ll leave this as an exercise for the readers. What do you think the behavior will be?)

3. Where do shadow copied bits go?

AppDomainSetup.CachePath + AppDomainSetup.ApplicationName control where shadow copied bits go. If both CachePath and ApplicationName are set, shadow copied bits will go to CachePath\ApplicationName. Otherwise shadow copied bits will go to your download cache (which is stored in %userprofile%\local settings\application data\assembly). The combination of CachePath and ApplicationName is really handy for ASP.NET, because they can set a common CachePath, and have each application shadow copy to a different location.

You are responsible to clean up the shadow copy cache if you set CachePath+ApplicationName. In the case of download cache as shadow copy cache, it is automatically managed by fusion.

On the default appdomain you cannot change its AppDomainSetup. But you can use AppDomain’s API to change the properties. The relevant APIs are:

SetCachePath, SetShadowCopyFiles, SetShadowCopyPath (the name of this one is not consistent with AppDomainSetup.ShadowCopyDirectories). The ApplicationName is set to the name of the application on default domain.


http://blogs.msdn.com/junfeng/archive/2004/02/09/69919.aspx