invoking unmanaged DLL and returning string - c#

I am attempting to invoke an unmanaged DLL and use what it returns for authentication. Could someone help me correct my code to return the correct authentication strings?
I am confused about the struct's, any assistance would be appreciated.
this is my C# code invoking the library
class Wrapper
{
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 1)]
public struct ARSSOUserCredentialsStruct
{
/// int
public int m_nARTCPNum;
public int m_nARRPCNum;
/// string*
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
public static string m_szARUserName;
public string m_szARPassword;
public string m_szARAuthenticationstring;
public int m_nARNumberOfServers;
//bool
public bool m_bARUsingPreferenceServer;
}
public struct ARSSOServerInformation
{
/// int
public int m_nARTCPNum;
public int m_nARRPCNum;
/// string*
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
public string m_szARServerName;
}
[System.Runtime.InteropServices.DllImportAttribute("ARSSOInfo.dll", EntryPoint = "ARGetSSOLoginCrendentials")]
public static extern System.IntPtr ARGetSSOLoginCrendentials(string m_szARUserName);
public static IntPtr getInfo(string m_szARUserName)
{
IntPtr ptr = ARGetSSOLoginCrendentials(m_szARUserName); // should this be IntPtr?
//return (ARSSOUserCredentialsStruct)(Marshal.PtrToStructure(ptr, typeof(ARSSOUserCredentialsStruct))); // what should type >
// var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(m_szARUserName));
if(ptr != null)
{
Marshal.StructureToPtr(m_szARUserName, ptr, false);
Console.WriteLine("Not null");
Console.WriteLine();
}
else
{
Console.WriteLine("null");
Console.ReadLine();
}
return ptr;
}
class Program
{
static void Main(string[] args)
{
getInfo("jp");
}
}
}
unmanaged dll code which is being called by C# app:
#include <string.h>
#include <stdafx.h>
struct ARSSOServerInformation
{
char * m_szARServerName;
int m_nARTCPNum;
int m_nARRPCNum;
};
struct ARSSOUserCredentialsStruct
{
char* m_szARUserName;
char* m_szARPassword;
char* m_szARAuthenticationString;
bool m_bARUsingPreferenceServer;
int m_nARNumberOfServers;
};
extern "C"
{
__declspec(dllexport) void
ARGetSSOLoginCrendentials(ARSSOUserCredentialsStruct
*pUserCredentialStruct)
{
// The required memory for struct ARSSOUserCredentialsStruct is allocated by user tool,
// This dll just needs to assign values.
// eg:
strcpy(pUserCredentialStruct->m_szARUserName, "Demo");
pUserCredentialStruct->m_nARNumberOfServers = 2;
}
}//End 'extern "C"
extern "C"
{
__declspec(dllexport) void
ARGetSSOServerInformation(ARSSOServerInformation *pServerInfo)
{
// The required memory for struct ARSSOServerInformation is allocated by user tool,
// This dll just needs to assign values.
// eg:
strcpy(pServerInfo[0].m_szARServerName, "ServerName1");
pServerInfo->m_nARTCPNum = 3040; pServerInfo->m_nARRPCNum
= 390622;
strcpy(pServerInfo[1].m_szARServerName, "ServerName2");
}
}//End 'extern "C"

Related

Importing C struct to C# program

