Hello I am trying to create a simple C++ dll to be used in Excel.
Here is what I did:
.cpp file:
double _stdcall Test(double z)
{
return z+2.0;
}
.def file:
LIBRARY
EXPORTS
Test
In visual studio:
Project Properties > Configuration properties > Command > "Path/EXCEL.EXE"
Configuration manager > Platform > x64
In VBA:
Declare PtrSafe Function Test Lib _
"Path\MyDLL" (ByVal z As Double) As Double
But when I call Test(2) in Excel, it returns 2, and not 4. It seems like argument are seen as 0 alway (actually if I output the value of z in a file while calling the function, it is 2.122e-314).
Any inputs would be greatly appreciated.
Thanks
Edit 1:
If I change the argument and return value to int and in VBA to Long, Test(2) returns 3.
Anyone has an idea why the argument is 1 for int?
Apologies: Would have posted this as a comment but can't yet:
First: Your function may be exported as a C++ mangled name.
Use extern "C" to prevent name mangling. There are other ways to do this, but this is most direct. However if the Test function is successfully being called I'm not sure this is the issue.
Using extern "C" your C function would look like this.
extern "C"
double _stdcall Test(double z)
{
return z+2.0;
}
Second: Are you absolutely certain you are picking up the right DLL, not an old one which is missing your fixes? Try changing the function name to something more specific in your source and in the Excel VBA wrapper.
Third: If you are not running on a 64bit Excel system remove the PtrSafe declaration from your function specification in VB and recompile as 32-bit.
Fourth: Do not try to call your DLL directly from an Excel Cell. Wrap the function in VBA (example below) or use the CALL function.
The code below is an example how to wrap write a VBA cell function.
Public Function CellFunction_Test(x As Double) As Double
'' Tells excel to call this function only if the inputs change
'' and not on every recalculation
Application.Volatile False
'' Set an error handler, good practise for more complicated functions you might write later
On Error GoTo CellFunction_Test_EH:
'' Call into the DLL
CellFunction_Test = Test(x)
Exit Function
'' Error Handler
CellFunction_Test_EH:
CellFunction_Test = "Error - " & Err.Description
End Function
Related
First post here. I'm trying to follow the numerous tutorials out there for compiling a C++ dll as an Excel function and running it as an Excel function, but can't seem to get it to work. I'm running Windows 10 64-bit, Visual Studio Community 2017, and Excel 2016 64-bit.
I've followed the tutorials out there. I started a new project as a "Windows Desktop Wizard", and created an empty project as a DLL. This is my code below.
There are only these two files, in the "Source Files" folder under my project.
funct.cpp:
double __stdcall squareFxn(double x) {
return x * x;
}
This is my Def file, defFile.def:
LIBRARY "square"
EXPORTS
squareFxn
In the project properties, I made sure to set the Platform to x64 in the configuration manager, and added "defFile.def" without quotes to Linker/Input/Module Definition File.
It compiled fine. I put the following into VBA:
Declare PtrSafe Function squareFxn Lib "C:\MyPath\square.dll" (ByVal x As Double) As Double
When I debugged in Visual Studio, and ran in an Excel cell:
=squareFxn(5)
Visual studio is saying that x is an extremely small number (6.234E-310) and the function is returning zero.
The other problem I'm having is when I try to do this as a reference, with the appropriate changes (squareFxn(double &x) in funct.cpp and ByRef instead of ByVal in the VBA function declaration), I'm getting a read access error. I tried running Excel as an administrator, to no avail.
Does anybody know what's going on here? Any help would be greatly appreciated!!
You might need a wrapper function in VBA in order to cast the Variant (14 bytes) representing a cell to a Double (8 bytes). At least, this works in my situation: VSE2017, x64 DLL-build together with XL2016x64. For example:
in CPP:
extern "C" __declspec(dllexport) double __cdecl myCPPfunc(double x)
{
return some_double_expression;
}
in VBA:
Public Declare PtrSafe Function myVBAfunc Lib "complete_path_to_my.DLL" Alias "myCPPfunc" (ByVal x As Double) As Double
Function WS_myVBAfunc(ByVal x#)
Dim result#
result = myVBAfunc(x)
WS_myVBAfunc = result
End Function
You would also want to modify WS_myVBAfunc so that it accepts and returns an entire range of cells. This speeds up the whole process considerably.
I would like to call one of my User Defined Function from VBA.
My User Defined Function is declared in C++:
XLOPER12*WINAPI HelloWorld()noexcept{
THROWS();
static XLOPER12 res=[](){
static std::array<wchar_t,13>str={
11,'H','e','l','l','o',' ','W','o','r','l','d','\0'};
XLOPER12 tmp;
tmp.xltype=xltypeStr;
tmp.val.str=str.data();
return tmp;}();
return &res;}
This is a simplified version of a real function from the field which can either return a String or a double or even arrays. Of course here I am only returning a String but this limit the return type of my UDF to LPXLOPER12.
I can successfully register my function with xlfRegister specifying a pxTypeText of "U$". I can then call my UDF from Excel:
=HelloWorld()
And it works!
If I try to call my function from VBA as suggested here:
Sub macro_test()
Dim hw As Variant
hw = Application.Run("D:\Path\MyAddIn.xll!HelloWorld")
End Sub
I get an error message from Application.run:
Run-time error '1004': Application-defined or object-defined error
If I try to call my function from VBA as suggested here:
Private Declare PtrSafe Function HelloWorld Lib "C:\Path\MyAddIn.xll" () As Variant
Sub macro_test()
Dim hw As Variant
hw = HelloWorld()
End Sub
I get an empty result instead of "Hello World".
What am I doing wrong ?
Miscellaneous pieces of information:
Using Excel 2013
Using VS 2017 15.5
Using the first method (Application.Run), VBA does not call my function (I cannot step into my function with the debugger).
Using the second method, VBA calls my function (I can step into my function with the debugger).
Using the second method, when I add xlbitDLLFree to my xltype, function xlAutoFree12 is not called, which makes me think that somehow the return value is not understood properly by VBA.
If your XLL is loaded and its UDFs are registered so that=HelloWord() in a cell works then you should just be able to call it from VBA like this (unless there is a problem with parameterless string functions)
var=Application.run("HelloWorld")
You can also use Evaluate
var=Application.Evaluate("=HelloWorld()")
I tested my REVERSE.TEXT XLL function like this and it worked correctly.
Sub testing()
Dim var As Variant
var = Application.Run("REVERSE.TEXT", "Charles")
var = Application.Evaluate("=REVERSE.TEXT(""Charles"")")
End Sub
Reverse.Text is registered using UQQ$ (there are 2 parameters , the Text and the Number of characters)
I've scoured the web and stackoverflow for this answer but can't find anything. I have written a com object in C++ (for the fist time) that works when used in vbscript and through cocreateinstance in an executable file. So I decided to see if it would work in Excel VBA.
So I went into "References" and located my object there. Checked the box and started coding away. The following is the VBA code.
Function doCos(x As Double) As Double
Dim t As SimpleLib.IMath
Set t = New SimpleLib.IMath ' <- "Invalid use of New keyword" error here
doCos = t.Cos(x)
End Function
Intellisense recognizes my object in the Dim statement, but it does not appear when I use a Set statement. Obviously I am using a registered type library or else intellisense wouldn't work at all. Again, the com object can be used in vbscript or an executable, but for some reason can't be used, at least with the new keyword, in VBA.
Does anyone have an idea what may be wrong, or what may have to be added to the com object? Thanks.
One approach is to define a coclass in the IDL that includes the interface needed (IMath in my case). NOTE: That the [default] interface is hidden by default. So I simply defined interface IUnknown as the default. After compiling with MIDL a type library is generated which one should register with regtlibv12.exe.
I then included an additional IF statement in DllGetClassObject like if (rclsid == CLSID_Math) where CLSID_Math is corresponds to the CLSID defined in the file automatically generated from MIDL. All I did was copy and paste the body of the IF statement from if ( rclsid == IID_IMath ), updated the DLLRegisterServer and DLLUnRegisterServer functions, recompiled the project, and regsvr32.exe.
So the following works now.
Function docos(x As Double) As Double
Dim a As SimpleLib.IMath
Set a = New SimpleLib.Math
docos = a.Cos(x)
End Function
Thanks to Hans for the tip about the coclass. Learned something new and useful.
I have tired to create a dll file in code blocks and then use it in my powerpoint presentation. In the dll file as I have mentioned below the argument of the function contains (LPCSTR)
void DLL_EXPORT SomeFunction(const LPCSTR sometext)
{
MessageBoxA(0, sometext, " DLL Message ", MB_OK | MB_ICONINFORMATION);
}
In my powerpoint file I have
Declare Function DLL_EXPORT _
Lib "myfile.dll" _
Alias "SomeFunction" (???)
and when I run the file I get
Because I do not know how to define the argument of my function in the powerpoint. I mean this part of the code:
Alias "SomeFunction" (???)
Your C++ code uses the wrong calling convention. It should be:
__declspec(dllexport) void __stdcall SomeFunction(const char* sometext)
You can use macros like DLL_EXPORT, APIENTRY, LPCSTR etc. if you wish. However, at least until you know what the macros all mean, it is probably easier to be explicit, as I show above.
The correct VBA declaration for your function is:
Declare Sub SomeFunction Lib "myfile.dll" (ByVal sometext As String)
However, this alone is not enough because your function will be subject to name decoration and mangling. You can use dumpbin or Dependency Walker to view the actual name by which your function is exported. Then your VBA declaration would need to be modified like so:
Declare Sub SomeFunction Lib "myfile.dll" Alias "<DecoratedNameHere>" (ByVal sometext As String)
I am having some problems with a c++ dll i recently got from a friend.
I am trying to import the dll into an Excel VBA Add-in.
(I am using Excel 2010 32bit and the dll is compiled in Visual Studio 2008)
First of all i tried to import it but it failed with a runtime error 49.
I concluded that it possibly comes from a missing c++ declaration (__stdcal).
(i thought visual studio should do this automatically but okay)
Now i have a new dll with __stdcal in its declaration, but the methods are declared with a underscore ( _ ) at its beginning.
So when i try to declare the Sub, Excel tells me something like "invalid character"
is there a possibility to avoid this without altering the c++ files ?
'void _SaveFile(const char *FileName_);'
Declare Sub _SaveFile Lib "D:/DLL/TableController.dll" (ByRef FileName_ As String)
^
|
Thanks in advance
Here are some additional informations which maybe lead to a solution for my problems.
I got now some c++ code to debug my problem...
I still get the runtime error 49 but the dependency walker gives me now the right names of the methods( which is pretty awesome i think ;) )
I tried to use the same dll in an C# program.
As i expected it worked perfectly normal.
Here is my C# code
this is the import:
[DllImport("JLTable.dll",CallingConvention=CallingConvention.StdCall)]
internal static extern void JLReadFile(string _FileName);
[DllImport("JLTable.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern long JLGetRowCount();
annd this happens if i click a Button
TableRunner.JLReadFile(#"D:\file.tab");
long rows = TableRunner.JLGetRowCount();
MessageBox.Show(rows.ToString());
Here is my code in VBA
The declaration:
Declare Function JLReadFile Lib "D:/JLTable.dll" (ByRef FileName_ As String)
Declare Function JLGetRowCount Lib "D:/JLTable.dll" () As Long
And the call:
Sub Read()
Dim path As String
path = "D:/file.tab"
JLReadFile (path)
Dim count As Long
count = JLGetRowCount()
End Sub
And Here is also the c++ code i want to call.
Because of some reason my friend doesn’t want me to show what the code exactly does but it still behaves the same way with this code.
The .h file(the JLTable_API is something like #define JLTable_API __declspec(dllexport))
#define STDCALL
extern "C"
{
JLTABLE_API void STDCALL JLReadFile(const char *FileName_);
JLTABLE_API void STDCALL JLSaveFile(const char *FileName_);
}
The .c++ file
void STDCALL JLReadFile(const char *FileName_)
{
//log something to a file
}
long STDCALL JLGetRowCount()
{
//log something to a file
return 0;
}
I am very grateful for every hint you can give me
And as always
Thanks in advance
I finally have a solution.
i found a blog which showed me what i did wrong.
http://aandreasen.wordpress.com/2008/05/05/how-to-create-a-dll-for-ms-excel-vba-with-microsoft-visual-c-2008-command-line-tools/
As described you need the correct method name, to figure out which is the correct one i used the dependency walker.
This finally led me to this declaration
VBA:
Declare Sub JLReadFile Lib "D:/JLTable.dll" (ByRef FileName_ As String) Alias "_JLReadFile#4" (ByRef FileName_ As String)
C++:
void __declspec (dllexport) _stdcall JLReadFile(const char *FileName_);
From a similar test I ran, you need to use the "Alias" keyword. So that it would be written similar to: Declare Sub SaveFile Lib "D:/DLL/TableController.dll" Alias "_SaveFile" (ByRef FileName_ As String)
Additionally, I don't think you have to pass filename by reference, but I could be wrong. It's been a while since I've used VB.
More information on importing functions from a dynamic library, check this page out