首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Excel VBA脚本在调用用C编写的DDL函数时崩溃,该函数返回字符串BSTR

Excel VBA脚本在调用用C编写的DDL函数时崩溃,该函数返回字符串BSTR
EN

Stack Overflow用户
提问于 2016-03-15 17:44:25
回答 2查看 764关注 0票数 2

我使用Code::块来用C编写DLL,我打算在Win批处理脚本中使用它,但目前我正在使用Excel测试它。当VBA脚本运行DLL getVersion()函数Excel时崩溃。在网上搜索了几天:我没有找到合适的解决方案。

C代码是这样的

代码语言:javascript
运行
复制
#define MQTTPUB_VERSION "V3.1.1Test"
DLL_EXPORT BSTR __stdcall WINAPI getVersion(void)
{
  return MQTTPUB_VERSION ;
}

VBA代码如下所示

代码语言:javascript
运行
复制
Public Declare Function mqttPubMsg Lib "mqttPubMsg.dll" _
 (ByVal MQTT_ADDRESS As String, ByVal MQTT_CLIENTID As String, ByVal MQTT_TOPIC As String, ByVal MQTT_PAYLOAD As String) As Long
Public Declare Function getVersion Lib "mqttPubMsg.dll" () As String
Sub Test_DDL_mqttPubMsg()
'
' to test mqttPubMsg.DLL used in Visual Basic (VBA)
'
Dim DLLVersion As String * 35
Dim WorkDir As String
DLLVersion = Space(35)
WorkDir = ThisWorkbook.Path
ChDir WorkDir

If Dir(WorkDir & "\mqttPubMsg.dll", vbDirectory) = vbNullString Then
   MsgBox "DLL not found"
Else
  On Error GoTo DLLError
  DLLVersion = getVersion() 'Excel crashes on executing this statement
End If
MsgBox ("Version DDL: " & DLLVersion)
Exit Sub
DLLError:
  MsgBox ("DDL error")
End Sub

调用DLL和getVersion()函数的C程序工作正常。

导致此运行时错误的原因是什么以及如何解决它。

提前谢谢。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-03-16 16:20:14

我最近还没有进行Windows编程,也没有现成的开发环境可供实验,但以下是一些想法。

这里最可能的问题是getVersion()返回BSTR,当返回类型被声明为String时,调用VBA代码也(可能)期望返回。现在,BSTR应该在内存中的前面加上一个4字节的长度前缀,参见https://msdn.microsoft.com/en-us/library/windows/desktop/ms221069%28v=vs.85%29.aspxgetVersion()只返回一个指向"V“的指针,这是字符串的第一个字符,当VBA试图将前面的4个字节解释为"V”之前的长度时,这是一个灾难.

以下是我看到的一些可能的解决方案:

  • 按照上述参考文章中的建议,使用BSTR中的SysAllocString()函数构造getVersion()
  • 通过引用将DLLVersion字符串传递给getVersion(),并查看是否可以在getVersion()中填充它。请记住,您在这里处理Unicode。

出于好奇,还有几件事情要玩儿:

  • 验证一个DLL函数是否可以从VBA中实际调用;让getVersion()返回void,并查看调用该函数是否会使Excel崩溃。如果是的话,那么问题就更深了。
  • getVersion()返回一个整数,看看是否可以在您的VBA代码中得到它。
  • 使getVersion()将一个整数作为参数,从VBA传递一个整数,并查看C代码是否能够得到正确的值。
  • 在C代码中有一个足够大的全局char数组,将(Unicode!)版本字符串从数组中的偏移量4开始,并使用一个(我认为是小endian)整数填充前4个字节,该整数指示Unicode字符串的长度(不包括空终止符)。让getVersion()返回指向buf + 4的指针,其中buf是全局char数组。

对不起,我的记忆不是新鲜的,我不能提供更具体的建议,但希望这能有所帮助。

票数 1
EN

Stack Overflow用户

发布于 2016-03-18 21:50:45

以下解决方案适用于安装了Office 2013的64位系统。

C代码:

为了使C代码对我有用,我必须稍微修改声明,并使用.def文件来确保DLL导出正确的函数名。

代码语言:javascript
运行
复制
#define MQTTPUB_VERSION L"V3.1.1Test"  // notice the L - it casts better to BSTR
extern "C" BSTR __stdcall WINAPI getVersion(void)
{
  return MQTTPUB_VERSION;
}

VBA代码:

在VBA方面,我的方法是

  1. 通过将getVersion的返回类型声明为LongLong而不是string来获取目标字符串的地址。
  2. 使用相同数量的字符分配空字符串
  3. 将内存从源地址复制到目标字符串地址。 私有声明PtrSafe子CopyMemUC Lib "kernel32“Alias "RtlMoveMemory”_ (ByVal目的为LongLong,ByVal源为LongLong,ByVal长度为Long)私有声明PtrSafe函数wcslen Lib "msvcrt“_ (ByVal Source ),长私有声明PtrSafe函数_ getVersionAddress Lib CopyMemUC_ Alias "getVersion”_ ()为LongLong Function () Dim LongLong()为,为字符串,sLen sAddress = getVersionAddress sLen = wcslen(sAddress)‘长度(每个字符2个字节) sTemp = String( sLen,“")’分配内存CopyMemUC ByVal StrPtr( sTemp ),getVersionAddress,sLen*2 getVersion =sTemp End函数

测试如果一切正常的话:

在“立即”窗口中计算?getVersion将返回字符串,而不会崩溃:

32位系统上的,或VBA版本< 7:

请记住删除PtrSafe关键字并将所有LongLong事件更改为Long。或者,使用编译器检查-在这里可以找到一个例子:how to make VBA code compatible for office 2010 - 64 bit version and older office versions

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36018442

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档