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.
The code is building without error but when I run it, it gets stuck in the function that gets called from DllImport in csharp. I tracked this with Debug.WriteLine() to confirm where the code gets stuck.
The DllImport line in the csharp project:
[DllImport("GettingData.dll", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
static unsafe extern bool GenerateItems(out ItemsSafeHandle itemsHandle,
out double* items, out int itemCount);
[DllImport("GettingData.dll", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
static unsafe extern bool ReleaseItems(IntPtr itemsHandle);
The dllexport code from cpp project is as follow. Note that the codes use header file and cpp because of a build error, where the functions being defined in the cpp project were being "already" defined in App.xaml.h. So to fix this, the class is defined in the header file and the member function definitions are in the cpp file.
header:
#define EXPORT extern "C" __declspec(dllexport)
typedef intptr_t ItemListHandle;
cpp:
EXPORT bool GenerateItems(ItemListHandle* hItems, double** itemsFound, int* itemCount)
{
auto items = new std::vector<double>();
for (int i = 0; i < 500; i++)
{
items->push_back((double)i);
}
*hItems = reinterpret_cast<ItemListHandle>(items);
*itemsFound = items->data();
*itemCount = items->size();
return true;
}
EXPORT bool ReleaseItems(ItemListHandle hItems)
{
auto items = reinterpret_cast<std::vector<double>*>(hItems);
delete items;
return true;
}
I am following the top-voted response to this post here to get the minimum required code working. As I am running this in a UWP solution and couldn't use C++/CLI wrapper as another project as part of this solution.
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#.
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.
I am currently developing an application for Windows CE on the TI OMAP processor, which is an ARM processor. I am trying to simply call a function in a C++ DLL file from C# and I always get a value of 0 back, no matter which data type I use. Is this most likely some kind of calling convention mismatch? I am compiling the DLL and the main EXE from the same Visual Studio solution.
C# Code Snippet:
public partial class Form1 : Form
{
private void button1_Click(object sender, EventArgs e)
{
byte test = LibWrap.test_return();
MessageBox.Show(test.ToString());
}
}
public class LibWrap
{
[DllImport("Test_CE.dll")]
public static extern byte test_return();
}
C++ DLL Code Snippet:
extern "C" __declspec (dllexport) unsigned char test_return() {
return 95;
}
It worked when I changed:
extern "C" __declspec (dllexport) unsigned char test_return() {
return 95;
}
to
extern "C" __declspec (dllexport) unsigned char __cdecl test_return() {
return 95;
}
In the DLL code. Why it doesn't assume this when compiled for WinCE is beyond me.
Try exporting test_return() as follows:
unsigned char __declspec(dllexport) WINAPI test_return() {
return 95;
}
Somewhere WINAPI is being defined as
__stdcall where it should have been __cdecl
No. WINAPI is defined as __stdcall.