Otro tutorial de los viejitos, de casi veinte años atrás, así que sirve como documento histórico :D
No se puede enviar todo un vector desde Visual Basic a una Dll, lo que se puede hacer al operar con arrays es llamar a la función en la Dll dentro de un bucle que recorra el array en el programa de Visual Basic, pero si se desea que nuestra aplicación sea más rápida es mejor dejarle el manejo de los bucles a C++. En este caso se le enviaría a la Dll la dirección de memoria donde está el array (es decir: la dirección de su primer elemento).
Un excelente tutorial sobre los punteros en C++ está akí:
Pero para este caso no se necesitan estas funciones, basta pasarle a la Dll el primer elemento del array tal y como lo explican en esta otra web: https://recursosvisualbasic.com.ar/rvb/htm/tutoriales/interaccion-visual-basic-c++.htm
Hay que tener cuidado con un par de detalles que no mencionan estas páginas y que explicaré más adelante.
En resumen, lo que se quiere es que la Dll programada en C++ reciba y devuelva un puntero.
Para una Dll que simplemente recibe un puntero y devuelve un dato el código es:
#include <windows.h> #include "puntero.h" BOOL WINAPI DllEntryPoint(HINSTANCE, DWORD, LPVOID) { return TRUE; } extern "C" long __declspec (dllexport) WINAPI sumatotal (long *puntero, int valor) { long sumatoria; sumatoria = 0; for (int i = 0; i < valor; i++) sumatoria= sumatoria+ puntero[i]; return sumatoria; } |
#ifndef _PUNTERO_H #define _PUNTERO_H #ifdef BUILD_DLL // en la construcción de la librería #define EXPORT __declspec(dllexport) #else // en la construcción del ejecutable #define EXPORT __declspec(dllimport) #endif #ifdef __cplusplus /* if in a C++ program */ extern "C" #endif long __declspec (dllexport) WINAPI sumatotal (long *puntero, int valor); #endif |
Private Sub Form_Load() Dim c As Long, i As Integer Dim mat() As Long ReDim mat(0 To 5) For i = 0 To UBound(mat) mat(i) = 2 * i Next c = sumatotal(mat(0), UBound(mat) + 1) Form1.Caption = c End Sub |
Public Declare Function sumatotal Lib "puntero.dll" (ByRef puntero As Long, ByVal valor As Integer) As Long |
En este ejemplo se nota la diferencia en el manejo de los índices entre Visual Basic y C++. Los índices del array "mat" son 0, 1, 2, 3, 4, 5. Si se hubiera creado en C++, para que tuviera seis elementos, se habría definido como mat[6] siendo sus índices también 0, 1, 2, 3, 4, y 5 ya que en C++ el número entre corchetes define la cantidad de elementos del array. Debido a la costumbre programé el bucle para tratar al array como si hubiera sido creado en C++, cuando en realidad es un array de Visual Basic en el cual el 5 representa el índice mayor mas no el número de elementos, por ello se le suma uno al segundo parámetro al enviarlo a la función programada en C++.
Y este es el resultado:

Ahora, para crear una Dll que reciba y devuelva punteros es necesario hacer unos pequeños cambios:
El archivo se llama "puntero2.cpp" y su código fuente es:
#include <windows.h> #include "puntero2.h" BOOL WINAPI DllEntryPoint(HINSTANCE, DWORD, LPVOID) { return TRUE; } extern "C" long* __declspec (dllexport) WINAPI punt (long *puntero, int valor) { for (int j=1; j<=valor+1; j++) puntero[j]=puntero[j]*2-15; return(puntero); } |
"valor" es el tamaño del array cuya dirección está recibiendo la función "punt" la cual, después de ejecutar el bucle, devuelve un puntero.
Para hacer la Dll también se necesita el archivo "puntero2.h" cuyo código es:
#ifndef _PUNTERO2_H #define _PUNTERO2_H #ifdef BUILD_DLL // en la construcción de la librería #define EXPORT __declspec(dllexport) #else // en la construcción del ejecutable #define EXPORT __declspec(dllimport) #endif #ifdef __cplusplus /* if in a C++ program */ extern "C" #endif long* __declspec (dllexport) WINAPI punt (long *puntero, int valor); #endif |
"Long" es el tipo de datos para enteros largos (de 64 bits) y lo manejan tanto C++ como Visual Basic. Lo malo es que cuando quise enviar punteros que apuntaban a datos de punto flotante el compilador del Visual Basic me botaba el error 16 en tiempo de ejecución: "Expresión demasiado compleja". Así llegué a descubrir que Visual Basic y Borland C++ sólo pueden intercambiar punteros que apunten a enteros, ya sean Long o Byte. Al trabajar con arrays no recomiendo datos del tipo Integer, al operar me salían resultados erróneos.
Después de compilar y copiar la Dll "puntero2.dll" a la carpeta system32 la probé el Visual Basic con el siguiente código, en un proyecto exe estándar:
Option Explicit Private Sub Form_Load() Dim mat(-1 To 8) As Long Dim i As Integer For i = 0 To UBound(mat) mat(i) = i Next mat(-1) = punt(mat(-1), UBound(mat)) For i = 0 To UBound(mat) Debug.Print mat(i) Next End Sub |
Declare Function punt Lib "puntero2.dll" (ByRef puntero As Long, ByVal valor As Integer) As Long |

