在C#中将字符串数组编组为char**

Sla*_*awa 5 c# arrays string pointers marshalling

我正在调用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;
Run Code Online (Sandbox Code Playgroud)

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

[MarshalAs(UnmanagedType.LPArray,ArraySubType = UnmanagedType.LPTStr,SizeConst = 9)] public String [] mFieldNames;

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

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

所以我搞砸了指针.代码对我来说似乎没问题,bug在哪里?

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;
  }
Run Code Online (Sandbox Code Playgroud)

Jar*_*Par 8

制作令人讨厌的字符串指针的最佳方法,尤其是结构中的双指针,只需使用IntPtr.

public IntPtr mFieldNames;
Run Code Online (Sandbox Code Playgroud)

这将是正确的元帅,尽管有一种不那么有用的类型.但是,如果您了解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;
}
Run Code Online (Sandbox Code Playgroud)

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