Use an unmanaged DLL (C)

385 views
Skip to first unread message

Fonseca

unread,
Mar 4, 2011, 1:52:35 PM3/4/11
to Excel-DNA
Hi,

I have a dll, that was automatically created by the program
mathematica (Wolfram Research). That is, this program converted it's
specific language code into C, and then compiled it into a dll.

I want to use the functions that are inside it.

Lets suppose that the library is called mathematica.dll, and there is
a function inside that is called function0, and that receives a
double, and returns a double

What is the simplest way of using the function0 inside another
function written directly on the .dna file, or preferably inside a .vb
file that I'll compile and package

Please take into account that I'm not a professional programmer.

I prefer if the example is in VB (know very little of CS).

Thank you in advance,
P. Fonseca

Govert van Drimmelen

unread,
Mar 4, 2011, 3:10:31 PM3/4/11
to Excel-DNA
Hi,

I paste an example below. The code could be in a .dna file or could be
extracted and compiled into a library.
Some comments:
* The keywords to search for are "PInvoke" and "DllImport". P/Invoke
is the name for the .NET feature that allows you to call native
libraries. DllImport is the attribute you use to mark a function as
being imported from a .dll.
In a Module you could declare the function to be imported as:
<DllImport("NativeFunctionLib")> _
Function fnNativeFunction(value As Double) As Double
End Function
In a class you'd have to add the 'Shared' modifier.

* You might need to check that the functions exported are not
decorated. The tool to look at your .dll is called 'DumpBin' and you'd
say "dumpbin /exports mathematica.dll" to check those exports.

* There is one other snag. Your library will be searched for in the
directory that the process is running from (where Excel.exe lives) and
in the Windows directories and your path etc. But it won't be found if
you just put it next to your .xll, which is probably what you'd want
to do. There are three ways to make this work:
1. Put the full path in the <DllImport> attribute.
2. Call LoadLibrary in the AutoOpen of your AddIn with the path that
you construct from the .xll path and your library name. I do this in
the example below. This ensured the library is already loaded in the
process before your function runs and the library is looked for.
3. Call the native function SetDllDirectory in your AutoOpen. This is
nicest, but only available on Windows XP SP1 and later. The signature
for this would be:
<DllImport("kernel32", SetLastError:=True)> _
Private Shared Function SetDllDirectory(ByVal directory As String)
_
As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
and you'd call this from an AutoOpen too.

Please write back if you need more explanation or help to get this to
work.

-Govert



<DnaLibrary>
<![CDATA[

Imports System.IO
Imports System.Runtime.InteropServices


Public Module TestDeclare

<DllImport("NativeFunctionLib")> _
Function fnNativeFunction(value As Double) As Double
End Function

<ExcelFunction(Description:="Call that native function",
Category:="My Native Functions")> _
Function CallMyFunction(value As Double) As Double
Return fnNativeFunction(value)
End Function

' A test function that will show error details.
Function TestCall() As String
Dim d As Double
Try
d = CallMyFunction(100)
Return "OK: " & d
Catch e as Exception
Return e.ToString()
End Try
End Function

End Module

Public Class MyAddIn
Implements IExcelAddIn

<DllImport("kernel32", SetLastError:=True)> _
Shared Function LoadLibrary(ByVal lpFileName As String) As IntPtr
End Function

Public Sub AutoOpen Implements IExcelAddIn.AutoOpen
Dim xllDirectory As String
Dim dllPath As String
Dim dllHandle as IntPtr

xllDirectory = AppDomain.CurrentDomain.BaseDirectory
dllPath = Path.Combine(xllDirectory, "NativeFunctionLib.dll")
dllHandle = LoadLibrary(dllPath)

' Can check for error: dllHandle = IntPtr.Zero etc.
End Sub

Public Sub AutoClose Implements IExcelAddIn.AutoClose
End Sub

End Class

]]>
</DnaLibrary>

Fonseca

unread,
Mar 4, 2011, 4:31:33 PM3/4/11
to Excel-DNA
Hi,

Thank you for the quick reply.

What I did was:
to place your code in a file named fonseca3.vb
add a line at the beginning of your code, with: Imports
ExcelDna.Integration
add a line at the beginning of the .dna file, with: <ExternalLibrary
Path="fonseca3.dll" Pack="true"/>
checked with dumpbin the name of the function
change the NativeFunctionLib to mathFunctions (the name of the dll
file)
change the fnNativeFunction to compiledFunction1 (the name of the
function retrieved with the dumbin)
compiled the fonseca3.vb (no errors)
packed the xll (no errors)
place the fonseca3.dll file in the same folder as the packed xll
opened excel (no errors)
tried the function: #VALUE! (it should have given the square of the
number: please see code below)

