Click here to Skip to main content
15,887,676 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
Well, I am intended to apply Marshalling between managed and unmanaged codes. That is, to retrieve the result from a c++ code to c# code. I am not intended to know the definition about managed and unmanaged code here, rather am willing to find the proper way out of doing so.

Section: Unmanaged code
File: unmanaged_operation.cpp

C++
#pragma managed(push, off)

typedef struct _IOperation
{
	double  stOutput;
	double* dblPtrValue;
	BSTR*   parseValue;
	BSTR*   evenValue;
	BSTR*   oddValue;
	BSTR*   stripValue;
	BSTR*   contextValue;
	BSTR*   rangeValue;
} IOperation, *LPIOperation;

#pragma managed(pop)

#if (_MANAGED == 1) || (_M_CEE == 1)
#include <vcclr.h>
using namespace System;
using namespace System::Runtime::InteropServices;
#endif

// In unmanaged_operation.h file
extern __declspec(dllexport) LPIOperation operation;

// In unmanaged_operation.cpp file
__declspec(dllexport) LPIFactory factory =  new IFactory();

extern "C" __declspec(dllexport) void __stdcall Parse(/* in */ BSTR* input)
{
	BSTR* value = Do_Something_With_BSTR_Input_Value(input);

	String^ _output = gcnew String(*value);
	IntPtr ptr = Marshal::StringToBSTR(_output);
	operation->parseValue = (BSTR*)ptr.ToPointer();
	Marshal::FreeBSTR(ptr);
}

extern "C" __declspec(dllexport) void __stdcall Strip(/* in */ BSTR* input)
{
	BSTR* value = Do_Something_With_BSTR_Input_Value(input);

	String^ _output = gcnew String(*value);
	IntPtr ptr = Marshal::StringToBSTR(_output);
	operation->stripValue = (BSTR*)ptr.ToPointer();
	Marshal::FreeBSTR(ptr);
}

extern "C" __declspec(dllexport) void __stdcall Range(/* in */ BSTR* input)
{
	BSTR* value = Do_Something_With_BSTR_Input_Value(input);

	String^ _output = gcnew String(*value);
	IntPtr ptr = Marshal::StringToBSTR(_output);
	operation->rangeValue = (BSTR*)ptr.ToPointer();
	Marshal::FreeBSTR(ptr);
}

extern "C" __declspec(dllexport) void __stdcall Operate(/* in */ double input)
{
	double output = Do_Something_With_Double_Input_Value(input);

	operation->stOutput = output;
}

extern "C" __declspec(dllexport) LPIOperation GetOperation()
{
	return operation;
}


Section: Managed code
File: managed_operation.cs

C#
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct IOperation
{
	/* MarshalAs(UnmanagedType.R8)] */
	public double stOutput;
	public double[] dblPtrValue;
	/* MarshalAs(UnmanagedType.BStr)] */
	public string parseValue;
};

Class Program
{
	[DllImport(@"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
	private static extern void Parse([In] String input);

	[DllImport(@"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
	private static extern void Strip([In] String input);

	[DllImport(@"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
	private static extern void Range([In] String input);

	[DllImport(@"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
	private static extern void Operate([In] Double input);

	[DllImport(@"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
	private static extern IntPtr GetOperation();

	[STAThread]
	public static void Main(string[] args)
	{
		try
		{
			IOperation operation = new IOperation();

			Parse("The parse value.");
			Strip("The strip value.");
			Range("The range value.");
			Operate((double)2.45);

			IntPtr ptr = GetOperation();

			// The following line throws the exception

			operation = (IOperation)(Marshal.PtrToStructure(ptr, typeof(IOperation)));

			// The above line throws the exception

			Console.WriteLine("{0}", operation.parseValue);
			Console.WriteLine("{0}", operation.stOutput);
		}
		catch (Exception e)
		{
			throw e;
			// Exception of type 'System.ExecutionEngineException' was thrown.
		}
	}
}


Let's say that the Do_Something_With_BSTR_Input_Value method in unmanaged_operation.cpp be:

C++
BSTR* __stdcall Do_Something_With_BSTR_Input_Value(/* in */ BSTR* input)
{
	return input;
}


that only for testing purpose, rather the original cited. And I wanted to print the same value to Console, that I passed as a parameter in Parse, Strip or Range method in managed_operation.cs

Any suggestion with code example will highly be solicited.

What I have tried:

I used the following code for testing purpose in unmanaged_operation.cpp:

C++
extern "C" __declspec(dllexport) void GetOperationPtr(LPIOperation output)
{
	operation->stOutput = (double)2;
	operation->parseValue = (BSTR*)"The BSTR string";

	*output = *operation;

	// OR output = operation;
}


And used the following code in managed_operation.cs

[DllImport(@"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
private static extern void GetOperationPtr([Out] [MarshalAs(UnmanagedType.Struct] out IntPtr ptr);

[STAThread]
public static void Main(string[] args)
{
try
{
IOperation operation = new IOperation();

Parse("The parse value.");
Strip("The strip value.");
Range("The range value.");
Operate((double)7.45);

IntPtr ptr;
GetOperationPtr(ptr);

// The following line throws the exception

operation = (IOperation)(Marshal.PtrToStructure(ptr, typeof(IOperation)));

// The above line throws the exception

Console.WriteLine("{0}", operation.parseValue);
Console.WriteLine("{0}", operation.stOutput);
}
catch (Exception e)
{
throw e;
// Cannot marshal 'parameter #1': Invalid managed/unmanaged type combination
// (Int/UInt must be paired with SysInt or SysUInt).
}
}

Again I changed the IntPtr to object in GetOperationPtr definition as follows:

[DllImport(@"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
private static extern void GetOperationPtr([Out] [MarshalAs(UnmanagedType.Struct] out object ptr);

and in the Main method:

Object ptr;
GetOperationPtr(ptr);

which caused the application terminated instantly without being executed further.

Again when I omitted the MarshalAs attribute from the GetOperationPtr definition, the parseValue
returns garbage value something like 䕃洭獥慳敧 or 옧ﺧ㲨Ѹ㲨Ѹ or 멄攓�ѵ�ѵ rather any visible result.

To get rid of this I added the Charset parameter to DllImport attribute for GetOperation definition, as:

[DllImport(@"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private static extern IntPtr GetOperation();

and used the same code in Main method as described earlier; which in such case suffled the
output value for each instance of IOperation field, as:

operation.parseValue returned "The strip value." for the method Parse("The parse value.");
operation.stripValue returned "The range value." for the method Strip("The strip value.");
operation.rangeValue returned "The parse value." for the method Range("The range value.");
Posted
Updated 22-Jun-16 4:32am

1 solution

What you do makes no sense at all.

It would make sense if you used C++ targeted to unmanaged code. Then you would use P/Invoke to call unamanged functions.

But you are using C++/CLI, which I could spot by gcnew :-). Most likely, your C++/CLI project is a mixed-mode project (managed + unmanaged).

But then, you don't have to P/Invoke anything with DLLImport! Instead, you should simply use the output of you C++ project as a usual .NET assembly and directly reference it by your C# assembly. Of course, you have to expose some managed "ref" classes as public and, in such classes, wrap all the functions you want to call from the referencing assembly.

Please see: Calling Native Functions from Managed Code.

—SA
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900