How to call from C# simple function in C++ DLL - c#

i have a simple function in c++ (not a method of a class)
__declspec(dllexport) extern "C" void __stdcall TestFunc();
i try to call it from c#:
[DllImport("ImportTest.dll")]
public static extern void TestFunc();
...
TestFunc();
It throws an "entry point could't be found" exception.
Whats wrong?
Thank you for helping me :)

Try (guessing, that DLL is written in VS)
extern "C" __declspec(dllexport) void __stdcall TestFunc();
That's:
__declspec(dllexport) to notify compiler, that this function is to be exported from the DLL;
extern "C" mainly to prevent function name decorations;
__stdcall, because this is default calling convention if you specify none in [DllImport] directive.
In the future, you can check if your function is exported from DLL using Dll export viewer.

In C++ function , at header(if your function is declared in header) add
extern "C" _declspec(dllexport) void TestFunc();
at the function definition use
_declspec(dllexport) void TestFunc()
{
}
At C# side,you need to declare a function like
[DllImport(#"ImportTest.dll",
EntryPoint = "TestFunc",
ExactSpelling = false,
CallingConvention = CallingConvention.Cdecl)]
static extern void NewTestFunc()
Now use , NewTestFunc()

Related

Passing List<int*[]> from c# to c++ dll

I have spent a lot of days trying to solve this problem. I have a c++ dll function that takes as a parameter a "std::vector<int*> *list", I need to pass a compatible pointer from c# in Unity.
I tried in this way:
UPDATE
c++ code:
#include <stdio.h>
#include <array>
#include <vector>
#if _MSC_VER
#define EXPORT_API
#else
#define EXPORT_API __declspec(dllexport)
#endif
extern "C"{
/* Other function */
EXPORT_API void GetBlocks(std::vector<int*> *list, int* listSize){
list->push_back(std::vector<int>(16*16*16, 1).data());
(*block_size)++;
}
}
c# code:
[DllImport("dllLib", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
unsafe private static extern void GetBlocks(ref List<int*[]> block, ref int blockSize);
void Start() {
int blockSize = 0;
List<int*[]> block = new List<int*[]>();
unsafe
{
GetBlocks(ref block, ref blockSize);
}
}
Unity returns this error:
MarshalDirectiveException: Type System.Collections.Generic.List`1<int[]> which is passed to unmanaged code must have a StructLayout attribute.
*
I suppose that I have to manage somehow the conversion using Marshal, but right now I don't know.
Maybe there could be another type instead of List<int*[]> to pass a list of the pointer to an array.
Can you help me?
Thanks

Calling Rust from C# with IntPtr fails on Enum (EntryPointNotFound)

I'm trying to use the 'opaque pointer' style of FFI where the C# (Unity) code only sees my Rust type as an IntPtr that it has to pass around to various Rust functions. However I am getting EntryPointNotFound exceptions as soon as the function that I'm referencing refers to an enum.
The two functions which don't refer to an enum work fine, but the function that does fails to bind apparently and throws a EntryPointNotFoundException. I've included the symbols in the dynlib (bundle) file to show that the symbol is in the file.
I've tried without the "C" in extern "C" in Rust, and CallingConvention=CDecl and CallingConvention=StdCall in C# but that didn't change the situation - the int and int pointer function will run (their respective print functions indeed print), but the enum pointer function just throws EntryPointNotFoundException and "enum pointer fine" is never printed.
ptr_test.rs
pub enum TestEnum{Test(i32)}
#[no_mangle]
pub extern "C" fn ptr_test(arg: *mut i32) {}
#[no_mangle]
pub extern "C" fn int_test(arg: i32) {}
#[no_mangle]
pub extern "C" fn consume_ptr(arg: *mut TestEnum) {}
RustTest.cs
using UnityEngine;
using System.Collections;
using System;
using System.Runtime.InteropServices;
public class RustTest : MonoBehaviour {
[DllImport("libptr_test")]
private static extern void ptr_test(IntPtr x);
[DllImport("libptr_test")]
private static extern void int_test(int x);
[DllImport("libptr_test")]
private static extern void consume_ptr (IntPtr x);
// Use this for initialization
void Start () {
print ("Hello World!");
ptr_test (new IntPtr (0));
print ("pointer fine");
int_test (0);
print ("int fine");
consume_ptr (new IntPtr (0));
print ("enum pointer fine");
print ("Done!");
}
// Update is called once per frame
void Update () {
}
}
nm -gU carved-unity/carved/Assets/libptr_test.bundle
...
00000000000009c0 T _consume_ptr
0000000000000990 T _int_test
0000000000000960 T _ptr_test
...
I'm not able to reproduce your behavior. Here is my Rust source:
pub enum TestEnum{Test(i32)}
#[no_mangle]
pub extern "C" fn consume_ptr(arg: *mut TestEnum) {
println!("{:?}", arg);
}
I compile it with rustc --crate-type=dylib example.rs
I then copied the resulting example.dll into a .NET Console application, and using this P/Invoke signature:
[DllImport("example.dll", EntryPoint = "consume_ptr", CallingConvention = CallingConvention.Cdecl)]
private static extern void consume_ptr(int arg);
static void Main(string[] args)
{
consume_ptr(0);
}
And 0x0 is printed to the console. I don't know how to properly create and Marshal a Rust enumeration from .NET, but it doesn't produce an "EntryPointNotFoundException", and have confirmed that it is properly exported by looking at the exports with Dependency Walker.

DLLimport returns gobbledygook

I have this define in C++ header file
extern "C" __declspec(dllexport) const char* __stdcall GetId(const My_Polyhedron *obj);
and has this in C#
[DllImport("polyhedra.dll", CallingConvention = CallingConvention.Cdecl)]
static private extern string GetId(IntPtr obj);
It returns me gobbledygook
îþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþ2§{€ûW#_‹p
in .net 4.0, but it works well in .net 3.5.
Note:
I have tried charset=...(all possible settings, e.g. Charset.Unicode) in DllImport.
I have tried to let it returns IntPtr instead of string, then use Marshal.PtrToStringAnsi convert it back to string.
I have tried StringBuilder instead of string.
All the solutions above do not work.
If you are the owner of the C++ code, you can modify the function so that it returns the string as a out parameter:
extern "C" __declspec(dllexport) void __stdcall GetId(
const My_Polyhedron *obj, char* result);
Then use this import in C#:
[DllImport("polyhedra.dll", CallingConvention = CallingConvention.Cdecl)]
static private extern void GetId(IntPtr obj, StringBuilder result);
Please note that result is a StringBuilder instead of a string.
You have to allocate enough space first by making the StringBuilder big enough.
Call it like this:
StringBuilder result = new StringBuilder(1000);
GetId(obj, result);
Use result.ToString() to get the string.
There's the return attribute (it isn't really a attribute) that specifies how marshal the returned value:
[return: MarshalAs(UnmanagedType.Bool)]
But I didn't tried it on your code.
Maybe this helps...

Unable to find an entry point when calling C++ dll in C#

I am trying to learn P/Invoke, so I created a simple dll in C++
KingFucs.h:
namespace KingFuncs
{
class KingFuncs
{
public:
static __declspec(dllexport) int GiveMeNumber(int i);
};
}
KingFuns.cpp:
#include "KingFuncs.h"
#include <stdexcept>
using namespace std;
namespace KingFuncs
{
int KingFuncs::GiveMeNumber(int i)
{
return i;
}
}
So it does compile, then I copied this dll into my WPF's debug folder, with code:
[DllImport("KingFuncDll.dll", EntryPoint = "GiveMeNumber", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int GiveMeNumber(
int i
);
And calling it in button click:
private void Button_Click(object sender, RoutedEventArgs e)
{
int num = GiveMeNumber(123);
}
But it gives me exception:
Unable to find an entry point named 'GiveMeNumber' in DLL
'KingFuncDll.dll'.
Really.... what have I done wrong... It obviously able to find the DLL, otherwise would be another exception. But my method name is exactly the same.... I can't think of other reason.
You need to use extern "C" when you export your function so that you suppress C++ name mangling. And you also should not try to p/invoke to members of a class. Use free functions instead:
extern "C" {
__declspec(dllexport) int GiveMeNumber(int i)
{
return i;
}
}
On the managed side your DllImport attribute is all wrong. Don't use SetLastError which is for Win32 APIs only. Don't bother setting CharSet if there are not text parameters. No need for ExactSpelling. And the calling convention is presumably Cdecl.
[DllImport("KingFuncDll.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int GiveMeNumber(int i);
The problem is that you are declaring the C++ "function" inside a C++ class and are telling P/Invoke to use StdCall.
Try to declare a C++ function outside a class and and export it like you did. Then your code should work.
If you really must have a C++ function inside a class, take a look at CallingConvention.ThisCall. But then you are responsible for creating your unmanaged class instance and pass it as the first parameter of your P/Invoke call
The entry point name of a dll file is given in .exp file which is found in debug folder where other source files are present. If dumpbin doesn't work you can try this.

Entry Point Not Found Exception

I'm trying to use a C++ unmanaged dll in a C# project and I'm getting an error when trying to call a function that says that entry point cannot be found.
public class Program
{
static void Main(string[] args)
{
IntPtr testIntPtr = aaeonAPIOpen(0);
Console.WriteLine(testIntPtr.ToString());
}
[DllImport("aonAPI.dll")]
public static extern unsafe IntPtr aaeonAPIOpen(uint reserved);
}
Here is the dumpbin for the function:
5 4 00001020 ?aaeonAPIOpen##YAPAXK#Z
I changed the dll import to [DllImport("aonAPI.dll", EntryPoint="?aaeonAPIOpen")] and [DllImport("aonAPI.dll", EntryPoint="_aaeonAPIOpen")] and no luck.
Using the undname.exe utility, that symbol demangles to
void * __cdecl aaeonAPIOpen(unsigned long)
Which makes the proper declaration:
[DllImport("aonAPI.dll", EntryPoint="?aaeonAPIOpen##YAPAXK#Z",
ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr aaeonAPIOpen(uint reserved);
It looks like the function you're trying to call is compiled as a C++ function and hence has it's name mangled. PInvoke does not support mangled name. You need to add an extern "C" block around the function definition to prevent name mangling
extern "C" {
void* aaeonAPIOpen(uint reserved);
}

Categories

Resources