dotnet / ClangSharp

Clang bindings for .NET written in C#
MIT License
909 stars 146 forks source link

The generated .cs file doesn't work (Unhandled exception. System.EntryPointNotFoundException) #533

Open Hancapo opened 6 months ago

Hancapo commented 6 months ago

I have the intention to generate bindings for a certain library, so in order to get used to it I did my own addition and subtraction C++ library.

Windows 11 23H2 (Build 22631.3235) ClangSharpPInvokeGenerator 17.0.1 JetBrains Rider 2023.3.3

sumarrestar.h

#pragma once

#ifndef SUMARRESTAR_H
#define SUMARRESTAR_H

class SumarRestar
{
public:
    static int sumar(int a, int b);
    static int restar(int a, int b);
};
#endif

sumarrestar.cpp


#include "sumarrestar.h"

int SumarRestar::sumar(int a, int b)
{
    return a + b;
}

int SumarRestar::restar(int a, int b)
{
    return a - b;
}

I would ideally like to avoid any changes to the original C++ code or at least they should be as minimal as possible.

Compilation settings:

image

The command line I used to generate it:

ClangSharpPInvokeGenerator --file "C:\Users\myusername\RiderProjects\SumarRestar\SumarRestar\sumarrestar.h" --libraryPath SumarRestar.dll -n SumarRestarSharp -o "C:\testing\SumarRestarSharp.cs"

The generated .cs file:

using System.Runtime.InteropServices;

namespace SumarRestarSharp
{
    public partial struct SumarRestar
    {
        [DllImport("SumarRestar.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?sumar@SumarRestar@@SAHHH@Z", ExactSpelling = true)]
        public static extern int sumar(int a, int b);

        [DllImport("SumarRestar.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?restar@SumarRestar@@SAHHH@Z", ExactSpelling = true)]
        public static extern int restar(int a, int b);
    }
}

The error I'm getting from the IDE:

Unhandled exception. System.EntryPointNotFoundException: Unable to find an entry point named '?sumar@SumarRestar@@SAHHH@Z' in DLL 'SumarRestar.dll'.

myd7349 commented 2 months ago

When using PInvoke to call a class implemented in C++, it's important to note that the same C++ class method may have different decorated names in x86 and x64 environments. This means you'll need two separate PInvoke declarations for the same method, each targeting x86 and x64 platforms respectively, and then you'll need to manually determine which version to call in your code.

https://github.com/myd7349/Ongoing-Study/tree/master/c%23/Console/PInvoke/MarshalCppClassV1

myd7349 commented 2 months ago

ClangSharpPInvokeGenerator also provides the following options:

Hancapo commented 2 months ago

I just need x64 modules, nothing else, I tried using the -m64 flag and I still get the same error.

myd7349 commented 2 months ago

You can use this tool to inspect the exposed functions/methods in the C++ DLL. Make sure to check the option to leave the decorations alone.

What is the target architecture of your C# project? Is it AnyCPU or x64?

tannergooding commented 2 months ago

In general binding against C++ is error prone and undesirable due to having an unstable ABI and no standard for exports across platforms/architectures.

Its typically recommended to have a C wrapper over your C++ and then bind against the C wrapper instead.

ClangSharp does get the mangled name directly from Clang but what that name is depends on several factors such as the CPU architecture (x86, x64, Arm64, etc) and the OS (Windows, Linux, MacOS, etc). So you may need to specify -m64 to ensure you have the right bitness and -c unix-types or -c windows-types if you want to explicitly use Unix vs Windows mangling.

Clang however does not work "perfectly" for cross compilation and so simplify specifying -c unix-types on Windows or -c windows-types on Unix may still result in other quirks in the codegen. That is a limitation of Clang