Search code examples
c++.net-corelinkerruntime-errorportable-executable

In Visual Studio 2019, linking plain C++ project to Dot Net 5 project builds an invalid executable


Summary: I am using Visual Studio 2019, and trying to create an example that links a plain-old-C++ project to a C++/CLR project that targets .NET 5.0. The solution compiles and builds, but attempting to run it produces an error message saying "not a valid Win32 application."

Question: How can I produce an executable that will actually run?

Steps to reproduce:

  1. Using Visual Studio 2019, create a new solution. Set the configuration to Debug and x86.

  2. Create a new project that is a C++ desktop application, using the C++ Windows Desktop Wizard project template with default options. Name it Interop5Test, and verify that it builds and runs.

  3. Create a new project that is a C++/CLR project targeting .NET 5.0, using the C++ CLR Class Library (.NET Core) template, and name it CppShim5.

  4. Modify the properties and source files of CppShim5 to remove the use of precompiled headers. (I did so to match the requirements of my intended use case.) Verify that the solution still builds and runs.

  5. In the project properties for Interop5Test:

    • modify Linker | Input | AdditionalDependencies by adding ../CppShim5/$(Configuration)/CppShim5.obj

    • modify Linker | General | AdditionalLibraryDirectories by adding C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Host.win-x86\5.0.3\runtimes\win-x86\native (or wherever ijwhost.lib can be found on your machine).

  6. Build the solution, which should succeed.

  7. Press Local Windows Debugger in Visual Studio to start the executable, and observe a popup window that says "Unable to start program ... not a valid Win32 application."

  8. Browse to the Debug folder of the solution, and observe that Interop5.exe, CppShim5.dll, ijwhost.dll, and CppShim5.runtimeconfig.json are all present.

  9. Use ildasm to examine Interop5.exe and CppShim5.dll, and observe that they are 32-bit portable executables with COFF Header | Machine value of 0x14c which means "Intel 386 or later processors and compatible processors."

  10. Open ijwhost.dll with Visual Studio and observe a reasonable looking Version resource. Perform a binary comparison of ijwhost.dll with the one in the additional library directory of step 5, and observe that the files match perfectly.

  11. Examine the contents of CppShim5.runtimeconfig.json and see something that looks reasonable.


Solution

  • I discovered that I can get a valid executable if I link to CppShim5.lib instead of CppShim5.obj.

    Details for how to alter the example in the original question:

    1. Add a class to CppShim5 and mark it as exported from the DLL (by using __declspec(dllexport), see also: https://learn.microsoft.com/en-us/cpp/build/exporting-from-a-dll-using-declspec-dllexport?view=msvc-160). This causes Visual Studio to generate CppShim5.obj and to put it in the Solution's Debug folder.

    2. Edit the properties of Interop5Test, changing Linker | Input | AdditionalDependencies from: ../CppShim5/$(Configuration)/CppShim5.obj to: ../$(Configuration)/CppShim5.lib

    3. In Visual Studio, add to project Interop5Test a dependency on project CppShim5.

    4. With these changes, build the solution. It should now execute.