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

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

Related

How to get content of array from C++ dll in C#

I want to use functions from DLL in C++ with C#.
I store string data in a vector.
My C++ file contains:
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
extern "C" __declspec(dllexport) std::vector<std::string> GetProduct();
std::vector<std::string> GetProduct()
{
std::vector<std::string> vectProduct;
vectProduct.push_back("Citroen");
vectProduct.push_back("C5");
vectProduct.push_back("MOP-C5");
return vectProduct;
}
In C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.InteropServices;
namespace ConsoleApplication
{
class Program
{
[DllImport("ProductLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern StringBuilder GetProduct();
static void Main(string[] args)
{
StringBuilder vectProduct_impl = GetProduct();
}
}
}
I don't know how to continue to browse the array in c#.
I don't know if the use of vector is optimal. if you have other solution I'm ready.
Please help.
My favourite way for passing an array of strings C++-->C# is by using a delegate.
C#:
// If possible use UnmanagedType.LPUTF8Str
// or under Windows rewrite everything to use
// wchar_t, std::wstring and UnmanagedType.LPWStr
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void AddAnsi([MarshalAs(UnmanagedType.LPStr)] string str);
[DllImport("CPlusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestReturnArrayStrings(AddAnsi add);
and then
var lst = new List<string>();
TestReturnArrayStrings(lst.Add);
foreach (string str in lst)
{
Console.WriteLine(str);
}
And C++:
#include <string>
#include <vector>
extern "C"
{
__declspec(dllexport) void TestReturnArrayStrings(void (add)(const char* pstr))
{
std::string str1 = "Hello";
std::string str2 = "World";
add(str1.data());
add(str2.data());
// Example with std::vector
add("--separator--"); // You can even use C strings
std::vector<std::string> v = { "Foo", "Bar" };
// for (std::vector<std::string>::iterator it = v.begin(); it != v.end(); ++it)
for (std::vector<std::string>::const_iterator it = v.begin(); it != v.end(); ++it)
{
add(it->data());
}
add("--separator--"); // You can even use C strings
// With C++ 11
// for (auto& it: v)
for (const auto& it: v)
{
add(it.data());
}
}
}
Here the "trick" is that C# passes to C++ a delegate to the List<string>.Add() method, and C++ "fills" directly the C# List<>. The memory managed by C++ remains in the C++ side, the memory managed by the C# remains in the C# side. No problems of cross-memory ownership. As you can imagine, it is quite easy to expand the "trick" to any other .Add() method, like HashSet<string>, or Dictionary<string, string>.
As a sidenote, I've created a github with many examples about marshaling between C/C++ and C# (both .NET Framework and .NET Core/5.0).
One way to do it is to use COM's SAFEARRAY structure as it's supported by .NET (the .NET Allocator used by P/Invoke is the COM allocator), including most of associated sub types, like BSTR.
So, in C/C++, you can define this:
extern "C" __declspec(dllexport) LPSAFEARRAY GetProduct();
LPSAFEARRAY GetProduct()
{
LPSAFEARRAY psa = SafeArrayCreateVector(VT_BSTR, 0, 3);
LONG index = 0;
// _bstr_t is a smart class that frees allocated memory automatically
// it needs #include <comdef.h>
// but you can also use raw methods like SysAllocString / SysFreeString
_bstr_t s0(L"Citroen"); // could use "Citroen" if you really want ANSI strings
// note SafeArrayPutElement does a copy internally
SafeArrayPutElement(psa, &index, s0.GetBSTR());
index++;
_bstr_t s1(L"C5");
SafeArrayPutElement(psa, &index, s1.GetBSTR());
index++;
_bstr_t s2(L"MOP - C5");
SafeArrayPutElement(psa, &index, s2.GetBSTR());
index++;
return psa;
}
And in C#, you can define this:
[DllImport("ProductLibrary.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.SafeArray)]
public static extern string[] GetProduct();

Why is C# Marshalling Strings not Working with C++ DLL

I am having issues passing strings to a certain function in my external DLL. I would post an actual code snippet but it's kind of messy and might be hard to read. The following snippets are what my personal code boils down to.
C# File (UNICODE)
[DllImport("InjectDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern ulong FindProgramProcessId(string procName);
[DllImport("InjectDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern bool InjectIntoProcess(ulong procId, string Dll);
string ProcName = "random_test_game.exe";
string DllPath = #"C:\ProgramData\Hack\File.dll";
ulong procId = FindProgramProcessId(ProcName);
bool Injected = InjectIntoProcess(procId, DllPath);
C++ File (ANSI)
DllExport DWORD FindProgramProcessId(const char* procName)
{
...
}
DllExport bool InjectIntoProcess(DWORD procId, const char* Dll)
{
if (Dll == nullptr)
{
MessageBox(NULL, "DLL", "EMPTY", MB_OK);
return false;
}
...
}
C++ Header File
#pragma once
#include <Windows.h>
#include <TlHelp32.h>
#include <string>
#ifdef EXPORT
#define DllExport __declspec(dllexport)
#else
#define DllExport __declspec(dllimport)
#endif
extern "C" DllExport DWORD FindProgramProcessId(const char* procName);
extern "C" DllExport bool InjectIntoProcess(DWORD procId, const char* Dll);
Referencing the snippets, the issue arising is that FindProgramProcessId will successfully pass a string, no problemo, but InjectIntoProcess will show const char* Dll as nullptr according to the "extra" code I put in that method.
Notice, I have tried passing an IntPtr in place of string and using Marshal.StringToHGlobalAnsi, and I still get the Dll == nullptr issue. It's breaking my code. More of the same info can be found here at my GuidedHacking thread.
The Win32 DWORD is a 32 bit integer, but the C# ulong is a 64 bit integer. The confusion stems from the fact that DWORD is an alias for unsigned long, but the C++ long isn't necessarily 64 bits (in fact, in MSVC, it's 32 bits; unsigned long long is the 64 bit unsigned integer).
Since you're using the cdecl calling convention, the caller is responsible for cleaning the stack (so no crash occurs), and arguments are passed right-to-left (so Dll ends up pointing somewhere in the middle of the value passed to procId, which probably contains zeroes). Or at least that's my guess, since we're in undefined behavior territory here.
You should declare the return value of FindProgramProcessId and the procId parameter of InjectIntoProcess as uint instead.

Getting AccessViolationException while Assigning Delegate from C# to FunctionPointer in NativeCode

From C# code I'm trying to call a API from a *.c file. But I'm getting AccessViolationException.
Earlier I was getting BadImageFormatException, but I solved it by putting both the exe and the dll together in one location.
I suspect that it has something to do with CallingConvention of Cdecl or StdCall or with PlatformType=x86 or x64.
In My C#. exe I've set the CPU to x86 and in my NativeCode.dll the calling convention is mentioned as Cdecl only.
I'm not able to understand that how to solve this issue,there are many posts on web explaining about this problem but somehow I'm not able to find any concrete solution to this issue.
In case of any more details please let me know.
Below mentioned is some code from files which can highlight the problem:
Header.H file
typedef int BOOL;
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
typedef BOOL REGFUNCTION( char *pRegKey, char *pBuffer, int iBufSize );
Implementation.c file
#ifdef SWSCANCODE_EXPORTS
#define SWSCANCODE_API __declspec(dllexport)
#else
#define SWSCANCODE_API __declspec(dllimport)
#endif
#ifdef SWSCAN_APIS
SWSCANCODE_API BOOL SWSetGetRegKeyValue ( REGFUNCTION* pGetRegKeyValue );
#endif
SWGLOBALS* pSWGlobals;
SWSCANCODE_API BOOL SWSetGetRegKeyValue( REGFUNCTION * pGetRegKeyValue )
{
BOOL bInitStatus; // initialization status
bInitStatus = TRUE; // assume initialization will be successful
pSWGlobals->pGetRegKeyValue = pGetRegKeyValue;
if(NULL!=pSWGlobals->pGetRegKeyValue)
{
printf("I don't know");
}
return( bInitStatus );
}
C# code
class Program
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int REGFUNCTION([MarshalAs(UnmanagedType.LPStr)]string pRegKey, [MarshalAs(UnmanagedType.LPStr)]string pBuffer, int iBufSize);
[DllImport("NativeCode.dll", EntryPoint = "SWSetGetRegKeyValue", CallingConvention = CallingConvention.Cdecl)]
[return:MarshalAsAttribute(UnmanagedType.Bool)]
public static extern Boolean SWSetGetRegKeyValue(IntPtr pGetRegKeyValue);
static void Main(string[] args)
{
REGFUNCTION dele = GetRegKeyValue;
IntPtr ptr = Marshal.GetFunctionPointerForDelegate(dele);
GC.KeepAlive(dele);
bool bSetRegKeyValueOk = SWSetGetRegKeyValue(ptr);
}
public static int GetRegKeyValue(string pRegKey, string pBuffer, int iBufSize)
{
Console.WriteLine("Successful in calling the method");
return 0;
}
}

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.

how to call a C++ dll exported function from c#

This is the first time I'm trying to mix c# an unmanaged C++ so this might be a very simple question , but I don't get it.
I need to call some functions from a C++ dll into C# code. Here is the code for the dll project:
the .h file :
#pragma once
#include <iostream>
#if defined FIRSTDLL_EXPORTS
#define DECLDIR __declspec(dllexport)
#else
#define DECLDIR __declspec(dllimport)
#endif
extern "C"
{
DECLDIR int Add( int a, int b );
DECLDIR void Function( void );
}
the .cpp file
#include "stdafx.h"
#include "myFct.h"
#include <iostream>
extern "C"
{
DECLDIR int Add( int a, int b )
{
return( a + b );
}
DECLDIR void Function( void )
{
std::cout << "DLL Called!" << std::endl;
}
}
I compiled this for both the debug and releas and copied it in the debug folder of my C# project. Neither version worked.
Here is the c# code:
[DllImport("firstDLL.Dll")]
public static extern int Add(int a, int b);
var cyu = Add(3, 5);
And when I try to run this I get
"Managed Debugging Assistant 'PInvokeStackImbalance' has detected a problem in 'C:\Program Files\Microsoft Office\Office14\WINWORD.EXE'.
Additional Information: A call to PInvoke function 'MyAddin!MyAddin.ThisAddIn::Add' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature."
But as I see the signatures are the same. What am I missing??
Thanks!
The default calling convention for DLLImport is stdcall, but the default of your C++ code is cdecl. The error message you have seen is what is shown when the calling conventions don't match. The parameter stack cleanup requirements are different for these two calling conventions, and the P/Invoke marshaller detects and reports this.
The fix is to make your calling conventions match.
For example you could change your P/Invoke like so:
[DllImport("firstDLL.Dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Add(int a, int b);
The other option is to change your C++:
#if defined FIRSTDLL_EXPORTS(returntype)
#define DECLDIR __declspec(dllexport) returntype __stdcall
#else
#define DECLDIR __declspec(dllimport) returntype __stdcall
#endif
Clearly you should only do one of these. If you change both C# and C++ you'll have the same problem in reverse!
If I were you I would leave the C++ code as cdecl and change the C# to match.

Categories

Resources