2013-11-03 157 views
1

期间唯一已知我有以下问题:从C++传递一个结构为C#当结构运行时

我有从非托管C++ DLL调用函数的C#应用​​程序。在dll中有一个初始化函数,它在C#和C++(基本上是一系列值和它们的类型)之间创建一个接口,并存储在一个结构中。

之后,有一个C#应用程序发送到dll的回调函数,dll每隔一段时间调用一次,它会返回一个结构变量(或字节数组),就像它在界面中定义的那样。

我的问题:你将如何传递和编组这个结构?是否有可能传递结构本身,或者我应该传递一个字节数组?

如果你传递一个字节数组,你会在返回到C#应用程序时编组它吗?

我现在所拥有的:

在C#应用程序:

封送回调函数:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
public delegate void ProcessOutputDelegate(???); // not sure what should be here. 

导入的DLL功能:

[DllImport("MyDLL.dll", CallingConvention=CallingConvention.Cdecl)] 
public static extern void Test(ProcessOutputDelegate ProcessOutput); 

调用的dll功能:

ProcessOutputDelegate process = new ProcessOutputDelegate(ProcessOutput); 
new thread(delegate() { Test(process); }).Start(); 

处理中的输出:

public void ProcessOutput(???) 
{ 
    // Assume we have a list of values that describes the struct/bytes array. 
} 

在C++的dll我有以下结构(这是一个例子,作为一个不同的dll可以在不同的运行时被调用):

struct 
{ 
    int x; 
    double y; 
    double z; 
} typedef Interface; 

并且由C#应用程序调用的函数:

__declspec(dllexport) void Test(void (*ProcessOutput)(Interface* output)) 
{ 
    int i; 
    Interface* output = (Interface*)malloc(sizeof(Interface)); 

    for (i = 0; i < 100; i++) 
    { 
     sleep(100); 
     output->x = i; 
     output->y = i/2; 
     output->z = i/3; 
     ProcessOutput(output); // or generate a bytes array out of the struct 
    } 
} 

编辑:

C#应用程序是一个通用的GUI,它假设显示一些C++ dll执行一些繁重的计算。在初始化过程中,dll告诉GUI关于应该呈现的变量(及其类型),并且根据这些方向构建GUI(再次,计算和变量可以改变,并且值可以是整数,浮点数,字符...)。之后,dll会运行,并在每个时间步骤中调用回调函数来更新GUI。这应该适用于实现此想法的任何dll:生成一个接口,然后根据此接口发送信息。

+1

我不理解所在的C++结构 “可能会发生变化” 的部分。在普通的C++中,结构体是在编译时定义的。它们在运行时不会奇迹般地改变(当然它们的内容,但不是它们的结构)。你能澄清一下吗? – Mat

+0

@Mat True,其含义是可以在每个运行时调用不同的dll,我会添加一个说明。每个DLL都会实现“测试”功能,但可能会构建不同的结构并执行不同的操作。 –

+0

所以你试图对不同的C++ DLL使用相同的C#接口,这可能“返回”完全不同的东西?我很抱歉你的整个事情听起来很奇怪。没有具体的例子说明你正在申请这个项目,这将是很难(我认为)提供好的建议。如果它完全是随机的东西,可能与XML或JSON接口。 – Mat

回答

1

我不知道,这是否是对您的问题的直接回答,但是我通过以下方式解决了问题。

  1. 你打电话占用资源的DLL,并得到一个IntPtr:

    (...) 
    IntPtr res = Native.GetResource(); 
    
  2. 然后,你实例为这个IntPtr的通用包装:

    ResourceWrapper rw = new ResourceWrapper(res); 
    
  3. 如果你想访问在结构的特定字段中,可以在包装上调用适当的方法:

    int field = rw.GetField(); 
    
  4. 包装程序调用的DLL中的函数:

    (...) 
    int result = Native.GetField(res); 
    
  5. DLL “重新objectizes” 的号召:

    __declspec(dllexport) int __stdcall GetField(MyStructure * s) 
    { 
        return s->GetField(); 
    } 
    
  6. 结构得到的数据(或抛出一个异常或设置错误国旗或返回错误等)

    int MyStructure::GetField() 
    { 
        return this->field; 
    } 
    

这种解决方案需要进行以下更改:

  • 你的数据结构应该由同一基类派生
  • 如果可能的话,他们应该成为教学班,虚拟方法允许访问各自不同的领域
  • 您必须实施某种安全机制,检查是否可以访问特定字段。最简单的是以下内容:

    __declspec(dllexport) BOOL __stdcall GetField(MyStruct * s, int & result) 
    { 
        result = 0; 
        try 
        { 
         result = s->GetField(); 
        } 
        catch(...) 
        { 
         return FALSE; 
        } 
    
        return TRUE; 
    } 
    

    和:

    protected class Native 
    { 
        [DllImport("MyDll.dll")] 
        [return: MarshalAs(UnmanagedType.Bool)] 
        public static extern bool GetField(IntPtr res, out int result); 
    } 
    

    和:

    // Wrapper 
    int GetField() 
    { 
        int result; 
        if !(Native.GetField(res, out result)) 
         throw new InvalidOperationException("Resource does not support field!"); 
    }