How do you call a c# method in c++? - c#

here and here they do talk about what to do, but i seem to be unable to find my c# project in c++.
I have added the c# project as a reference in the c++ project but whenever i try to use the method i need, it can't find the namespace. i have added it by right clicking the c++ project and going for 'reference' then added the c# project with add new reference. both projects are in the same solution.
In the below code excamples i have given the full c# code (except for usings) and a part of the c++ code (the method where i am trying to call the c# method from). I have also changed some of the namespacing to be more generic and contain no sensitive information.
the c# code is like this.
namespace Company.Pins.Bank.Decryption
{
public class Decrypt
{
[DllImport("decryptsn.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr decryptsn(byte[] InpData);
//__declspec(dllimport) char* decryptsn(char* InpData);
public static String Decryption(string param2)
{
byte[] InpData = new byte[255];
InpData = StrToByteArray(param2);
return Marshal.PtrToStringAnsi(decryptsn(InpData));
}
public static byte[] StrToByteArray(string str)
{
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
return encoding.GetBytes(str);
}
}
}
C++ code
CPReSInterfaceApp theApp;
extern "C" DllExport BOOL WINAPI UserInstruction(
HWND hWnd, HINSTANCE hInst, double* lpNumeric,
TCHAR* lpAlpha1, TCHAR* lpAlpha2)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
if (lpNumeric == NULL || lpAlpha1 == NULL || lpAlpha2 == NULL)
return FALSE;
ReconcileUHParameter(lpNumeric, lpAlpha1, lpAlpha2);
int iCommand = (int)lpNumeric[0];
lpNumeric[0] = 6;
lpAlpha2 = Company.Pins.Bank.Decryption.Decrypt.Decryption("123456");
return TRUE;
}

You need to add a #using directive to the code. For example, if your C# dll were named Decrypt.dll, add this to the top of your C++ compiland:
#using "Decrypt.dll"
You also need to make sure the C++ code that calls a managed method is also compiled as managed using the /clr compiler option.
Also, I believe you need to use :: as a namespace separator, rather than ..
lpAlpha2 = Company::Pins::Bank::Decryption::Decrypt::Decryption("123456");

Related

How to fix 'Method's type signature is not PInvoke compatible' error in C#

All I need to know is how to return a struct from a PInvoke in C++ that has the following struct. For the moment I can deal with it being blank and I just want to know how to return the struct under the conditions set in the code.
I've tried with with the entire struct that I need to return and isolated each part of struct to know which part is giving me the issue (which will be made apparent in the code provided).
I've tried the same method by wanting to return a few integers within the struct which works fine. (Tried to make this bold using ***, ___)
//.header file
typedef struct { //Defintion of my struct in C++
TCHAR msg[256];
}testTCHAR;
//.cpp file
extern "C" {
__declspec(dllexport) testTCHAR* (_stdcall TestChar(testTCHAR* AR))
{
AR->msg;
return AR;
}
}
In my C# I Call the .dll as:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void testChar(testTCHAR AR);
[DllImport("C:\\Users\\jch\\source\\repos\\FlatPanelSensor\\x64\\Debug\\VADAV_AcqS.dll", EntryPoint = "TestCallBackChar", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern testTCHAR TestCallBackChar([MarshalAs(UnmanagedType.FunctionPtr)] testChar call);
//Struct
public struct testTCHAR
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string rMsg; //I assume the error should be fixed here
but to what exactly I don't know.
}
//Defining the callback
testChar tChar =
(test) =>
{
//As far as I'm aware this part can be left blank
as I followed a tutorial online
};
testTCHAR returned = TestCallBackChar(tChar); //This is where the error
happens
I just need to return the struct, preferably with a value attached to it.
The error I get is 'Method's type signature is not PInvoke compatible.' Which is in the title, but I'm covering all basis.
If you need anymore information about this please ask away and I should be able to provide.
Since the testTCHAR type contains managed references, you cannot use a pointer as a part of signature (which you didn't), but also it does not make sense to pass it by value (this is why the runtime produces the error you see).
You need to change your signatures so that it's apparent that you want to pass a pointer into the native method:
public delegate void testChar(IntPtr data);
public unsafe static extern IntPtr TestCallBackChar([MarshalAs(UnmanagedType.FunctionPtr)] testChar call);
When calling you need to explicitly marshal structures to the managed equivalent (and vice versa):
testChar tChar =
(inputPtr) =>
{
testTCHAR input = (testTCHAR)Marshal.PtrToStructure(inputPtr, typeof(testTCHAR));
};
IntPtr returnedPtr = TestCallBackChar(tChar);
testTCHAR returned = (testTCHAR)Marshal.PtrToStructure(returnedPtr, typeof(testTCHAR));
Additionally, I guess there is a mismatch between C++ and C# signature and method name.

How to pass a string or string equivalent between C# and C++ that is in a struct? [duplicate]

I thought the problem is inside my C++ function,but I tried this
C++ Function in C++ dll:
bool __declspec( dllexport ) OpenA(std::string file)
{
return true;
}
C# code:
[DllImport("pk2.dll")]
public static extern bool OpenA(string path);
if (OpenA(#"E:\asdasd\"))
I get an exception that the memory is corrupt,why?
If I remove the std::string parameter,it works great,but with std::string it doesnt work.
std::string and c# string are not compatible with each other. As far as I know the c# string corresponds to passing char* or wchar_t* in c++ as far as interop is concerned.
One of the reasons for this is that There can be many different implementations to std::string and c# can't assume that you're using any particular one.
Try something like this:
bool __declspec( dllexport ) OpenA(const TCHAR* pFile)
{
std::string filename(pFile);
...
return true;
}
You should also specify the appropriate character set (unicode/ansi) in your DllImport attribute.
As an aside, unrelated to your marshalling problem, one would normally pass a std:string as a const reference: const std:string& filename.
It's not possible to marshal a C++ std::string in the way you are attempting. What you really need to do here is write a wrapper function which uses a plain old const char* and converts to a std::string under the hood.
C++
extern C {
void OpenWrapper(const WCHAR* pName) {
std::string name = pName;
OpenA(name);
}
}
C#
[DllImport("pk2.dll")]
public static extern void OpenWrapper( [In] string name);
I know this topic is a tad old but for future googlers, this should also work (without using char* in C++)
C#:
public static extern bool OpenA([In, MarshalAs(UnmanagedType.LPStr)] path);
C++:
bool __declspec( dllexport ) OpenA(std::string file);
std::wstring and System.string can be compatible through below conversion:
C++ :
bool func(std::wstring str, int number)
{
BSTR tmp_str = SysAllocStringLen(str.c_str(), str.size());
VARIANT_BOOL ret = VARIANT_FALSE;
// call c# COM DLL
ptr->my_com_function(tmp_str, number, &ret);
SysFreeString(tmp_str);
return (ret != VARIANT_FALSE) ? true : false;
}

AccessViolationException when calling C++/CLI wrapper from C#

I am trying to create a C++/CLI wrapper for passing class objects from unmanaged C++ DLL into managed C# code (which subsequently displays the content of the objects on web pages). I have this function in the unmanaged C++ code:
ProbeState _cdecl ManagerAPI::getProbeState()
{
ProbeState ps = psdao.getLastProbeStateByProbeId(1);
return ps;
}
I call the function in the C++/CLI wrapper:
using namespace System;
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "../ManagerApp/ProbeState.h"
typedef ProbeState(*PSFunc)(void);
public ref class ManagerAPIWrapper
{
private:
HINSTANCE managerApp;
public:
ManagerAPIWrapper()
{
managerApp = LoadLibrary(L"ManagerApp.dll");
}
System::String^ testFunc()
{
PSFunc psFunc = (PSFunc)GetProcAddress(managerApp, "?getProbeState#ManagerAPI##QAA?AVProbeState##XZ");
ProbeState *ps = new ProbeState(psFunc());
System::String ^s = gcnew System::String(ps->getName().c_str());
delete ps;
return s;
}
};
And finally I call the wrapper from my C# controller:
ManagerAPIWrapper.ManagerAPIWrapper wrapper = new ManagerAPIWrapper.ManagerAPIWrapper();
ViewBag.DllMessage = wrapper.testFunc();
It always throws an exception on the line ProbeState *ps = new ProbeState(psFunc());
Strange thing, though, is when I compile the C++/CLI wrapper as a console application with added main function:
int _tmain(int argc, _TCHAR* argv[])
{
ManagerAPIWrapper::ManagerAPIWrapper wrapper;
System::Console::WriteLine(wrapper.testFunc());
getchar();
return 0;
}
This code works just fine and prints out the name of the state retrieved from the database by the C++ DLL. How come the C++/CLI works in console app and throws an exception when called from C#?
P.S.: The wrapper is compiled with /clr option. When I compiled the wrapper with /clr:pure, the exception was the same as with the C# call. Does it mean that when the wrapper is compiled within and called from C# app, it takes the pure option?
The wrapper is meant to convert the data between C++ and C#, so according to my opinion it should not be compiled with more strict options in the C# app. Is there any way to tell the C# compiler that this assembly contains mixed code?
OK, I finally got through this. After many hours spent with try&fail way of finding a solution, I tried to call a function from the unmanaged DLL directly from the C# code first, and then called a constructor of the wrapper, which succeeded in the LoadLibrary call. Code in the C# controller now looks like this:
[DllImport("C:\\ManagerApp.dll", CharSet = CharSet.Unicode,
EntryPoint = "?initFunc#ManagerAPI##QAEHXZ")]
private static extern int initFunc();
public ActionResult APITest()
{
ViewBag.Message = "API output test page.";
if (initFunc() == 0)
{
ViewBag.Error = "Could not initialize the library.";
return View();
}
ManagerAPIWrapper.ManagerAPIWrapper wrapper = new ManagerAPIWrapper.ManagerAPIWrapper();
ViewBag.DllMessage = wrapper.testFunc();
return View();
}
I am thinking it might help to add a dependency to the wrapper DLL on the unmanaged DLL and therefore get rid of the necessity of calling the initFunc.

Need to use a 'block' of C++ code in C# app

I was given a block of c++ code that looks like it was from a c++ app that makes use of
Shared Memory for sending messages to other programs.
The c++ code has no #include or anything yet. I was given the code to use in my C# application and I am pretty stuck. I somewhat understand what the code does, but I don't know it well enough to translate it to C# as I am pretty new to coding.
My question is, what is the easiest way to be able to use the functionality of the code in my project? The end result is to send messages to another program, that will in turn do something that I'm not worried about.
I have tried creating different c++ projects and file types in my solution to link them using a reference later on, but I can never get it to compile properly.
Please let me know if you have some advice or a good place to look. I can always provide more information.
Code (I had to remove comments, sorry):
UINT WM_HELO_ZOOM_XYZ = RegisterWindowMessage("WM_HELO_ZOOM_XYZ");
int HELO_Broadcast_Zoom_Message(
double dbX,
double dbY,
double dbZ,
UINT uMessage=WM_HELO_ZOOM_XYZ) {
#ifndef HELO_
typedef struct {
UINT uMajVersion;
UINT uMinVersion;
DWORD dwReserved;
double dbX;
double dbY;
double dbZ;
} HELOCoordsStruct;
#endif
char *szSharedMemory = "HELO-_Coords";
char szErr[_MAX_PATH*3];
HANDLE hMem = OpenFileMapping(FILE_MAP_WRITE, FALSE, szSharedMemory);
if (NULL == hMem) {
return(0);
}
void *pvHead = MapViewOfFile(hMem, FILE_MAP_WRITE, 0,0,0);
if (NULL == pvHead) {
CloseHandle(hMem);
sprintf(szErr, "Unable to view", szSharedMemory);
AfxMessageBox(szErr, MB_OK|MB_ICONSTOP);
return(0);
}
HELOCoordsStruct *pHELOCoords = (HELOCoordsStruct *)pvHead;
BOOL bVersionOk=FALSE;
if (1 == pHELOCoords->uMajorVersion) {
if (WM_HELO_ZOOM_XYZ==uMessage) {
pHELOCoords->dbX = dbX;
pHELOCoords->dbY = dbY;
pHELOCoords->dbZ = dbZ;
}
bVersionOk=TRUE;
}
else {
sprintf(szErr, "Unrecognized HELO- shared memory version: %d.%d", pHELOCoords->uMajVersion, pHELOCoords->uMinVersion);
AfxMessageBox(szErr, MB_OK);
}
if (NULL != hMem) CloseHandle(hMem);
UnmapViewOfFile(pvHead);
if (bVersionOk) {
PostMessage(HWND_BROADCAST,uMessage,0,0);
return(1);
}
else return(0);
}
EDIT: The feedback has been completely unreal. I must say that the community sure spoils folks around here.
Thanks,
Kevin
I think you have three options:
Create a Managed C++ project of type Class Library, put the code in it, make a reference from your main app to this project.
Create an unmanaged C++ DLL project, put the code in a function (or functions), export the function(s) (using .def file), and build the project. Use the functions from that dll using [DllImport] attribute. (See here and here)
Convert the code to C#. This will require some knowledge of unmanaged code, Win32 and P/Invoke (See here and here). And as I see your code, it is takes a little time!
Here is your converted code (option 3):
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace UnmanagedBlock
{
public class ConvertedClass
{
public uint WM_HELO_ZOOM_XYZ = RegisterWindowMessageA("WM_HELO_ZOOM_XYZ"); // Your code uses the ANSI string
int HELO_Broadcast_Zoom_Message(
double dbX, double dbY, double dbZ, uint uMessage) // Porting the default value for 'uMessage' is not possible
{
string szSharedMemory = "HELO-_Coords";
IntPtr hMem = OpenFileMapping(FileMapAccessRights.Write, FALSE, szSharedMemory);
if (IntPtr.Zero == hMem)
return 0;
IntPtr pvHead = MapViewOfFile(hMem, FileMapAccessRights.Write, 0, 0, UIntPtr.Zero);
if (IntPtr.Zero == pvHead)
{
CloseHandle(hMem);
MessageBox.Show(
"Unable to view " + szSharedMemory, // Your code does not concat these two strings.
"Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
return 0;
}
HELOCoordsStruct pHELOCoords = new HELOCoordsStruct();
Marshal.PtrToStructure(pvHead, pHELOCoords);
int bVersionOk = FALSE;
if (1 == pHELOCoords.uMajVersion) // I think it had a typo (it was uMajorVersion)
{
if (WM_HELO_ZOOM_XYZ == uMessage)
{
pHELOCoords.dbX = dbX;
pHELOCoords.dbY = dbY;
pHELOCoords.dbZ = dbZ;
}
Marshal.StructureToPtr(pHELOCoords, pvHead, false);
bVersionOk = TRUE;
}
else
{
MessageBox.Show(
"Unrecognized HELO- shared memory version: " +
pHELOCoords.uMajVersion.ToString() + "." + pHELOCoords.uMinVersion.ToString());
}
if (IntPtr.Zero != hMem)
CloseHandle(hMem);
UnmapViewOfFile(pvHead);
if (bVersionOk == TRUE)
{
PostMessage(HWND_BROADCAST, uMessage, 0, 0);
return 1;
}
else
return 0;
}
[StructLayout(LayoutKind.Sequential)]
private class HELOCoordsStruct
{
public uint uMajVersion;
public uint uMinVersion;
public uint dwReserved;
public double dbX;
public double dbY;
public double dbZ;
}
[DllImport("user32", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public static extern uint RegisterWindowMessageW([In]string lpString);
[DllImport("user32", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern uint RegisterWindowMessageA([In]string lpString);
[DllImport("kernel32", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
public static extern IntPtr OpenFileMapping(FileMapAccessRights dwDesiredAccess, int bInheritHandle, [In]String lpName);
[DllImport("kernel32", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
public static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, FileMapAccessRights dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, UIntPtr dwNumberOfBytesToMap);
[DllImport("kernel32", CallingConvention = CallingConvention.StdCall)]
public static extern int UnmapViewOfFile(IntPtr lpBaseAddress);
[DllImport("kernel32", CallingConvention = CallingConvention.StdCall)]
public static extern int CloseHandle(IntPtr hObject);
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
public const int FALSE = 0, TRUE = 1;
public enum FileMapAccessRights : uint
{
Write = 0x2,
Read = 0x4,
Execute = 0x20,
}
public const IntPtr HWND_BROADCAST = (IntPtr)0xffff;
}
}
I've done an exact conversion and I think that it should work fine, however I have not tested it.
Let me know if it works.
You can dump the C++ code into a Visual C++ project and build that. When you build it, go into the project settings and select the option that generates tlb files (It's a proxy class for c++/.net interop, I can't remember the name of the option).
Once you have this, you can add a reference to the tlb interop assembly from a C# project.
Also, look here for a Microsoft example http://msdn.microsoft.com/en-us/library/fx82zhxa.aspx
Chances are that if you post the code here, some nice person will port the code from C++ to C# for you. However, for future reference when dealing with using native C++ code from within a .NET application, you can use the InteropServices of the .NET framework to reference native functions in native dlls.
To do this requires a few steps on both the C++ and C# side of things. Firstly you need to build your entry point as an exported function in C++.
For example, say I wanted to write a trivial C++ function to add 2 numbers together and then call it from a C# app, I would have to do the following:
Step 1: Writing the C++ function.
In order for external sources to locate your functions, you need to let the compiler know that the function is to be 'exported'. A point to note is that if you're calling other functions from within your exported function, you do not need to mark them all as exported.
So let's write the "add" function in C++:
#define DLLEXPORT extern "C" declspec(dllexport)
DLLEXPORT int __cdecl add(int x, int y)
{
return (x + y);
}
The first line defines a macro we'll use to mark exported methods. The extern "C" part tells the compiler to avoid mangling the exported name of the function (so it will always be 'add', and not something like #YZadd_), next comes the function definition marked as a DLLEXPORT. Before I continue there's one more point on the 'name mangling' in exported functions, and that is for functions declared __stdcall or any of its variations (WINAPI..etc). Functions that are marked for exporting and declared with extern "C" with the calling convention __stdcall will always be appended with #X where X is the number of bytes of the function paramaters (so for the above example if add was declared __stdcall then the exported function name would be add#8. Just keep this in mind if C# ever has trouble locating your functions.
Now, the C++ side of things is done, compile that as a DLL and move over to C#.
In C# it's rather straightforward to import external functions. First you'll need to reference the InteropServices namespace:
using System.Runtime.InteropServices;
And then you will need to make a [DllImport] declaration:
[DllImport(#"path to your C++ dll here")]
public static extern int add(int x, int y) //make sure the function definition matches.
Provided the function names match, that should be all that's required to import the function. Now you can call add just as you would any normal function in C#
int x = 5;
int y = 10;
int z = add(x, y); //z should be 10
That about concludes how to simply export C++ functions and call them from a C# application.
If you can't get the C++ code to work as-is then there's no point trying to graft it into your C# app.
Figure out the C++ code first (read MSDN documentation for the APIs used, ask the person that gave you the code, post specific questions). Once you understand it better and can make it work then you'll have a better chance of figuring out the best way to do what's needed in C#.

C# dll call not passing char* to C++ MFC regular dll

I have a C++ MFC regular DLL I am calling with the following:
public static class Access3rdPartyDLL
{
public static string FilePath;
[DllImport("3rdparty.dll")]
// I have also tried LPWStr
public static extern long Download([MarshalAs(UnmanagedType.LPStr)]string sDownloadFile,
int iDeviceNum
...);
public static long DownloadToDevice()
{
long result;
string FilePath = "C:\\myfile.txt"
result = Download(FilePath, 1, ...);
// check if success or error
if(result > 0)
...
}
}
I get an error back from the DLL saying "File: 'C:\myfile.txt' not found. But its there...
I have also tried using StringBuilder but this also fails.
Could this be a problem with the DLL or am I doing something wrong?
I found this current code here: SO: equivalent char* in C#
EDIT: I have done this in C++ before and this code works:
extern "C" __declspec(dllimport) HRESULT __stdcall Download(char* sDownloadFile, int ...
which I call with:
HRESULT result = Download(file_for_download, 1, .. // where file_for_download is a char*
The only thing wrong with the P/invoke is that you are using C# long which is 64 bits, but an HRESULT is only 32 bits.
You have matching calling conventions, default marshalling for managed string is char* on the unmanaged side.
Mismatching return value size would not explain why your C# code receives a string message File: 'C:\myfile.txt' not found so your main problem most likely lies in the code that you haven't shown us.
I don't see any reason why the following wouldn't work in this simple scenario:
[DllImport( "3rdparty.dll", CharSet = CharSet.Ansi )]
static extern long Download(string sDownloadFile, int iDeviceNum, ...)
long result = Download("C:\\myfile.txt", 1, ...);

Categories