Question

I originally posted this as an answer to Trouble with file location in excel/fortran dll connection since the problem seems similar. But since that is an older question referencing fortran I decided to post as a new question since I think more people will have experience with this in c++ (sorry, first time posting on SO so please bear with me).

The problem arises when calling from VBA a c++ dll which references a second c++ dll. The reason I am doing this is because I am working on different dll projects which share some similar functionality, so I want to have a single utilities dll which they can all reference. My set-up is:

Visual Studio Express 2013 V12.0

Excel 2010 V14.0 (64-bit), VBA V7.0

When there is just one dll project everything works fine as usual. In its most simplified form, I have a Solution called 'a' containing a Project called 'a' containing the following two files:

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

// a.def
EXPORTS
    a

I specify a.def as the Module Definition File under Linker Input in the Project Property pages and then compile to produce the a.dll file. Over in VBA I have:

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

Sub test()
  a
End Sub

a.dll is sitting where it was created in "C:\ ... \Visual Studio 2013\Projects\a\x64\Debug\" and the test Sub enters it fine. Then I add to the Solution a second dll project called 'b' containing the following three files:

// b.h
void __stdcall b();

// b.cpp
#include "b.h"
void __stdcall b() {}

// b.def
EXPORTS
    b

I change the a.cpp definition to:

// a.cpp
#include "b.h"

void __stdcall a() {
  b();
}

In the 'a' Project properties I specify the path to b.h under Additional Include Directories and I add a Reference to b under Common Properties. Everything compiles fine. Back in VBA I add:

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

Sub test1()
  a
  b
End Sub

test1 produces the run-time error '53': File not found "C:\ ... \a.dll" despite the fact that I can see it sitting there in the same location it was before. Bizarrely though, if I change the calling order of a and b in the sub then it does work:

Sub test2()
  b
  a
End Sub

So VBA can find b and somehow it seems that's enough to prompt it to find a again.

Anyway, the solution was to place copies of a.dll and b.dll in the Excel/VBA current working directory CurDir(). Strangely, I did not actually have to change the Lib path in the Declare statement to the CurDir (although I could). And I can tell it is really the dlls in the original VS folder which are running because if I make detectable changes to a.cpp and b.cpp and recompile without re-copying the new dlls to the CurDir, VBA definitely runs the new dlls in the VS folder not the old copies in the CurDir. Somehow the mere presence of the copies in the CurDir is enough to prompt VBA to find the specified dlls in the VS folder. Maybe someone else can throw some light on this behavior ...

Although mystifying to me, it was not inconvenient since I am developing the dlls and did not have to keep copying them after every compilation. Another solution was to save the Excel Workbook in the VS folder where the output dlls are so that the CurDir automatically contains them. Everything above holds whether VS complied in Debug or Release mode. I got help from http://communities.bentley.com/products/microstation/f/273/t/12979.aspx, however I would like to understand this seemingly inconsistent behaviour rather than just have a workaround. Thanks

No correct solution

OTHER TIPS

As the developers, I think most of us experienced this kind weird bugs at some time. One great tool to debug this kind problem is process monitor from the system internal kit.

It can capture the file load/unload activities for the process, from its log, you can see how the process is searching for the dlls. So you get two logs for the both case and compare them to find what is wrong; it won't be an easy task because that log file could be huge. I used to use this tool to get the process monitoring logs for the clients for debugging the bugs we can't not reproduce, and it helped me to figure out some of them.

A search path by which DLL's are found by Windows is given here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682586%28v=vs.85%29.aspx. After checking for special cases and loaded DLL's, it is

# 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. 

I say "a search path" because it does depend a bit on which version of windows, and how windows is set up.

Note that "a directory from which some other DLL has already been loaded" is not part of the search path, and that "dll that is already loaded" comes before it even starts searching.

As I write this, the Visual Studio Documentation pages (even the current VS2015 pages) are much shorter, much less complete, and out of date, repeating only old information about old versions of Windows.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top