【问题标题】:Marshalling array of strings to char ** in C#在 C# 中将字符串数组编组为 char **
【发布时间】:2009-09-30 15:34:55
【问题描述】:

我正在调用一个 C DLL 函数,需要提供以下 C 结构:

typedef struct
{
    char      *mTableId;
    char     **mFieldNames;
    int        mNumFields;
    char      *mFilter;
    char      *mSort;
    int        mOffset;
    int        mMaxRecords;
    char      *mTargetRecordFilter;
    int        mSurroundingRecordsCount;
    int       *mOwnerIds;
    int     mNumOwnerIds;
    gsi_bool   mCacheFlag;
} SAKESearchForRecordsInput;

问题在于 char **mFieldNames;我试过像这样自动编组:

[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPTStr, SizeConst = 9)] 公共字符串[] mFieldNames;

这样我在 Marshal.SizeOf() 中得到一个错误 - 无法计算正确的大小。然后我决定手动处理指针。它实际上只是一个指向 C 字符串数组的指针。这是我的代码,它导致

System.AccessViolationException:试图读取或写入受保护的内存。这通常表明其他内存已损坏。

所以我在某个地方搞砸了指针。代码对我来说似乎没问题,错误在哪里?

C#:

 [StructLayout(LayoutKind.Sequential)]
 unsafe public class SAKESearchForRecordsInput {
  [MarshalAs(UnmanagedType.LPTStr)]
  public String mTableId;
  //[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPTStr, SizeConst = 9)] // HARDCODED!?!
  //public String[] mFieldNames;      // char     **mFieldNames;
  public IntPtr mFieldNames;
  public int mNumFields;
  [MarshalAs(UnmanagedType.LPTStr)]
  public String mFilter;
  [MarshalAs(UnmanagedType.LPTStr)]
  public String mSort;
  public int mOffset;
  public int mMaxRecords;
  //[MarshalAs(UnmanagedType.LPTStr)]
  public IntPtr mTargetRecordFilter;
  public int mSurroundingRecordsCount;
  public IntPtr mOwnerIds;
  public int mNumOwnerIds;
  public gsi_bool mCacheFlag;
 }

  [DllImport("saketestd.dll")]
  unsafe static extern void* sakeSearchForRecords(
   IntPtr sake,
   IntPtr input, //SAKESearchForRecordsInput *
   SAKERequestCallback callback, //SAKERequestCallback 
   IntPtr userData);

  unsafe public bool sakeSearchForRecordsE() {
   bool ret = false;
   try {
    searchInput.mTableId = "bbdx_score";
    //searchInput.mFieldNames = mFieldNames.to;
    searchInput.mFilter = "num_ratings = 0 AND filestore > 0";
    searchInput.mSort = "";
    searchInput.mOffset = 0;
    searchInput.mMaxRecords = 1;
    //searchInput.mTargetRecordFilter = "";
    searchInput.mSurroundingRecordsCount = 0;
    searchInput.mOwnerIds = IntPtr.Zero;
    searchInput.mNumOwnerIds = 0;
    searchInput.mCacheFlag = true;

    int sakeSize = Marshal.SizeOf(sake);
    debug.AddLine(this.getMethodName() + ": sizeof(sake): " + sakeSize);
    IntPtr pSake = Marshal.AllocHGlobal(sakeSize);
    Marshal.StructureToPtr(sake, pSake, true);

    int inputSize = Marshal.SizeOf(searchInput);
    debug.AddLine(this.getMethodName() + ": sizeof(input): " + inputSize);
    IntPtr pInput = Marshal.AllocHGlobal(inputSize);
    Marshal.StructureToPtr(searchInput, pInput, true);

    IntPtr[] mFieldNamesPtr;
    int i;
    if (true) { // IntPtr[]
     mFieldNamesPtr = new IntPtr[mFieldNames.Length];
     i = 0;
     foreach (string str in mFieldNames) {
      mFieldNamesPtr[i++] = Marshal.StringToHGlobalAnsi(str);
     }
     //searchInput.mFieldNames = mFieldNamesPtr;
    } else {
     //searchInput.mFieldNames = mFieldNames;
    }
    searchInput.mNumFields = mFieldNames.Length;

    void* pRequestInternal = null;
     void* p = mFieldNamesPtr[0].ToPointer();
     searchInput.mFieldNames = (IntPtr)p;
     pRequestInternal = sakeSearchForRecords(
      pSake,
      pInput,
      new SAKERequestCallback(this.sakeSearchForRecordsCB),
      IntPtr.Zero
     );


    sake = (SAKEInternal)Marshal.PtrToStructure(pSake, typeof(SAKEInternal));
    if (searchRequest == null) {
     debug.AddLine(this.getMethodName() + ": mStartRequestResult: " + sake.mStartRequestResult);
    } else {
     ret = true;
     this.searchRequest = (SAKERequestInternal)Marshal.PtrToStructure(
      new IntPtr(pRequestInternal),
      typeof(SAKERequestInternal)
     );
     searchInput = (SAKESearchForRecordsInput)Marshal.PtrToStructure(
      pInput,
      typeof(SAKESearchForRecordsInput)
     );

     if (true) {
      i = 0;
      foreach (string str in mFieldNames) {
       Marshal.FreeHGlobal(mFieldNamesPtr[i++]);
      }
     }

     PrintStruct ps = new PrintStruct(sake);
     debug.AddLine(this.getMethodName() + ": sake: " + ps);
     ps = new PrintStruct(searchRequest);
     debug.AddLine(this.getMethodName() + ": searchRequest: " + ps.print_r());
     ps = new PrintStruct(searchInput);
     debug.AddLine(this.getMethodName() + ": searchInput: " + ps.print_r());
    }
    Marshal.FreeHGlobal(pSake);
    Marshal.FreeHGlobal(pInput);
   } catch (Exception ex) {
    debug.Text += ex.ToString();
   }
   return ret;
  }

