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
Related
I'm working on a .NET 5 project to bind the AWS-Encryption-SDK from C and C++ to C# using PInvoke, type marshalling, and the DllImportAttribute. When attempting to call this C++ function
aws_cryptosdk_keyring *Build(const Aws::String &generator_key_id, const Aws::Vector<Aws::String> &additional_key_ids = {}) const;
using
[DllImport("aws-encryption-sdk-cpp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern aws_cryptosdk_keyring Build(string generator_key_id, string[] additional_key_ids = null);
I get this error
System.EntryPointNotFoundException : Unable to find an entry point named 'Build' in DLL 'aws-encryption-sdk-cpp.dll'.
The dll signature for this function is
?Build#Builder#KmsKeyring#Cryptosdk#Aws##QEBAPEAUaws_cryptosdk_keyring##AEBV?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##AEBV?$vector#V?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##V?$allocator#V?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std###2##7##Z
So far nothing that I have tried as the EntryPoint has been successful. Does anyone have any ideas?
Expanding on #jwezorek's comment, C# does not know how to call C++ (class) functions, only C functions. There are several reasons why this is so, but I won't get into them here.
You need to create (in C++) a wrapper that converts each C++ call to a 'flattened' C call. For example:
extern "C" void *createClass() {
return (void*) new myClass();
}
extern "C" void destroyClass(void* self) {
delete (myClass*) self;
}
extern "C" int callFunction1(void* self, int x, int y) {
return ((myClass*) self)->function1(x, y);
}
You'll need to write a function for each class function (plus the constructor and destructor). Then compile, linking it to that original C++ DLL's library. Then in C#, write a wrapper to call your flattened class:
public class myClass : IDisposable {
[DllImport("myFlattenedDll", EntryPoint="createClass")]
public static extern IntPtr createClass();
[DllImport("myFlattenedDll", EntryPoint="destroyClass")]
public static extern void destroyClass(IntPtr self);
[DllImport("myFlattenedDll", EntryPoint="callFunction1")]
public static extern int callFunction1(IntPtr self, int x, int y);
private IntPtr self;
public myClass() {
self = createClass();
}
~myClass() {
Dispose();
}
public virtual void Dispose() {
lock (this) {
destroyClass(self);
GC.SuppressFinalize(this);
}
}
public int callFunction1(int x, int y) {
return callFunction1(self, x, y);
}
}
You should also look into a tool called SWIG that can automate a good deal of the wrapper coding for you.
I have a DLL developed in C++ that performs some computations. I am trying to link this DLL into my C# application.
I have a struct within a struct which contains a char array (C string). I have a pointer to the object created by the C++ DLL within my C# application.
namespace Test
[StructLayout(LayoutKind.Sequential)]
public unsafe struct Child
{
public float number1;
public float number2;
[MarshalAs(UnmanagedType.LPArray)]
public char[] name;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct DataStructure
{
public Child child;
...
}
.
.
.
public unsafe partial class Form1:Form {
[DllImport("Calculation.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern IntPtr createInstance();
[DllImport("Calculation.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern DataStructure* processData(IntPtr source); // Setup of data
[DllImport("Calculation.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern int calculate(IntPtr calc); // Perform calculation
.
.
public Form2()
{
private void button1_Click_1(object sender, EventArgs e)
{
// test functions
unsafe
{
IntPtr ptr1 = createInstance(); // Returns pointer to the instance
DataStructure* data = processData(ptr1); // Returns pointer to DataStructure object created through DllImport
data->child.number1 = 1.234F; // Works
data->child.number1 = 9.876F; // Works
data->child.name = "......" // This doesn't work!!
int result = calculate(ptr1); // Returns value when name commented
}
}
}
}
If I don't comment name, I get the Error - Cannot take the address of managed object.
If I comment name, I am able to run the calculation without issue.
I wish to update the value of the char array name but can't seem to figure out the solution. Below is my usual C solution. Thanks!
#include "DataStructure.h"
#define DLLNAME "Calculation.dll"
int main(int argc, char** argv) {
HINSTANCE dllHandle = LoadLibraryA(fileNameDll);
const void* ptr1 = 0;
int result = 0;
DataStructure *data;
ptr1 = createInstance();
data = processData(ptr1);
data->child.number1 = 1.234;
data->child.number2 = 9.876;
sprintf(data->child.name, "3104-03");
result = calculate(ptr1);
}
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).
Apologies if there is a duplicate - I have struggled to find an answer (I have found a few questions around C++ functions that use Function callbacks, and some answers that use Classes as callbacks when called from C/C++ but..
I am in C#.
I am calling a C++ function
I can not change the signature of the C++ function.
Also, I am using the dynamic method of p/invoke rather then the static binding (my example below);
I can handle the situation where a function takes a simple value, or a struct containing simple values, and returns simple values, but in this instance I have a C function which is taking a call-back object.
Following some ideas online, I tried to make a class that had the same signature, then pinned that class, and passed it in., but I get the C# error of 'Object is non-Blittable' (which it doesn't have any variables in it!).
Header file:
again apologies if there are any mistakes in my example, I've tried to strip all non relevant code and explode the macros, but I hope you understand the essence of what is going on
struct someData_t
{
int length; /**< JSON data length */
char* pData; /*< JSON data */
};
namespace FOO {
class ICallback
{
public: virtual ~ICallback() {}
virtual void Callback(const someData_t &response) = 0;
};
}
extern "C" __declspec(dllexport) void process(const someData_t *inData, FOO::ICallback *listener);
My C# file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace Scratchpad {
class Program {
static void Main(string[] args) {
Console.Out.WriteLine("I'm in Managed C#...");
IntPtr user32 = NativeMethods.LoadLibrary(#"somelongpath\my_c.dll");
IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(user32, "process");
process proc = (process)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(process));
String someJson = "{ \"type\":\"someTestJson\"}";
byte[] rawdata = Encoding.UTF8.GetBytes(someJson);
someData myData = new someData();
int dataLength = rawdata.Length * Marshal.SizeOf(typeof(byte)); // I realise byte is size 1 but..
myData.length = rawdata.Length;
myData.pData = Marshal.AllocHGlobal(dataLength);
Marshal.Copy(rawdata, 0, myData.pData, dataLength);
Console.Out.WriteLine("Size of mydata: " + Marshal.SizeOf(myData));
IntPtr unmanagedADdr = Marshal.AllocHGlobal(Marshal.SizeOf(myData));
Marshal.StructureToPtr(myData, unmanagedADdr, true);
// ################################################################
// FIXME: This area still working
Callbacker myCallback = new Callbacker();
GCHandle gch = GCHandle.Alloc(myCallback, GCHandleType.Pinned);
IntPtr mycallbackPtr = gch.AddrOfPinnedObject();
// FIXME: close of working area.
// ################################################################
// CALL THE FUNCTION!
proc(unmanagedADdr, mycallbackPtr);
myData = (someData) Marshal.PtrToStructure(unmanagedADdr, typeof(someData));
Marshal.FreeHGlobal(unmanagedADdr);
Marshal.FreeHGlobal(myData.pData);
gch.Free();
unmanagedADdr = IntPtr.Zero;
bool result = NativeMethods.FreeLibrary(user32);
Console.Out.WriteLine("Fini!)");
}
private delegate void process(IntPtr data, IntPtr callback);
[StructLayout(LayoutKind.Sequential)]
private struct someData {
public int length;
public IntPtr pData;
}
private class Callbacker {
public void Callback(someData response) {
Console.WriteLine("callback Worked!!!");
}
}
}
static class NativeMethods {
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
}
}
Any suggestions welcome
You could create some un-managed wrapper for Your managed Callbacker class which implements ICallback interface.
Something like this:
typedef void (*PointerToManagedFunctionToInvoke)(const someData_t&);
class UnmanagedDelegate : public FOO::ICallback {
private:
PointerToManagedFunctionToInvoke managedCallback;
public:
UnmanagedDelegate(PointerToManagedFunctionToInvoke inManagedCallback)
: managedCallback(inManagedCallback) {}
virtual void Callback(const someData_t &response)
{
managedCallback(response);
}
};
// Export this to managed part
UnmanagedDelegate* CreateUnmanagedDelegate(PointerToManagedFunctionToInvoke inManagedCallback)
{
return new UnmanagedDelegate(inManagedCallback);
}
Then at C# part You could create a delegate to marshal as PointerToManagedFunctionToInvoke, pass it to CreateUnmanagedDelegate receive unmanaged implementation of ICallback and use that to pass to your process
Please be aware that managedCallback should stay allocated at C# side while UnmanagedDelegate class object is alive. And You should delete the UnmanagedDelegate object when it is not used anymore.
OR You could use thin C++/CLI to implement this wrapper.
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