I'm making messaging system with C library.
To send messages from C library (DLL), I made this DLL file and tried to callback from C to C#
here is C DLL Code
logging.h
#pragma once
#include <stdio.h>
struct loggingMessage {
void (*fp)(char *, int, int);
};
struct loggingMessage messageConfig;
__declspec(dllexport) void loggingInitialize();
__declspec(dllexport) void print();
__declspec(dllexport) void setEventCallBack(void(*fp)(char *, int, int));
logging.c
void loggingInitialize()
{
messageConfig.fp = NULL;
}
void print(char *str, int length)
{
char buf[1024];
memset(buf, 0, sizeof(char) * 1024);
sprintf(buf, "%s", str);
if (messageConfig.fp != NULL)
{
messageConfig.fp(buf, length, 0);
}
}
void setEventCallBack(void (*fp)(char *buf, int length, int debugLevel))
{
messageConfig.fp = fp;
char str[512] = "stringTmp";
fp(str, 1, 0);
fp(str, 1, 1);
fp(str, 1, 2);
fp(str, 1, 3);
}
Program.cs in C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
delegate void EventCallback(string _input, int length, int level);
namespace console
{
class Program
{
[DllImport("logging.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void print();
[DllImport("logging.dll")]
public static extern void setEventCallBack(EventCallback fp);
[DllImport("logging.dll")]
public static extern void loggingInitialize();
public static void update(string _input, int length, int level)
{
Console.WriteLine("Message : {0} Length {1} Level {2}", _input, length , level);
}
static void Main(string[] args)
{
loggingInitialize();
setEventCallBack(update);
}
}
}
And in console I could see the message, but there was a error about function pointer.
I didn't understand what error was and I wonder how to debug and how to set parameters, pointers between C# and C.
You have declared your DllImport statements as CallingConvention = CallingConvention.StdCall however in your C code you declared them as __declspec, you need to use CallingConvention = CallingConvention.Cdecl instead on all of the imports (the default if you don't specify is CallingConvention.Winapi which maps to StdCall on most platforms).
Related
Below is a toy example to better understand marshaling of string types between c# and c++/dll.
What's the best way to marshal the "MyStrCopy" Function below into c#?
(Preferably without using the unsafe keyword and by marshaling the type into and out of c# as a string type.)
File: MyStrCopy.cs
using System.Runtime.InteropServices;
namespace MySpace {
class MyDll {
[DllImport (#"MyStrCopy")]
public static extern void MyStrCopy(
string dest????, string source????, int dest_length????);
}
}
FILE: MyStrCopy.h:
extern "C" {
void __declspec(dllexport) MyStrCopy(
char* dest, const char* source, int dest_length);
}
FILE: MyStrCopy.cpp
#include <cstring>
#include "MyStrCopy.h"
void MyStrCopy(char* dest, const char* source, int dest_len) {
strncpy(dest, source, dest_len);
dest[dest_len-1] = 0; // zero terminated when source > dest
}
I compile the above file "MyStrCopy.cpp" into a dll called: MyStrCopy.dll
I'm also a little bit curious what it would look like if you return char* as well under same preference of not using unsafe and marshaling type to string. example, if dll exported function looks like this instead:
char* MyStrCopy(char* dest, const char* source, int dest_len) {
return strncpy(dest, source, dest_len);
}
using System.Text;
using System.Runtime.InteropServices;
namespace MySpace
{
class MyDll {
[DllImport("MyStrCopy.dll", CharSet = CharSet.Ansi)]
public static extern void MyStrCopy(
StringBuilder dst_str,
string src_str,
int dst_len);
static void ExampleUsage() {
int dest_len = 100;
StringBuilder dest_str = new StringBuilder(dest_len);
string source = "this is a string";
MyStrCopy(dest_str, source, dest_len);
return;
}
} //class
} //namespace
I have the following issue:
I have to use an external DLL library written in C, which implements a function that two pointer as function inputs. I do not know how to do this from C#...Something like:
typedef struct{
double[3] dArray;
int a;
int b;
}myInput
[...]
myFunction(myInput *input)
I can define a similar struct in C#, but I cannot make my code works fine
Is it possible todo this from C#? Thank you in advance!
Try code like this. I'm not sure of the sizes of the int and double. Int in c can be 16, 32, or 64. Double can be 4 or 8 (usually 8).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
[DllImport("XXXXXXXX.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void myFunction(IntPtr input);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MyInput
{
[MarshalAs (UnmanagedType.ByValArray, SizeConst = 3)]
double[] dArray;
int a;
int b;
}
static void Main(string[] args)
{
MyInput myInput = new MyInput();
IntPtr dataPtr = Marshal.AllocHGlobal(Marshal.SizeOf(myInput));
Marshal.StructureToPtr(myInput, dataPtr, true);
myFunction(dataPtr);
}
}
}
I am trying to do 2 things: get a return value from a C dll function, and have that same function modify 1 of the members of a structure that is passed to it. After much experimentation I was able to get the function to return a value, but I am still unable to get it to return a modified value to the C# code; the value remains (unmodified) as 0.
I've tried lots of variations (ref, [In,Out], etc) to no avail
using System;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
namespace Vexing.Problem{
public class myIdx : VexingObject {
public myIdx(object _ctx) : base(_ctx) { }
private IPlotObject plot1;
[StructLayout(LayoutKind.Sequential)]
public class PLEX { public int yowser; }
[DllImport("my.dll", CharSet = CharSet.Unicode)]
public static extern int cFunction(
[MarshalAs(UnmanagedType.LPStruct)] PLEX mPlex);
PLEX a;
protected override void Create() { a = new PLEX(); }
protected override void CalcBar() {
int mf = cFunction(a);
plot1.Set(a.yowser); }
}}
// pertinent c dll code
typedef struct s_plex { int yowser;} cplex;
extern "C" __declspec( dllexport )
int cFunction(cplex *Cplex){ Cplex->yowser = 44; return 1;}
Your import declaration is wrong.
Setting the CharSet in your case doesn't make any sense (there are no string parameters in the native function declaration).
If you want to pass class instance, ref/out also must be thrown away (classes being passed by reference).
And the main point: extern "C" __declspec( dllexport ) means CallingConvention.Cdecl.
UPDATE. Here's complete working code sample:
C++ (header):
struct CStruct
{
int myField;
};
extern "C" __declspec( dllexport ) int MyFunction(CStruct *pStruct);
C++ (code):
int MyFunction(CStruct *pStruct)
{
pStruct->myField = 100;
return 1;
}
C#:
[StructLayout(LayoutKind.Sequential)]
class MyStruct
{
public int myField;
};
class Program
{
MyStruct myStruct = new MyStruct();
[DllImport("MyLib.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int MyFunction(MyStruct pStruct);
static void Main(string[] args)
{
var p = new Program();
var result = MyFunction(p.myStruct);
Console.WriteLine("Result: {0}, MyStruct.myField = {1}", result, p.myStruct.myField);
}
}
Prints:
Result: 1, MyStruct.myField = 100
I'm trying to learn how to use DLL's in C#. I have a very simple DLL just to test the basics.
// MainForm.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace DLL_Test
{
public partial class Form1 : Form
{
[DllImport("TestDLL.dll",
EntryPoint="?Add##YGHHH#Z",
ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
public static extern int Add(int a, int b);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int num;
try
{
num = Add(2, 3);
richTextBox1.AppendText(num.ToString() + "\n");
}
catch (DllNotFoundException ex)
{
MessageBox.Show(ex.ToString());
}
catch (EntryPointNotFoundException ex)
{
MessageBox.Show(ex.ToString());
}
}
}
}
And the DLL code:
// TestDLL.cpp
__declspec(dllexport) int __stdcall Add(int a, int b) {
return(a + b);
}
dumpbin returns the following:
ordinal hint RVA name
1 0 00011005 ?Add##YGHHH#Z = #ILT+0(?Add##YGHHH#Z)
This (and other attempts listed below) have all returned the same exception:
System.EntryPointException: Unable to find entry point named "..."
So I am at a loss for how to solve this. Perhaps I do not understand how DllMain functions as the C# entry point for a DLL. TestDLL.dll works when I test it in a C++ application.
After searching for help, I've attempted the following changes:
// TestDLL.cpp
extern "C" __declspec(dllexport) int __stdcall Add(int a, int b) {
return(a + b);
}
Which results in this from dumpbin
ordinal hint RVA name
1 0 00011005 _Add#8 = #ILT+135(_Add#8)
Thus, I changed my C# code:
// MainForm.cs
...
[DllImport("TestDLL.dll",
EntryPoint="_Add",
ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
public static extern int Add(int a, int b);
...
I've also tried __cdecl:
// TestDLL.cpp
extern "C" __declspec(dllexport) int __cdecl Add(int a, int b) {
return(a + b);
}
.
// MainForm.cs
...
[DllImport("TestDLL.dll",
EntryPoint="_Add",
ExactSpelling = true,
CallingConvention = CallingConvention.Cdecl)]
public static extern int Add(int a, int b);
...
Perhaps I'm misunderstanding the calling conventions. Any help would be very appreciated. Thank you.
use
extern "C" __declspec(dllexport) int __stdcall Add(int a, int b) { ... }
and
[DllImport("TestDLL.dll", CallingConvention = CallingConvention.Stdcall)]
public static extern int Add(int a, int b);
extern "C" will prevent name mangling with params and return type such as ?Add##YGHHH#Z.
__stdcall will prepend an _ and add #8 : _Add#8 (where 8 is the total size of arguments). Note that it also affects the way parameters are pushed on the stack.
In your DLLImport statement, since you specify CallingConvention.StdCall, you don't need to specify the name mangling. Just give the regular name (Add) and .NET will take care of name mangling (_Add#8).
Note that you must specify the CallingConvention or .NET wouldn't emit the correct code to push arguments on the stack
The following should work.
Unmanged:
extern "C" __declspec(dllexport) int Add(int a, int b)
{
return(a + b);
}
Managed:
class Program
{
[DllImport("TestDLL.dll")]
public static extern int Add(int a, int b);
static void Main()
{
Console.WriteLine(Add(1, 2));
}
}
For future reference: I had a similar problem, solved creating an EMPTY C++ dll project. Probably the standard Visual Studio template causes some trouble.
Refer to this link: http://www.codeproject.com/Articles/9826/How-to-create-a-DLL-library-in-C-and-then-use-it-w
I would like to call this C++ function from my C# code:
void GetArrayOfNames(char** names, int nbOfNames);
To call it in C++, I just define an array of char*:
char* aNames[20];
And allocate each name in a loop:
for(int i-0; i<20; i++)
{
aNames[i] = new char[50];
}
Then call:
GetArrayOfNames(aNames, 20);
In my C# code, I have:
[DllImport("MyDLL.dll")]
unsafe static extern void GetArrayOfNames(char** ppNames, int nbOfNames);
Now, how do I do the memory allocation and call GetArrayOfNames? Also, any way of not having to declare my function as "unsafe"?
Probably:
static extern void GetArrayOfNames([MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr] StringBuilder[] args, int count);
// Call using
StringBuilder[] arr = new StringBuilder[20];
for (int i = 0; i < arr.Length; i++)
{
arr[i] = new StringBuilder(50);
}
GetArrayOfNames(arr, arr.Length);
As I understand char** is just a reference to string. I created a small test for myself and it worked fine:
class Program
{
[DllImport("TestCppLib.dll", CharSet = CharSet.Ansi, EntryPoint = "?fnTestCppLib##YAHPAPAD#Z", CallingConvention=CallingConvention.Cdecl)]
extern static int fnTestCppLib(ref string s);
static void Main(string[] args)
{
var s = "some";
var t = fnTestCppLib(ref s);
Debug.Assert(s == "test");
}
}
The implementation of function in C++ is:
TESTCPPLIB_API int fnTestCppLib(char ** str)
{
*str = "test";
return 42;
}
This ended up working:
static extern int GetArrayOfNames(IntPtr[] astr, int iLength);
And called/setup like this:
IntPtr[] intArr = new IntPtr[200];
for (int i = 0; i < intArr.Length; i++)
{
intArr[i] = Marshal.AllocHGlobal(256);
}
int nbOfNames = 2;
GetArrayOfNames(intArr, nbOfNames);
And to put back in a string:
string tmp;
tmp = Marshal.PtrToStringAnsi(intArr[i]);
Can i help you? my sample code in C++:
//CamMethods.cpp:
#include <iostream>
using namespace std;
#include "CamPS.h"
namespace Export {
char * CameraPS::CamStart(char *s){
return "teste";
};
}
//CamPs.h
namespace Export{
class CameraPS
{
public:
_declspec(dllexport) char * _stdcall CamStart(char *s);
};
}
and in C# i call:
using UnityEngine;
using System;
using System.Collections;
using System.Runtime.InteropServices;
public class DllTeste : MonoBehaviour
{
public int x;
[DllImport ("PS_Cam.dll", EntryPoint="CamStart", CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr CamStart (int s);//i can put <string> in same place of <int>
void Start ()
{
}
void Update ()
{
// IntPtr a = CamStart(x);
// string b = Marshal.PtrToStringAnsi(a);
// Debug.Log(b);
Debug.Log(Marshal.PtrToStringAnsi(CamStart(x)));
}
}
Sorry for my english.