Visual Basic 和 C 之间传递数组的方式

原文网址:http://support.microsoft.com/kb/207931

本页

概要

本文讨论了与 Microsoft Visual Basic 和 C 函数之间传递数组中的数据相关的某些方面。它比较标准 C 数组和 SAFEARRAY 数据类型,并讨论了不同的方案,从 Visual Basic 或 C 中编写的函数调用 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 的更详细的说明。

方案 1: 将一个数组从 Microsoft Visual Basic 传递指针的函数

很普遍,若要调用从 Microsoft Visual Basic 不是专门针对 Visual Basic,例如 Windows API 函数的 DLL 中的函数。由于 C 函数需要数组的第一个元素的地址,则可以在 Visual Basic 中通过引用传递的数组的第一个元素的。

例如,下面是返回之长型整数数组中的 C 函数的原型。第一个参数是指向数组的指针,第二个提供的数组中的元素数。
long AddLongs_Pointer(long *plArrayOfLongs, long lElements);
				
函数声明的 Visual Basic 版本如下所示:
   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)
				
LIMITATIONS: See the 'Important Considerations' topic. " _mstchunk="true" _msthash="21481">限制: 请参阅"重要事项"的主题。

方案 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
   };
				
SAFEARRAYBOUND 成员也是一种结构,如下所示:
   struct SAFEARRAYBOUND
   {
      DWORD cElements; // number of elements on a given dimension
      long lLbound; // lower boundary on a given dimension
   };
				
它是要详细说明如何使用 C 或 c + + 中的 safearray 之所以这篇文章的讨论范围之内。您可以在提供引用中找到此信息。此处的目的是向您展示如何原型函数在 C 中,使其能够用 Visual Basic 正常工作。

下面的示例演示如何创建一个采用长型值数组作为参数的函数:
   Declare Function AddLongs_SafeArray Lib "MyDll.dll" (FirstElement() As
   Long, lSum As Long) As Long
				
C 声明需要如下所示:
   long AddLongs_SafeArray(SAFEARRAY **psaArray, long *plSum);
				
请注意,现在,该函数不需要参数,以显示数组中的元素数量。SAFEARRAY 结构中包含此信息。

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);
				
.Idl 文件包含以下声明:
   [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。两者都应返回相同的值。
  1. 在名为 MyStDll 的 Visual C 中创建 Win32 DLL。
  2. 添加新的头文件,并将下面的代码粘贴到该:
        /* 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);
    					
  3. 添加新的源代码文件使用以下代码:
       /* 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);
       }
    					
  4. .Def 文件添加到您的项目,并粘贴下面的代码中:
    EXPORTS
       AddLongs_Pointer @1
       AddLongs_SafeArray @2
    					
  5. F7来生成 DLL。
  6. 在 Visual Basic 中创建一个新的标准 EXE 项目。默认情况下,将创建 Form1。
  7. 命令按钮的位置名为 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
    					
  8. f5 键以运行该项目。
  9. 单击 command1。您应该看到两个消息 boxe。第一个显示的第一个函数的返回的值,并将指针传递给数组。第二个显示中使用安全的第二个函数返回的结果。这两个函数都返回的数字 6。

示例 2

此示例计算用 ActiveX 模板库 (ATL) 向导创建的 safearray 之所以使用 ActiveX DLL 中的函数中的长整数之和。
  1. 在 Visual C 将创建一个使用 ATL COM 应用程序向导的 ATL DLL。其命名为 MyAtlDll。
  2. 插入 菜单中选择 新的 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 的短名称字段中,键入然后单击属性选项卡检查以下选项,然后单击确定
    线程处理模型: 单元
    接口: 双
    聚合: 是
  3. 添加方法。方法名称 文本中的类型 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
    					
Note for Visual C version 6.0: If you are using Visual C version 6.0 you may receive the following error message: " _mstchunk="true" _msthash="21496">对于 Visual C 6.0 版的注意: 如果您使用的 Visual C 版本 6.0,您可能会收到以下错误消息:
不能创建该函数,因为标头或实现文件找不到。
这是一个已知的问题,在向导中的 Visual C 6.0 版。您可以通过传递此错误手动更改页眉、 源和.idl 文件中的 AddLongs 方法的参数,如下所述:

在中的参数列表类型:
[in,out] long *psaMyArray, [in,out] long *plSum
				
文件查看 选项卡上展开。在标头文件下双击 MyClass.h 头文件。在此文件的最后,您将找到您的方法的声明: " _mstchunk="true" _msthash="21499">FileView tab in the workspace, expand MyAtlDll files. " _mstchunk="true" _msthash="21499">在工作区中FileView选项卡上,展开 MyAtlDll 文件。在头文件下双击 MyClass.h 头文件。此文件的最末尾,您将找到您的方法的声明:
STDMETHOD(AddLongs)(/*[in,out]*/ long *psaMyArray, /*[in,out]*/ long *plSum);
				
将其更改为如下:
STDMETHOD(AddLongs)(/*[in,out]*/ SAFEARRAY **psaMyArray, /*[in,out]*/ long *plSum);
				
在源文件列表中,双击 MyClass.cpp。这里您可以找到您的方法的实现。更改以下行:
STDMETHODIMP CMyClass::AddLongs(long *psaMyArray, long *plSum)
				
为:
STDMETHODIMP CMyClass::AddLongs(SAFEARRAY **psaMyArray, long *plSum)
				
此外,在下的源文件列表中,双击.idl 文件 (扩展名为.idl 只有一个) 并找到下面一行:
[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);
				
  1. 双击一个实现文件,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;
       }
    					
  2. 通过按下F7键生成 DLL。
  3. 在 Visual Basic 中,将创建一个新的标准 EXE 项目。默认情况下,将创建 Form1。
  4. 将一个名为 command1 在 Form1 上的命令按钮。
  5. 项目菜单中选择引用,并添加 ActiveX 以前创建的 DLL (MyAtlDll) 的引用。
  6. 将下面的代码粘贴到 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
    					
  7. f5 键以运行您的项目。
  8. 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 知识库中相应的文章:
198017 错误: 意外的错误使用 ATL 接口向导来添加方法
使用 Visual Basic 从 Visual C++ Dll 的其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
188541 使用导出的 Dll 的信息: Visual Basic 要求
171583 如何填充 UDType 通过 Visual C++ 的 DLL 的 32 位 VBA 数组
194609 如何将 Udt 包含可变长度字符串的数组传递到 C/c + +
177218 如何从 VC + + DLL 或 OLE 服务器到 VB 中返回数组

猜你喜欢

转载自blog.csdn.net/sunnyxidian/article/details/8929177
今日推荐