原文网址:http://support.microsoft.com/kb/207931
本页
概要
本文讨论了与 Microsoft Visual Basic 和 C 函数之间传递数组中的数据相关的某些方面。它比较标准 C 数组和 SAFEARRAY 数据类型,并讨论了不同的方案,从 Visual Basic 或 C 中编写的函数调用 C 函数时
讨论了以下方案:
注: 该代码示例作为示例使用一维数组。如果传递的多维数组需要记住 SAFEARRAY 是列为主,而在 C 中的数组是行优先。 " _mstchunk="true" _msthash="21362">NOTE: The code samples use a one-dimensional array as an example. " _mstchunk="true" _msthash="21362">注意: 代码示例作为示例使用一维数组。如果传递的多维数组,您需要记住 safearray 之所以是列为主,,而在 C 中的数组是行优先。
讨论了以下方案:
- 方案 1: 将从 Visual Basic 的数组传递到一个函数,该函数需要一个指针。
- 方案 2: 在 C/c + + 来接收 safearray 之所以编写的函数。
- 方案 3: 声明用 C 语言编写的组件对象模型 (COM) Dll 中的 SAFEARRAYs
注: 该代码示例作为示例使用一维数组。如果传递的多维数组需要记住 SAFEARRAY 是列为主,而在 C 中的数组是行优先。 " _mstchunk="true" _msthash="21362">NOTE: The code samples use a one-dimensional array as an example. " _mstchunk="true" _msthash="21362">注意: 代码示例作为示例使用一维数组。如果传递的多维数组,您需要记住 safearray 之所以是列为主,,而在 C 中的数组是行优先。
更多信息
在 Microsoft Visual Basic safearray 之所以作为存储数组。在 C 中它基本上是指向数组的第一个元素的指针。Safearray 之所以提供有关数组大小、 尺寸等的信息,而使用只是一个指针的 C 方法会使控制对程序员的数据的所有责任。第二种情况中,您将找到有关 SAFEARRAYs 的更详细的说明。
例如,下面是返回之长型整数数组中的 C 函数的原型。第一个参数是指向数组的指针,第二个提供的数组中的元素数。
函数声明的 Visual Basic 版本如下所示:
若要调用该函数,请使用下面的代码:
LIMITATIONS: See the 'Important Considerations' topic. " _mstchunk="true" _msthash="21481">限制: 请参阅"重要事项"的主题。
SAFEARRAY 基本上是一种结构,提供了一个数组中,并包括一个指向数组的实际元素有关的重要信息。Safearray 之所以是在 OLEAUTO 中定义的。H 头。此标头还包含一组用于操作 SAFEARRAYs 的 API 函数的原型。
在 Win32 中之后替换所有类型定义和条件包括在其声明中,, 这是 SAFEARRAY 如下所示:
SAFEARRAYBOUND 成员也是一种结构,如下所示:
它是要详细说明如何使用 C 或 c + + 中的 safearray 之所以这篇文章的讨论范围之内。您可以在提供引用中找到此信息。此处的目的是向您展示如何原型函数在 C 中,使其能够用 Visual Basic 正常工作。
下面的示例演示如何创建一个采用长型值数组作为参数的函数:
C 声明需要如下所示:
请注意,现在,该函数不需要参数,以显示数组中的元素数量。SAFEARRAY 结构中包含此信息。
LIMITATIONS: See the 'Important Considerations' topic. " _mstchunk="true" _msthash="21490">限制: 请参阅"重要事项"的主题。
.Idl 文件包含以下声明:
这是一个已知的问题,在向导中的 Visual C 6.0 版。您可以通过传递此错误手动更改页眉、 源和.idl 文件中的 AddLongs 方法的参数,如下所述:
在中的参数列表类型:
文件查看 选项卡上展开。在标头文件下双击 MyClass.h 头文件。在此文件的最后,您将找到您的方法的声明: " _mstchunk="true" _msthash="21499">FileView tab in the workspace, expand MyAtlDll files. " _mstchunk="true" _msthash="21499">在工作区中FileView选项卡上,展开 MyAtlDll 文件。在头文件下双击 MyClass.h 头文件。此文件的最末尾,您将找到您的方法的声明:
将其更改为如下:
在源文件列表中,双击 MyClass.cpp。这里您可以找到您的方法的实现。更改以下行:
为:
此外,在下的源文件列表中,双击.idl 文件 (扩展名为.idl 只有一个) 并找到下面一行:
将其更改为:
方案 1: 将一个数组从 Microsoft Visual Basic 传递指针的函数
很普遍,若要调用从 Microsoft Visual Basic 不是专门针对 Visual Basic,例如 Windows API 函数的 DLL 中的函数。由于 C 函数需要数组的第一个元素的地址,则可以在 Visual Basic 中通过引用传递的数组的第一个元素的。例如,下面是返回之长型整数数组中的 C 函数的原型。第一个参数是指向数组的指针,第二个提供的数组中的元素数。
long AddLongs_Pointer(long *plArrayOfLongs, long lElements);
Declare Function AddLongs_Pointer Lib "MyDll.dll" (FirstElement As Long, ByVal lElements As Long) As Long
Dim MyArrayOfLongs(0 to 10) as Long Dim lTotal as Long ' Insert code here to initialize the elements of the array ' Calling the function lTotal = AddLongs_Pointer (MyArrayOfLongs(0), UBound(MyArrayOfLongs) + 1)
方案 2: 在 C 中接收 safearray 之所以编写的函数
若要编写 C 可以接收或来自或发往 Visual Basic 中返回数组中的一个函数,您需要 SAFEARRAY 作为该参数的类型。就像它在 Visual Basic 中编写将能够调用很自然地在 Visual Basic 中的函数。您还将检查是否已分配足够的内存,以及如果尺寸足够。在某些情况下,甚至可以调整数组的维数的大小。SAFEARRAY 基本上是一种结构,提供了一个数组中,并包括一个指向数组的实际元素有关的重要信息。Safearray 之所以是在 OLEAUTO 中定义的。H 头。此标头还包含一组用于操作 SAFEARRAYs 的 API 函数的原型。
在 Win32 中之后替换所有类型定义和条件包括在其声明中,, 这是 SAFEARRAY 如下所示:
struct SAFEARRAY { WORD cDims; // number of dimensions WORD fFeatures; // bitfield indicating attributes of a particular // array DWORD cbElements; // size of an element of the array DWORD cLocks; // lock counter Void * pvData; // pointer to the array's elements (use only if // cLocks>0) SAFEARRAYBOUND rgsabound[1]; // structure containing information for // each dimension };
struct SAFEARRAYBOUND { DWORD cElements; // number of elements on a given dimension long lLbound; // lower boundary on a given dimension };
下面的示例演示如何创建一个采用长型值数组作为参数的函数:
Declare Function AddLongs_SafeArray Lib "MyDll.dll" (FirstElement() As Long, lSum As Long) As Long
long AddLongs_SafeArray(SAFEARRAY **psaArray, long *plSum);
LIMITATIONS: See the 'Important Considerations' topic. " _mstchunk="true" _msthash="21490">限制: 请参阅"重要事项"的主题。
方案 3: 声明 COM 在用 C 编写的 Dll 中的 SAFEARRAYS
若要使用类型库或 COM Dll 时,应始终声明 SAFEARRAY 参数作为 [中,out] 以使它能够在 Visual Basic 中正常工作。这是声明中的显示方式:
STDMETHOD(AddLongs)(/*[in,out]*/ SAFEARRAY ** psaMyArray, /*[in,out]*/ long *plSum);
[id(1), helpstring("method AddLongs")] HRESULT AddLongs([in,out] SAFEARRAY (long) * psaMyArray, [in,out] Long *plSum);
重要的考虑因素
- 方案 1 和 2 中所述的解决方案不能在包含字符串的字符串或用户定义类型 (Udt) 的数组。这是因为内部 UNICODE/ANSI 转换调用的 DLL 函数时,Visual Basic 执行。但是,这种限制不适用于使情形 3 中描述的解决方案是有效的字符串的数组或 Udt 包含字符串类型库中定义的函数。
- Microsoft Visual Basic 版本 5.0 和更早版本的 Visual Basic 不能作为返回值来处理 SAFEARRAY。在 Microsoft Visual Basic 版本 6.0 中,这是可能的并且 C 原型应类似下面这样:
STDMETHOD(ReturnAnArray) (/*[out,retval]*/SAFEARRAY **psaArray);
示例代码
以下是一些代码示例,演示了本文中介绍的概念。第一个示例有两个函数,与相关的方案 1 的第一个 (AddLongs_Pointer) 和第二个 (AddLongs_SafeArray) 与第二种情况。第二个示例有只有一个函数 (AddLongs),与方案 3。示例 1
此示例计算 Win32 DLL 中使用两个函数的长型整数数组的总和。第一个函数需要标准的 C 数组的指针,并且第二个使用) 的 SAFEARRAY。两者都应返回相同的值。- 在名为 MyStDll 的 Visual C 中创建 Win32 DLL。
- 添加新的头文件,并将下面的代码粘贴到该:
/* prototypes */ #include <windows.h> #include <oleauto.h> long __declspec (dllexport) __stdcall AddLongs_Pointer(long *plArrayOfLongs, long lElements); long __declspec (dllexport) __stdcall AddLongs_SafeArray(SAFEARRAY **psaArray, long *plSum);
- 添加新的源代码文件使用以下代码:
/* functions */ #include "MyDll.h" // use here the name of your header file /*======================================================== AddLongs_Pointer - a function to add all elements of an array passed as a pointer (long *) returns the sum of the elements ==========================================================*/ long __declspec (dllexport) __stdcall AddLongs_Pointer( long *plArrayOfLongs, // the array's pointer long lElements) // number of elements to add { long lSum; long i; lSum = 0; if(lElements > 0) { for (i=0; i < lElements; i++) lSum = lSum + plArrayOfLongs[i]; } // returning return(lSum); } /*=============================================================== AddLongs_SafeAray - a function to add all elements of an one-dimensional array of longs return codes: 0 - Success >0 - Failure =================================================================*/ long __declspec (dllexport) __stdcall AddLongs_SafeArray( SAFEARRAY **psaArray, // the array to use long *plSum) // returns the sum of all elements of the array { long lElements; // number of elements in the array long iCount; HRESULT lResult; // return code for OLE functions long *pArrayElements; // pointer to the elements of the array // initializing the output *plSum=0; // checking if it is an one-dimensional array if ( (*psaArray)->cDims != 1 ) return(1); // checking if it is an array of longs if ( (*psaArray)->cbElements != 4 ) return(2); // how many elements are there in the array lElements=(*psaArray)->rgsabound[0].cElements; // locking the array before using its elements lResult=SafeArrayLock(*psaArray); if(lResult)return(3); // using the array pArrayElements=(long*) (*psaArray)->pvData; for (iCount=0; iCount < lElements; iCount++) *plSum = *plSum + pArrayElements[iCount]; // releasing the array lResult=SafeArrayUnlock(*psaArray); if (lResult) return(4); // returning return(0); }
- .Def 文件添加到您的项目,并粘贴下面的代码中:
EXPORTS AddLongs_Pointer @1 AddLongs_SafeArray @2
- 按F7来生成 DLL。
- 在 Visual Basic 中创建一个新的标准 EXE 项目。默认情况下,将创建 Form1。
- 命令按钮的位置名为 command1 在 Form1 上,并将下面的代码粘贴到 Form1 的代码窗口中:
Option Explicit Private Declare Function AddLongs_Pointer Lib "MyStDll.dll" _ (FirstElement As Long, ByVal lElements As Long) As Long Private Declare Function AddLongs_SafeArray Lib "MyStDll.dll" _ (FirstElement() As Long, lSum As Long) As Long Private Sub Command1_Click() Dim ArrayOfLongs(2) As Long Dim lSum As Long Dim k As Long ArrayOfLongs(0) = 1 ArrayOfLongs(1) = 2 ArrayOfLongs(2) = 3 lSum = AddLongs_Pointer(ArrayOfLongs(0), UBound(ArrayOfLongs)+1) MsgBox "Result with C array = " & Str$(lSum) k = AddLongs_SafeArray(ArrayOfLongs(), lSum) If k = 0 Then MsgBox "Result with Safearray = " & Str$(lSum) Else MsgBox "Call with Safearray failed" End If End Sub
- 按f5 键以运行该项目。
- 单击 command1。您应该看到两个消息 boxe。第一个显示的第一个函数的返回的值,并将指针传递给数组。第二个显示中使用安全的第二个函数返回的结果。这两个函数都返回的数字 6。
示例 2
此示例计算用 ActiveX 模板库 (ATL) 向导创建的 safearray 之所以使用 ActiveX DLL 中的函数中的长整数之和。- 在 Visual C 将创建一个使用 ATL COM 应用程序向导的 ATL DLL。其命名为 MyAtlDll。
- 插入 菜单中选择 新的 ATL 对象。突出显示的分类列表中的 对象,并选择在 对象 列表中的 简单对象。单击 下一步。在 MyClass 短名称 字段中键入,然后单击 属性 选项卡,检查以下选项,然后单击 确定:" _mstchunk="true" _msthash="184796">Insert menu, select New ATL Object. " _mstchunk="true" _msthash="184796">从插入菜单中,选择新的 ATL 对象。Objects in the category list and select Simple Object in the Objects list. " _mstchunk="true" _msthash="369592">在类别列表中突出显示的对象,并在对象列表中选择简单的对象。Next. " _mstchunk="true" _msthash="554388">单击下一步。Short Name field, and then click the Attributes tab. Check the following options and then click OK" _mstchunk="true" _msthash="739184">在 MyClass 的短名称字段中,键入然后单击属性选项卡检查以下选项,然后单击确定
线程处理模型: 单元
接口: 双
聚合: 是 - 添加方法。方法名称 文本中的类型 AddLongs 框,然后在 参数 文本框中键入以下: " _mstchunk="true" _msthash="184797">Add Method. " _mstchunk="true" _msthash="184797">在工作区中的 ClassView 窗格中,用鼠标右键单击 IMyClass,然后选择添加方法。Method Name text box and type the following in the Parameters text box:" _mstchunk="true" _msthash="369594">在方法名称文本框中键入 AddLongs,并在参数] 文本框中键入以下内容:
[in,out] SAFEARRAY (long) *psaMyArray, [in,out] long *plSum
不能创建该函数,因为标头或实现文件找不到。
在中的参数列表类型:
[in,out] long *psaMyArray, [in,out] long *plSum
STDMETHOD(AddLongs)(/*[in,out]*/ long *psaMyArray, /*[in,out]*/ long *plSum);
STDMETHOD(AddLongs)(/*[in,out]*/ SAFEARRAY **psaMyArray, /*[in,out]*/ long *plSum);
STDMETHODIMP CMyClass::AddLongs(long *psaMyArray, long *plSum)
STDMETHODIMP CMyClass::AddLongs(SAFEARRAY **psaMyArray, long *plSum)
[id(1), helpstring("method AddLongs")] HRESULT AddLongs([in,out] long *psaMyArray, [in,out] long *plSum);
[id(1), helpstring("method AddLongs")] HRESULT AddLongs([in,out] SAFEARRAY(long) *psaMyArray, [in,out] long *plSum);
- 双击一个实现文件,MyClass.cpp,在源文件列表下。在实现文件中为您的函数中添加以下代码:
STDMETHODIMP CMyClass::AddLongs(SAFEARRAY * * psaArray, long * plSum) { // TODO: Add your implementation code here long lElements; // number of elements in the array long iCount; HRESULT lResult; // return code for OLE functions long *pArrayElements; // pointer to the elements of the array // initializing the output *plSum=0; // checking if it is a one-dimensional array if ( (*psaArray)->cDims != 1 ) return(E_FAIL); // checking if it is an array of longs if ( (*psaArray)->cbElements != 4 ) return(E_FAIL); // how many elements are there in the array lElements=(*psaArray)->rgsabound[0].cElements; // locking the array before using its elements lResult=SafeArrayLock(*psaArray); if(lResult)return(lResult); // using the array pArrayElements=(long*) (*psaArray)->pvData; for (iCount=0; iCount<lElements; iCount++) *plSum = *plSum + pArrayElements[iCount]; // releasing the array lResult=SafeArrayUnlock(*psaArray); if (lResult) return(lResult); return S_OK; }
- 通过按下F7键生成 DLL。
- 在 Visual Basic 中,将创建一个新的标准 EXE 项目。默认情况下,将创建 Form1。
- 将一个名为 command1 在 Form1 上的命令按钮。
- 从项目菜单中选择引用,并添加 ActiveX 以前创建的 DLL (MyAtlDll) 的引用。
- 将下面的代码粘贴到 command1 的 Click 事件中:
Private Sub Command1_Click() Dim MyObj As New MYATLDLLLib.MyClass Dim ArrayOfLongs(3) As Long Dim lSum As Long ArrayOfLongs(0) = 1 ArrayOfLongs(1) = 2 ArrayOfLongs(2) = 3 MyObj.AddLongs ArrayOfLongs, lSum MsgBox "Result from ATL dll = " & Str$(lSum) End Sub
- 按f5 键以运行您的项目。
- Command1. " _mstchunk="true" _msthash="185374">单击command1。您应该看到一个消息框,显示从 AddLongs 方法返回的值 6。
参考
MSDN 库: 平台 SDK。COM 和 ActiveX 对象服务 ; 示例:自动化 ;转换和处理功能 ;数组操作 API 函数
MSDN 库: 技术文章 ;可视化工具 ;Visual Basic ;扩展 Visual Basic 使用 c + + Dll。
VB5Dll.doc: 此文件位于 Microsoft Visual Basic 5.0 安装 CD,在 \Tools\Docs 目录中。
有关其他信息注意 Visual C 版本 6.0' 节上面所述,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章: 使用 Visual Basic 从 Visual C++ Dll 的其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
MSDN 库: 技术文章 ;可视化工具 ;Visual Basic ;扩展 Visual Basic 使用 c + + Dll。
VB5Dll.doc: 此文件位于 Microsoft Visual Basic 5.0 安装 CD,在 \Tools\Docs 目录中。
有关其他信息注意 Visual C 版本 6.0' 节上面所述,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章: 使用 Visual Basic 从 Visual C++ Dll 的其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
171583
() http://support.microsoft.com/kb/171583/EN-US/
如何填充 UDType 通过 Visual C++ 的 DLL 的 32 位 VBA 数组