从 VBA 访问 C++ 中的嵌套结构
Accessing a nested structure in C++ from VBA
问题
我有一个用 C++ 编写并使用 Visual Studio 2010 编译为 DLL 的库。DLL 有多个导出函数。使用 Declare Function
.
从 Excel 访问导出的函数
我正在尝试在程序中实现一项新功能,该功能需要 C++ 部分中的嵌套结构,然后可以从 VBA 访问这些结构。 C++ 代码如下所示。
第一个结构
struct Parameter {
double value;
char* label;
char* description;
char* units;
};
二级结构
此结构用于构建另一个结构如下:
struct Output {
Parameter field_1;
Parameter field_2;
Parameter field_3;
};
我正在考虑从 VBA 访问结构的几种方法。其中之一来自这样的 void
函数。
void Function1(Output* output_function1);
另一种是returnsOutput
结构的函数,比如这个
Output Function2();
以上两个函数的内部结构此时无关紧要。我已验证这两种实现都按 C++ 代码中的预期工作。
问题
我无法使用 Function1
或 Function2
从 VBA 访问这两个结构。
我在 VBA 中声明了两个自定义类型。
Type Parameter
Value as Double
Label as String
Description as String
Units as String
End Type
Type Output
Field1 as Parameter
Field2 as Parameter
Field3 as Parameter
End Type
对于Function1
我声明导出函数如下。
Declare Sub Function1 Lib "C:\Path\to\library.dll" (ByRef OutputStruct as Output)
对于 Function2
,以下。
Declare Sub Function2 Lib "C:\Path\to\library.dll" () as Output
Function2
崩溃 Excel 和 Function1
给我错误 vba byref argument type mismatch
.
我在这里做错了什么?正确的做法是什么?
您不能将 VBA Types
作为参数传递。 (我知道。我知道。VBA.....)
您需要做的是传递 in/out 各个属性。像这样的东西(我不懂 C++,所以请耐心等待。)
void Function1(Output* Field1, Output* Field2, Output* Field3)
然后在VBA这边
Declare PtrSafe Sub Function1 Lib "C:\Path\to\library.dll" (ByRef Field1 As LongPtr, ByRef Field2 As LongPtr, ByRef Field3 As LongPtr)
或任何合适的数据类型。 MSDN doc on the Declare statement 也可能有所帮助。
尝试调用 DLL 时不要嵌套 Type
。而是创建一个包含所有成员的复合 Type
:
Type Output
Value1 as Double
Label1 as String
Description1 as String
Units1 as String
Value2 as Double
Label2 as String
Description2 as String
Units2 as String
Value3 as Double
Label3 as String
Description3 as String
Units3 as String
End Type
不幸的是,我不认为你可以按照你想要的方式将 String
放入 Type
并使其与 C 兼容。你只需要将 Long
在那里保存指针并使用访问函数在 Long
和 String
之间进行转换
如果将结构的定义包含在 #pragma pack(4)
... #pragma pack()
指令中,Function1 将正常工作。
https://msdn.microsoft.com/en-US/en-en/library/office/bb687915.aspx
C:
#pragma pack(4)
struct Parameter{
double value;
char* label;
char* description;
char* units;
};
struct Output{
struct Parameter field_1;
struct Parameter field_2;
struct Parameter field_3;
};
#pragma pack()
static char Buffer [4096];
static void Append_Field_Values (struct Parameter* field)
{
static char b [1024];
sprintf (b, "%f %s %s %s\n", field -> value, field ->label, field -> description, field -> units);
strcat (Buffer, b);
}
void _stdcall Function1(struct Output* output_function1)
{
Buffer [0] = 0;
Append_Field_Values (&output_function1 -> field_1);
Append_Field_Values (&output_function1 -> field_2);
Append_Field_Values (&output_function1 -> field_3);
MessageBox (0, Buffer, "FUNCTION1", 0);
}
VB:
Type Parameter
Value As Double
Label As String
Description As String
Units As String
End Type
Type Output
Field1 As Parameter
Field2 As Parameter
Field3 As Parameter
End Type
Declare Sub Function1 Lib "E:\Serge\Whosebug\NestedStructures\Debug\NestedStructures.dll" (ByRef OutputStruct As Output)
Dim Out1 As Output
Sub Test1()
Out1.Field1.Value = 3.14
Out1.Field1.Label = "ARINC 429"
Out1.Field2.Units = "Miles and Inches"
Out1.Field3.Description = "Read MSDN"
Call Function1(Out1)
End Sub
问题
我有一个用 C++ 编写并使用 Visual Studio 2010 编译为 DLL 的库。DLL 有多个导出函数。使用 Declare Function
.
我正在尝试在程序中实现一项新功能,该功能需要 C++ 部分中的嵌套结构,然后可以从 VBA 访问这些结构。 C++ 代码如下所示。
第一个结构
struct Parameter {
double value;
char* label;
char* description;
char* units;
};
二级结构
此结构用于构建另一个结构如下:
struct Output {
Parameter field_1;
Parameter field_2;
Parameter field_3;
};
我正在考虑从 VBA 访问结构的几种方法。其中之一来自这样的 void
函数。
void Function1(Output* output_function1);
另一种是returnsOutput
结构的函数,比如这个
Output Function2();
以上两个函数的内部结构此时无关紧要。我已验证这两种实现都按 C++ 代码中的预期工作。
问题
我无法使用 Function1
或 Function2
从 VBA 访问这两个结构。
我在 VBA 中声明了两个自定义类型。
Type Parameter
Value as Double
Label as String
Description as String
Units as String
End Type
Type Output
Field1 as Parameter
Field2 as Parameter
Field3 as Parameter
End Type
对于Function1
我声明导出函数如下。
Declare Sub Function1 Lib "C:\Path\to\library.dll" (ByRef OutputStruct as Output)
对于 Function2
,以下。
Declare Sub Function2 Lib "C:\Path\to\library.dll" () as Output
Function2
崩溃 Excel 和 Function1
给我错误 vba byref argument type mismatch
.
我在这里做错了什么?正确的做法是什么?
您不能将 VBA Types
作为参数传递。 (我知道。我知道。VBA.....)
您需要做的是传递 in/out 各个属性。像这样的东西(我不懂 C++,所以请耐心等待。)
void Function1(Output* Field1, Output* Field2, Output* Field3)
然后在VBA这边
Declare PtrSafe Sub Function1 Lib "C:\Path\to\library.dll" (ByRef Field1 As LongPtr, ByRef Field2 As LongPtr, ByRef Field3 As LongPtr)
或任何合适的数据类型。 MSDN doc on the Declare statement 也可能有所帮助。
尝试调用 DLL 时不要嵌套 Type
。而是创建一个包含所有成员的复合 Type
:
Type Output
Value1 as Double
Label1 as String
Description1 as String
Units1 as String
Value2 as Double
Label2 as String
Description2 as String
Units2 as String
Value3 as Double
Label3 as String
Description3 as String
Units3 as String
End Type
不幸的是,我不认为你可以按照你想要的方式将 String
放入 Type
并使其与 C 兼容。你只需要将 Long
在那里保存指针并使用访问函数在 Long
和 String
如果将结构的定义包含在 #pragma pack(4)
... #pragma pack()
指令中,Function1 将正常工作。
https://msdn.microsoft.com/en-US/en-en/library/office/bb687915.aspx
C:
#pragma pack(4)
struct Parameter{
double value;
char* label;
char* description;
char* units;
};
struct Output{
struct Parameter field_1;
struct Parameter field_2;
struct Parameter field_3;
};
#pragma pack()
static char Buffer [4096];
static void Append_Field_Values (struct Parameter* field)
{
static char b [1024];
sprintf (b, "%f %s %s %s\n", field -> value, field ->label, field -> description, field -> units);
strcat (Buffer, b);
}
void _stdcall Function1(struct Output* output_function1)
{
Buffer [0] = 0;
Append_Field_Values (&output_function1 -> field_1);
Append_Field_Values (&output_function1 -> field_2);
Append_Field_Values (&output_function1 -> field_3);
MessageBox (0, Buffer, "FUNCTION1", 0);
}
VB:
Type Parameter
Value As Double
Label As String
Description As String
Units As String
End Type
Type Output
Field1 As Parameter
Field2 As Parameter
Field3 As Parameter
End Type
Declare Sub Function1 Lib "E:\Serge\Whosebug\NestedStructures\Debug\NestedStructures.dll" (ByRef OutputStruct As Output)
Dim Out1 As Output
Sub Test1()
Out1.Field1.Value = 3.14
Out1.Field1.Label = "ARINC 429"
Out1.Field2.Units = "Miles and Inches"
Out1.Field3.Description = "Read MSDN"
Call Function1(Out1)
End Sub