The Sctruct
I am not much of a C# or C programmer and have not found much guidance on specifying pointers and structs within a struct. I am attempting to import the following from a C dll into a C# program:
#define MAXFILENAME 259
struct IDentry {
char* IDname;
int length;
};
typedef struct IDentry idEntry;
struct SMOutputAPI {
char name[MAXFILENAME + 1];
FILE* file;
struct IDentry *elementNames;
long Nperiods;
int FlowUnits;
int Nsubcatch;
int Nnodes;
int Nlinks;
int Npolluts;
int SubcatchVars;
int NodeVars;
int LinkVars;
int SysVars;
double StartDate;
int ReportStep;
__int64 IDPos;
__int64 ObjPropPos;
__int64 ResultsPos;
__int64 BytesPerPeriod;
};
I am not sure how to handle the *elementsNames, file or name properties. What I have so far in C# is:
int MAXFILENAME = 259
[StructLayout(LayoutKind.Sequential)]
public struct SMOutputAPI
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAXFILENAME+1)]
public string name;
IntPtr file;
IntPtr elementNames;
public long Nperiods;
public int FlowUnits;
public int Nsubcatch;
public int Nnodes;
public int Nlinks;
public int Npolluts;
public int SubcatchVars;
public int NodeVars;
public int LinkVars;
public int SysVars;
public double StartDate;
public int ReportStep;
public int IDPos;
public int ObjPropPos;
public int ResultsPos;
public int BytesPerPeriod;
};
The C# application builds fine, but when I call the C initialization function that should return a new SMOutputAPI struct I get an error:
System.Runtime.InteropServices.MarshalDirectiveException
Method's type signature is not PInvoke compatible.
Any thoughts on how to properly specify this struct in C# would be much appreciated. Thanks!
Initializing the Struct
The struct is initialized in the c-code with:
SMOutputAPI* DLLEXPORT SMO_init(void)
//
// Purpose: Returns an initialized pointer for the opaque SMOutputAPI
// structure.
//
{
SMOutputAPI *smoapi = malloc(sizeof(struct SMOutputAPI));
smoapi->elementNames = NULL;
return smoapi;
}
The corresponding c# code is:
[DllImport("swmm-output.dll")]
static extern SMOutputAPI SMO_init();
static void Main(string[] args)
{
Console.Write("Hello World!");
SMOutputAPI SMO = SMO_init();
}

How to assign value to a char array (C-String) within C++ DLL through C# code

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);
}

Unmanaged c++ DLL to get struct from c#

