Can't access public methods of C++/CLI wrapper class in C# - c#

I have written a Wrapper assembly in C++/CLI. Now I want to call into C++/CLI from C#,however I get the error:
Error CS0122 'USBWrapper.setPortDisable(int)' is inaccessible due to its protection level
So my .h and .cpp file are as follows(which is compiled with the /clr switch):
The .h file:
// USBFunctionsWrapper.h
#pragma once
#include "C:\Users\admin\Downloads\brainstem_dev_kit_win32_i386_17\brainstem_dev_kit_win32_i386\development\lib\BrainStem2\BrainStem-all.h"
#include "C:\Users\admin\Downloads\brainstem_dev_kit_win32_i386_17\brainstem_dev_kit_win32_i386\development\lib\BrainStem2\aUSBHub2x4.h"
using namespace System;
namespace USBFunctionsWrapper {
public ref class USBWrapper
{
public:
USBWrapper();
~USBWrapper();
aErr setPortDisable(int portNumber);
aErr setPortEnable(int portNumber);
aErr usbSleep(int time);
private:
};
}
The .cpp file:
// This is the main DLL file.
#include "stdafx.h"
#include "USBFunctionsWrapper.h"
#include "C:\Users\admin\Downloads\brainstem_dev_kit_win32_i386_17\brainstem_dev_kit_win32_i386\development\lib\BrainStem2\BrainStem-all.h"
#include "C:\Users\admin\Downloads\brainstem_dev_kit_win32_i386_17\brainstem_dev_kit_win32_i386\development\lib\BrainStem2\aUSBHub2x4.h"
aUSBHub2x4 stem;
aErr err = aErrNone;
USBFunctionsWrapper::USBWrapper::USBWrapper() {
err = stem.linkDiscoverAndConnect(USB);
}
USBFunctionsWrapper::USBWrapper::~USBWrapper() {
err = stem.linkDisconnect();
}
aErr USBFunctionsWrapper::USBWrapper::setPortDisable(int portNumber) {
return stem.usb.setPortDisable(portNumber);
}
aErr USBFunctionsWrapper::USBWrapper::setPortEnable(int portNumber) {
return stem.usb.setPortEnable(portNumber);
}
aErr USBFunctionsWrapper::USBWrapper::usbSleep(int time) {
return aTime_MSSleep(time);
}
The C# file I am calling these functions from:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using USBFunctionsWrapper;
namespace USBPortWrapper
{
public class WrapperClass
{
USBWrapper usb;
public WrapperClass()
{
usb = new USBWrapper();
}
public void setPortEnable(int portNumber)
{
usb.setPortEnable(portNumber);
}
}
}
I get the error for the usb.setPortEnable(portNumber) function in the c# code.
Do you know what could be a possible reason for that?

Related

How to fix memory leak when passing object from C# COM library to a C++ application

