Returning Struct from VC++ to C# - c#

I have written a structure in VC++. I have made a dll of the VC++ code and calling this dll in C# using PInvoke.
The VC++ dll looks like this
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <iostream>
#if defined(_MSC_VER)
#include <windows.h>
#define DLL extern "C" __declspec(dllexport)
#else
#define DLL
#endif
struct SYSTEM_OUTPUT
{
int status;
};
DLL SYSTEM_OUTPUT* getStatus()
{
SYSTEM_OUTPUT* output;
output->status = 7;
return output;
}
I am calling the getStatus() function from the dll in my C# code, which looks as follows;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace UsingReturnStructDLL
{
[StructLayout(LayoutKind.Sequential)]
public struct SYSTEM_OUTPUT
{
[MarshalAs(UnmanagedType.I4)]
int Status;
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public SYSTEM_OUTPUT output;
[DllImport("ReturnStructDLL", EntryPoint = "getStatus")]
[return: MarshalAs(UnmanagedType.Struct)]
public extern static SYSTEM_OUTPUT getStatus();
private void button1_Click(object sender, EventArgs e)
{
try
{
SYSTEM_OUTPUT output = getStatus();
}
catch (AccessViolationException e)
{
label1.Text = e.Message;
}
}
}
}
I want to retrieve the values in the struct in my C# code. With the above setup of my code, I am getting the following error;
Cannot marshal 'return value': Invalid managed/unmanaged type combination (Int32/UInt32
must be paired with I4, U4, or Error).
Can someone please help me with the issue?
Thanks.

Make your C++ code work first. It is junk as posted, you don't initialize the pointer. It will crash with an AccessViolation.
Returning pointers to structures is very hard to get right in C/C++ as well, the client of your code won't know how the memory needs to be released. Which plays havoc on the P/Invoke marshaller as well, it is going to try to release the pointer with CoTaskMemFree(). That's a kaboom on Vista and up, a memory leak on XP.
All of these problems disappear if you let the client pass a pointer to the structure as an argument:
void getStatus(SYSTEM_OUTPUT* buffer)
Which then in C# becomes:
[DllImport("mumble.dll")]
private static extern void getStatus(out SYSTEM_OUTPUT buffer);

Related

Calling integer method works but string method return null when call DLL from C++ in C#

