Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A continued saga of C# interoprability with unmanaged C++

After a day of banging my head against the wall both literally and metaphorically, I plead for help:

I have an unmanaged C++ project, which is compiled as a DLL. Let's call it CPP Project. It currently works in an unmanaged environment. In addition, I have created a WPF project, that shall be called WPF Project. This project is a simple and currently almost empty project. It contains a single window and I want it to use code from Project 1. For that, I have created a CLR C++ project, which shall be called Interop Project and is also compiled as a DLL.

For simplicity I will attach some basic testing code I have boiled down to the basics.

CPP Project has the following two testing files:

tester.h

#pragma once
extern "C" class __declspec(dllexport) NativeTester
{
public:
    void NativeTest();
};

tester.cpp

#include "tester.h"
    void NativeTester::NativeTest()
    {
        int i = 0;
    }

Interop Project has the following file:

InteropLib.h

#pragma once
#include <tester.h>
using namespace System;
namespace InteropLib {
    public ref class InteropProject
    {
    public:
        static void Test()
        {
            NativeTester nativeTester;
            nativeTester.NativeTest();
        }
    };
}

Lastly, WPF Project has a single window refrencing Interop Project:

MainWindow.xaml.cs

using System;
using System.Windows;
using InteropLib;
namespace AppGUI
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            InteropProject.Test();
        }
    }
}

And the XAML itself has an empty window (default created).

Once I am trying to run the WPF project, I get the following error:

System.Windows.Markup.XamlParseException: 'The invocation of the constructor on type 'AppGUI.MainWindow' that matches the specified binding constraints threw an exception.' Line number '3' and line position '9'. ---> System.IO.FileNotFoundException: Could not load file or assembly 'InteropLib.dll' or one of its dependencies. The specified module could not be found.
at AppGUI.MainWindow..ctor()

Interestingly enough, if I do not export the class from CPP Project, I do not get this error. Say, if i change tester.h to:

#pragma once
class NativeTester
{
public:
    void NativeTest()
    {
        int i = 0;
    }
};

However, in this case I cannot use my more complex classes. If I move my implementation to a cpp file like before, I get unresolved linkage errors due to my not exporting my code. The C++ code I want to actually use is large and has many classes and is object oriented, so I can't just move all my implementation to the h files.

Please help me understand this horrific error I've been trying resolve without success.

Thanks.

like image 708
Gilad Avatar asked Oct 06 '22 14:10

Gilad


1 Answers

This went wrong right from the start, your tester.h file is not correct. The class should only have the __declspec(dllexport) attribute when you are building the tester project. Any other project that uses the DLL must see the class with the __declspec(dllimport) attribute. Start fixing this by using a macro in tester.h:

#undef EXPORTED
#ifdef BUILDING_DLL
#   define EXPORTED __declspec(dllexport)
#else
#   define EXPORTED __declspec(dllimport)
#endif

class EXPORTED NativeTester {
    // etc..
};

And in your tester project, use C/C++, Preprocessor, Preprocessor Definitions and add BUILDING_DLL.

Next thing is to make sure that the DLL is being stored in the right directory. Which is what the exception is complaining about, it can't find the DLL. The build directory for C++ projects is Debug but for WPF projects it is bin\Debug. Fix that by changing the General + Output Directory setting, make it $(SolutionDir)$bin\(ConfigurationName).

Build the C++ project and verify that you can find the DLL back in the Solution's bin\Debug directory. And check that you also have the .lib file, you'll need that when you build the C++/CLI project. As an extra verification step, run Dumpbin.exe /exports foo.dll from the Visual Studio Command Prompt and check that you indeed see the class getting exported.

The C++/CLI project next, you need to change the Output Directory setting the same way. Add the .lib file to the linker's Additional Dependencies properties. You'll get the kind of linker errors you were talking about if you skip that step. Build it and verify that you get the DLL in the correct bin\Debug directory again.

Repeat these changes for the Release configuration.

Set the project dependencies, the WPF project depends on the C++/CLI project. The C++/CLI project depends on the C++ project. This ensures the projects are getting built in the right order.

You should now have a good shot at using these DLLs in your WPF project.

like image 79
Hans Passant Avatar answered Oct 13 '22 00:10

Hans Passant