I have a C++ MFC application which consumes C# COM wrapper. The issue is whenever I invoke a function inside wrapper, I am experiencing memory leak. Can anyone explain how to clean up the allocations that are made within the C# COM wrapper.
Below code blocks mimic what I was trying to do, can anyone provide me the references/rightway to pass the structure object/ clean up the memory allocation
C# wrapper exposed as COM
using System;
using System.Runtime.InteropServices;
namespace ManagedLib
{
[ComVisible(true)]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct comstructure
{
public string[] m_strName;
public UInt32[] m_nEventCategory;
}
[Guid("4BC57FAB-ABB8-4b93-A0BC-2FD3D5312CA8")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface ITest
{
comstructure TestBool();
}
[Guid("A7A5C4C9-F4DA-4CD3-8D01-F7F42512ED04")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class Test : ITest
{
public comstructure TestBool( )
{
comstructure testvar = new comstructure();
testvar.m_strName = new string[100000];
testvar.m_nEventCategory = new UInt32[100000];
return testvar;
}
}
}
C++ code
#include <iostream>
#include <afx.h>
#include <afxwin.h>
#include <afxext.h>
#include <afxdtctl.h>
#include "windows.h"
#include "psapi.h"
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>
#endif // _AFX_NO_AFXCMN_SUPPORT
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS
#include <atlbase.h>
#import "..\comlibrary\bin\Debug\comlibrary.tlb"
comlibrary::ITest* obj;
class mleak
{
public:
void leakmemory()
{
comlibrary::comstructure v2;
v2 = obj->TestBool();
}
};
int main()
{
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
CLSID clsid;
HRESULT hResult = ::CLSIDFromProgID(L"ManagedLib.Test", &clsid);
hResult = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER,
__uuidof(comlibrary::ITest), (void**)&obj);
std::cout << hResult;
if (FAILED(hResult))
{
std::cout << "COM import failed!\n";
}
mleak m1;
for (int i = 0; i < 600; i++)
{
m1.leakmemory();
Sleep(100);
}
return 0;
}
You should release memory if it's allocated and no-one else frees it, obviously. Here, the allocated memory is the .NET's string[] and uint[] which are represented as SAFEARRAY* in the native world.
But, long story short: you can't really use structs as return type for COM methods. It's causes not only copy semantics issues (who owns struct's field memory, etc.), but in general, it won't even work depending on struct size, etc. lots of trouble, COM methods should return 32/64 bit-sized variables (or void).
So you can fix this using COM objects instead of structs. For example:
[ComVisible(true)]
public interface IOther
{
string[] Names { get; set; }
uint[] EventCategories { get; set; }
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class Other : IOther
{
public string[] Names { get; set; }
public uint[] EventCategories { get; set; }
}
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ITest
{
Other TestOther();
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class Test : ITest
{
public Other TestOther()
{
var other = new Other();
other.Names = new string[100000];
other.EventCategories = new UInt32[100000];
return other;
}
}
and in C++ side:
#include "windows.h"
#import "..\ManagedLib\bin\Debug\ManagedLib.tlb"
using namespace ManagedLib;
int main()
{
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
{
ITestPtr test; // see https://stackoverflow.com/a/16382024/403671
auto hr = test.CreateInstance(__uuidof(Test));
if (SUCCEEDED(hr))
{
IOtherPtr other(test->TestOther());
auto names = other->Names;
// do what you want with safe array here
// but in the end, make sure you destroy it
SafeArrayDestroy(names);
}
}
CoUninitialize();
return 0;
}
Note: you can also use CComSafeArray to ease SAFEARRAY programming.

How to get calculated value from C++ to C#

I have c++ and c# codes.
When I run my code, I want c++ codes calculate a value and my c# take the value and use it.
The value will change in c++, while running. And c# will take it.
c++ codes (simple):
int main()
{
int x;
void (int a); //calculate x and return x
}
how can I do this?
I tried with dll or I tried within adding reference. didn't work.
what should write in c# for getting x value?
edit: I'm trying to do like this:
NativeLib.h:
#ifndef _NATIVELIB_H_
#define _NATIVELIB_H_
#ifndef MYAPI
#define MYAPI
#endif
#ifdef __cplusplus
extern "C" {
#endif
//MYAPI void print_line(const char* str);
MYAPI void print_line(const int a);
#ifdef __cplusplus
}
#endif
#endif // _NATIVELIB_H_
NativeLib.cpp:
#include "NativeLib.h"
#include <stdio.h>
int x = 10;
MYAPI void print_line(const int a) {
int a = x;
}
c# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace PInvokeTest {
class Program {
static void Main(string[] args) {
print_line(a);
}
[DllImport("NativeLib.dll")]
private static extern void print_line(int a);
}
}
why do I get error?

Calling managed C++ lib from C# passing values by reference

I'm trying to set up a test project to figure out how to correctly wrap a native C++ lib in a managed C++ DLL, to then be called by a C# application.
For my test case I have 3 application:
CPP lib
I compile a library with a class containing four functions.
1) return a double value
2) return a string
3) take a double by reference, modify it.
4) take a string by reference, modify it.
cpp_lib.h
namespace cpp_lib
{
class CPPLibFunctions{
public:
CPPLibFunctions();
public:
static double getValue();
static void incrementValue(double& val);
static std::string getText();
static void changeText(std::string& txt);
};
}
cpp_lib.cpp
#include "cpp_lib.h"
#include <stdexcept>
namespace cpp_lib{
CPPLibFunctions::CPPLibFunctions(){};
double CPPLibFunctions::getValue(){
return 5.5;
}
void CPPLibFunctions::incrementValue(double& val){
val += 1.0;
}
std::string CPPLibFunctions::getText(){
return "success";
}
void CPPLibFunctions::changeText(std::string& txt){
txt += "_123";
}
}
Managed CPP lib
This is a managed C++ project compiled to a DLL, which wraps the functions from cpp_lib.
managed_cpp_lib.h
#include "cpp_lib.h"
#pragma once
#include <msclr\marshal_cppstd.h>
using namespace System;
namespace managed_cpp_lib {
System::String^ stdToSysString(std::string str){
return gcnew System::String(str.c_str());
}
public ref class ManagedCppFunctions
{
private:
cpp_lib::CPPLibFunctions * cppLibFunctions;
public:
ManagedCppFunctions(){
cppLibFunctions = new cpp_lib::CPPLibFunctions();
}
double getValue(){
return cppLibFunctions->getValue();
}
void incrementValue(double& val){
cppLibFunctions->incrementValue(val);
}
System::String^ getText(){
return stdToSysString(cppLibFunctions->getText());
}
void changeText(System::String^ txt){
//this does not work:
std::string txtstd = msclr::interop::marshal_as<std::string>(txt);
cppLibFunctions->changeText(txtstd);
txt = stdToSysString(txtstd);
}
};
}
csharp_app
Lastly, cssharp_app is a C# console application, which references managed_cpp_lib, and calls the functions.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using managed_cpp_lib;
namespace csharp_app
{
class Program
{
static void Main(string[] args)
{
managed_cpp_lib.ManagedCppFunctions managedCppFunctions = new managed_cpp_lib.ManagedCppFunctions();
//works:
double val = managedCppFunctions.getValue();
Console.WriteLine("\managedCppFunctions.getValue() = {0}", val);
//works:
string txt = managedCppFunctions.getText();
Console.WriteLine("\managedCppFunctions.getText() = {0}", txt);
//does not work:
/*managedCppFunctions.incrementValue(val);
Console.WriteLine("\managedCppFunctions.incrementValue(val) = {0}", val);*/
//the string is not modified:
managedCppFunctions.changeText(txt);
Console.WriteLine("\managedCppFunctions.changeText(txt) = {0}", txt);
Console.WriteLine("...");
}
}
}
The output of csharp_app is:
managedCppFunctions.getValue() = 5.5
managedCppFunctions.getText() = success
managedCppFunctions.changeText(txt) = success
So managedCppFunctions.getValue() and managedCppFunctions.getText() works.
managedCppFunctions.changeText(txt) does not modify the content of the string.
I'm not sure how to implement managedCppFunctions.incrementValue(val).
What is the correct way to pass a double and a string by reference, to then change their values using managed C++ and C#?

