Visual studio 2010 如何从c++;到c#?

Visual studio 2010 如何从c++;到c#?,visual-studio-2010,visual-c++,c++-cli,Visual Studio 2010,Visual C++,C++ Cli,我打算在托管代码和非托管代码之间应用编组。也就是说,将结果从C++代码检索到C代码。我不想在这里知道托管或非托管的定义,而是想找到正确的方法 节:非托管代码 文件:unmanaged_operation.cpp #pragma managed(push, off) typedef struct _IOperation { double stOutput; double* dblPtrValue; BSTR* parseValue; BSTR* even

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

节:非托管代码 文件: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;
}
假设unmanaged_operation.cpp中的Do_Something_With_BSTR_Input_Value方法为:

BSTR* __stdcall Do_Something_With_BSTR_Input_Value(/* in */ BSTR* input)
{
    return input;
}
仅用于测试目的,而非引用原文。我想将相同的值打印到控制台,在managed_operation.cs的Parse、Strip或Range方法中作为参数传递

[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.
        }
    }
}
[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).
    }
}
我在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中使用以下代码

[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.
        }
    }
}
[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).
    }
}
我再次在GetOperationPtr定义中将IntPtr更改为object,如下所示:

[DllImport(@"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
private static extern void GetOperationPtr([Out] [MarshalAs(UnmanagedType.Struct] out object ptr);
在主要方法中:

Object ptr;
GetOperationPtr(ptr);
这导致应用程序立即终止,而没有进一步执行

同样,当我从GetOperationPtr定义中省略MarshalAs属性时,parseValue 返回垃圾值,类似于䕃洭獥慳敧 或옧ﺧ㲨Ѹ㲨Ѹ或멄攓�ѵ�而不是任何可见的结果

为了解决这个问题,我将Charset参数添加到GetOperation定义的DllImport属性中,如下所示:

[DllImport(@"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private static extern IntPtr GetOperation();
并在Main方法中使用与前面所述相同的代码;在这种情况下,会对IOOperation字段的每个实例的输出值进行洗牌,如下所示:

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++中调用一些C++代码。在这种情况下,通常最好写一个C++/CLI托管类(关键字<代码>公共REF类< /COD>),并直接从那里调用C++代码。您可以用正常的方式从C#访问托管类,只需添加一个.Net引用并用C#

new
实例化该类

具体问题 如果您正在访问托管类型(
String^
),则它不是非托管代码

分配字符串的方式不正确。您正在
StringToBSTR
中分配一个新的
BSTR
对象,保存指向它的指针,然后释放
BSTR
对象。您现在有一个指向解除分配/无效内存的指针!这种记忆可以在任何时候重复使用

public struct IOperation
不要从
I
开始,除非它们是一个接口。这就是命名惯例,坚持下去

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct IOperation
{
    /* MarshalAs(UnmanagedType.R8)] */
    public double stOutput;
    public double[] dblPtrValue;
    /* MarshalAs(UnmanagedType.BStr)] */
    public string parseValue;
};
如果要从非托管复制到托管,double数组有多大?它应该使用多种字符串表示形式中的哪一种

推荐 我推荐两件事中的一件:

<> LI>如果你是一个非托管库,你是作者,考虑把这个结构变成更容易的自动封送器处理。<李>
  • 如果您不是非托管库的作者,或者如果您有其他原因使结构看起来像这样,或者如果您只是不想处理自动封送器,则手动执行封送:编写一个以托管结构为参数的C++/CLI托管类(
    public ref class
    ),手动将所有内容转换为非托管结构,调用非托管函数,然后将所有内容复制回