I'm not good at all with C++, so please excuse me if this is easy. I have had to write 2 C++ DLL's in order to call functions in an SDK that we have to use. The first is a native C++ DLL, which imports the SDK functionality. The second is a CLI C++ DLL, which calls the first DLL. The second DLL is referenced in a C# project and is then called in order to communicate with the SDK from within C#, as I have no other way of talking to the C++ SDK from C# (P/Invoke, etc, nothing works). My main problem is how to pass values from C# to the C++ DLL. When I debug, my values come through empty Here are small snippets which summarize what I've done.
C++ Native DLL.h
namespace N
{
class __declspec(dllexport) C
{
public:
int Write(char a[14], char* B);
}
}
C++ Native DLL.cpp
namespace N
{
int N::C::Write(char a[14], char* B)
{
// Here I need to add a 1 in front and \0 at the end of variable a
char t[16] = {0};
t[0] = 0x31;
strcpy(&t[1], a);
}
}
C++ CLI DLL.h
namespace N
{
class C;
namespace NN
{
public ref class CC
{
public:
int Write(char a[14], char* B);
}
}
}
C++ CLI DLL.cpp
N::NN::C::CC() : _impl(new N::NN::C())
{
}
int Write(char a[14], char* B)
{
return _impl->Write(a, B);
}
My big problem is how to pass the values from C# to the CLI dll so they are usable. I cannot change the data types or definitions in the C++ dll's. At the moment this is what I have in C#, after referencing the CLI dll:
C# method (partial):
N.NN.C.CC c = new N.NN.C.CC();
byte[] ba = Encoding.ASCII.GetBytes("11111111111111");
byte[] bB = Encoding.ASCII.GetBytes("BBBBBB");
unsafe
{
sbyte* bpa;
sbyte* bpB;
fixed (byte* pa = ba)
{
bpa = (sbyte*)pa;
}
fixed (byte* pB = bB)
{
bpB = (sbyte*)pB;
}
int iResult = c.Write(bpa, bpB);
}
This is all done in VS 2012. My problem is getting the first parameter a to be usable in the first DLL. Please let me know if I should give more details anywhere or try explain better, but I hope this will suffice.
Related
I have a SDK written in C++ which manages a device. My program controlling the device is written in C#, so naturally a CLI wrapper class does the translation between both languages. My C# project includes the wrapper as a DLL.
My issue is that the C++ SDK is using pointers to head to arrays of data. These pointers are also available in the wrapper.
Wrapper .cpp code:
Wrapper::Wrapper()
{
myData = new DataAquis(); //initiate C++ class's instance
}
int Wrapper::Start()
{
//(..)
int num = myData->Start();
ptr = (myData->img);
return num;
}
This code initializes the device and creates a pointer to a data structure (array of unsigned char).
Wrapper SDK .cpp code:
int DataAquis::Start()
{
// (...)
// Pointer from SDK
img = pBuffer;
// (...)
return FAILED(nError) ? -1 : 0;
}
Wrapper .h code:
public ref class Wrapper
{
public:
Wrapper();
// (...)
unsigned char *ptr;
private:
// (...)
};
Code C#:
public static Wrapper myDataAqui;
// (...)
private static void DataAquisition()
{
// call the start function in wrapper
myDataAqui.Start();
// Unsafe code for pointer use
unsafe
{
// point to aquired data
byte* imgptr1 = myDataAqui.ptr;
// AccesViolationException in above line.
// Data processing
for (y = 0; y < 256; y++)
{
for (x = 0; x < 320; x++)
{
int temp = x * 256 + 255 - y;
Spectrum1.X_Data_brut[bufferIndex][temp] = (UInt16)(*imgptr1++ + *imgptr1++ * 256);
aquirData[temp] = Spectrum1.X_Data_brut[bufferIndex][temp];
}
}
// (...)
}
}
As shown, an AccessViolationException is triggered at the line where I cast the Wrapper pointer to a local byte pointer.
If I put a breakpoint on that line, I can see that the Wrapper pointer correctly points to a memory address, but says that it is unable to read memory, so the pointed data is never gathered in C#.
I have read that the C# equivalent of an unsigned char in C++ is a byte, so normally I should read the same amount of data and never go outside the boundaries of my data structure.
Additionnal information that could be useful:
This project has been copied from another PC and the same code is functional on that PC.
Both PC have same Visual Studio, same .Net version, same SDK, both 64 bit compiled. Only Windows versions differ (working on Windows 8 and not working on Windows 7).
I unsuccessfully tried using Marshal functions.
Do you have any ideas how to fix this ?
I'm not sure why you're getting an exception but I'd marshal it into a CLR array on the C++/CLI side so no unsafe code is needed on the C# side.
C++/CLI:
#include <vcclr.h>
#include <algorithm>
#pragma unmanaged
const int data_size = 100;
unsigned char * foo()
{
auto p = new unsigned char[data_size];
for (int i = 0; i < data_size; ++i)
p[i] = (unsigned char)i;
return p;
}
#pragma managed
public ref class Wrapper
{
public:
array<unsigned char>^ data;
void Start()
{
auto p = foo();
data = gcnew array<unsigned char>(data_size);
pin_ptr<unsigned char> data_pin = &data[0];
std::copy(p, p+data_size, (unsigned char *)data_pin);
}
};
C#:
class Program
{
static void Main(string[] args)
{
var wrapper = new Wrapper();
wrapper.Start();
System.Console.WriteLine($"{wrapper.data[15]}");
}
}
This will contain any possible problems close to the source and make debugging a lot less confusing. If it crashes in std::copy then you're just using your C++ object wrong.
Stuck in very naive issue. I have two project, One in C++ and other in C#. Idea is to use C++ project as wrapper around some C libs. and performing actual logic in C#.
Passing the Value type is very convenient. But with reference type i am having hard time WITHOUT USING unsafe or DllImport attribute.
C++
Cryptoki.Wrapper.h File
using namespace System;
#pragma comment(lib, "legacy_stdio_definitions.lib")
namespace CryptokiWrapper {
public ref class CryptokiInit
{
public:
char* TESTString(char* test);
double TESTDouble(double test);
};
}
Cryptoki.Wrapper.cpp File
#include "stdafx.h"
#include "Cryptoki.Wrapper.h"
using namespace std;
using namespace CryptokiWrapper;
char* CryptokiInit::TESTString(char* test)
{
char* r = test;
return r;
}
double CryptokiInit::TESTDouble(double test)
{
unsigned long int r = test;
return r;
}
C# Code
using System;
using System.Runtime.InteropServices;
using CryptokiWrapper;
namespace CallCryptoki
{
class Program
{
//[MarshalAs(UnmanagedType.LPTStr)]
//public String msg = "Hello World";
static void Main(string[] args)
{
CryptokiInit ob = new CryptokiInit();
//This Works
doubled d = ob.TESTDouble(99);
//But having hard time accepting the char* reference
//or sending string as refrence without using unsafe
// like
string text = "Hello World!";
string res = (*something*)ob.TESTString((*something*)text);
}
}
}
IS there any type of cast (i.e. something) .....
is there anyway where i am able to easily perform this action. (only reference transfer will be sufficient, then i can build string or object)
Like on another function works, using the double as parameter and return type.
Though above example speak of string only, but would like to understand as concept so that i could write interop for any reference type between two project(i.e. C# and C++)
Thanks in advance for help!
First, that's not plain C++, but C++/CLI - which is primarily designed for managed/unmanaged code interoperability.
Your C++/CLI function can use .NET's string type like this:
System::String^ TESTString(System::String^ test);
^ means managed reference, think of it as the managed equivalent of *.
Now, to use the string data in pure C++, you have two choices:
marshal it - see Overview of Marshaling in C++
For instance, if you need to convert it to a const char*, do the following:
#include <msclr/marshal.h>
msclr::interop::marshal_context ctx;
auto testCStr = ctx.marshal_as<const char*>(test);
// testCStr is freed when ctx goes out of scope
This will copy the string data, as the memory representation needs to change from 2 bytes par character to a single one.
access the memory directly as const wchar_t*. You need to pin the string beforehand so it's not moved by the GC.
#include <vcclr.h>
pin_ptr<const wchar_t> testCStr = PtrToStringChars(test);
// testCStr behaves just like a const wchar_t*
Do not modify the string data this way.
To send a string back to the managed side, you can either use marshal_as<System::String^>(yourCString), or call gcnew System::String(yourCString);
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 done this using unmanaged dll but having some difficulty using managed dll.
I want to pass a string to my managed c++ wrapper class which process it and return modified string
the purpose of c++ dll is to return hex code of a file(and later modifies it to do some complicated tasks within dll) which i pass it as a string, for this approcach i have used managed c++ dll instead of unmanaged one.
my c++ class is as follows :
using namespace std;
//main function - entry point of program __declspec(dllexport)
const char* hexclass:: getfilename(char* filename)
{
//to open file
ifstream::pos_type size;
string virusScanStatus;
char* fileAccessError;
string errorDes ="Exception has Occured With Current File Operation";
//hexcode logic goes here
fileAccessError = new char[errorDes.size()];
errorDes.copy(fileAccessError,errorDes.size());
return fileAccessError;
}
my c++ wrapper class is as follows :
header file including c++ file here (not shown for code readabitily)
using namespace System;
namespace CWrapperHexValue {
public ref class cWrapperHexValue
{
public:
cWrapperHexValue();
const char* hexValue;
const char* getHexValue(char* fileName);
private:
hexclass* pHexClass;
};
}
and my wrapper class is as follows :
// This is the main DLL file.
#pragma once
#include "stdafx.h"
#include "CWrapperHexValue.h"
#include "D:\Projects\program10\program10\hexclass.cpp"
#include "D:\Projects\program10\program10\hexclass.h"
CWrapperHexValue::cWrapperHexValue::cWrapperHexValue()
{
pHexClass = new hexclass();
}
const char* CWrapperHexValue::cWrapperHexValue::getHexValue(char* fileName)
{
hexValue= pHexClass -> getfilename(fileName);
return hexValue;
}
and finally my c# code to send filename is as follows :
//my c++ dll name is CWrapperHexValue
CWrapperHexValue.cWrapperHexValue objHexClass = new CWrapperHexValue.cWrapperHexValue();
byte[] fileNameBytes = Encoding.ASCII.GetBytes(fileNameForHexScan);
unsafe
{
fixed (byte* p= fileNameBytes)
{
sbyte* sp = (sbyte*)p;
sbyte* returnSp = objHexClass.getHexValue(sp);
}
}
now how do i get back the returnSp value as a string or any other better way to pass and get string, please provide useful code because i have not much experience with c++/c# cli conversion
please advice how can i improve my code for better memory management becuase i have to pass a whole lot of system files one by one and get their hex code
Depending on what
// hexcode logic
is, it might be better/easier to implement it in C# and forget about the marshaling.
If you really need to marshal strings from C++ to C#, this topic will likely help you quite a bit: How to marshall c++ char* to C# string using P/INVOKE (code is here)
Basically just build a native DLL out of your native code and you can just pinvoke it from C# rather than have the intermediate managed C++ class if all you need is a string.
In terms of memory management, if you can move the hexcode logic into C#, that would eliminate the need to marshal strings and likely save you some processing, but more importantly, be less painful to debug. Namely, why can't you do this?
C#
public void SomeFunction()
{
string filepath = "foo.bar"
string hexVal = HexCodeLogicInManaged(filepath);
... other code ...
}
private string HexCodeLogicInManaged(string filepath)
{
// hexcode logic converted from native to managed
}
I have written some code in native C++ which makes calls to fortran routines of the blas and lapack library (linked as dll's). The native code runs fine, with no compiler or runtime errors or warnings.
Now, I was trying to wrap the native C++ into a dll to use in the .NET framework, so I started a Visual C++ project, wrote a wrapper ref class, compiled this into a dll and included that dll into a C# project.
After I finally managed to get the C# project to compile, I tried to run it. But I get a runtime error at the first call to a lapack routine! (i.e. at the line dpbtrf_( &LOWER, &n, &izero, D.data(), &ione, &info ); in Test.cpp)
The error says:
System.AccessViolationException was unhandled
Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Source=DotNetTest
Am I missing some compiler option?
Or is there really corrupt memory which the native C++ compiler\runtime did not see?
Any tips on how to debug this?
Thanks a lot!
Here's a tiny reproduction that generates the same problem as the original code (which is too extensive to post here):
the native C++ Test.h:
#ifndef TEST_H_INCLUDED
#define TEST_H_INCLUDED
#include <vector>
namespace NativeTest
{
class Test
{
public:
Test( int size );
~Test();
void set( int i, double d );
double get( int i ) const;
int chol();
private:
std::vector<double> D;
int n;
}; // class Test
} // namespace NativeTest
#endif // TEST_H_INCLUDED
the native C++ Test.cpp (included in the VC++ project) (!EDITTED with __cdecl):
#include "Test.h"
using namespace NativeTest;
const int ione = 1;
const int izero = 0;
const char LOWER = 'L';
extern "C"
{
// factorization for banded matrix
void __cdecl dpbtrf_( const char *UPLO, const int *N, const int *KD,
double *AB, const int *LDAB, int *INFO );
}
Test::Test( int size ) : n( size )
{ D.resize( n ); }
Test::~Test() { }
void Test::set( int i, double d ) { D[ i ] = d; }
double Test::get( int i ) const { return D[ i ]; }
int Test::chol()
{
int info = 0;
dpbtrf_( &LOWER, &n, &izero, D.data(), &ione, &info );
return info;
}
The C++\CLI wrapper:
// TestNet.h
#pragma once
#include "Test.h"
using namespace System;
namespace DotNetTest {
public ref class TestNet
{
public:
TestNet( int size ) { test = new NativeTest::Test( size ); }
~TestNet() { this->!TestNet(); }
!TestNet() { delete test; test = NULL; }
void set( int i, double d ) { test->set( i, d ); }
double get( int i ) { return test->get( i ); }
int chol() { return test->chol(); }
protected:
NativeTest::Test * test;
};
}
The C# call:
DotNetTest.TestNet t = new DotNetTest.TestNet(2);
t.set(0, 2);
t.set(1, 3);
int info = t.chol();
I got it fixed, at least for the test code posted here. I wrongly added the gcc-compiled lapack\blas binaries, the same as I used for my original project (which also used gcc).
Today, I downloaded the precompiled binaries for windows from here, put those in the bin folder of my C# project (together with the 3 MinGW dll's: libgcc_s_dw2-1.dll, libgfortran-3.dll and libquadmath-0.dll) and made sure the Visual C++\CLI project used the same libraries to link to (by setting the Additional Link Directory to the C# bin folder). This fixed it, and I got the result I wanted (no access violation and the calculation was correct).
I also removed the /clr flag in the compile options for the Test.cpp file, and switch the 'Use precompiled headers' off for that file.