我有一个VB应用程序请求来自C DLL的用户列表:
VB将询问DLL有多少用户,然后将数组初始化为适当的大小
然后,VB将通过引用DLL函数传递其数组,该函数将用用户名填充它。
我开始编写像这样的C函数:foo(char **bar);
,它将被视为一个字符串数组。但后来我意识到,我将使数组中的每个项指向不同的C字符串(char *username
链表中的struct userlist
),而不是修改已经指向的数据。数组数组按值传递:地址列表的副本,因此地址指向原始数据,但修改该副本中的地址不会更改调用者的地址列表(我想,无论如何)。那么,我应该宣布它foo(char ***bar);
吗?这将是一个指向字符串数组的指针,因此,如果我更改数组所指向的字符串,它将修改调用者(VB)正在使用的字符串数组....对吗?
这是我到目前为止的用法(还没有测试过......我还是刚刚编写DLL的代码,到目前为止还没有VB前端调用它)
EXPORT void __stdcall update_userlist(char ***ulist){
int i = 0;
userlist *cur_user = userlist_head; //pointer to first item in linked list
for(; i < usercount_; ++i){
*ulist[i] = cur_user->username;
cur_user = cur_user->next;
}
}
答案 0 :(得分:4)
一般来说,做你要问的事情并不简单,因为VB只是不理解C风格的ASCIIZ字符串和数组。
如果您的DLL不期望BSTR的VB SafeArray,那么填充它会有一些困难。
通过引用第一个元素让VB传入一个Long(C int)数组会很简单,你可以用指向各个字符串的指针填充它。 VB端可以将它们复制到VB字符串。但在那种情况下,谁处理了C字符串,何时处理?
如果你创建VB数组并用预先调整大小的字符串填充它,你仍然需要处理C端的SafeArray,因为你不能通过引用传递单个VB字符串数组元素并期望在内存中找到与其相邻的剩余字符串。
最好,最安全的方法是让你的DLL创建一个所谓的'Ansi BSTR'的SafeArray,并在VB中声明该函数返回一个字符串数组。那么你不需要两个调用,因为数组边界将告诉整个故事。
===== edit =====
当VB将字符串数组传递给Declared函数时,它会在幕后执行一些伏都教。它首先将所有字符串从Unicode转换为通常称为“Ansi BSTR”的混蛋形式。对于C,这些看似并且可以被视为ASCIIZ或LPSTR,除了你不能以正常的C方式创建或延长它们,你只能填充它们。在C方面,传递的数组看起来像ppSA(SAFEARRAY **)。 Ansi BSTR是由SafeArray的pData成员引用的一系列指针。
你绝对不能从数组中传递一个字符串(作为char *),并期望在内存中找到与其连续的其余字符串。您必须传递数组本身并使用SafeArray API(或SA结构的知识)对其进行操作。
这就是为什么总体上最好的选择是直接在DLL中完成所有这些操作。使用SafeArrayCreate创建数组,然后使用SysAllocStringByteLen创建Ansi BSTR并将这些字符串(BSTR,这是一个4字节指针)放入数组插槽中。返回时,VB会执行其voodoo并将字符串转换为Unicode。
在VB中,您的函数将被声明为返回String()。
答案 1 :(得分:1)
两个星号是要走的路。
char* // is a pointer to a char
char** // is a pointer to a char pointer
char*** // is a pointer to a pointer to a char pointer - e.g. multi-dimensional array (err...)
我迷惑了自己:)
答案 2 :(得分:0)
所以让我直截了当。您的函数是否填充了链表中包含的数据的字符串数组?
如果你事先知道列表的大小,你可以传递一个char **,但是如果你不知道它的大小并且需要能够增加列表,你将需要一个char ***。 / p>
通过查看代码,您似乎已经知道了长度,因此您只需要在调用函数之前分配一个正确长度的数组。这是一个例子:
void update_userlist(char **ulist)
{
int i = 0;
userlist *cur_user = userlist_head;
for(; i < usercount_; ++i)
{
ulist[i] = cur_user->username; // I am assuming that username is a char *
cur_user = cur_user->next;
}
}
// This sets up the array and calls the function.
char **mylist = malloc(sizeof(char*) * usercount_);
update_userlist(mylist);
更新:以下是各种指针级别之间的差异:
void func1(char * data)
这会将指针的副本传递给C字符串。如果将指针更改为指向其他字符串,则调用函数仍将指向原始字符串。
void func2(char ** data)
这会将指针的副本传递给指向C字符串的指针数组。您可以将指针替换为数组中的任何字符串,并且调用函数的数组将被更改,因为它没有创建数组的副本,它只指向调用者的数组。
void func3(char *** data)
这会将指针传递给指向C字符串指针数组的指针。有了这个,您就可以完全替换整个阵列。如果需要增长数组,则只需要这种级别的间接,因为C数组无法重新调整大小。