使用Excel VBA中的C ++ DLL位置的无法解释的行为

我最初发布这个作为一个答案与文件位置在Excel / Fortran DLL连接故障,因为问题似乎相似。 但是由于这是一个涉及fortran的老问题,我决定发布一个新的问题,因为我认为更多的人会在c ++上有这方面的经验(对不起,第一次发布在这里,请耐心等待)。

从VBA调用c ++ dll引用第二个c + + dll的问题出现。 我这样做的原因是因为我正在使用不同的DLL项目共享一些相似的function,所以我想有一个单一的实用程序的DLL,他们都可以参考。 我的设置是:

Visual Studio Express 2013 V12.0

Excel 2010 V14.0(64位),VBA V7.0

当只有一个dll项目时,一切正常。 在其最简单的forms中,我有一个名为“a”的解决scheme,其中包含一个名为“a”的项目,其中包含以下两个文件:

// a.cpp void __stdcall a() {} // a.def EXPORTS a 

我在项目属性页面的链接器input下指定a.def作为模块定义文件,然后编译生成a.dll文件。 在VBA中我有:

 Declare PtrSafe Sub a Lib "C:\ ... \a.dll" () Sub test() a End Sub 

a.dll是在“C:\ … \ Visual Studio 2013 \ Projects \ a \ x64 \ Debug \”中创build的,并且testingSubinput正确。 然后我添加到解决scheme第二个dll项目名为“b”包含以下三个文件:

 // bh void __stdcall b(); // b.cpp #include "bh" void __stdcall b() {} // b.def EXPORTS b 

我将a.cpp定义更改为:

 // a.cpp #include "bh" void __stdcall a() { b(); } 

在“a”项目属性中,我指定了“附加包含目录”下的bhpath,并在“通用属性”下添加了对b的引用。 一切都编译好。 回到VBA我补充说:

 Declare PtrSafe Sub b Lib "C:\ ... \b.dll" () Sub test1() a b End Sub 

test1生成运行时错误“53”:尽pipe事实上我可以看到它坐在原来的位置,但没有find“C:\ … \ a.dll”文件。 奇怪的是,如果我改变a和b的调用顺序,那么它的工作:

 Sub test2() b a End Sub 

所以VBA可以findb,似乎足以促使它find一个。

无论如何,解决scheme是将a.dll和b.dll的副本放在Excel / VBA当前工作目录CurDir()中。 奇怪的是,我实际上并不需要将Declare语句中的Libpath更改为CurDir(尽pipe我可以)。 我可以告诉它是真正的原始VS文件夹中运行的dll,因为如果我对a.cpp和b.cpp进行可检测的更改并重新编译而不将新的dll复制到CurDir,VBA肯定会运行新的dll在VS文件夹中不是CurDir中的旧副本。 不知何故,CurDir中存在的副本足以提示VBA在VS文件夹中查找指定的dll。 也许别人可以对这种行为有所了解

虽然对我来说很神秘,但是由于我正在开发DLL,并不是每次编译后都不要复制,所以不是很不方便。 另一种解决scheme是将Excel工作簿保存在输出dll所在的VS文件夹中,以便CurDir自动包含它们。 上面的所有内容保持VS是否在Debug或Release模式下编译。 我从http://communities.bentley.com/products/microstation/f/273/t/12979.aspx获得了帮助,但是我想了解这个看起来不一致的行为,而不是仅仅有一个解决方法。 谢谢

作为开发者,我认为我们大多数人在某个时候经历过这种奇怪的错误。 debugging这类问题的一个好工具是系统内部工具包的进程监视器 。

它可以捕获进程的文件加载/卸载活动,从日志中可以看到进程是如何searchdll的。 所以你得到这两种情况的两个日志,并比较他们发现什么是错的; 这将不是一件容易的事情,因为这个日志文件可能是巨大的。 我曾经使用这个工具来获取客户端的进程监控日志,以便debugging我们无法重现的错误,并帮助我找出其中的一些错误。

由WindowsfindDLL的searchpath如下: https : //msdn.microsoft.com/en-us/library/windows/desktop/ms682586%28v=vs.85%29.aspx 。 在检查特殊情况并加载DLL之后,它是

 # The directory from which the application loaded. # The system directory. Use the GetSystemDirectory function to get the path of this directory. # The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched. # The Windows directory. Use the GetWindowsDirectory function to get the path of this directory. # The current directory. # The directories that are listed in the PATH environment variable. 

我说“一个searchpath”,因为它取决于哪个版本的窗口,以及如何设置窗口。

请注意,“一个其他DLL已经被加载的目录”不是searchpath的一部分,而且“已经加载的DLL”在它开始search之前就已经出现了。

在我写这篇文章的时候,Visual Studio文档页面(甚至是当前的VS2015页面)要短得多,不太完整,而且只能重复旧版Windows的旧信息。