While migrating a few Azure Cloud Services to Web Jobs, we started facing the error, Could not load assembly … /msshrtmi.dll,for just one of the projects. The error provides the exact path from where it is trying to load the DLL and is the same path from which the process is running. But the location does have the msshrtmi.dll, which for some reason the process is not able to load.

msshrtmi dll load error

To our surprise, this was happening only with a specific worker, while all others (around 8) were working fine. All of the workers are generated by the same build process on a server. For some reason (I am still investigating into this) the msshrtmi.dll is added as an external reference in the project and referred from there in all the project files. This was done mainly because we had a few external dependencies that were dependent on specific Azure SDK version (2.2). But this explicit reference should not have caused any issues as all, as the other processes were working fine and only a specific one was failing.

One useful tool to help diagnose why the .NET framework cannot locate assemblies is Assembly Binding Log Viewer(Fuslogvw.exe). The viewer displays an entry for each failed assembly bind. For each failure, the viewer describes the application that initiated the bind; the assembly the bind is for, including name, version, culture and public key; and the date and time of the failure.

Fuslogvw.exe is automatically installed with Visual Studio. To run the tool, use the Developer Command Prompt with administrator credentials.

Running fuslogvw with the application shows the assembly binding error, double clicking which gives a details error information, as shown below. This error message gives more details and tells us that the assembly platform or ContentType is invalid.

LOG: Assembly Name is: msshrtmi, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
ERR: Invalid assembly platform or ContentType in file (hr = 0x8007000b).
ERR: Run-from-source setup phase failed with hr = 0x8007000b.
ERR: Failed to complete setup of assembly (hr = 0x8007000b). Probing terminated.

In the Task Manager, the worker with the assembly loading error (last worker in the image below) shows as a 64-bit process, while the others as 32-bit. Since the referred msshrtmi DLL is 32-bit, it explains why it was unable to find the correct platform matching msshrtmi assembly.

msshrtmi task manager

CorFlags.exe is used to determine whether an .exe file or .dll file is meant to run only on a specific platform or under WOW64. Running the corflags on all the workers produces the below two results:

corflags Problematic_Worker.exe
Version : v4.0.30319
CLR Header: 2.5
PE : PE32
CorFlags : 0x1
ILONLY : 1
32BITREQ : 0
32BITPREF : 0
Signed : 0

corflags Worker.exe
Version : v4.0.30319
CLR Header: 2.5
PE : PE32
CorFlags : 0x20003
ILONLY : 1
32BITREQ : 0
32BITPREF : 1
Signed : 0

The 32BITPREF flag is ‘0’ for the worker that shows the error, whereas for the rest shows 1. The 32BITPREF flag indicates that the application runs as a 32 bit process even on 64-bit platforms. This explains why the problematic worker was running as 64-bit process since the flag is turned off.

From .NET 4.5 and Visual Studio 11, the default for most .NET projects is again AnyCPU, but there is more than one meaning to AnyCPU now. There is an additional sub-type of AnyCPU, “Any CPU 32-bit preferred”, which is the new default (overall, there are now five options for the /platform C# compiler switch: x86, Itanium, x64, anycpu, and anycpu32bitpreferred). When using that flavor of AnyCPU, the semantics are the following:

  • If the process runs on a 32-bit Windows system, it runs as a 32-bit process. IL is compiled to x86 machine code.
  • If the process runs on a 64-bit Windows system, it runs as a 32-bit process. IL is compiled to x86 machine code.
  • If the process runs on an ARM Windows system, it runs as a 32-bit process. IL is compiled to ARM machine code.

All the projects are getting built using the same build scripts, and we are not explicitly turning off/on this compiler option. So the next possible place where any setting for this flag is specified is the csproj file. On the properties of the worker project file (the one that shows error), I see that ‘Prefer 32-bit’ option is not checked and the csproj file has it explicitly set to false (as shown below). For other projects, this option is checked in Visual Studio and has no entry in the csproj file, which means the flag defaults to true.

msshrtmi prefer 32bit csproj

Deleting the Prefer32Bit attribute from the csproj and building fixed the assembly loading issue of msshrtmi!

Though this ended up being a minor fix (in terms of code change), I learned a lot of different tools that can be used to debug assembly loading issues. It was using these right tools that helped me identify this extra attribute on the csproj file and help solve the issue. So the next time you see such an error , either with mssrhtmi or another DLL, hope this helps to find your way through!

Comments