如何从C#代码解析和调用C ++的struct字段? [英] How do I parse and call struct fields of C++ from C# code?
问题描述
好吧,我打算在托管代码和非托管代码之间应用编组。也就是说,将结果从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 bygcnew
:-). Most likely, your C++/CLI project is a mixed-mode project (managed + unmanaged).
But then, you don't have to P/Invoke anything withDLLImport
! 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 aspublic
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屋!