Since I'm an amateur, I probably didn't understood something

Help me please,
P Fonseca


- - - - - - - - - - - - - - - - - - -
mathFunctions.dll original C code:

#include "math.h"

#include "WolframLibrary.h"

#include "WolframCompileLibrary.h"

static WolframCompileLibrary_Functions funStructCompile;

static mbool initialize = 1;

DLLEXPORT mint WolframLibrary_getVersion()
{
return WolframLibraryVersion;
}

static int Initialize_compiledFunction1(WolframLibraryData libData)
{
if( initialize)
{
funStructCompile = libData->compileLibraryFunctions;
initialize = 0;
}
return 0;
}

static void Uninitialize_compiledFunction1(WolframLibraryData libData)
{
if( !initialize)
{
initialize = 1;
}
}

DLLEXPORT int WolframLibrary_initialize(WolframLibraryData libData)
{
return Initialize_compiledFunction1(libData);
}

void WolframLibrary_uninitialize(WolframLibraryData libData)
{
Uninitialize_compiledFunction1(libData);
}

DLLEXPORT char * WolframCompileLibrary_exportName()
{
return "compiledFunction1";
}

DLLEXPORT char * WolframCompileLibrary_wrapper()
{
return "Function[CompiledFunction[List[8, 8.`, 5468],
List[Blank[Real]], List[List[3, 0, 0], List[3, 0, 1]], List[], List[0,
0, 2, 0, 0], List[List[40, 56, 3, 0, 0, 3, 0, 1], List[1]],
Function[List[x], Power[x, 2]], Evaluate, Slot[1]]]";
}

DLLEXPORT mint WolframCompileLibrary_getArgumentTypes(int ** pt, mint
** pr)
{
static int types[] = {3, 3};
static mint ranks[] = {0, 0};
*pt = &types[0];
*pr = &ranks[0];
return 1;
}






DLLEXPORT int compiledFunction1(WolframLibraryData libData, mint Argc,
MArgument *Args, MArgument Res)
{
mreal R0_0;
mreal R0_1;
R0_0 = MArgument_getReal(Args[0]);
R0_1 = R0_0 * R0_0;
MArgument_setReal(Res, R0_1);
return 0;
}





- - - - - - - - - - - - - - - - - - -
fonseca3.vb content


Imports ExcelDna.Integration
Imports System.IO
Imports System.Runtime.InteropServices


Public Module TestDeclare

'em DLLImport, colcoa-se o nome da DLL e nao da funcao

<DllImport("mathFunctions")> _
Function compiledFunction1(value as double) as double
End Function

<ExcelFunction(Description:="Call that native function",
Category:="My Native Functions")> _
Function CallMyFunction(value As Double) As Double
Return compiledFunction1(value)
End Function

' A test function that will show error details.
Function TestCall() As String
Dim d As Double
Try
d = CallMyFunction(100)
Return "OK: " & d
Catch e as Exception
Return e.ToString()
End Try
End Function

End Module

Public Class MyAddIn
Implements IExcelAddIn

<DllImport("kernel32", SetLastError:=True)> _
Shared Function LoadLibrary(ByVal lpFileName As String) As IntPtr
End Function

Public Sub AutoOpen Implements IExcelAddIn.AutoOpen
Dim xllDirectory As String
Dim dllPath As String
Dim dllHandle as IntPtr

xllDirectory = AppDomain.CurrentDomain.BaseDirectory
dllPath = Path.Combine(xllDirectory, "mathFunctions.dll")
dllHandle = LoadLibrary(dllPath)

' Can check for error: dllHandle = IntPtr.Zero etc.
End Sub

Public Sub AutoClose Implements IExcelAddIn.AutoClose
End Sub

End Class


- - - - - - - - - - - - - - - - - - -
DUMP content:
Dump of file mathFunctions.dll

File Type: DLL

Section contains the following exports for compiledFunction1.dll

0 characteristics
4D7157F7 time date stamp Fri Mar 04 21:21:59 2011
0.00 version
1 ordinal base
6 number of functions
6 number of names

ordinal hint RVA name

1 0 000012D0 WolframCompileLibrary_exportName
2 1 000012E8 WolframCompileLibrary_getArgumentTypes
3 2 000012DC WolframCompileLibrary_wrapper
4 3 00001280 WolframLibrary_getVersion
5 4 0000128C WolframLibrary_initialize
6 5 00001304 compiledFunction1