I am trying to implement a CLR concept on C++ and C#. I am create two simple method on C++.
this is test.cpp as main file
#include <iostream>
#include "test.h"
using namespace std;
string SayHi(){
return "hi";
}
int CallNumber(){
return 911;
}
This is test.h as header
#include <iostream>
using namespace std;
#define EXPORT_API __declspec(dllexport)
extern "C" EXPORT_API string SayHi();
extern "C" EXPORT_API int CallNumber();
Then I compile them using
g++ -shared -o test.dll test.cpp
On my C# program, I try to call them
using System;
namespace Test.Main
{
class Program
{
[DllImport(#"test.dll")]
public static extern string SayHi();
[DllImport(#"test.dll")]
public static extern int CallNumber();
static void Main(string[] args)
{
Console.WriteLine(SayHi());
Console.WriteLine(CallNumber());
Console.ReadKey();
}
}
}
This program show
`dDO↓☻
911
The 911 integer works perfectly but the string show weird thing. And that 'dDO↓☻ always change every time I run the C# program.
I am using
G++ version 9.4.0
.Net Framework 4.8
Is there something I miss here? Any help appreciated. Thanks.

Call c++ DLL from C# application

I have C# as my front end application and I want to call c++ dll from my c# but I am getting error.
I am posting my code here please help me out how to resolve this:
Program.cs
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestCSharp
{
class Program
{
[DllImport("C:\\Users\\xyz\\source\\repos\\Project1\\Debug\\TestCpp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void DisplayHelloFromDLL(StringBuilder name, int appId);
static void Main(string[] args)
{
try
{
StringBuilder str = new StringBuilder("name");
DisplayHelloFromDLL(str, str.Length);
str.Clear();
}
catch(DllNotFoundException exception)
{
Console.WriteLine(exception.Message);
}
catch(Exception exception)
{
Console.WriteLine("General exception " + exception.Message);
}
finally
{
Console.WriteLine("Try again");
}
}
}
}
and cpp code like below:
Header: source.h
#include <string>
using namespace std;
extern "C"
{
namespace Test
{
class test
{
public:
test();
__declspec(dllexport) void DisplayHelloFromDLL(char * name, int appId);
}
}
}
c++ class: source.cpp
#include <stdio.h>
#include "source.h"
Test::test::test()
{
printf("This is default constructor");
}
void Test::test::DisplayHelloFromDLL(char * name, int appId)
{
printf("Hello from DLL !\n");
printf("Name is %s\n", name);
printf("Length is %d \n", appId);
}
Code is building successfully but when I run this I got Unable to find an entry point named 'DisplayHelloFromDLL' in DLL.
Same CPP code when I am writing without using namespace and class, it is working fine.
i.e.
Header: source.h
extern "C"
{
__declspec(dllexport) void DisplayHelloFromDLL(char * name, int appId);
}
c++ class: source.cpp
#include "source.h"
void DisplayHelloFromDLL(char * name, int appId)
{
printf("Hello from DLL !\n");
printf("Name is %s\n", name);
printf("Length is %d \n", appId);
}
So how do I use DLL which has namespaces and claases in my c# application.
Thanks for the answers.
I resolved the issue by making one extra class(Wrapper class) that contains the managed code. This wrapper class is called by the c# classes in the same way as I mentioned in the question. This wrapper class than call the c++ class and return the result to the UI.
The easiest way is to create a "proxy": set of clear-C functions, these will call your c++ functions.
I think calling c++ function is not good idea: name decoration is changed from version to version of compiler.
Do you have this project hosted somewhere?
On the first view I would say that you need to build the c++ project first (only the c++!!!) and then run the C# project.
Perhaps you would like to have a look here: Testprojects
Especially the "MessageBox" stuff shows how to use C++ with C#. There are some Testprojects with UWP as well.

Passing a pointer to COM interface pointer from managed c++ to .NET C#

I have a COM Dll written in unmanaged C++ that expose a com interface. The trivial sample of idl is shown below:
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(A806FAED-FCE2-4F1B-AE67-4B36D398508E),
dual,
nonextensible,
pointer_default(unique)
]
interface IObjectToPass : IDispatch{
[propget, id(1)] HRESULT Name([out, retval] BSTR* pVal);
[propput, id(1)] HRESULT Name([in] BSTR newVal);
};
[
uuid(B99537C0-BEBC-4670-A77E-06AB5350DC23),
version(1.0),
]
library {
importlib("stdole2.tlb");
[
uuid(FB7C7D08-0E38-40D2-A160-0FC8AE76C3CF)
]
coclass ObjectToPass
{
[default] interface IObjectToPass;
};
};
so IObjectToPass interface is the interface exposed.
Now I've implemented a .NET library (ObjectConsumer) in C# that import via an Interop created by tlbimp this COM object so I can use the IObjectToPass interface. Here below the sample code of this library:
using Interop.ComTest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;ComTestLib
using System.Threading.Tasks;
namespace ObjectConsumer
{
public class Consumer
{
public void PassObject(IObjectToPass passobj)
{
string str = passobj.Name;
}
}
}
both c++ and c# module compiles without problem
Now I've created a simple console application in c++ wich support CLR that import both ComTestLib via import
and Consumer adding a reference to console application project. My goal is to get an instance of ObjectToPass inside my console application and pass to consumer calling the public method PassObject as show below
#include "stdafx.h"
#import "..\Debug\ComTest.tlb" no_namespace, raw_interfaces_only
int main()
{
CoInitialize(NULL);
IObjectToPassPtr obj;
HRESULT hr = obj.CreateInstance(__uuidof(IObjectToPass) );
ObjectConsumer::Consumer^ consumer = gcnew ObjectConsumer::Consumer();
obj->put_Name(_T("MyName"));
consumer->PassObject(obj);
CoUninitialize();
return 0;
}
This code won't compile. Compiler cannot convert argument 1 from 'IObjectToPassPtr' to 'Interop::ComTest::IObjectToPass ^'. I've made different attempts but it seems that IObjectToPass* cannot be casted to 'Interop::ComTest::IObjectToPass^. Is there a way to do it?
Skip the COM interface and just export a factory function in c++.
#define MY_API __declspec(dllexport)
#ifdef __cplusplus
# define EXTERNC extern "C"
#else
# define EXTERNC
#endif
EXTERNC MY_API IObjectToPass* APIENTRY MyFactory(void);
EXTERNC MY_API void APIENTRY SomeFunction(IObjectToPass* handle);
// Implementation:
MY_API IObjectToPass* APIENTRY MyFactory(void)
{
return new ObjectToPass();
}
MY_API void APIENTRY SomeFunction(IObjectToPass* handle)
{
handle->DoFunction();
}
// And then export any needed functions too.
Now in C#:
public class MyWrapper{
IntPtr myReference;
public ObjectWrapper()
{
try
{
this.myReference = MyFactory();
if (myReference == IntPtr.Zero)
{
throw "Error or something";
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
throw e;
}
}
//Path to dll
[DllImport(lib_path)]
private static extern IntPtr MyFactory();
[DllImport(lib_path)]
private static extern void SomeFunction(IntPtr toTrigger);
}
Consider getting rid of AT LEAST one of the three forms of C to simplify the design or else what I suggested does work and I have used it before!

Wrap a C struct for access in WPF application

for a project of mine, I'd like to wrap a C struct in order to use it in C# code.
My C++ algorithm returns a struct object and I'd like to see contained informations from C#/WPF code.
When I try "my way", I get an error message while compiling :
"'Wrapper.WrapperAlgo.createMediumMap()' is inaccessible due to its protection level"
my C# piece of code:
public partial class MainWindow : Window
{
unsafe public MainWindow()
{
WrapperAlgo algo = new WrapperAlgo();
square** map = (square**)algo.createMediumMap();
}
}
My wrapper
#ifndef __WRAPPER__
#define __WRAPPER__
#include "../CivilizationAlgo/mapalgo.h"
#pragma comment(lib, "../Debug/CivilizationAlgo.lib")
using namespace System;
namespace Wrapper {
public ref class WrapperAlgo {
private:
Algo* algo;
public:
WrapperAlgo(){ algo = Algo_new(); }
~WrapperAlgo(){ Algo_delete(algo); }
square** createSmallMap() { return algo->createSmallMap(); }
square** createMediumMap() { return algo->createMediumMap(); }
int computeFoo() { return algo->computeFoo(); }
};
}
#endif
My C++ algo.h with the struct I'd like to use in C#
#ifdef WANTDLLEXP
#define DLL _declspec(dllexport)
#define EXTERNC extern "C"
#else
#define DLL
#define EXTERNC
#endif
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <vector>
using namespace std;
struct square {
// 0: Mountain, 1: Plain, 2: Desert
int type;
// 0: No additionnal ressource, 1: Additionnal Iron, 2: Additionnal Food
int bonus;
// 0: free, 1: checked, 2: frozen
int state;
};
class DLL Algo {
public:
Algo() {}
~Algo() {}
square** createSmallMap();
square** createMediumMap();
int computeFoo();
};
Do you have any idea where i'm wrong ?
If I understand you well you are looking for a way to use non-managed library (like cpp/c library) in your C# code. If yes, then you should read about marshaling, for ex. here, here, or here.
Basics about including unmanaged code in C# app can be found on MSDN, or MSDN Magazine.
Hope it helps.

SEHException with simple DLL

I am currently trying to use a simple C++ DLL in a C# program for a school project but I have trouble making the DLL and Program link with each other. When I try to call the DLL's function in the main program, I get a SEHExcpetion thrown from the DLL.
Here is the DLL code
#include <stdio.h>
#include <string>
using namespace std;
extern "C"
{
__declspec(dllexport) string Crypter(string sIn)
{
return sIn+ " from DLL";
}
}
And here's the C# code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
[DllImport("CryptoDLL2.dll")]
public static extern string Crypter(string sIn);
public Form1()
{
InitializeComponent();
}
private void BTN_Crypter_Click(object sender, EventArgs e)
{
TB_OUT.Text = ("");
TB_OUT.Text = Crypter(TB_IN.Text); //exception thrown here
}
}
}
Strings in C# and C++ - Those are completely different types, layouts, etc. And you expect them to work.
Check out char* with marshalling.
you cannot use std::string from C#, it is a c++ class, .NET does not know how to handle it.
Try using wchar_t* or BSTR.
I did a test to make sure the linking was fine, here is a sample app showing it does work.
#include <stdio.h>
#include <string>
using namespace std;
extern "C"
{
__declspec(dllexport) string Crypter(string sIn)
{
printf("test");
return "from DLL";
}
}
And
public class Test
{
[DllImport("TestDll.dll")]
public static extern string Crypter(string sIn);
static void Main(string[] args)
{
Console.WriteLine(Crypter("a"));
}
}
This prints me out
test
followed by a newline.
You need to probably marshal the data from c++ to .net, or use a c++/clr managed dll which would make things easier on you.

Categories