2

I have a problem when compiling a managed DLL project. The solution consists of two projects, the first is a .NET DLL written in C# and the other is Managed C++ DLL that directly references the C# project.

Both projects/DLLs are strongly named with an snk file on disk. The C# dll has a target framework of "AnyCPU" while the Manage C++ project is compiled twice, one for an x86 target and the other for an x64.

My problem is that when I compile the Managed C++ project to target the x86 platform, the result DLL has a PublicKeyToken = null as reported by ILSpy. When compiling to target an x64 platform, the DLL has the correct PublicKeyToken. I have checked my project properties, the snk file is referenced correctly for both platform targets under Configuration Properties -> Linker->Advanced->Key File with no delay signing; the Target Machine option is also set correctly based on the desired compilation target.

Here is the information shown by ILSpy when I load my DLL.

For the x64 dll:

// MyDll.x64, Version=1.1.1000.1, Culture=neutral, PublicKeyToken=XXXXXXXXX

// Architecture: x64
// This assembly contains unmanaged code.
// Runtime: .NET 2.0

For the x86 dll:

// MyDll.x86, Version=1.1.1000.1, Culture=neutral, PublicKeyToken=null

// Architecture: AnyCPU (64-bit preferred)
// This assembly contains unmanaged code.
// Runtime: .NET 2.0

What concerns me is the Architecture description for the x86 assembly: AnyCPU (64-bit preferred)

I am not sure why its using the AnyCPU configuration and what the 64-bit preferred annotation means exactly?

I also like to mention that my project is built against .NET Framwork 2.0 for the C# project while the Managed c++ project is built against the v90 Platform Toolset. I am using Visual Studio 2010 running on a Windows 7 64-bit machine.

Can someone tell me why this is happening and how can I solve this issue?

Zaid Amir
  • 4,727
  • 6
  • 52
  • 101

1 Answers1

2

It is simply a consequence of how the COR header in the assembly can indicate what processor architecture is desired. You can see the declarations in the CorHdr.h SDK header file, you'll find it in your Windows SDK directory on your machine. You can use the CorFlags.exe utility to display the values.

The only flag available is COMIMAGE_FLAGS_32BITREQUIRED. When set, it indicates to the CLR that you want to run the program in 32-bit mode, even on a 64-bit operating system. An additional flag got added in .NET 4.5, COMIMAGE_FLAGS_32BITPREFERRED, it resolves an ambiguity on ARM cores. Too many assemblies around where 32BITREQUIRED actually means "x86 required" instead of "32-bit required".

So there is nothing similar to a "64 bit required" flag, an assembly can only indicate "32-bit" or "doesn't matter". With the jitter providing the "doesn't matter" glue, it generates the architecture dependent machine code at runtime. Since the 32BITREQUIRED option isn't turned on in your assembly, the disassembler cannot display anything else but AnyCPU.

The next detail is the IMAGE_FILE_HEADER.Machine field in the PE header of the executable file, it indicates what kind of machine the executable can run on. That's a weak signal for .NET assemblies since they don't normally contain any executable code, just MSIL. And it is readily ignored by the Windows loader, .NET assemblies normally have this field set to IMAGE_FILE_MACHINE_I386 to indicate x86. You still get a 64-bit process out of such an EXE assembly, some pretty heroic loader structure patching occurs when such an EXE is loaded. The job of mscoree.dll, the "loader shim". More about that in this post.

Since you targeted x64 in your C++/CLI project, the IMAGE_FILE_HEADER.Machine was set to IMAGE_FILE_MACHINE_AMD64 by the linker. The disassembler saw that, thus producing the "64-bit preferred" annotation.

Don't be fooled by the word "preferred" here. The disassembler didn't look deep enough to see that your assembly actually contains machine code, generated by the C++/CLI compiler. They don't like to, there isn't any disassembler that will decompile the machine code back to C++/CLI source code. The assembly isn't ever going to run on a 32-bit operating system. Kaboom on a 32-bit OS, the program fails with error 11, ERROR_BAD_FORMAT, "An attempt was made to load a program with an incorrect format".

This answers your question, it doesn't otherwise have anything to do with a strong name.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thanks for the thorough answer, however this only addresses my concern about the (64-bit preferred) annotation. My actual problem is with `PublicKeyToken=null` since I depend on it to load the assembly from my client application. – Zaid Amir Feb 03 '14 at 11:35
  • 1
    Yes, I was happy that you didn't actually ask that question. Of course I have no idea, you didn't describe the *exact* steps you took to sign the assembly so I can't tell where this went wrong. Knowing where to *not* look for the problem is 50% of the battle. Keep trying. – Hans Passant Feb 03 '14 at 11:39