VBA: how to set a dynamic library path for declare statement? - excel

I have a program in Excel's VBA that need to call some methods from a DLL, both are under my control in term of code development. I need to be able to select what DLL library (from various releases) should be used at runtime, but the Declare statement from VBA only allow for constants in the Lib path.
The objective is multiple :
Be able to select the library release at runtime from one Excel workbook
Ease the code maintenance because a lot of library methods are used, meaning a lot of Declare statement to be edited when library path is changed
Search dynamically at runtime the library from various predefined locations, to comply with deployment purposes. In dev environment, the library is in a dev directory, but for end-users, the library could be in 3 various locations: the workbook path, a specific directory on the computer, or a specific directory from a network drive.
I tried to declare the path in Public Const variable, but it doesn't works:
' This work
Private Declare PtrSafe Function addAmplifier Lib "path\to\lib.dll" (...) As Integer
' This don't work
Public Const libPath As String = "path\to\lib.dll"
Private Declare PtrSafe Function addAmplifier Lib libPath (...) As Integer
Is there a hijack way to use a variable in the Declare statement ? Or is there a way to create a VBA module able to edit the VBA code of the second module where the Declare statements are located in, and replace with regexp all occurrences of a String, save the VBA project then execute the module ? More formally : can VBA code rewrite itself at runtime ?

As suggested by L8n, the solution to change the working directory works fine.
So if the libraries are located in a sub-folder into the workbook path, we can do the following process each time a DLL native function is called :
Change the working directory to go into the sub-folder with ChDir()
Execute the native function
Go back to the parent folder with ChDir()
However, I suspect the Frequent call of ChDir() could slow down the VBA execution since it also seems to update the execution context with some hidden variables from the VBA runtime.

Related

Excel cannot find DLL