【问题讨论】:

  • 更新:sakeSearchForRecords() 中断;

标签: c# arrays string pointers marshalling


【解决方案1】:

编组讨厌的字符串指针,尤其是结构中的双指针的最佳方法是简单地使用 IntPtr。

public IntPtr mFieldNames;

这将正确编组,尽管类型不太有用。但是,如果您了解 IntPtr 的结构,则很容易得到结果字符串。

public static List<string> GetAllStrings(IntPtr ptr, int size) {
  var list = new List<string>();
  for ( int i = 0; i < size; i++ ) {
    var strPtr = (IntPtr)Marshal.PtrToStructure(ptr, typeof(IntPtr));
    list.Add(Marshal.PtrToStringUni(strPtr));
    ptr = new IntPtr(ptr.ToInt64()+IntPtr.Size);
  }
  return list;
}

唯一真正的缺点是您必须手动释放内存

【讨论】:

  • 如果我尝试使用您的函数(如果您查看我的代码(使用数组),我有一个类似的函数),我如何从“List”中获取 IntPtr? IntPtr pList = &list; // ??
【解决方案2】:

更好的方法是简单地使用带有 sbyte 的不安全代码,它与 c-char (-128 到 127) 1 字节相同。 您可以自己编写一些外部函数,如 alloc_txt、free_txt 等。用于从堆中分配和释放。大多数情况下,当我使用互操作编写时,我确实使用了不安全的代码,因为 IntPtr 为您获取地址,但您仍然必须使用外部函数来获取它指向的结构中的成员,或者如果原语必须编组方法来提取值是什么。

您必须将 c# 结构声明为不安全的唯一情况是,如果您使用的不是实际指针,而是使用 MarshalAs 代替。我仍然希望你通过 MarshalAs(UnmanagedType.?) 使用不安全的指针,它允许你直接处理成员。

[Struct(Layout.Sequential)]
public unsafe struct SAKESearchForRecordsInput
{
sbyte*mTableId;
sbyte**mFieldNames;
int mNumFields;
sbyte*mFilter;
sbyte*mSort;
int mOffset;
int mMaxRecords;
char*mTargetRecordFilter;
int mSurroundingRecordsCount;
int*mOwnerIds;
int mNumOwnerIds;
bool mCacheFlag;//?don't know what the typedef for the bytes
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-13
    • 1970-01-01
    • 2023-03-18
    • 2021-05-03
    • 2012-10-28
    • 2015-08-01
    • 2013-10-29
    • 1970-01-01
    相关资源
    最近更新 更多