2

我一直在用 C++ 编写一个 DLL,现在我必须从 VB6 应用程序中调用这个 DLL。

这是来自此 DLL 的代码示例:

#include <vector>
#include <string>

using namespace std;    

void __stdcall DLLFunction (vector<Object>*)
{
 // performs a few operations on the Objects contained in the vector.
}

struct Object
{
    long CoordX;
    long CoordY;
    long Width;
    long Height;
    LPSTR Id;
};

我还在 VB6 中定义了“对象结构”

Private Type Object
    CoordX As Integer 
    CoordY As Integer 
    Width As Integer
    Height As Integer
    Id As String 
End Type

问题是我不知道什么 vb6 类型可以代表 std::vector 以调用 DLL 的函数。

注意:
- 我使用 DLL 的向量来添加对象。
- 我使用指针以尽可能少地使用内存。
- 对不起我的英语,它根本不是我的母语。
- 感谢您阅读并尝试帮助我。

编辑:
- 我修复了打字问题(ID 肯定由 NullChar 结束,所以 LPSTR 应该可以解决问题)。- 我读了你们的答案,我要感谢你们两个,你们的答案很接近,还有一个主要问题。我的 DLL 肯定需要向容器中添加元素。因此,我想知道我怎么能做到这一点。也许我可以向我的函数添加一个返回类型,然后使该函数能够返回它创建的项目(而不是将其直接放入容器中),以便 vb6 应用程序获取这些项目并能够处理它们,但是我不知道该怎么做

编辑之二:

@Rook:我觉得我可以通过使用新结构来实现这一点。
结构 ObjectArrayPointer
{
对象* 指针;
size_t 计数器;
}

然后以这种方式调用我的函数:

void __stdcall DLLFunction (ObjectArrayPointer*);

然后我就可以为我的 VB6 应用程序添加对象并编辑大小参数以找到这些新对象。那是你的意思吗?

4

3 回答 3

5

无论如何,您都不应该尝试从 DLL 导出模板容器。当面对更新的编译器和库时,它们可能会崩溃(例如,在 C++03 下构建的库无法与使用 C++11 构建的代码很好地配合)。

最不痛苦的事情是接受一个指向缓冲区的指针和一个长度参数,

void __stdcall DLLFunction (Object* buffer, size_t nObjects);

如果容器的大小在执行期间不会改变。这个接口非常简单,任何理解 C 调用约定的语言都可以轻松访问(例如,几乎每一个。)

您已经放弃了 a 的大部分使用,std::vector因为您已经将它专门用于Object; 您可以考虑一路创建自己的ObjectCollection类,该类在std::vector内部使用但呈现非模板化接口。这是一个简单的例子:

// In your public API header file:
typedef struct object_collection_t *object_collection;

object_collection CreateObjectCollection();
void DestroyObjectCollect(object_collection collection);
void AddObjectToCollection(object_collection collection, Object* object);
// etc

标头中不以任何形式公开模板类型。这很好。

// And the corresponding code file:

struct object_collection_t
{
    std::vector<Object*> objects;
};

object_collection CreateObjectCollection() { return new object_collection_t; }
void DestroyObjectCollect(object_collection collection) { delete collection; }
void AddObjectToCollection(object_collection collection, Object* object)
{
    collection->objects.push_back(object);
}
// etc

所有的模板代码都被隐藏起来,给你留下了一个相当干净和简单的界面,它提供了一个不透明的指针类型,可以由外部代码传递,但只能由你自己的查询和修改等。

编辑:顺便说一句,我在Object*整个上述代码中都使用过。Object只使用普通的旧代码并避免所有与内存管理和客户端代码操作指针相关的问题可能会更安全、更简单。如果Object足够小且简单,则按值传递可能是更好的方法。

(注意:未检查可编译性或功能。E&OE。警告实施者!)

于 2012-06-27T08:52:37.880 回答
1

你不能这样做,因为它是一个 C++ 类/模板。在内部,它是一个数组,但不是可以从 VB6 创建的。

最好的办法是更改函数以接受指向带有计数参数的数组的指针。

您还需要非常小心类型的结构。

  1. C++ s在 VB6int中是s。Long
  2. 此外,Id字符串将不兼容。VB6 将有一个指向 unicode BString 的指针(除非您将其设为固定长度),其中 C++ 将具有 std::string ,它是一个 ANSI 字符数组。如果传递对象数组(而不是指针),VB6 可能会编组
于 2012-06-27T08:39:51.663 回答
1

VB6 ABI 是 COM 自动化 ABI。

因此,如果您需要一个兼容 VB6 ABI 的阵列,您可能应该使用 SAFEARRAY。我建议您还应该使用 Compiler COM Support 类:

http://msdn.microsoft.com/en-US/library/5yb2sfxk(v=vs.80).aspx

这个问题似乎完全符合您的要求,使用 ATL 的 CComSafeArray 类:

您可能还想看看这些:

SAFEARRAY 的替代品

SAFEARRAY 的替代方法是提供一个 COM 集合对象。这只是一个带有 Dispinterface 或 Dual 接口的 COM 对象,带有方法CountItem. 项目应该有 dispid=0 作为默认方法。您可能还希望提供_NewEnumwithDISPID_NEWENUM以支持 For Each 语法。

于 2012-06-27T09:40:43.937 回答