Calling function of Csharp Application using a injected dll

I want call the ConsoleAppTwo.Program::PrintMessage the application...
i create a csharp test software with a function to call me:
using System;
namespace ConsoleAppTwo
{
class Program
{
static void PrintMessage(string str)
{
Console.WriteLine(str);
}
static void Main(string[] args)
{
while (true)
{
if(Console.ReadKey().Key == ConsoleKey.NumPad0)
{
PrintMessage("[Whatever string]");
}
}
}
}
}
and i use the IDA pro to found the functions:
IDA PRO
after i use the cheat engine and add the 'address' "ConsoleAppTwo.Program::PrintMessage" and get the address pointer and i create the dll:
#include <string>
#include <Windows.h>
#include "main.h">
using namespace std;
typedef void(__cdecl *_FuncA)(string str);
DWORD WINAPI MainThread(LPVOID param) {
_FuncA func = (_FuncA)(0x3504F0);
while (!GetAsyncKeyState(VK_HOME))
{
if (GetAsyncKeyState(VK_INSERT) & 1)
{
func("What is your name");
}
Sleep(10);
}
FreeLibraryAndExitThread((HMODULE) param, 0);
return 0;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule);
CreateThread(0, 0, MainThread, GetModuleHandle(NULL), 0, 0);
default:
break;
}
return true;
}
and after i inject the dll in software and the code not work..., i get the output error: "EXCEPTION_ACCESS_VIOLATION"

Callbacks from C++ back to C#

