I have the following minimal example:
Suppose we have three libraries:
CoreLibrary (C# .NetFramework library)
ExportLibrary (C# .NetFramework library, but with DllExports library)
CppTests (C++ Native Unit Test Project).
The libraries contains one file each:
CoreLibrary.cs
// sample Class1 class.
namespace CoreLibrary{
public class Class1{}
}
ExportLibrary.cs
using CoreLibrary;
namespace ExportLibrary
{
public static class DllExports
{
[DllExport]
public static int Fun1()
{
return 5;
}
[DllExport]
public static int Fun2()
{
return 6;
}
[DllExport]
public static int Fun3()
{
var class1 = new Class1();
return 7;
}
}
}
CppTests.cpp
#include "pch.h"
#include "CppUnitTest.h"
#include <Windows.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace CppTests
{
typedef int (*func_type)();
TEST_CLASS(CppTests)
{
public:
TEST_METHOD(TestMethod1)
{
HMODULE mod = LoadLibrary(L"ExportLibrary.dll");
func_type MyFunct = reinterpret_cast<func_type>(GetProcAddress(mod, "Fun1"));
int result = MyFunct();
Assert::AreEqual(5, result);
}
TEST_METHOD(TestMethod2)
{
HMODULE mod = LoadLibrary(L"ExportLibrary.dll");
func_type MyFunct = reinterpret_cast<func_type>(GetProcAddress(mod, "Fun2"));
int result = MyFunct();
Assert::AreEqual(6, result);
}
TEST_METHOD(TestMethod3)
{
HMODULE mod = LoadLibrary(L"ExportLibrary.dll");
func_type MyFunct = reinterpret_cast<func_type>(GetProcAddress(mod, "Fun3"));
int result = MyFunct();
Assert::AreEqual(7, result);
}
};
}
The idea is to be able to test interoperability between C++ and C# (new libraries written in C#, run on the old legacy C++ app) from the actual interoperability.
Problem is, when CppTests is referencing CoreLibrary and ExportLibrary, I have passed TestMethod1 and TestMethod2, but not TestMethod3 - apparently using class from outside library is the big problem.
The only thing I changed in config of libraries were CoreLibrary and ExportLibrary Platform target to x86. C++ project doesn't use CLR. Also, I've tested the case on the old Borland C++ Studio 6.0, and there code is run without problems.
Related
I'm trying to wrap a C++ class using std::array. In C# I'd like to use this std::array as a normal array. The C++ class could look like this:
#pragma once
#include <array>
struct Data
{
int member1, member2;
};
class TestClass
{
public:
std::array<Data, 2> GetExampleArray() {
Data item1;
Data item2;
item1.member1 = 0; // Initialize with some boring data
item1.member2 = 1;
item2.member1 = 3;
item2.member2 = 4;
return { item1, item2 };
}
};
Usage in C# would be as follows:
class Program
{
static void Main(string[] args)
{
TestClass testClass = new TestClass();
Data myData = testClass.GetExampleArray()[0];
Console.WriteLine(myData.member1);
}
}
My first thought was "Let's pass a pointer to C# using typemaps!" until I realized that this is no C-style array and even if it was I wouldn't be able to pass the length.
So my question is: Which steps can I take to get its data into a C# array?
I'm using VS2010, I'm try to use a C# dll to get any enum member name in C++,
My C# dll source code:
namespace CSharpFuncion
{
public class CSFun
{
public string GetEnumName(Enum en)
{
return Enum.GetName(typeof(Enum), en);
}
}
}
My C++ code
#using "CSharpFuncion.dll"
using namespace CSharpFuncion;
CSFun ^ csFun = gcnew CSFun;
cout << csFun->GetEnumName(MyTestEnum::E_A) << endl;
Error message:
cannot convert parameter from 'MyTestEnum' to 'System::Enum ^'
How can I fix it?
Rather than
public enum MyTestEnum
{
E_A = 1,
E_B = 2
};
You need to make it
public enum class MyTestEnum
{
E_A = 1,
E_B = 2
};
So just add the class keyword.
And change return Enum.GetName(typeof(Enum), en); to return en.ToString()
you have to give Enum.GetName(typeof(MyTestEnum ), 1); to get the name of value (E_A) in that enum
I have unsafe class Multipolynomial:
[StructLayoutAttribute(LayoutKind.Sequential)]
public unsafe class Multipolynomial
{
private int _n;
private int _max_power;
//get/set code below for _n and _max_power
...
public double* X { get; set; }
public double** Y { get; set; }
}
and I have two classes containing exclusively doubles and Multipolynomials properties kind of:
[StructLayoutAttribute(LayoutKind.Sequential)]
public unsafe class Input
{
public double S_M { get; set; }
public Multipolynomial C_x_M { get; set; }
...
public Input()
{
C_x_M = new Multipolynomial();
...
}
}
and
[StructLayoutAttribute(LayoutKind.Sequential)]
public unsafe class Output
{
//very same as Input
...
}
and there is function dummy_solution with unmanaged signature
KERNEL_API output dummy_solution(input *in_p); //input and output are unmanaged structs
and managed signature
[DllImport("kernel.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Output dummy_solution(Input in_p);
The problem is when Im trying to execute code
Input input = new Input();
Output output = new Output();
MathKernel.no_solution(input); //works great except it does nothing and returns nothing =P
output = MathKernel.dummy_solution(input); //does nothing, simply returns empty Output object and crashes
it threw exception XamlParseException with inner exception {"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."}.
no_solution function is just dummy test function with unmanaged signature
KERNEL_API void no_solution(input *in_p);
and managed signature
[DllImport("kernel.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void no_solution(Input in_p);
Hence I concluded that there is something wrong with returning Output object.
Honestly said Im newbie to such monstrous things as marshaling so maybe there are ugly stupid mustakes which I cannot see.
Please point out whats wrong or give an advice how to build unmanaged function which simply gets class Input and returns class Output.
PInvoke cannot have non-blittable types as a return value.
The easiest way is to make all classes structs.
Nested struct unmarshalling
Consider the code below that is meant to be accessed by C++ using com
namespace MarshalLib
{
//define an interface for account services
[ComVisible(true)]
[Guid("39B8A693-79BB-4638-92DE-245A88720953")]
public interface IAccountStructLookup
{
AccountStruct RetrieveAccount(int acctId);
void UpdateBalance(ref AccountStruct account);
Alias[] GetRef();
}
//Implement an account struct
[ComVisible(true)]
[Guid("DB48C5B6-9646-491A-B030-C0CADCFC03E0")]
public struct AccountStruct
{
public int AccountId;
[MarshalAs(UnmanagedType.BStr)]
public string AccountName;
[MarshalAs(UnmanagedType.Currency)]
public decimal Balance;
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
//[MarshalAs(UnmanagedType.SafeArray)]
//public Alias[] Aliases;
}
[ComVisible(true)]
[Guid("9829CAB3-4020-47EA-BE72-86EC7CFFAE1D")]
public struct Alias
{
public string Name;
}
//implement a class to provide account services
//using an AccountStruct
[ComVisible(true)]
[Guid("CEFE5CAA-5C7E-464F-8020-E0FC78180D9B")]
[ClassInterface(ClassInterfaceType.None)]
public class DniNetStructsObj : IAccountStructLookup
{
public AccountStruct RetrieveAccount(int acctId)
{
AccountStruct result = new AccountStruct();
if (acctId == 123)
{
result.AccountId = acctId;
result.AccountName = "myAccount";
result.Balance = 1009.95M;
//result.Aliases = new Alias[5];
//result.Aliases[0].Name = "1";
//result.Aliases[1].Name = "2";
//result.Aliases[2].Name = "3";
//result.Aliases[3].Name = "4";
//result.Aliases[4].Name = "5";
}
return result;
}
public void UpdateBalance(ref AccountStruct account)
{
//update the balance
account.Balance += 500.00M;
}
public Alias[] GetRef( )
{
Alias[] al= new Alias[2];
al[0].Name = "1";
al[1].Name = "2";
return al;
}
}
And the C++ side of things
#include "stdafx.h"
#include "ConsoleApplication1.h"
#import "D:\Source Code\MarshalLib\MarshalLib\bin\Debug\MarshalLib.tlb" raw_interface_only
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// The one and only application object
CWinApp theApp;
using namespace std;
using namespace MarshalLib;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
HMODULE hModule = ::GetModuleHandle(NULL);
if (hModule != NULL)
{
// initialize MFC and print and error on failure
if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
_tprintf(_T("Fatal Error: MFC initialization failed\n"));
nRetCode = 1;
}
else
{
try
{
CoInitialize(NULL);
IAccountStructLookupPtr api(__uuidof(DniNetStructsObj));
api->GetRef();
CoUninitialize();
}
catch (...)
{
}
}
}
else
{
// TODO: change error code to suit your needs
_tprintf(_T("Fatal Error: GetModuleHandle failed\n"));
nRetCode = 1;
}
return nRetCode;
}
I get an error when I call api-GetRef() to get an array of structs. Please help me return an array of structs from c# and use it in c++.
thanks in advance.
The problem with returning the array is that in the C++ you will see a pointer to struct and have no information about array size. You can try to marshal it as a SAFEARRAY, but IMO, SAFEARRAYs are pain in the neck.
I prefer to model it as this:
[ComVisible(true)]
[Guid("C3E38106-F303-46d9-9EFB-AD8A8CA8644E")]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MyStruct
{
public int Value;
// I marshal strings as arrays! see note at the bottom
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string Unit
}
[ComVisible(true),
Guid("BD4E6810-8E8C-460c-B771-E266B6F9122F"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
]
public interface IMyService
{
int GetData([MarshalAs(UnmanagedType.LPArray)] out MyStruct[] data);
}
The client code is like this:
Lib::MyStruct* data;
long size = svc->GetData(&data);
for(size_t i = 0; i < size; ++i)
{
Lib::MyStruct& current = data[i];
long val = current.Value;
bstr_t unit = current.Unit;
// ...
}
// now you need to release the memory. However, if you marshal
// strings in struct as BSTRs, you need to first release them by
// calling SysFreeString. This is why I prefer to marshal strings
// as arrays whenever I can: you can still easily construct a bstr_t
// in your client code, but you don't need to release them explicitly
CoTaskMemFree(data);
With regard to comment about SAFEARRAYs: they are required only if the interface must be automation compliant i.e. late-bound i.e. an IDispatch interface i.e. marked as ComInterfaceType.InterfaceIsIDispatch. If this is not the case (and I declared the interface as custom i.e. ComInterfaceType.InterfaceIsIUnknown) using the standard arrays is perfectly fine and they are equally well supported as SAFEARRAYs. Furthermore, working with SAFEARRAYs of custom structs brings some additional complexity which I prefer to avoid. If you don't need late binding, there is no reason to fight with SAFEARRAYs.
With regard to CComSafeArray, as documented, it doesn't support VT_RECORD which is required to support arrays of structs (another option is to marshal it as VT_VARIANT with IRecordInfo but I won't even go into that).
You first need to expose your managed code via an interface and register it using regasm and create type library (tlb file). Then you can use this in your unmanaged code.
Refer to this article: http://blogs.msdn.com/b/deeptanshuv/archive/2005/06/26/432870.aspx
I am using MS ScriptControl to add scripting to my application. Everything works fine except when I have a class that is visible to the script engine that has an array. I can't access the array elements from the script.
Example C# class:
using System.Runtime.InteropServices;
namespace my_app
{
[ComVisible(true)]
public class test
{
public string this_works = "Hello";
public string[] no_workey = new string[4];
public test()
{
no_workey[0] = "I";
no_workey[1] = "can't";
no_workey[2] = "see";
no_workey[3] = "you!";
}
}
}
I add the class to the script engine:
script.AddObject("test", new test(), true);
I've built in some functionality to print an object. Here are my results:
test.this_works -> "Hello"
test.no_workey -> System.String[]
test.no_workey[0] -> 'test.no_workey.0' is null or not an object.
Does anyone know how I can do this?
Thanks!