TL;DR
I have legacy c++ code which does stuff (sometimes returns stuff), calls other cpp code, but is not a full class/obj. This code I cannot alter. I am making fresh c# code which I am looking to call the c++ code from. I don't understand whether to create a dll that calls the original legacy c++, or create a CLR?? which also calls the legacy cpp. Below I have example code that I have implemented (with problems).
Main
I have legacy.cpp and legacy.h which I can not alter.
This is not a class/object and only has public functions, values, and #defines.
legacy.cpp and .h both #include other files and cpp libraries to do its job.
I have a new project where I can add C#, C++ code
I am having trouble understanding what I need to do/research in order to call any of the functions defined in legacy.cpp (or the values/defines) from within my new C# code.
Some of what I have looked at include
Managed CLR wrappers
https://drthitirat.wordpress.com/2013/06/03/use-c-codes-in-a-c-project-wrapping-native-c-with-a-managed-clr-wrapper/
https://web.archive.org/web/20140806022309/http://blogs.msdn.com/b/borisj/archive/2006/09/28/769708.aspx
DLLs??
How to call C++ DLL in C#
CLR
I have currently tried to create a CLR (thought I feel like it is not what I need in this situation), but I had problems in the clr_wrapper.cpp, it could not find the reference to foo()
//Wrapper.cpp
#include "Wrapper.h"
#include "abs\path\to\legacy\code\legacy.h"
#include "abs\path\to\legacy\code\legacy.cpp"
int foo_Wrapper()
{
return foo(); //int foo() is declared in legacy.h and defined in legacy.cpp
}
#pragma once
#include "abs\path\to\legacy\code\legacy.h"
#include "abs\path\to\legacy\code\legacy.cpp"
using namespace System; //What things use this?
//Can I just prepend System:: to whatever needs it?
namespace Wrapper {
public ref class Wrapper
{
public:
int foo_Wrapper();
};
}
the foo_Wrapper() is not able to call foo().
My confusion with this method is that it looks like I would need to make the clr wrapper an object class with member functions that will be called as needed. Leading to a syntax of obj.foo(). Is this what needs to be done if I chose to do some sort of CLR wrapper?
DLL
I have also looked at making this all a dll like in (How to call C++ DLL in C#)
However I am confused on setting this up. My current idea is to have a cpp dll call the original cpp (ie create legacyDll.dll which would make calls to foo(), then my main c# would call the __declspec(dllexport) functions defined within extern "C" {}
current setup (from "How to call c dll in c sharp")
dllmain.cpp
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <iostream>
#include <Windows.h>
using namespace std;
extern "C"
{
__declspec(dllexport) void bar_Dll()
{
cout << "calling bar() in legacy code" << endl;
}
__declspec(dllexport) int foo_Dll()
{
cout << "calling foo() in legacy code" << endl;
//realistically I would have,
//return foo()
}
}
Class1.cs
using System;
using System.Runtime.InteropServices;
namespace Test_DLL_Calling
{
class Class1
{
[DllImport("dllmain.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void bar_Dll();
[DllImport("dllmain.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int foo_Dll();
public static void Main(string[] arg)
{
bar_Dll(); //The specified module could not be found (Exception)
Console.WriteLine(foo_Dll()); //Im guessing this will also ^^
}
}
}
This part I don't follow. What and why are the attributes done the way they are?
Alright, so you have a header and cpp which you need to use. In order to use it you have to have make the c++ into C code. This is pretty much what you see in the DLL example code that you showed. However, I do suggest that you remove the includes from the header as I'm unsure how that would translate to the C code. Put the includes in the cpp files instead.
I find it rather difficult to answer this questions other than just showing a whole bunch of example code. Full code in: https://github.com/ze413X/Cpp-Code-Gems/ Where "CSharpExampleUsingCpp" calls from MainWindow.xaml.cs the "DLLExample" which uses the file in the directory includes and source.
DLL:
The header which is exposing functions to be used:
#pragma once
#define DLLExport _declspec(dllexport)
extern "C" DLLExport void __cdecl GetCppText(char* str, int* strLength);
extern "C" DLLExport void __cdecl DLLPrint();
.cpp
#include "../includes/DLLExampleCode.h"
#include <string>
#include <iostream>
void __cdecl GetCppText(char* str, int* strLength)
{
std::string text = "This is called from within the DLL.\0";
if (*strLength < text.length() || str == nullptr)
{
return;
}
memset((void*)str, 0, (*strLength) * sizeof(char));
strcpy_s(str, *strLength,text.c_str());
*strLength = text.length();
}
void __cdecl DLLPrint()
{
std::cout << "This is printed from inside DLLExample.dll.\n";
}
C#:
using System.Runtime.InteropServices;
namespace CSharpExampleUsingCpp
{
public partial class MainWindow : Window
{
const string PATH = "DLLExample.dll";
[DllImport(PATH, CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern void GetCppText(byte[] str, out System.Int32 strLength);
....
private void CppInteropButton_Click(object sender, RoutedEventArgs e)
{
System.Int32 size = 256;
System.Byte[] str = new byte[size];
for (int i = 0; i < size; i++)
{
str[i] = (byte)'1';
}
GetCppText(str, out size);
string result = System.Text.Encoding.UTF8.GetString(str, 0, size);
CppInteropButtonTextBox.Text = result;
}
Although, rereading my solution of obtaining a string might not be the best way of doing it. You could probably marshal that thing to avoid all this stupid char* conversions. I probably had some good reason at that point in time when I wrote it. That should be much easier to google though.
Related
So I am writing a plug-in DLL which is pure C (and a heap of foreign includes), but most of the real code is in an existing C# class library. I'm looking for the shortest path from C (not C++) to C#. The foreign includes are not C++ safe.
There are heaps of samples around for C++, not much for pure C.
It seems I should be able to compile the whole DLL as /clr but compile the C as not; then include in the same DLL a C++ wrapper that presents a C API but contains managed code to call the C# class.
So instantiate the C# class and hang onto it in a gcroot in a C++ class, and pass the C++ class pointer as a void* back for the the C code to keep for future calls.
Quite a few details to get right, but not all that much code. Is there a better way?
Thought it was time to add a bit of code.
// Wrapper.h
#pragma once
// API for call by C
#ifdef __cplusplus
extern "C" {
#endif
void* wrap_create();
void wrap_doit(void* wrapper, char* input, char* output, int maxlen);
#ifdef __cplusplus
}
#endif
// Wrapper.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <vcclr.h>
#include "Wrapper.h"
using namespace System;
class Wrapper {
public:
gcroot<Wrappee::Evaluator^> eval;
Wrapper() {}
};
void* wrap_create() {
Wrapper* w = new Wrapper();
w->eval = gcnew Wrappee::Evaluator();
return w;
}
void wrap_doit(void* wrapper, char* input, char* output, int maxlen) {
Wrapper* w = (Wrapper*)wrapper;
String^ s = w->eval->Doit(gcnew String(input));
pin_ptr<const wchar_t> wch = PtrToStringChars(s);
wcstombs(output, wch, maxlen);
}
// Wrappee.cs
using System;
namespace Wrappee {
public class Evaluator {
string _s;
public static Evaluator Create() {
return new Evaluator {
_s = "wrapped evaluator"
};
}
public string Doit(string s) {
return _s + ":" + s;
}
}
}
Why wouldn't that work? The code is based on this link: https://msdn.microsoft.com/EN-US/library/481fa11f%28v=VS.140,d=hv.2%29.aspx.
The answer is no, that won't work. The managed class depends on the CLR runtime, which needs to be hosted by the application. For a managed app that happens automatically (mscoree.dll during startup), but for a native app there is no host so no CLR.
So we have to provide one. As #hanspassant helpfully pointed out, this is "Reverse P/Invoke" and it really is different. You have to get there by the COM hosting interfaces, specifically ICLRMetaHost.
And the good news is that there is a sample here to show how it's done: https://code.msdn.microsoft.com/windowsdesktop/CppHostCLR-e6581ee0.
There are other samples too: search for CppHostCLR.
I have a problem with the allocVector() function. I wrote a code (DLL) in C that can be called in R. I would like to call a method of that DLL into my application in C#. The DLL code works fine but when the compiler reaches the next line ROut = allocVector(INTSXP, 2*3)
It gives following exception:
Unhandled exception: System.AccessViolationException: Attempted to read or write protected memory.
The code is as follow:
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <R.h>
#include <Rinternals.h>
#include <Rmath.h>
#include <R_ext/Applic.h>
#include <R_ext/Lapack.h>
#include <Rdefines.h>
#ifdef __cplusplus
extern "C" {
#endif
void castType(int *result)
{
SEXP ROut;
ROut = allocVector(INTSXP, 2*3)
ROut = PROTECT(ROut);
int *clustOut = INTEGER(ROut);
result= clustOut ;
}
#ifdef __cplusplus
}
#endif
Quick read, I think you use the value 6 as the value of the pointer. There are other things in there that are problematic.
Stepping back, I suggest a couple of things for your C#/C interop:
If you do not have a lot of existing C code of your own in this API, I'd suggest you port it to C# and use R.NET (NuGet package, source code)
If you do have a substantial amount of C/C++ code, or absolutely need a C layer, you can have a look at this sample code. I put that together to test a couple of things before answering, so I may as well commit the source.
For the sake of other readers, the crux of it elaborated from the function in the question is:
C API:
void c_api_call(int * result)
{
SEXP p = some_R_calculations();
int* int_ptr = INTEGER_POINTER(p);
int n = length(p);
for (int i = 0; i < n; i++)
result[i] = int_ptr[i];
// We are done with p; irrespective of where some_R_calculations got it,
// we can notify the R GC that this function releases it.
R_ReleaseObject(p);
}
C# interop wrapper:
public class CppInterop : DynamicInterop.UnmanagedDll // http://www.nuget.org/packages/DynamicInterop/
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void _c_api_call([In, Out] [MarshalAs(UnmanagedType.LPArray)] int[] values);
public int[] CreateArray()
{
// These two following references could be cached for efficiency later on.
var getLength = this.GetFunction<_c_api_call_getlength>("c_api_call_getlength");
var getValues = this.GetFunction<_c_api_call>("c_api_call");
// As far as I know it is preferable, if not needed, to handle arrays in two steps:
// we need to know the expected length to allocate in C# buffer memory.
int expectedLength = getLength();
int[] result = new int[expectedLength];
getValues(result);
return result;
}
I have a dll for third party app and normally communicate with it via Named Pipes. But NPs works only when the other app is started. Can I directly call a method from the dll to see its version.
C++ VS2012
#ifdef MTFST_EXPORTS
#define MTFST_API __declspec(dllexport)
#else
#define MTFST_API __declspec(dllimport)
#endif
#define LIBRARY_VERSION "3.0"
....
using namespace std;
MTFST_API char *__stdcall FST_LibraryVersion()
{
return LIBRARY_VERSION;
}
I tried the following code, but it doesn't work. .NET 4.
internal class Program
{
[DllImport("Library.dll")]
private static extern char[] FST_LibraryVersion();
private static void Main(string[] args)
{
Console.WriteLine(new string(FST_LibraryVersion()));
}
}
.NET arrays aren't compatible with raw pointers. You'll need to either use IntPtr or pass in a destination buffer to the function:
void __stdcall FST_LibraryVersion(char *dest)
{
strcpy(dest, LIBRARY_VERSION);
}
Obviously, you'll need to include checks to prevent buffer overflow.
Also, see PInvoke for C function that returns char *
I will spend a little time explaining my project stucture:
There are three dlls:
mclController.dll - a third party dll written in C# to control the hardware..
MCLWrapper.dll - I wrote this ll in C# such that it will be working as a COM to expose the mclControl.dll to a native C++ dll.
ThorDetectorSwitch.dll - I wrote this dll with native C++.
Structure:
The ThorDetectorSwitch.dll calls the MCLWrapper.dll which wraps mclController.dll.
I am implementing a small testing console application in C++, TDSTest.exe to call ThorDetecttorSwitch.dll.
So it basically works like this: TDSTest.exe -> ThorDetectorSwitch.dll -> MCLWrapper -> mclController.dll
Some code:
-How TDSTest.exe (Windows console application, built with x64 configuration) calls ThorDetectorSwitch.dll:
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <math.h>
#include <windows.h>
#include "TDSTest.h"
typedef long (*TDSFindDevices)(long&);
typedef long (*TDSGetParam)(const long, double&);
typedef long (*TDSTeardownDevice)();
typedef long (*TDSStartPosition)();
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
if (argc < 2)
{
cout<<"This is ThorDetecttorSwitch test program."<<endl;
return 1;
}
HINSTANCE hInst = LoadLibrary(_T(".\\Modules_Native\\ThorDetectorSwitch.dll"));
if( hInst == NULL )
{
DWORD err = GetLastError();
cout<<"Error loading ThorDetectorSwitch.dll. Program exiting..."<<endl;
return 1;
}
}
-Constructor of the ThorDetectorSwitch.dll EDITTED! on 06/15/2013, Central Time 19:41
ThorDetectorSwitch::ThorDetectorSwitch() :_mcSwitch(ComHelper(__uuidof(MCLControlClass)))
{
CoInitialize(NULL);
MCLWrapper::MCLControlPtr mclSmartPtr;
HRESULT hr = CoCreateInstance(__uuidof(MCLWrapper::MCLControlClass), NULL, CLSCTX_ALL, __uuidof(MCLWrapper::MCLControl), (void**)&mclSmartPtr); // program breaks right here!!!
_mcSwticth = mclSmartPtr;
_A = WstringToBSTR(L"A");
_B = WstringToBSTR(L"B");
_C = WstringToBSTR(L"C");
_D = WstringToBSTR(L"D");
_deviceDetected = FALSE;
}
The MCLWrapper that makes a COM object
// C# COM wrapper
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using mcl_RF_Switch_Controller64;
using System.Runtime.InteropServices;
// for function reference see miniCircuit RF controller manual
namespace MCLWrapper
{
[Guid("7C312A7C-2E77-4de7-A76F-990F268AB818")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface MCLControl
{
[DispId(1)]
void Connect(string SerialNumber);
[DispId(2)]
void Set_Switch(string SwitchName, int Val);
[DispId(3)]
void Set_SwitchesPort(byte binVal);
[DispId(4)]
void GetSwitchesStatus(int statusRet);
[DispId(5)]
void Disconnect();
};
[Guid("BEC33A1D-BB98-4332-B326-92D480ECC246"),
ClassInterface(ClassInterfaceType.None)]
public class MCLControlClass : MCLControl
{
private USB_RF_SwitchBox _sb = new USB_RF_SwitchBox();
public void Connect(string SerialNumber)
{
_sb.Connect(ref SerialNumber);
}
public void Set_Switch(string SwitchName, int Val)
{
_sb.Set_Switch(ref SwitchName, ref Val);
}
public void Set_SwitchesPort(byte binVal)
{
_sb.Set_SwitchesPort(ref binVal);
}
public void GetSwitchesStatus(int statusRet)
{
_sb.GetSwitchesStatus(ref statusRet);
}
public void Disconnect()
{
_sb.Disconnect();
}
}
}
My Problem:
When the TDSTest is executed, it first hits
HINSTANCE hInst = LoadLibrary(_T(".\\Modules_Native\\ThorDetectorSwitch.dll"));
then it breaks at:
hr = CoCreateInstance(......) in the ThorDetectorSwitch.cpp
hr = -858993460 is the return;
A few additionals
I am kept be told it was because CoInitialized() did not get called, and that is the reason, but I feel that is not the reason because this ThorDetectorSwitch.dll works perfectly fine with another application, and I beleive I have called CoInitialized() in my code.
I have registered my MCLWrapper.dll with regasm MCLWrapper.dll /tlb:MCLWrapper.tlb /codebase
Debugger output: "Attempting managed execution inside OS Loader lock. Do not attempt to run managed code inside a DllMain or image initalization function since doing so can cause the application of hang."
So right now I have no idea what direction I should go, and I have been struglling with this problem for days. So I really hope someone can give some pointers for me. Thanks!
You need to lazily construct your object instead of have it as a global variable created on DLL load.
Maybe you could have your DLL provide an Initialize() function that would be called by the client? Assuming you can't make your object "not global at all" of course.
I'm trying to use C++ DLL(borland c builder) from c#. Function writeParameter works fine, it writes correct data to a file, but then i have an exception "An unhandled exception of type 'System.StackOverflowException' occurred in PresentationFramework.dll"
C++ code:
#include <vcl.h>
#include <windows.h>
#include <fstream.h>
#pragma hdrstop
#pragma argsused
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, LPVOID lpvReserved)
{
return 1;
}
//---------------------------------------------------------------------------
#pragma pack (push,1)
typedef struct
{
int a;
}ABC;
#pragma pack (pop)
//---------------------------------------------------------------------------
extern "C" void __declspec(dllexport) __cdecl writeParameter(ABC *abc)
{
ofstream outfile("result.txt");
outfile<< "A=" <<endl;
outfile << abc->a <<endl;
outfile.close();
}
c#:
[StructLayoutAttribute(LayoutKind.Sequential)]
public class ABC
{
public int a;
}
[DllImport("D:\\monitorVC.dll", EntryPoint = "_writeParameter", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void WriteParameter(
[In,MarshalAs(UnmanagedType.LPStruct)]
ABC abc
);
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
var abc = new ABC() {a = 123};
WriteParameter(abc);
}
Read this blog post about the very poorly chosen name for UnmanagedType.LPStruct and how it does not do what everybody thinks it does. Fix your declaration like this:
[DllImport(...)]
public static extern void WriteParameter([In] ref ABC abc);
class != struct in C#.
Also, the packing of your structure is not the same between the C# version and the C++ version.
I know that it's been a while since this question was posted but I had the same experience trying to load to a VS2010 C# project, a .dll built with CodeGear C++ Builder 2007.
The work-around was to remove all TForms from my .dll. It seemed to me that the exported symbols from these forms (which by the way I could not remove) where "leading" the loader to a stack overflow.
Regards.