I have an excel VBA macro that calls a function in a C++ DLL.
The declaration is
Public Declare PtrSafe Function XXXX Lib "xxxx.dll" (...
This has worked for years, but on some new computers, it fails with the message
“xxxx.dll not found”.
The DLL is in the current folder. I checked with CurDir() and the current folder is correct.
I have a C++ test program that calls the DLL and it succeeds so there are no missing dependencies.
If I add the folder to the PATH environment variable, it will succeed.
According to all the documentation I found, Excel will always look in the current folder for DLLs.
Any ideas would be appreciated.
Thank you.

How to create a COM visible dll in F#

My aim is to create a COM Visible Type that can be imported into VBA (Excel) and consumed there.
The Excel Object Browser can see my class but not any public methods of the class and fails on the attempt at calling a public method.
My F# Code:
namespace DotNetLibrary
type public Class1() =
member public this.DotNetMethod (x:string) = "Hello" + x
In the AssemblyInfo.fs I also amend to [<assembly: ComVisible(true)>]
I run regasm with switches of /codebase /tlb and a .tlb file is generated.
VBA finds my library in the references browser but does not pick up the DotNetMethod defined on Class1 above.
I have tried to follow the C# guidance for this topic in getting to where I am at the moment but I'm not getting to the finish line.
Run-Time Errors
The reason that VBA could not run the method succesfully is because I used the 32-bit version of the regasm utility instead of the 64-bit version to register the type library for COM. Given that I am using 64-bit Excel it needed 64-bit COM type libraries.
Compile-Time Issues
The reason that VBA could not identify the methods in the IDE could be related to the above, but is also likely to be due to the interface issues raised by
#HansPassant in his comments above.

VBA: my program is throwing compile error on Format

So I'm working with my program. It works and runs, so I copied it and made a second version which I can add extra features to while still having a working program. I don't see any problems with my new code, so I prepare to step thru it. Suddenly things which work perfectly fine in my other version are throwing compile errors. Specifics below.
Dim elapsedTime As String
Dim startTime As Date
startTime = Now()
'code
elapsedTime = Format(Now() - startTime, "h:m:s")
I checked again and again, but this exact same code works fine in my other file. In this file, it highlights "Format" and throws the following Compile Error.
Does anyone know why excel would be getting so upset at this? If there are compile errors, should I even be looking at the places it highlights at all?
You have something named Format somewhere that's in-scope (i.e. accessible from where that Format function is being invoked), and that Format thing is shadowing the Format function you mean to call.
As YowE3K suggested if you right-click on that Format call and select "Definition" (or Shift+F2), that will take you to the shadowing declaration.
From there you have several options:
Rename the Format identifier [and possibly break a ton of things in your code]
Fully-qualify that Format call, i.e. VBA.Strings.Format, or just Strings.Format, or even VBA.Format.
My suggestion would be to fully-qualify that call, then try to compile again, and fully-qualify any/all calls that cause that same compile error. Then rename whatever it is that is named Format, so that it no longer shadows the function from the VBA standard library.
If you're using Rubberduck (an open-source VBIDE add-in project I manage), you can easily locate all references of the shadowing identifier:
The Rubberduck toolbar tells you when Format is referring to the correct function:
And you can easily locate all references to that as well, qualified or not - so it's easy to fully-qualify them wherever they are.
Rubberduck also makes it easy to refactor/rename the function, in a way that search/replace fails:
Coincidentally, a new code inspection is currently in works, specifically to locate shadowing identifiers like this.
As for the ByRef argument type mismatch, it seems your screenshot doesn't match with the code you posted, so anything is possible, but unless eqn is assigned in the body of that MakeEqn function (it should be returned, not assigned), then there's no need to pass it ByRef (implicitly or explicitly) - it can be passed ByVal (Rubberduck gives you an inspection result for that - and another for the implicit return type, and another for the implicit Public access modifier on both procedures, and several others for other issues in the code you posted).

How to release inprocess COM Server object from Excel VBA

How do you force Excel (2007) VBA to release references to a COM server object?
I have written an inprocess (Single instance DLL) COM Server in Visual Foxpro 9 SP2 which is instantiated from Excel 2007 VBA code on my development machine. Excel seems to be holding a reference to the COM object/dll even though I set it = Nothing. This prevents me from rebuilding the DLL due to a "File access is denied TestCOM.dll" message until I quit Excel which is a pain everytime I want to make a change and test it.
I have boiled the code down to a very simple test setup:
The VFP9 project (TestCOM) has just one .prg file with the following contents
DEFINE CLASS TestClass As Session OLEPUBLIC
ENDDEFINE
The VBA Code is as follows:
Sub Test()
Set objTest = CreateObject("TestCOM.TestClass")
Set objTest = Nothing
End Sub
I have tried removing the reference to the COM server library in the VBA project but this does not make any difference.
I have tried with and without DIMing the object variables and it makes no difference.
I have tried creating a new VFP DLL project but the problem persists.
If I build the VFP application/dll as an INPROCESS/DLL and run the VBA code I get this problem but if I build it as an OUTOFPROCESS/EXE and run the VBA code I do NOT get this problem.
I found a very similar problem in COM Object Cleanup except that my COM Server is written in Visual Foxpro 9 SP2 whereas that relates to C# and the OP has not explained in detail how they resolved the problem so I don't know how to get around it; if that is even possible.
The procedure used to instantiate your COM class from the code in your DLL is that Excel calls the COM library layer to find your implementation using either the ProgID or the ClassID. When you have an inproc server this means it finds a path to your DLL and uses LoadLibrary to load it into your client process and then creates the class factory and calls methods in the DLL. So the end result is that Excel calls LoadLibrary on your DLL and this locks the file until Excel calls FreeLibrary on the handle.
Using COM interfaces you don't have control of this. You call CoCreateInstance() (or from VBA you create the object with New or CreateObject which calls this Win32 API underneath). The implementation of this handles the LoadLibrary and everything else until you get handed an interface pointer to work with. Some applications will periodically call CoFreeUnusedLibraries() to attempt to release loaded COM dlls that are not currently in use. The default class factory implementation maintains a counter of objects created that can be used to determine if a DLL is in use or not - but this is not always reliable as COM class writers may not obey the rules. Exiting Excel obviously releases the lock on the file.
When you create your COM class as an out-of-process server - it lives in a separate executable or DLL whose lifetime is managed differently. Excel no longer holds a lock on the DLL and releasing the COM instance may well allow the hosting process to exit.
You can convert a DLL to be used as a localserver (out-of-process) by arranging to have it hosted by DllHost. If you use the OleView utility and find your classes ProgId then you can enable hosting in a surrogate process (dllhost). It's been a while since I did that but there should be information on the web about using surrogate hosting. Obviously hosting a COM object out-of-process makes everything slower and introduces the potential for various marshalling issues. Provided you keep oleautomation compatible interfaces it should be fine.
Adding a shorter answer ....
Releasing the DLL is knotty problem and one familiar to developers of in process COM components for use in Excel.
There are two conditions that need to be satisfied
1) Do not use an early binding library references (Tools->Reference) , use late binding instead. Early binding tools reference will hold a lock.
2) Call CoFreeUnusedLibraries to unload COM servers which no longer have clients.
From your sample code you are already late binding but please check your references. Whilst point 2) is referred to in payyhoyts answer no code is given.
Here is a copy and pasteable declaration
Private Declare Sub CoFreeUnusedLibraries Lib "ole32.dll" ()

Export function from a DLL - Use DEF file or dllexport?

Module-definition (.def) files provide
the linker with information about
exports, attributes, and other
information about the program to be
linked. A .def file is most useful
when building a DLL. Because there are
linker options that can be used
instead of module-definition
statements, .def files are generally
not necessary. You can also use
__declspec(dllexport) as a way to specify exported functions.
http://msdn.microsoft.com/en-us/library/28d6s79h%28VS.80%29.aspx
I was wondering, should we prefer .def way? or dllexport way?
Module-definition (.def) files provide us with more flexibility to define how data going to be exported.
For example, function exported can be anonymous (identified by ordinal) which prevent people without the declaration information from using it.
It can also ddo function forwarding/redirection as stated below :
http://msdn.microsoft.com/en-us/library/hyx1zcd3(v=VS.80).aspx
If you plan on users using your DLL in Visual Basic (not VB.NET), then you may want to opt for using the .DEF file. Visual Basic requires that functions use the stdcall calling convention, and exported stdcall function names are decorated with underscores (_). You can override this function name decoration by explicitly specifying the function name in the .DEF file.
For more information: http://msdn.microsoft.com/en-us/library/dt232c9t%28VS.90%29.aspx

Resources