在http://topic.csdn.net/u/20080616/09/56f758f7-78a3-4a5c-8bad-903a5deb0acb.html
一贴中提问到int GetListName(char** pList[]) 

   *pList= (char **) malloc (sizeof(char *) * 128);
//返回字符串 组数量
} [DllImport("mydll.dll",EntryPoint="GetListName", SetLastError=true, CharSet=CharSet.Ansi,
CallingConvention=CallingConvention.StdCall)] 
public static extern  int GetNameList(IntPtr[] pList); 
调用
IntPtr[] PtrList =new IntPtr[2]{new IntPtr(0),new IntPtr(0)};
int bn=GetNameList(PtrList);
if(bn>0)
        { 
  IsOk=true;
  Drv=Marshal.ReadIntPtr(PtrList[0]);
  string as1 =Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(PtrList[0]));
         }
能够获得PtrList[0]正确的字符串,但是当返回的字符串是有两组或多组的时候
PtrList[1]永远是0
不知道怎么解决

解决方案 »

  1.   

    在C++里调用
    char** str
    int nCount= GetUsbDeviceListName(&str);
    char* msg=_strdup(strConnectedDevices[1]);
    if(nCount>1)
     MessageBox(0,msg, 0,0);可以正确获得,但C#里就不行,高手来啊
      

  2.   

    int GetListName(char** pList[]) That is a poor design :)
      

  3.   

    char** str类型 对应 C#中的是
    ref char str
      

  4.   

    int GetListName(char** pList[]) 这个等价int GetListName(char*** pList) 
    你觉得在C#如何写?
      

  5.   

    ref IntPtr也不行,
    ref IntPtr[]也不行
    ref StringBuilder[]也不行
      

  6.   

    只有IntPtr[](ref IntPtr[]一样)
    能够获得第一个地址
    不知道这个串组地址是否连续的
    IntPtr[0]是否整个串组的首地址。
    如果是,有什么办法获得偏移地址的串?
      

  7.   

    int bn=0;
    IntPtr a1=Marshal.ReadIntPtr(PtrList[0]);
    IntPtr a2=(IntPtr)((int)a1+Marshal.SizeOf(bn));这样能计算出。等下试一下结果
      

  8.   

    暂时没办法测试,要等到明天。
    理论上,只要malloc分配的内存是连续的,就可以了
    最后的计算方法是int bn=0; 
    IntPtr a1=Marshal.ReadIntPtr(PtrList[0]); 
    先获得a1指向的字符串的长度len
    然后
    IntPtr a2=(IntPtr)((int)a1+len*Marshal.SizeOf(bn)); //也即a1+4×len不知道正确不?
      

  9.   

    你用[in,out] sring[] 试试看。
      

  10.   

    能够获得PtrList[0]正确的字符串,但是当返回的字符串是有两组或多组的时候,PtrList[1]永远是0 
    LZ在C++的代码赋值本身就有问题,
    C++中
    *pList= (char **) malloc (sizeof(char *) * 128); 这里实际只给PtrList[0]赋值了,而没有给PtrList[1]赋值。
    试下改成用下面的代码,就可使PtrList[1]的值不为0
    int GetListName(char** pList[])
    {
    pList[0] = (char **)malloc (sizeof(char*) * 128);//这里仅是指针数组的指针。
    pList[1] = (char **)malloc (sizeof(char*) * 128);
    *pList[0] = (char *)malloc (sizeof(char) * 128);//这里才是字符串指针。
    *pList[1] = (char *)malloc (sizeof(char) * 128);
    strcpy(*pList[0], "abcdefb!!!");
    strcpy(*pList[1], "eeeeeeee!!!");
    }所以返回的字符串是有两组或多组的时候,完全没有必要传个IntPtr[]数组进去,只传个IntPtr进去就可,记住这个IntPtr的值是指向一个指针数组,该数组的元素才是一个指向字符串的指针。
      

  11.   


    int GetListName(char** pList[]) 

      *pList= (char **) malloc (sizeof(char *) * 128); 
      //返回字符串 组数量 

    问题一、 采用T a[]传递参数的时候,隐含的意义是传入一个数组。
    采用T *a也可以传递数组,但它意义侧重于传入a的指针,这个指针可以指向一串a,也可以是一个a的引用。下列用一些简单的例子解释:
    int main(int argc, char* argv[])       // 这是C++常见的入口函数,旧的用法是(int argc, char** argv)
    {
        *argv = new char[100];             // 用了char* charv[]更强调数组的传入,给argv赋值明显是错误的理解
    }void doubleUp(int length, int nums[])  // 传入数组的示例
    {
        for(int i=0; i<length; i++)
        {
           nums[i] = nums[i] + nums[i];    // 好的做法,传入数组,同时传入数组的长度
        }
    }void GetStringSafe(char* buf, int maxLength)             // 传入指针的示例
    {
       StringCbCopyA(buf, maxLength, "safe method call");    // 好的做法
    }void GetStringNeedYourCare(char** str)
    {
        *str = new char[32];                                 // 比较不好的做法,要特别关注以免内存泄露
        StringCbCopyA(*str, 32, "You need to delete me!");
    }void Test()
    {
       char buf[128];
       GetStringSafe( buf, 128 );                            // 安全的调用
       char* result = NULL;
       GetStringNeedYourCare( &result );
       delete[] result;                                      // 调用者需要释放内存。 
    }
    *pList= (char **) malloc (sizeof(char *) * 128); 违背了调用惯例,调用者也无从知道返回了128个指针。
    问题二、
    指针的指针不实用。先看看两种给*pList赋值的方法:
    int GetListName(char** pList[]) 

      *pList= (char **) malloc (sizeof(char *) * 128); 
      //返回字符串 组数量   char* string1 = new char[32];
      StringCbCopyA(string1, 32, "string one");
      //pList[0] = &string1;                           // 错误,string1指针本身(不是它指向的内容)是本地变量。  char** ptrToString1 = new char*;                 // 需要分配一个指针的指针。
      *ptrToString1 = string1;
      pList[0] = ptrToString1;                         
      
      //...
    }int GetListName(char** pList[]) 

      *pList= (char **) malloc (sizeof(char *) * 128); 
      //返回字符串 组数量 
      pList[0] = new char* [8];
      pList[0][0] = new char[32];
      StringCbCopyA( pList[0][0], 32, "string 00");  pList[0][1] = new char[32];
      StringCbCopyA( pList[0][1], 32, "string 01");  //...
      pList[0][7] = new char[32];
      StringCbCopyA( pList[0][7], 32, "string 07");  //...
    }
    第一种方法,本意跟char* pList[]一样,无缘无故增加了一个转意,调用者还要多清理一次内存。
    第二种方法,传回128个“字符串的数组”,但调用者又如何知道每个“字符串的数组”有多长呢,是上述例子的8个还是80个?这样写函数的方式,内存的分配和释放没有约定,数组的长度无从知道,不要说托管非托管的交互,就是C++客户也难以调用。
      

  12.   


    dk385 
    gomoku
    两位的回复很有启发,需要些时间认真的消化消化。
    gomoku后面提到的“字符串的长度”有多长,应该是可以知道的吧?好像char* char[]遇到"\0"就结束的吧?,
    在C#里面也能正确获得一个这个长度,直接Marshal.PtrToStringAnsi(首地址)就是这个字符串的长度了,知道第一个长度,就能知道第二个长度了。
    至于这个数组的长度是int GetListName()给返回的。C++的客户也难调用:),这的确。我用c++也搞了很久才调得出来,
    对C++不熟,只能达到调用的层次,修改dll有点难度,只能去硬着头皮去调用。然后小心的管理善后:)
    感谢两位的帮忙。:)
      

  13.   

    MYDLL_API int fnmydll( char** pList[] )
    {

    *pList= (char **) malloc (sizeof(char *) * 128);
        *pList[0] =strdup("foo"); 
    return 0;
    }
      

  14.   

    [DllImport("test\\mydll.dll")]
    public extern static int  fnmydll( ref System.IntPtr p );
    /// <summary>
    /// 应用程序的主入口点。
    /// </summary>
    [STAThread]
    static void Main(string[] args)
     {
    //
    // TODO: 在此处添加代码以启动应用程序
    //
                Console.Read();
                System.IntPtr p = System.IntPtr.Zero;
    fnmydll( ref p );
    System.IntPtr b = (IntPtr)Marshal.PtrToStructure( p , typeof( System.IntPtr )) ;
                string str = Marshal.PtrToStringAnsi(b); //str is "foo"
    }
      

  15.   

    如果*pList[1]有内容,应该怎么取出来呢?
      

  16.   

    迟点再开贴请教,现在有另外些事情忙,,先结贴总结来说有三位的答案,都是能获得第一个字符串,而第二个取值在C#中都没有提及或者取不出来。这个C++函数不知道对还是不对,但至少在C++里调用是正常的,能够返回多组字符串数组但在C#里只能得这个一系列字符串数组的首地址,。