Is it possible to call a method in c# from c++ DLL ?
I have good communication c# -> C++ , but I would like to be able to initiate a call C++ -> c# to poll some data
my code looks something like this...
host.h
/*
define the exporter for the C API
*/
#ifdef DLL_EXPORT
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
class myClass
{
public:
myCLass(){
//do some initialising
}
~myCLass(){
//do some tidying
}
int getInt(int target) {
switch (target)
{
case 0:
return someIntValue;
case 1:
return someOtherIntValue;
default:
return 0;
}
}
std::string getString(int target) {
switch (target)
{
case 0:
return someStringValue;
case 1:
return someOtherStringValue;
default:
return "";
}
}
}
extern "C" {
DLL_EXPORT myClass* myClassConstructor();
DLL_EXPORT void DestroySpatialser(const myClass* _pContext);
DLL_EXPORT int getInt(myClass* _pContext, int target);
DLL_EXPORT void getString(myClass* _pContext, int target, __out BSTR* returnStr);
}
host.cpp
extern "C" {
myClass* myClassConstructor()
{
return new myClass();
}
void myClassDestructor(const myClass* _pContext)
{
if (_pContext != nullptr)
{
_pContext->~myClass();
delete _pContext;
}
}
//example
int getInt(myClass* _pContext, int target)
{
if (_pContext == nullptr)
{
return K_ERR_INT;
}
return _pContext->getInt(target);
}
void getString(myClass* _pContext, int target, __out BSTR* returnStr)
{
std::string str;
if (_pContext == nullptr)
{
str = K_ERR_CHAR;
}
else {
str = _pContext->getString(target);
}
const std::string stdStr = str;
_bstr_t bstrStr = stdStr.c_str();
*returnStr = bstrStr.copy();
}
}
csharp.cs
private const string dllname = "myhost";
[DllImport(dllname)]
private static extern IntPtr myClassConstructor();
[DllImport(dllname)]
private static extern void myClassDestructor(IntPtr _pContext);
[DllImport(dllname)]
private static extern int getInt(IntPtr _pContext, int target);
[DllImport(dllname)]
private static extern float getFloat(IntPtr _pContext, int target);
[DllImport(dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void getString(IntPtr _pContext, int target, [MarshalAs(UnmanagedType.BStr)] out String str);
private static IntPtr _pContext;
public static IntPtr Create()
{
_pContext = myClassConstructor();
Debug.Log("_pContextcreated");
return _pContext;
}
public static void Destroy()
{
myClassDestructor(_pContext );
}
private static int _getInt(int target)
{
return getInt(_pContext , target);
}
private static string _getString(int target)
{
String str;
getString(_pContext , target, out str);
return str;
}
This all works fine.
I'd like to add the ability to get a value from the .cs app (it will be a struct of 3 floats) which should be called from the instance of myClass.
I don't want to initiate this on c# side (i know how to do that).
Any advice?
EDIT
I implemented the solution found here
https://forum.unity.com/threads/communicating-c-with-c.89930/#post-586885
with two changes (see comments below for why)
public int whatever = 0;
public delegate int myCallbackDelegate( int n, int m );
private myCallbackDelegate myCallback; // <-- add this line
[DllImport ("DLLImport_CProj")]
private static extern int TakesCallback( myCallbackDelegate fp, int n, int m );
void Awake()
{
myCallback = new myCallbackDelegate( this.myCallback ) // <-- add this
int resp = TakesCallback(myCallback, 10, 20 ); // <-- change to this
Debug.Log( resp );
}
Well, One way I can think of is to pass a JSON string as char array from C++ to C# and then parse it on C# and get the data. it's a communication method both languages are familiar with.
Also, you will need to pass a callback from C# to C++ to allow this like explained in this question.
Let me know if this helps :)
Make your C# types COMVisible, which means you will be able to call them from COM aware C++ code, e.g., using ATL.

Passing C# struct to C++ unmanaged DLL returning incorrect result

I have a simple C++ win32 DLL developed in visual studio 2017 and compiled in 64 bit environment having the following code:
typedef struct sum {
struct {
int num1;
int num2;
} nums;
} sum1;
extern "C" {
__declspec(dllexport) int initialize(sum1 *summing)
{
int res;
res = summing->nums.num1 + summing->nums.num2;
return res;
}
}
The above code contains a method which returns the sum of two integers by taking a typedef struct as an argument.
I have a C# client application which consumes this Win32 C++ DLL using PInvoke. Following is the code of my C# client application:
[StructLayout(LayoutKind.Sequential)]
public struct nums
{
public int a;
public int b;
}
[StructLayout(LayoutKind.Sequential)]
public struct mydef
{
public IntPtr sum;
}
public class LibWrap
{
[DllImport("C++.dll", EntryPoint = "initialize")]
public static extern int Initialize(ref mydef mydef);
}
class Program
{
static void Main(string[] args)
{
mydef mydef = new mydef();
nums nums;
nums.a = 6;
nums.b = 6;
IntPtr buffer1 = Marshal.AllocCoTaskMem(Marshal.SizeOf(nums));
Marshal.StructureToPtr(nums, buffer1, false);
mydef.sum = buffer1;
int res = LibWrap.Initialize(ref mydef);
Console.WriteLine(res);
}
}
With the above code, I am expecting '12' as output, but instead I am getting '-1504178328' as output.
I am a C# developer with no experience in C++ at all. Please help me to solve this problem.
Use a simpler P/Invoke wrapper:
public static class LibWrap
{
[DllImport("C++.dll", EntryPoint = "initialize")]
public static extern int Initialize(ref Nums nums);
[StructLayout(LayoutKind.Sequential)]
public struct Nums
{
public int a;
public int b;
}
}
and use it like this:
void CSharpExample()
{
LibWrap.Nums nums;
nums.a = 6;
nums.b = 7;
int res = LibWrap.Initialize(ref nums);
Console.WriteLine(res);
}
In your example, you don't need any memory allocation and marshaling, because:
LibWrap.Nums is a struct, thus local variable nums in CSharpExample() is allocated completely on stack.
passing managed struct LibWrap.Nums by ref to LibWrap.Initialize will pass the pointer to local variable nums on stack.
LibWrap.Initialize is called synchronously, so that the pointer you pass to it isn't used anywhere after LibWrap.Initialize function exits. This is important because the pointer becomes invalid as soon as CSharpExample() exits.
On the C# side, you are not handling the nested struct correctly. Try this instead:
[StructLayout(LayoutKind.Sequential)]
public struct mynums {
public int num1;
public int num2;
}
[StructLayout(LayoutKind.Sequential)]
public struct sum1 {
public mynums nums;
}
public class LibWrap {
[DllImport("C++.dll", EntryPoint = "initialize")]
public static extern int Initialize(ref sum1 summing);
}
class Program {
static void Main(string[] args) {
sum1 mysum;
mysum.nums.num1 = 6;
mysum.nums.num2 = 6;
int res = LibWrap.Initialize(ref mysum);
Console.WriteLine(res);
}
}
That being said, having a struct whose sole data member is another struct is redundant and unnecessary. You should remove the outer struct altogether, eg:
struct nums {
int num1;
int num2;
};
extern "C" {
__declspec(dllexport) int initialize(nums *summing) {
return summing->num1 + summing->num2;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct nums {
public int num1;
public int num2;
}
public class LibWrap {
[DllImport("C++.dll", EntryPoint = "initialize")]
public static extern int Initialize(ref nums summing);
}
class Program {
static void Main(string[] args) {
nums mynums;
mynums.num1 = 6;
mynums.num2 = 6;
int res = LibWrap.Initialize(ref mynums);
Console.WriteLine(res);
}
}

Pass char array value from C++ to C# app

I have a huge C++ structure with a lot of validation code in C++, that I want to import in a C# project. I'm able to transfer all value except the CHAR* and CHAR[].
With a CHAR*, my string is full of chiness caracter BUT, if i go look in memory, my string is there, i can see "This is a test #1".
With a CHAR[x], I can see only the 1rst char, same in memory.
In the following test, i can extract the integer : value1 = data.Value1;
and value 1 is 123, but the the CHAR.
Question: What i miss, why i can't get the value with a char array.
Thank you
The C++ DLL
//This is the main DLL file.
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
extern "C"
{
public struct Data
{
int Value1;
char* pValue2;
char Value3[1024];
};
typedef int( *FN_CCP_INVOKE_NEW_BOARD_OPTIMIZER) (struct Data* data);
FN_CCP_INVOKE_NEW_BOARD_OPTIMIZER _pInvokeCallback;
int __declspec(dllexport) DLLTestCPlusPlus_Initialize()
{
return 0;
}
int __declspec(dllexport) DLLTestCPlusPlus_RegisterDllInvokeProcessCallback(void* fnInvokeCaller)
{
_pInvokeCallback = (FN_CCP_INVOKE_NEW_BOARD_OPTIMIZER)fnInvokeCaller;
struct Data data;
// INT
data.Value1 = 123;
// CHAR*
data.pValue2 = new char[1024];
sprintf(data.pValue2, "This is a test #1");
// CHAR [1024]
sprintf(data.Value3, "This is a test #2");
if (_pInvokeCallback)
{
_pInvokeCallback(&data);
}
return 0;
}
}
And here's the C# program that import the DLL.
using System;
using System.Runtime.InteropServices;
public unsafe struct Data
{
public int Value1;
public char* pValue2;
public fixed char Value3[1024];
}
public static class Interop
{
public delegate Int32 Callback([MarshalAs(UnmanagedType.Struct)] Data data);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SetDllDirectory(string lpPathName);
[DllImport("C:\\DATA\\CODE\\ApplicationTestCSharp\\x64\\Debug\\DLLTestCPlusPlus.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 DLLTestCPlusPlus_Initialize();
[DllImport("C:\\DATA\\CODE\\ApplicationTestCSharp\\x64\\Debug\\DLLTestCPlusPlus.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 DLLTestCPlusPlus_RegisterDllInvokeProcessCallback([MarshalAs(UnmanagedType.FunctionPtr)] Callback handler);
}
public class MyTest
{
private Interop.Callback _callback = null;
public MyTest()
{
int returnCode = 0;
returnCode = Interop.DLLTestCPlusPlus_Initialize();
_callback = new Interop.Callback(CallbackHandler);
returnCode = Interop.DLLTestCPlusPlus_RegisterDllInvokeProcessCallback(_callback);
}
private Int32 CallbackHandler(Data data)
{
int value1 = 0;
string value2 = "";
string value3 = "";
unsafe
{
// INT
value1 = data.Value1;
// CHAR* - MUST BE "This is a test #1"
value2 = new string(data.pValue2);
// CHAR [1024] - "This is a test #2"
value3 = new string(data.Value3);
}
return 1;
}
}
class Program
{
static void Main(string[] args)
{
MyTest myTest = new MyTest();
}
}
Ok i found it, i made that change in the structure declaration
/*
public char* pValue2;
public fixed char Value3[1024];
*/
[MarshalAs(UnmanagedType.LPStr)] public String pValue2;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)] public String pValue3;
And pull the data like that!
// CHAR* - "This is a test #1";
// value2 = new string(data.pValue2);
value2 = data.pValue2
// CHAR [1024] - "This is a test #2"
//value3 = new string(data.Value3);
value3 = data.pValue3;

Categories

Resources