0

I have a third party library as source code. It comes with a Visual Studio project that builds a .lib from it.

I want to access the functionality from C# though, so I copied the project and changed it to create a dll.

The DLL didn't contain any exported functions though, so I also created a module definition (.def) file, and added the functions I need in the EXPORTS section.

This works for all the functions that are declared in extern "C" blocks. However, some of the functions that I need are declared outside of those blocks.

If I add those to the EXPORTS section I get the error LNK2001 unresolved external symbol XYZ :(

I don't want to change the sourcecode of the 3rd party library if I can avoid it.

What's the most elegant and hopefully simplest way to access those functions as well?

Edit

One more point for clarification: As far I can tell there is no C++ functionality involved in the interface I want to expose. I honestly don't understand why the 3rd party authors did not just include the few remaining functions into the extern "C" blocks. These functions are at the bottom of the header file, maybe the person that added them just made a mistake and put them outside of the extern "C" blocks scope.

katzenversteher
  • 810
  • 6
  • 13
  • 4
    An extra layer of indirection: write your small library that calls the functions and then have those functions exposes as extern C . – CuriouslyRecurringThoughts Jul 10 '19 at 07:08
  • 3
    Or if it's a relatively big project which requires a lot of P/Invokes, use C++/CLI, which is specifically designed for this purpose. You can then directly add a reference to your mixed binary. – Neijwiert Jul 10 '19 at 07:14
  • Your code very specifically does **not** concern C. – Antti Haapala -- Слава Україні Jul 10 '19 at 07:29
  • @AnttiHaapala I thought it does at bit because I want C-linkage. – katzenversteher Jul 10 '19 at 07:43
  • @CuriouslyRecurringThoughts I thought about this as well, it seemed the .def file would be less work, that's why I didn't do it (yet). Another thing that kind of bothers me with this approach is that (as far as I know) to rename all the functions to avoid duplicate declaration errors. Or is there a way to preven those? – katzenversteher Jul 10 '19 at 07:44
  • @Neijwiert Thank you, that sounds pretty useful. I never worked with C++/CLI though and the language looks a little confusing. Since the project deadline is pretty close I'm not sure if there's enough time to learn it. I'll definitely keep this option in mind though! – katzenversteher Jul 10 '19 at 07:46
  • 1
    @katzenversteher the C/C++ linkage is a C++ thing. If you've got a function with non-C-linkage the only solutions are those that do involve C++ and not C at all. – Antti Haapala -- Слава Україні Jul 10 '19 at 07:51
  • @katzenversteher unfortunately if you want a c interface I don't see any other way. But maybe you can automate the process with some scripting – CuriouslyRecurringThoughts Jul 10 '19 at 09:11
  • 2
    This just isn't a real problem, you've got the [DllImport]'s EntryPoint property to declare the mangled name. In fact it is highly desirable, if the library owner ever changes the definition of a function then you get a nice runtime error instead of a very ugly stack corruption problem. But you have to get the .def file correct and you left no breadcrumbs to guess why that is a problem. Use the linker's .map file to see the mangled name. – Hans Passant Jul 10 '19 at 09:24
  • @HansPassant Thank you for the hint with the .map file. I didn't think of it and activated creation of the map file. Thanks a lot! – katzenversteher Jul 10 '19 at 10:11

1 Answers1

2

For C++ one way (IMHO the most elegant) would be using C++/CLI, which is designed for that. Especially if you have not only functions but also classes.

You create a thin wrapper layer which is fairly simple:

  1. Create a CLI class
  2. put a member instance of the original class
  3. wrap all public methods from the original class

Like This (untested):

C++ nativ:

// original c++ class
class Foo {
public:
    Foo(int v) : m_value(v) {}
    ~Foo() {}

    int  value()      { return m_value; }
    void value(int v) { m_value = v; }

private:
    int m_value;
};

CLI wrapper:

// C++/CLI wrapper
ref class FooWrap
{
public:
    FooWrap(int v) { m_instance = new Foo(v); }
    !FooWrap()     { delete m_instance; }
    ~FooWrap()     { this->!FooWrap(); }

    property int value {
        int  get()      { return m_instance->value(); }
        void set(int v) { m_instance->value(v); }
    }

private:
    Foo *m_instance;
};

Here you can find a short howto, which describes it in more detail.

Edit:

From your comments:

[...] I never worked with C++/CLI though and the language looks a little confusing. Since the project deadline is pretty close I'm not sure if there's enough time to learn it. I'll definitely keep this option in mind though!

If you are not looking for the most elegant way, as in your original question, but for the fastest/shortest way: For C++ one way (IMHO the shortest way) would be using C++/CLI, which is designed for that. Especially if you have not only functions but also classes. You create a thin wrapper layer which is fairly simple... Here you can find a short (in 10 min) howto, which describes it in more detail.

user1810087
  • 5,146
  • 1
  • 41
  • 76
  • Your `FooWrap` class should implement `IDisposable`. Also `~FooWrap` is implicitly the `Dispose` method. Since you're handling unmanaged handles you should use the finalizer, which is `!FooWrap`. – Neijwiert Jul 10 '19 at 08:11
  • Thank you for providing an example! Just for clarification (I also added it to my original question): I only want to expose functions, there is (as far I can tell) no cpp functionality involved and I don't understand why the original author of the library did not put these functions inside of the `extern "C"` block as well... – katzenversteher Jul 10 '19 at 09:15
  • 2
    @katzenversteher there are some [disadvantages](https://stackoverflow.com/a/46116950/1810087) with extern "C". Putting every function into extern "C" is not convenient for C++ functions. I could imagine the original author only extern "C" 'd functions he needed for his own work?! – user1810087 Jul 10 '19 at 09:55