如何从C#代码解析和调用C ++的struct字段? [英] How do I parse and call struct fields of C++ from C# code?

查看:77
本文介绍了如何从C#代码解析和调用C ++的struct字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好吧,我打算在托管代码和非托管代码之间应用编组。也就是说,将结果从c ++代码检索到c#代码。我不打算在这里知道托管和非托管代码的定义,而是愿意找到正确的方法。



部分:非托管代码

文件:unmanaged_operation.cpp



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

#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;
}





部分:托管代码

文件:managed_operation.cs





Section: Managed code
File: managed_operation.cs

[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.
		}
	}
}





假设unmanaged_operation.cpp中的Do_Something_With_BSTR_Input_Value方法为:





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

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





仅用于测试目的,而不是原始引用。我想将相同的值打印到Console,我在managed_operation.cs中作为参数传递给Parse,Strip或Range方法



任何带代码示例的建议都将高度请求。



我尝试过:



我用过以下用于unmanaged_operation.cpp测试目的的代码:





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:

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

	*output = *operation;

	// OR output = operation;
}





并在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(解析值。);

Strip(条带值。);

范围(范围值。);

操作((双)7.45) ;



IntPtr ptr;

GetOperationPtr(ptr);



/ /以下行抛出异常



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



//以上行抛出异常



Console.WriteLine( {0},operation.parseValue);

Console.WriteLine({0},operation.stOutput);

}

catch(例外e)

{

throw e;

//无法编组'参数#1':无效的托管/非托管类型组合

//(Int / UInt必须与SysInt或SysUInt配对)。

}

}



我再次将IntPtr更改为GetOperationPtr定义中的对象,如下所示:



[DllImport(@unmanaged_operation.dll),CallingConvention = CallingConvention。 StdCall)]

private static extern void GetOperationPtr([Out] [MarshalAs(UnmanagedType.Struct)out object ptr);



in主要方法:



对象ptr;

GetOperationPtr(ptr);



导致应用程序立即终止而不是exec进一步。



再次当我从GetOperationPtr定义中省略MarshalAs属性时,parseValue

返回类似于䕃洭garbage的垃圾值敧或옧خ㲨Ѹ㲨Ѹ或멄攓 ѵ ѵ而不是任何可见的结果。



为了摆脱这种情况,我将Charset参数添加到DllImport属性中GetOperation定义,如:



[DllImport(@unmanaged_operation.dll),CallingConvention = CallingConvention.StdCall,CharSet = CharSet.Unicode)]

private static extern IntPtr GetOperation();



并使用与前面描述的Main方法相同的代码;在这种情况下,每个IOperation字段实例的

输出值都为:



operation.parseValue返回条带值。 方法Parse(解析值。);

operation.stripValue返回范围值。对于方法Strip(条带值。);

operation.rangeValue返回解析值。方法范围(范围值。);



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.");

推荐答案

你所做的一切都没有意义。



如果您使用针对非托管代码的C ++,那将是有意义的。然后你会使用P / Invoke来调用unamanged函数。



但你使用的是C ++ / CLI,我可以通过 gcnew :-)。最有可能的是,您的C ++ / CLI项目是混合模式项目(托管+非托管)。



但是,你不必使用 DLLImport 来P / Invoke任何东西!相反,您应该只使用C ++项目的输出作为通常的.NET程序集,并通过C#程序集直接引用它。当然,您必须将一些托管的ref类公开为 public ,并且在这些类中,包装您要从引用程序集调用的所有函数。



请参阅:从托管代码调用本机函数



-SA
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


这篇关于如何从C#代码解析和调用C ++的struct字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