2017-03-07 50 views
2

如果这听起来太具体,我很抱歉,但我需要完全按照这种方式完成此操作,并且我坚持了几天。在我的真实情况下,我有一个MyStruct **ppObject哪个地址必须传递给第三方DLL,以便它将指向一个结构数组。之后,我必须p /调用这个指向数组的指针,但是我遇到了结构内容的问题。这里有一个MCVE:如何编组指向包含无符号字符数组的结构数组的指针?

Unmanaged.h:

#ifndef _NATIVELIB_H_ 
#define _NATIVELIB_H_ 

#ifndef MCVE 
#define MCVE 
#endif 

struct PlcVarValue 
{ 
    unsigned char byData[8]; 
}; 

#ifdef __cplusplus 
extern "C" { 
#endif 
    MCVE __declspec(dllexport) void FillArray(void); 
    MCVE __declspec(dllexport) void Clear(void); 
    MCVE __declspec(dllexport) PlcVarValue** GetValues(void); 

#ifdef __cplusplus 
} 
#endif 

#endif // _NATIVELIB_H_ 

Unmanaged.cpp

#include "stdafx.h" 
#include "Unmanaged.h" 
#include <iostream> 

PlcVarValue** values; 

MCVE __declspec(dllexport) void FillArray(void) { 
    values = (PlcVarValue**)malloc(sizeof(PlcVarValue*) * 5); 

    for (int i = 0; i < 5; i++) 
    { 
     values[i] = new PlcVarValue(); 
     *values[i]->byData = i; 
    } 
} 

MCVE __declspec(dllexport) void Clear(void) { 
    delete *values; 
    free(values); 
} 

MCVE __declspec(dllexport) PlcVarValue** GetValues(void) { 
    return values; 
} 

PlcVarValue.cs

using System.Runtime.InteropServices; 

namespace Managed 
{ 
    [StructLayout(LayoutKind.Sequential)] 
    public class PlcVarValue 
    { 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 
     public byte[] Data; 
    } 
} 

ManagedClass.cs

using System; 
using System.Collections.Generic; 
using System.Runtime.InteropServices; 

namespace Managed 
{ 
    public class ManagedClass 
    { 
     public ManagedClass() 
     { 
      FillArray(); 
     } 

     [DllImport("Unmanaged.dll", CallingConvention = CallingConvention.Cdecl)] 
     private static extern void FillArray(); 

     [DllImport("Unmanaged.dll", CallingConvention = CallingConvention.Cdecl)] 
     private static extern IntPtr GetValues(); 

     [DllImport("Unmanaged.dll", CallingConvention = CallingConvention.Cdecl)] 
     private static extern void Clear(); 

     public List<PlcVarValue> GetList() 
     { 
      int size = 5; 
      var list = new List<PlcVarValue>(); 
      IntPtr ptr = GetValues(); 

      var gch = GCHandle.Alloc(ptr, GCHandleType.Pinned); 

      try 
      { 
       int memSize = Marshal.SizeOf(typeof(PlcVarValue)); 
       for (int i = 0; i < size; i++) 
       { 
        //I know this is wrong, it would work for a PlcVarValue* but I need it to work with a PlcVarValue** 
        list.Add((PlcVarValue)Marshal.PtrToStructure(new IntPtr(ptr.ToInt32() + memSize * i), typeof(PlcVarValue))); 
       } 
      } 
      finally 
      { 
       gch.Free(); 
      } 

      return list; 
     } 

     public void FreeMemory() 
     { 
      Clear(); 
     } 
    } 
} 

Program.cs的

using Managed; 
using System; 

namespace Test 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var managed = new ManagedClass(); 
      var list = managed.GetList(); 
      foreach(var value in list) 
       Console.WriteLine(BitConverter.ToInt32(value.Data, 0)); 

      managed.FreeMemory(); 
      Console.ReadKey(); 
     } 
    } 
} 

我希望代码是不言自明,如果需要的话,我会提供更多的信息。我的问题是在Program类中的字节数组(我试图从上述的unsigned char数组中编组)在每次运行程序时打印不同的随机数据,但是当我使用调试器检查C++端时(在编组之前地方),一切都很好。就像我在代码中的注释中提到上述,我认为问题就出在这条线的ManagedClass类:

list.Add((PlcVarValue)Marshal.PtrToStructure(new IntPtr(ptr.ToInt32() + memSize * i), typeof(PlcVarValue)));

我知道一个事实,这正常工作与MyStruct *pObject其中*pObject是数组相同的结构,但在这种情况下,我需要一个指针指针,因为第三方DLL需要的实际上是一个MyStruct ***pppObject(击败我,但这是我必须处理)。我试图将数据从ppObject复制到一个单一的指针,但即使它工作,我对结果并不满意,因为它在实际应用中有一些不希望的副作用。另外,如果我对GCHandle的使用是错误的,我很乐意接受如何解决它的建议,但这不是这个问题的主要焦点。

+0

如果它是8个字节,你可以很容易地直接使用一个C#“ulong”值(即8个字节长)。 – xanatos

+1

你在混合使用'delete'和'free'! :-( – xanatos

回答

2

C#的一面:

public List<PlcVarValue> GetList() 
{ 
    int size = 5; 
    var list = new List<PlcVarValue>(size); 
    IntPtr ptr = GetValues(); 

    for (int i = 0; i < size; i++) 
    { 
     IntPtr ptrPlc = Marshal.ReadIntPtr(ptr, i * IntPtr.Size); 
     var plc = (PlcVarValue)Marshal.PtrToStructure(ptrPlc, typeof(PlcVarValue)); 
     list.Add(plc); 
    } 

    return list; 
} 

和C++侧(是在Clear()唯一的错误):

__declspec(dllexport) void Clear(void) 
{ 
    for (int i = 0; i < 5; i++) 
    { 
     delete values[i]; 
    } 

    free(values); 
} 

但你不应该混合mallocnew

如果您需要解释我正在做什么,请记住您正在返回一个指向指针数组的指针!

+0

亲爱的主,它的工作原理......非常感谢,我已经浪费太多时间了。 – makoshichi

+0

顺便说一句,我只混合malloc和新来快速掀起这个例子,但我会牢记。 – makoshichi