say I have a C++ library function for computing PI:
// pi.h:
#ifdef BUILDING_DLL
#define DLL_MACRO __declspec(dllexport)
#else
#define DLL_MACRO __declspec(dllimport)
#endif
namespace Cpp {
class PI {
public:
static double DLL_MACRO compute();
};
};
// pi.cpp:
#include "pi.h"
#include <cmath>
double
Cpp::PI::compute() {
// Leibnitz summation formulae:
double sum = 0.0;
for(long n = 0; n < 100*1000*1000; n++)
sum += 4.0*pow(-1.0, n)/(2*n + 1.0);
return sum;
}
I need to call this function from C#, and I want to use C++/CLI as a "bridge". However this C++ function is somewhat slow. Therefore the C# code calling this function need to get callbacks telling it how far the function has come in %.
The C# code might need some state, e.g. a progress bar, to deal with this info.
So the callbacks from C++ must enter into a member function on the C# side.
So I introduce:
// piCLI.h: The C++/CLI "bridge" between C# and C++
#include "pi.h"
#pragma once
namespace CLI {
public ref class PI abstract {
public:
double compute() {
return Cpp::PI::compute();
}
virtual void progress(int percentCompleted) = 0;
};
};
and
namespace CSharp
{
public class PI : CLI.PI
{
public override void progress(int percentCompleted)
{
System.Console.WriteLine(percentCompleted + "% completed.");
}
}
}
Now invoking CSharp.PI.compute() works fine :-). It forwards the call to Cpp::PI::compute() as intended.
But how do I get the C++ library to forward progress updates to CSharp.PI.progress() whilst Cpp::PI::compute() is running?
Thanks in advance for any answers!
I would take a function pointer/delegate approach as well:
// pi.h:
#pragma once
#ifndef DLL_MACRO
#ifdef BUILDING_DLL
#define DLL_MACRO __declspec(dllexport)
#else
#define DLL_MACRO __declspec(dllimport)
#endif
#endif
namespace Cpp {
typedef void (__stdcall *ComputeProgressCallback)(int);
class PI {
public:
static double DLL_MACRO compute(ComputeProgressCallback callback);
};
}
// pi.cpp:
#include "pi.h"
#include <cmath>
double Cpp::PI::compute(Cpp::ComputeProgressCallback callback) {
double sum = 0.;
for (long n = 0L; n != 100000000L; ++n) {
sum += 4. * std::pow(-1., n) / (2L * n + 1.);
callback(/*impl*/);
}
return sum;
}
// piCLI.h: The C++/CLI "bridge" between C# and C++
#pragma once
#include "pi.h"
namespace CLI {
public delegate void ComputeProgressDelegate(int percentCompleted);
public ref class PI abstract sealed {
public:
static double compute(ComputeProgressDelegate^ callback) {
using System::IntPtr;
using System::Runtime::InteropServices::Marshal;
IntPtr cbPtr = Marshal::GetFunctionPointerForDelegate(callback);
return Cpp::PI::compute(
static_cast<Cpp::ComputeProgressCallback>(cbPtr.ToPointer())
);
}
};
}
namespace CSharp {
public static class PI {
public static double compute() {
CLI.PI.compute(
percentCompleted => System.Console.WriteLine(
percentCompleted.ToString() + "% completed."
)
);
}
}
}
Or, to override an abstract progress method rather than creating a delegate on the C# side:
// piCLI.h: The C++/CLI "bridge" between C# and C++
#pragma once
#include "pi.h"
namespace CLI {
public ref class PI abstract {
delegate void ComputeProgressDelegate(int percentCompleted);
public:
double compute() {
using System::IntPtr;
using System::Runtime::InteropServices::Marshal;
ComputeProgressDelegate^ callback = gcnew ComputeProgressDelegate(
this,
&PI::progress
);
IntPtr cbPtr = Marshal::GetFunctionPointerForDelegate(callback);
return Cpp::PI::compute(
static_cast<Cpp::ComputeProgressCallback>(cbPtr.ToPointer())
);
}
protected:
virtual void progress(int percentCompleted) abstract;
};
}
namespace CSharp {
public sealed class PI : CLI.PI {
protected override void progress(int percentCompleted) {
System.Console.WriteLine(
percentCompleted.ToString() + "% completed."
);
}
}
}
Pass a C++/cli defined Native function to your native C++ as a callback and upon callback use a gcroot to call your managed C# function.

Categories

Resources