Ya funciona :)
Ahora la gran pregunta: ¿por qué definí el array "mat" desde -1 y no desde 0? Visual Basic tiene la ventaja de poder definir arrays con índices negativos, cosa que no se puede en C++. El índice -1 terminará conteniendo la dirección de memoria del array, un valor que no interesa a la hora de operar y con el que es mejor no trabajar. El índice -1 en Visual Basic pasará a ser el índice 0 en C++, así el índice 0 para Visual Basic será el índice 1 en C++, etc. Para Visual Basic los índices van de -1 a 8 en este ejemplo, para C++ van de 0 a 9 (la cantidad de elementos del array es 10 en total), donde el valor en el índice 0 no interesa, y no se debe operar con él pues al devolver el puntero al Visual Basic éste "chanca" el primer elemento del array con su dirección de memoria.
Al modificar el bucle del programita en Visual Basic que escribe en la ventana inmediato para que vaya desde -1 se podrá ver el valor de mat(-1):

He aquí a los diez elementos del array
Una manera de poder operar con valores de punto flotante es convirtiendo estos valores enteros multiplicándolos por 10^n, donde "n" es la cantidad de decimales con la que vamos a trabajar.
Por ejemplo, queremos una Dll que calcule las raíces cuadradas de algunos números dentro de un vector:
La nueva Dll será "puntero3.dll" y el código fuente de puntero3.h es:
#ifndef _PUNTERO3_H #define _PUNTERO3_H #ifdef BUILD_DLL // en la construcción de la librería #define EXPORT __declspec(dllexport) #else // en la construcción del ejecutable #define EXPORT __declspec(dllimport) #endif #ifdef __cplusplus /* if in a C++ program */ extern "C" #endif long* __declspec (dllexport) WINAPI cuadrado (long *puntero, int valor, long d); #endif |
#include <windows.h> #include <math.h> #include "puntero3.h" BOOL WINAPI DllEntryPoint(HINSTANCE, DWORD, LPVOID) { return TRUE; } extern "C" long* __declspec (dllexport) WINAPI cuadrado (long *puntero, int valor, long d) { double *c; c= new double[valor+1]; for (int j=1; j<=valor+1; j++){ c[j-1]= double(puntero[j])/d; c[j-1]=pow(c[j-1], 0.5); puntero[j]= long(c[j-1]*d); } return(puntero); } |
Y ahora el código fuente del programa en Visual Basic, primero el módulo .bas (todo es una sola línea):
Declare Function cuadrado Lib "puntero3.dll" (ByRef puntero As Long, ByVal valor As Integer, ByVal d As Long) As Long |
Private Sub Form_Load() Dim mat() As Double Dim matl() As Long Dim divisor As Long ReDim mat(-1 To 4) ReDim matl(-1 To 4) divisor = 100000000 For i = 0 To UBound(mat) mat(i) = (i + 1) / 25 mat(i) = mat(i) * divisor matl(i) = CLng(mat(i)) Debug.Print mat(i) / divisor Next matl(-1) = cuadrado(matl(-1), UBound(matl), divisor) For i = 0 To UBound(mat) mat(i) = matl(i) / divisor Debug.Print mat(i) Next End Sub |

Los cinco primeros números son los datos originales, y los otros cinco sus raíces cuadradas hasta ocho decimales de aproximación.
Éste es un mal ejemplo sobre cómo usar una Dll para hacer cálculos numéricos ya que sería mejor sacar las raíces cuadradas directamente con el Visual Basic y usando solamente el array "mat" declarado como Double, pero deja entender cómo se debe manejar los datos para poder operar con números de punto flotante a partir de enteros. Si se trata de números muy grandes y con muchos decimales se pueden poner la parte entera y la decimal en variables separadas, dividir luego en la Dll la parte decimal entre 10^n y sumar ambas variables. Una Dll es útil cuando se requieren cálculos complejos o comandos que el programa principal realizaría más lentamente, con arrays mucho más largos (se declaran los punteros como "far" tanto en el archivo .cpp y el .h) y todas esas cosillas para las que el Visual Basic se vuelve lentecillo (considerando el hardware de inicios de los 2000s)