【问题标题】:passing array of structs from c# to regular dll将结构数组从 c# 传递到常规 dll
【发布时间】:2009-11-19 02:54:07
【问题描述】:

我有一个常规 dll 导出了以下函数。

extern "C" __declspec(dllexport) int FindNearestStuff(double _latitude, double _longitude , LocationStruct * locations[])

LocationStruct 很简单

struct LocationStruct
  {
   long positionIndex;
   long item;
  };

我正在尝试使用 c# 调用它

 [DllImport("myclever.dll", CharSet = CharSet.None)]
        private static extern int FindNearestStuff(double _latitude, double _longitude, 
                                                    ref LocationStruct [] locations);

这一切都很酷很时髦,我可以从调试器进入 dll 函数。 在 dll 中,LocationStruct 数组被正确填充,一切都很好。

我遇到的问题是当它从 dll 返回时,LocationStruct 数组没有返回数据 - 只是空值...

我错过了什么?

【问题讨论】:

  • struct LocationStruct { long roadIndex;长tdist; };并在 c# internal struct LocationStruct { public int roadIndex; //在 C 代码中为 long public int tdist;//在 C 代码中为 long };
  • 我不认为 P/Invoke 可以在这里处理双重间接(在 C# 端由 ref 传递的数组,在 C++ 端由指针传递)。你真的需要它吗?指向结构数组中第一个元素的指针不会在 C++ 中执行吗?
  • 我能想到的最简单的方法是使用不安全的 C#。还记得使用 StructLayout.Sequential
  • 所有 C# 结构都是隐式的Sequential
  • 感谢帕维尔;这为我节省了很多打字时间!

标签: c# arrays data-structures


【解决方案1】:

非常感谢您的帮助 - 您确实将我引向了正确的方向,我非常感谢您的帮助!

这是似乎对我有用的解决方案;

[DllImport("myclever.dll", CharSet = CharSet.None)]
        private static extern int FindNearestStuff(double _latitude, double _longitude,  IntPtr locations);


public static int FindNearestStuff(double _latitude, double _longitude, LocationStruct[] locations)
        {
            int returnValue = -1;
            LocationStruct temp;
            temp.roadIndex = 1;
            temp.tdist = 1;
            int iStructSize = Marshal.SizeOf(temp);
            try
            {
                IntPtr locationsPtr = IntPtr.Zero;

                IntPtr buffer = Marshal.AllocHGlobal(iStructSize * 10);
                FindNearestRoads(_latitude, _longitude,  buffer);
                for (int i = 0; i < 10; i++)
                {
                    IntPtr ptr = new IntPtr(buffer.ToInt32() + iStructSize * i);
                    locations[i] = (LocationStruct)Marshal.PtrToStructure(ptr, typeof(LocationStruct));

                }

                returnValue = 0;
            }
            catch
            {
            }
            return returnValue;
        }

【讨论】:

  • 当然不要忘记 Marshal.FreeHGlobal(buffer); !
  • 如果这对您有用,您的原始代码中确实有太多的间接级别,就像 Pavel 暗示的那样。您不必自己进行所有这些分配和复制,您应该能够获取原始函数声明并只需删除数组参数上的 ref 修饰符。
【解决方案2】:

我不确定您是否能够自动执行此操作,因为 C# 无法知道位置变量中返回了多少项目(我假设 FindNearestStuff 的返回值是地点。)

您将不得不使用Marshall 类和这样的过程手动编组您的数据:

[DllImport("myclever.dll", CharSet = CharSet.None)]
private static extern int FindNearestStuff(double _latitude, double _longitude, 
                                           out IntPtr locations);

public static LocationStruct[] FindNearestStuff(double latitude, double longitude) {
    IntPtr locationsPtr = IntPtr.Zero;
    int numLocations = FindNearestStuff(latitude, longitude, out locationsPtr);

    LocationsStruct[] locations = new LocationsStruct[numLocations];

    for (int i = 0; i < numLocations; i++) {

          // locationsPtr is a pointer to the struct, so read the value
          // at locationPtr which will be the address of the struct and
          // then manually marshal the struct from that address
          locaitonsStruct[i] = (LocationStruct)Marshal.PtrToStructure(
              Marshal.ReadIntPtr(locationsPtr), typeof(LocationsStruct));

          // Move to the location pointer to the next address of a 
          // pointer to a struct
          locationsPtr += IntPtr.Size;  
    }

    return locations;
}

我还没有真正尝试过这个,所以请自告奋勇。

【讨论】:

    猜你喜欢
    • 2011-06-01
    • 1970-01-01
    • 2021-09-13
    • 2011-01-19
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多