Summary

1000 .CRT
1000 .bss
1000 .data
1000 .edata
1000 .eh_fram
1000 .idata
1000 .rdata
1000 .reloc
1000 .text
1000 .tls

- - - - - - - - - - - - - - - - - - -

Govert van Drimmelen

unread,
Mar 4, 2011, 5:02:33 PM3/4/11
to Excel-DNA
Hi,

1. Is mathFunctions.dll in the same directory as the .xll?
2. What is the error you see when you put =TestCall() in Excel?
3. Could you add a check to the AutoOpen()?
If dllHandle = IntPtr.Zero Then
XlCall.Excel(XlCall.xlcAlert, "LoadLibrary Failed")
Else
XlCall.Excel(XlCall.xlcAlert, "LoadLibrary OK")
End If
4. Could there be some other dependencies of mathFunctions.dll which
are not found?
5. Could you attach a debugger to the Excel process before you load
your add-in and check whether there is anything useful in the Debug
Output?
- Start Excel without loading your .xll.
- Select Debug->Attach to Process... in your Visual Studio.
- Pick Excel.exe from the list.
- Load your add-in in Excel and examine the Output window.

You do not have to do the packing if you just put the .dna file and
fonseca3.dll next to the .xll.

-Govert

Fonseca

unread,
Mar 4, 2011, 5:18:20 PM3/4/11
to Excel-DNA
Hi,

1. Is mathFunctions.dll in the same directory as the .xll?
yes

2. What is the error you see when you put =TestCall() in Excel?

This doesn't look good, but I don't know what it's saying:


System.AccessViolationException: Attempted to read or write protected
memory. This is often an indication that other memory is corrupt.

at TestDeclare.compiledFunction1(Double value)

at TestDeclare.TestCall()


3. Could you add a check to the AutoOpen()?
it answered "LoadLibrary OK"


4. Could there be some other dependencies of mathFunctions.dll which
are not found?
Don't know how to check this.


5. Could you attach a debugger to the Excel process before you load
your add-in and check whether there is anything useful in the Debug
Output?
I've been doing this without visual studio (just notepad... I told you
I'm an amateur...). I'll install visual studio during the weekend...

Any idea?
P. Fonseca

Govert van Drimmelen

unread,
Mar 4, 2011, 6:01:06 PM3/4/11
to Excel-DNA
Hi,

Ah! Your library is loaded and your function is being called. But it
is not a function that takes a double and returns a double. The
signature of your C function is:
int compiledFunction1(WolframLibraryData libData, mint Argc,
MArgument *Args, MArgument Res)

It also looks like you might need to call a few other functions, like
the Wolfram_initialize.
You could call these function from VB, but it will take some serious
effort to understand exactly how it works.
Excel-DNA (and .NET) knows nothing about the datatypes
"WolframLibraryData", "mint" or "MArgument".

I think you will have to create some simpler functions in C that wrap
the Mathematica calls and those MArgument types....
If can get a C function that takes a double and returns a double, the
bits you have so far should work fine.

It seems like Mathematica has something called ".NET/Link". Is this
not perhaps a better route to try?

-Govert

Fonseca

unread,
Mar 4, 2011, 6:15:52 PM3/4/11
to Excel-DNA
Hi,

Before all, thank you for your effort on this one.

To bad I can't profit from the dll that easily.

I think that .NET/Link needs mathematica installed (not sure; do you
know?), and the purpose of the xll I'm creating is exactly to be able
to distribute some functions that need more advanced math capacities
without distribution Mathematica to all the users. I'll turn to plain
VB and some other .net libraries (probably will be bothering you with
these ones too... I was looking to math.net...)

I'll ask on the Mathematica forum if someone has linked a generated
dll with other programs other than Mathematica (it's a recent feature,
so don't know what people are doing).

On the other hand, I think that the Matlab compilation features
enables the user to install a runtime (don't know if it is completely
free), and I think that that would do the trick.

At least, thanks to your help, I already have the piece of code that
I'll probably be using :-)

Thank you,
P. Fonseca

Frits Hermans

unread,
Nov 16, 2013, 9:31:45 AM11/16/13
to exce...@googlegroups.com
Hi Fonseca,

Have you already managed to get a DLL working in Excel that was generated in Mathematica? Please let me know because I'm very much interested in it.

Best,

Frits
Reply all
Reply to author
Forward
0 new messages