#ifdef SERVER_TCP_EXPORTS
class __declspec(dllexport) Sock_Server
#else
class __declspec(dllimport) Sock_Server
#endif
{
public:
int Server(const char* strErr,int bufSize);
...
}
cpp file
int Sock_Server::Server(const char* strErr,int bufSize)
{
// do something and assign the string to strErr
(say) strErr = "Hello World";
return -1;
}
in C#
[DllImport("Hello.dll", EntryPoint = "Server" CallingConvention = CallingConvention.StdCall)]
private extern static int Server(StringBuilder strErr, int bufSize);
public static int Connect(StringBuilder strErr, int bufSize)
{
int res = Server(strErr,bufSize); /// when the calls come here, strErr is empty
return res; // res has the value -1
}
private void Form1_Load(object sender, EventArgs e)
{
int res = 0;
int bufSize = 4096;
StringBuilder strErr = new StringBuilder(bufSize+1);
res = Connect(strErr, bufSize); //when the calls come here, strErr is empty
MessageBox.Show(strErr.ToString()); // it has the value -1
}
I am not a C# guy.I did some reading before posting this out,and tried all the possible combinations but some reason it didn't work.I am using Visual Studio 2013.I have couple of Q
[Q1] When I did MessageBox.Show(strErr.ToString()); in my C#, it just prints a blank string! Would really appreciate if anyone can help me as I am pretty new to all this.
[Q2] If I give EntryPoint ="Server" my code doesn't work.It complains,enrty point of Server is not found in Hello.dll. So,every time I have to find the exact entry in my dll using dumpbin.exe and then provide exactly how the compiler has created for me
[DllImport("Hello.dll", EntryPoint = "?Server#Sock_Server##QAEHPBDH#Z" CallingConvention = CallingConvention.StdCall)]
Is there a better way of doing this.This is making the code cupled
[Q3] Is there a way to call C++ constructor/destructor.I do need to call both of them.I have called C'tor through some other method,I know that's not a good idea.Any help would be appreciated.
Thanks.
I don't think it's all related to static. It's all about proper assignment of an object. The same piece of code work perfectly fine when called from other C++. I tweaked the code little bit. Now, I used strcpy_s and it's working because while marshing the data, I was loosing the data.
I am still waiting for Q2 to be answered.
Related
I am currently working on a group project where aside from the GUI which is done in C# all of the code is done in C++.
In C++ we are using a funtion that reads formatted text from a file and seperates it into an array of structs, an example of a struct is:
typedef struct example{
string id;
string name;
string email;
char status;
string phone_number;
}example;
And of an array:
example* example_arr=new example[10];
We then want to return that array from the C++ function and use it in C# (DLLImport) to display that info in a Form.
When I started thinking about the implementation of the C# part I realized that I'm pretty much clueless about how to approach it since I've never seen unmanaged memory being used in C#, so I started looking for possible solutions but most of the cases I found are about sending info the opposite direction(from C# to C++) or were rather unclear in their explanation. The only thing I got from them really is that the concept of marshalling might be of use, however, I couldn't really wrap my head around it.
I would honestly appreciate any help on the topic, I really want to make it happen even though I'm not experienced with handling unmanaged memory in C#.
EDIT:
I've tried implementing what Hans suggested but I've faced a problem.
The C# code runs, executes the C++ function which runs as well up until it gets to the return statement and just stops(doesn't break/throw just freezes like it's waiting for something to happen) which in return stops the C# code from continuing it's run.
This is the code(I simplified the struct for the sake of testing):
C++
///header file
typedef struct example
{
int num;
char str[5];
}example;
extern "C" __declspec(dllexport) int __stdcall test(a arr[],int len);
///Cpp file
int __stdcall test(example* arr,int len)
{
for (int i = 0; i < len; i++)
{
arr[i].num = i;
std::cout <<"arr["<<i<<"].num = "<< arr[i].num<<"\n";//for debugging purposes
strcpy(arr[i][0].str, "test");
}
std::cout << "not yet\n";
return -1;//*does not get executed*
}
C#
public partial class Form1 : Form
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct example
{
public int num;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
public string str;
};
[DllImport("unmanaged.dll", CallingConvention=CallingConvention.StdCall)]
public static extern int test(out example[] arr,int len);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)//Form Loaded Event function
{
example[] arr = new example[10];
int res = test(out arr,10);//*Code freezes here*
Debug.WriteLine(res);
_text.Text =arr[0].str;
}
}
The output in the debug window is(should end with -1 if everything works):
arr[0].num = 0
arr[1].num = 1
arr[2].num = 2
arr[3].num = 3
arr[4].num = 4
arr[5].num = 5
arr[6].num = 6
arr[7].num = 7
arr[8].num = 8
arr[9].num = 9
not yet
I find it to be really weird, it might be some mechanic related to the [out] modifier but I've got not clue.
I am currently working on some C# code that talks to a C++ dll. This is not an area in which I - or anyone else at my company - has any experience. It's been an eye-opener to say the least.
After a lot of reading, trial and error, and frustration, I've managed to iron out most of the kinks and get something that's largely functional. However, from time to time, it still throws this at me ...
System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
.. and then dies. This error only appears when I run the call on parallel threads - it's fine single threaded. This dll is supposed to be thread safe and we've good reason to believe it ought to be, if handled correctly.
The cause of this error is always a call to the same function:
[DllImport(DLL, SetLastError = true, CharSet = CharSet.Ansi)]
public static extern int QABatchWV_Close(IntPtr vi1);
I have the header file for the library, which defines this function as:
__declspec(dllimport) int __stdcall QABatchWV_Close(int);
From what I understand there are additional tools at my disposal like SafeHandle and MarshalAs. But, frankly, I'm unsure as to how to best deploy them in this situation.
This error tends to take several hours of use time to show up, so tweaking and hoping isn't going to be a productive approach here. Can anyone point me as to what I might be doing wrong in calling down to the C++ function?
Well, first of all you don't need setting Charset here, because there are no strings.
Second of all - function in cpp should be declared as exported not imported, so it should look like:
__declspec(dllimport) int __stdcall QABatchWV_Close(int);
Next, you should set calling convention in your C# code to stdcall:
[DllImport(DLL, SetLastError = true, CallingConvention=CallingConvention.Stdcall)]
Next you should have int instead of IntPtr in C# code. And I'm nearly sure that name of this function (in C++ dll) is mangled and it's not QABatchWV_Close but rather something like QABatchWV_Close#32. You should check it using "dll export viewer".
Have a look at the following code which I use to call a c (not c++) dll. I know it is not really an answer to your question, but perhaps you can use some of this going foreward.
Note the "CallingConvention"-specifier in the dll declaration and also the "FreeGlobal" in the "finally" part of the try catch.
public class csInterface
{
[DllImport(#"myDLL.dll", EntryPoint = "dllFunc", CallingConvention = CallingConvention.StdCall)]
private static extern void dllFunc(IntPtr inp, IntPtr outp);
public static int myDll(ref MyInput myInput, ref MyOutput myOutput)
{
int sizeIn, sizeOut;
IntPtr ptr_i = IntPtr.Zero, ptr_u = IntPtr.Zero;
sizeIn = Marshal.SizeOf(typeof(myInput));
sizeOut = Marshal.SizeOf(typeof(myOutput));
/* Calling C */
try
{
ptr_i = Marshal.AllocHGlobal(sizeIn);
ptr_u = Marshal.AllocHGlobal(sizeOut);
Marshal.StructureToPtr(myInput, ptr_i, true);
Marshal.StructureToPtr(myOutput, ptr_u, true);
dllFunc(ptr_i, ptr_u);
myOutput = (MyOutput)(Marshal.PtrToStructure(ptr_u, typeof(MyOutput)));
}
catch (Exception)
{
//Return something meaningful (or not)
return -999;
}
finally
{
//Free memory
Marshal.FreeHGlobal(ptr_i);
Marshal.FreeHGlobal(ptr_u);
}
//Return something to indicate it all went well
return 0;
}
}
In C# I declare my types
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MySubType
{
public int a;
public double b;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyInput
{
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 4)]
public string aString; //A string of length 3
public bool aBoolean;
public int anInt;
public char aChar;
public double aDouble;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 12)]
public MySubType[] aSubType; //Array of struct of length 12
}
And something similar for the output.
Now in C (its probably the same or similar in c++) i declare my dll
__declspec(dllexport) void _stdcall dllFunc(MyCInput *myCInput, MyCOutput *myCOutput)
{
//Code
}
And the corresponding C types which obviously have to mirror the C# types exactly
typedef struct
{
int a;
double b;
} MyCSubType;
typedef struct
{
char aString[4];
int aBoolean; //This needs to be cast over to your C boolean type
int anInt;
char aChar;
double aDouble;
MyCSubType myCSubType[12];
} MyCType;
Now the types I have used in this example do not exactly match what I have used in my code, and i have not tested this code. So there may be typos and such, but the "principle" is ok.
I am learning C# from my C++/CLR background by rewriting a sample C++/CLR project in C#.
The project is a simple GUI (using Visual Studio/ Windows Forms) that performs calls to a DLL written in C (in fact, in NI LabWindows/CVI but this is just ANSI C with custom libraries). The DLL is not written by me and I cannot perform any changes to it because it is also used elsewhere.
The DLL contains functions to make an RFID device perform certain functions (like reading/writing RFID tag etc). In each of these functions, there is always a call to another function that performs writing to a log file. If the log file is not present, it is created with a certain header and then data is appended.
The problem is: the C++/CLR project works fine.
But, in the C# one, the functions work (the RFID tag is correctly written/read etc.) but there is no activity regarding the log file!
The declarations for DLL exports look like this (just one example, there are more of them, of course):
int __declspec(dllexport) __stdcall Magnetfeld_einschalten(char path_Logfile_RFID[300]);
int save_Logdatei(char path_Logdatei[], char Funktion[], char Mitteilung[]);
The save_Logdatei function is called during execution of Magnetfeld_einschalten like this:
save_Logdatei(path_Logfile_RFID, "Magnetfeld_einschalten", "OK");
In the C++/CLR project, I declared the function like this:
#ifdef __cplusplus
extern "C" {
#endif
int __declspec(dllexport) __stdcall Magnetfeld_einschalten(char path_Logfile_RFID[300]);
#ifdef __cplusplus
}
#endif
then a simple call to the function is working.
In the C# project, the declaration goes like:
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "Magnetfeld_einschalten", CharSet = CharSet.Ansi, ExactSpelling = false)]
private static extern int Magnetfeld_einschalten(string path_Logfile_RFID);
and, as I said, although the primary function is working (in this case, turning on the magnetic field of the RFID device), the logging is never done (so, the internal DLL call to save_Logdatei is not executing correctly).
The relevant code in the Form constructor is the following:
pathapp = Application.StartupPath;
pathlog = string.Format("{0}\\{1:yyyyMMdd}_RFID_Logdatei.dat", pathapp, DateTime.Now);
//The naming scheme for the log file.
//Normally, it's autogenerated when a `save_Logdatei' call is made.
Magnetfeld_einschalten(pathlog);
What am I missing? I have already tried using unsafe for the DLL method declaration - since there is a File pointer in save_Logdatei - but it didn't make any difference.
===================================EDIT==================================
Per David Heffernan's suggestion, i have tried to recreate the problem in an easy to test way. For this, i have created a very simple DLL ("test.dll") and I have stripped it completely from the custom CVI libaries, so it should be reproducible even without CVI. I have uploaded it here. In any case, the code of the DLL is:
#include <stdio.h>
int __declspec(dllexport) __stdcall Magnetfeld_einschalten(char path_Logfile_RFID[300]);
int save_Logdatei(char path_Logdatei[], char Funktion[], char Mitteilung[]);
int __declspec(dllexport) __stdcall Magnetfeld_einschalten(char path_Logfile_RFID[300])
{
save_Logdatei(path_Logfile_RFID, "Opening Magnet Field", "Success");
return 0;
}
int save_Logdatei(char path_Logdatei[], char Funktion[], char Mitteilung[])
{
FILE *fp; /* File-Pointer */
char line[700]; /* Zeilenbuffer */
char path[700];
sprintf(path,"%s\\20160212_RFID_Logdatei.dat",path_Logdatei);
fp = fopen (path, "a");
sprintf(line, "Just testing");
sprintf(line,"%s %s",line, Funktion);
sprintf(line,"%s %s",line, Mitteilung);
fprintf(fp,"%s\n",line);
fclose(fp);
return 0;
}
The C# code is also stripped down and the only thing i have added to the standard Forms project, is Button 1 (and the generated button click as can be seen). The code is this:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TestDLLCallCSharp
{
public partial class Form1 : Form
{
public int ret;
public string pathapp;
public string pathlog;
[DllImport("test", CallingConvention = CallingConvention.StdCall, EntryPoint = "Magnetfeld_einschalten", CharSet = CharSet.Ansi, ExactSpelling = false)]
private static extern int Magnetfeld_einschalten(string path_Logfile_RFID);
public Form1()
{
pathapp = #"C:\ProgramData\test";
pathlog = string.Format("{0}\\20160212_RFID_Logdatei.dat", pathapp);
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
ret = Magnetfeld_einschalten(pathlog);
}
}
}
As can be seen, I have avoided using an automatic naming scheme for the log file (normally i use the date) and in both the dll and the C# code, the log file is "20160212_RFID_Logdatei.dat". I have also avoided using the app path as the directory where to put the log file and instead I have opted for a folder named test i created in ProgramData
Again, no file is created at all
This looks like a simple typo in your calling code. Instead of:
ret = Magnetfeld_einschalten(pathlog);
you mean to write:
ret = Magnetfeld_einschalten(pathapp);
In the C# code, these two strings have the following values:
pathapp == "C:\ProgramData\\test"
pathlog == "C:\ProgramData\\test\\20160212_RFID_Logdatei.dat"
When you pass pathlog to the unmanaged code it then does the following:
sprintf(path,"%s\\20160212_RFID_Logdatei.dat",path_Logdatei);
which sets path to be
path == "C:\\ProgramData\\test\\20160212_RFID_Logdatei.dat\\20160212_RFID_Logdatei.dat"
In other words you are appending the file name to the path twice instead of once.
An extensive overview for P/Invoke in C# is given in Platform Invoke Tutorial - MSDN Library.
The problematic bit is you need to pass a fixed char array rather than the standard char*. This is covered in Default Marshalling for Strings.
The gist is, you need to construct a char[300] from your C# string and pass that rather than the string.
For this case, two ways are specified:
pass a StringBuilder instead of a string initialized to the specified length and with your data (I omitted non-essential parameters):
[DllImport("MyDLL.dll", ExactSpelling = true)]
private static extern int Magnetfeld_einschalten(
[MarshalAs(UnmanagedType.LPStr)] StringBuilder path_Logfile_RFID);
<...>
StringBuilder sb = new StringBuilder(pathlog,300);
int result = Magnetfeld_einschalten(sb);
In this case, the buffer is modifiable.
define a struct with the required format and manually convert your string to it:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
struct Char300 {
[MarshalAs(UnmanagedType.ByValTStr,SizeConst=300)]String s;
}
[DllImport("MyDLL.dll")]
private static extern int Magnetfeld_einschalten(Char300 path_Logfile_RFID);
<...>
int result = Magnetfeld_einschalten(new Char300{s=pathlog});
You can define an explicit or implicit cast routine to make this more straightforward.
According to UnmanagedType docs, UnmanagedType.ByValTStr is only valid in structures so it appears to be impossible to get the best of both worlds.
The String is in Unicode format, convert it to byte[]
Encoding ec = Encoding.GetEncoding(System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ANSICodePage);
byte[] bpathlog = ec.GetBytes(pathlog);
and change parameter type to byte[]
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "Magnetfeld_einschalten", CharSet = CharSet.Ansi, ExactSpelling = false)]
private static extern int Magnetfeld_einschalten(byte[] path_Logfile_RFID);
For me it is working
JSh
I am investigating what is possible with Robert Giesecke's approach to calling C# DLL Functions from Unrealscript. I have been following the fine examples Located at gamedev.net as well. (Okay, now everything is sourced :)
I would like to pass a structure from C# back to Unrealscript that contains a string. I have tried several things, none of which quite have worked, but my current c# struct looks like this:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct PSTest
{
public float a;
public int b;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string c;
}
..And my additional C# code looks like this:
private static IntPtr MarshalToPointer(object data)
{
IntPtr buf = Marshal.AllocHGlobal(
Marshal.SizeOf(data));
Marshal.StructureToPtr(data,
buf, false);
return buf;
}
[DllExport("ReturnTesting", CallingConvention = CallingConvention.StdCall)]
static IntPtr ReturnTesting()
{
PSTest ps = new PSTest();
ps.a = 1.0f;
ps.b = 2;
ps.c = "a";
IntPtr lpstruct = MarshalToPointer(ps);
return lpstruct;
}
I am calling those functions from UnrealScript, and my UnrealScript looks like the following:
struct MYTest
{
var float a;
var int b;
var string c;
};
dllimport final function MYTest ReturnTesting();
function DoWork()
{
local MYTest my;
my = ReturnTesting();
}
The Unrealscript here is a little simplified, but it works.
When I execute the Unrealscript, the struct comes back from ReturnTesting() with valid data for the float and int values in variables a and b, but the variable c is a blank string. This is the best situation I have gotten this code into, as mucking with the string variables in the structs usually ends up with crashes.
Has anyone had experience with a similar situation? How can I properly pass string information in my c# struct back to Unrealscript? Any hints and information is much appreciated, and I thank everyone ahead of time.
I am trying to use a C++ dll to edit my StringBuilder object in C#. My C++ code looks like this:
extern "C" __declspec(dllexport) void __stdcall PrintHead(char* myString)
{
myString = "testIsOkey";
}
and my C# code is:
[DllImport("StringEdit.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = false)]
public static extern void PrintHead([MarshalAs(UnmanagedType.LPStr)] StringBuilder stringBuilder);
private void button1_Click(object sender, EventArgs e)
{
StringBuilder stringBuilder = new StringBuilder("123456");
PrintHead(stringBuilder);
}
After PrintHead is called, i am expecting to see that the stringBuilder object's value is changed from "123456" to "testIsOkey" , but it does not change. I can't figure out where do i make a mistake.
Thanks for your help.
void __stdcall PrintHead(char* myString) {
myString = "testIsOkey";
}
That's not correct C++ code. It merely changes the pointer that was passed to the function. This has no side effects whatsoever. Fix:
void __stdcall PrintHead(char* myString) {
strcpy(myString, "testIsOkey");
}
But never write interop code like this, the C++ function can easily destroy the garbage collected heap this way. Which is exactly what happens in your code, the StringBuilder's Capacity isn't enough. You should add an extra argument that provides the size of the passed buffer. Fix:
void __stdcall PrintHead(char* myString, size_t bufferSize) {
strcpy_s(myString, bufferSize, "testIsOkey");
}
Pass the string builder's Capacity for that extra argument, like this:
var buffer = new StringBuilder(666);
PrintHead(buffer, buffer.Capacity);
var result = buffer.ToString();
Doesn't it have to be marked ref or with some other attribute, so that .NET knows that the marshalling should occur both ways?