Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Calling a dll from a dll

Frank_M
Beginner
2,856 Views

 I need to break a library of functions into a number of separate dlls.

How do I call a dll from within a dll?

Can I do it within the same solution?

Do I need to specify a library for the linker?

Current attempt:

!DEC$ ATTRIBUTES DLLEXPORT::G_Trend
!DEC$ ATTRIBUTES ALIAS:'G_Trend' :: G_Trend
!DEC$ ATTRIBUTES DLLIMPORT, ALIAS: 'G_Filter' ::G_Filter

( blah, blah, blah)

 Call G_Filter(pdlen, lg, psh, lnc, tmp1)

gives the error

>G_Trend.obj : error LNK2019: unresolved external symbol __imp_G_Filter referenced in function G_Trend
2>Debug\G_Trend.dll : fatal error LNK1120: 1 unresolved externals

The G_Filter function compiles and works great.( Tested by calling with netlink in mathematica)

G_Filter and G_Trend are in different projects under the same solution.

Thanks

Frank

 

 

 

0 Kudos
11 Replies
DavidWhite
Valued Contributor II
2,856 Views

the sample code "DLL" - file DynamicLoad.f90 - installed with the compiler may be helpful - it shows hows to load a DLL and access the functions in it.

David

0 Kudos
Steven_L_Intel1
Employee
2,856 Views

There's no problem calling one DLL from another - it's done all the time. Assuming you want to resolve this at link time, just link the DLL to the export library of the other DLL(s), exactly as you would for linking an executable to DLLs.

David's suggestion is a more complex approach for dynamic loading - it doesn't sound as if you need that.

0 Kudos
Frank_M
Beginner
2,856 Views

 Thanks for the help.

 I decided to try David's method first as it looks like it could be useful in the future.

The current problem is that .net can't find the dll.

The error message is " A .NET exception occurred: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. at Wolfram.NETLink.DynamicDLLNamespace.DLLWrapper1.G_Trend(Int32& , Double[] , Double[] )."

I don't think the error is in the way I am call the dll.

So what am I doing in this code that prevents it from being called?

 

 

subroutine G_Trend(pdlen, pdata, acc)
use kernel32  ! Declares Windows API routines
use, intrinsic :: iso_c_binding

IMPLICIT NONE
!DEC$ ATTRIBUTES DLLEXPORT::G_Trend
!DEC$ ATTRIBUTES ALIAS:'G_Trend' :: G_Trend

    abstract interface
        subroutine G_Filter_void(pdlen0, lag0, lead0, pdata0, fdata0)
            integer(4), intent(IN)                      :: pdlen0      
            integer(4), intent(IN)                      :: lag0       
            integer(4), intent(IN)                      :: lead0      
            real(8),    dimension(pdlen0), intent(IN)    :: pdata0     
            real(8),    dimension(pdlen0), intent(INOUT) :: fdata0    
        end subroutine G_Filter_void
    end interface

    procedure(G_Filter_void), pointer :: G_Filter
    
    integer(C_INTPTR_T) :: p_G_Filter

    integer(HANDLE) :: dll_handle
    integer(BOOL) :: free_status
    
    integer(4), intent(IN)                                  :: pdlen           
    real(8),    dimension(pdlen), intent(IN)         :: pdata         
    real(8),    dimension(pdlen), intent(INOUT)   :: acc            
    integer(4)                                                 :: lg, psh
    real(8),    dimension(pdlen)                        :: lnc            
    real(8),    dimension(pdlen)                         :: ldc            
    real(8),    dimension(pdlen)                         :: tmp1           
    
   

    dll_handle = LoadLibrary (lpLibFileName="G_Filter.dll"//C_NULL_CHAR)
    
    p_G_Filter = GetProcAddress (hModule=dll_handle, lpProcName="G_Filter"//C_NULL_CHAR)
    
    call C_F_PROCPOINTER (TRANSFER(p_G_Filter, C_NULL_FUNPTR), G_Filter)

    
    lnc = DLOG(pdata)
    tmp1 = 0.0
    acc = 0.0                                                    
   
    lg = 1                                                        
    psh = 0

    Call G_Filter(pdlen, lg, psh, lnc, tmp1)
    
    free_status = FreeLibrary (hLibModule=dll_handle)
    
   return
   
end subroutine G_Trend   

 

Again, Thanks for the help. If I can't make this work, I'll try Steve's suggestion.

Frank

0 Kudos
JVanB
Valued Contributor II
2,856 Views

Your code looked fine to me, I added a G_Filter dll and a main program to invoke G_Trend and put in a couple of write statements to check that everything was going OK, and it all seemed to work. Output:

 dll_handle =        140729806094336
 GetLastError() =            0
 p_G_Filter =        140729806098432
 GetLastError() =            0
 Called G_Filter

 

0 Kudos
FortranFan
Honored Contributor III
2,856 Views

Frank_M wrote:

 ...

The current problem is that .net can't find the dll.

The error message is " A .NET exception occurred: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. at Wolfram.NETLink.DynamicDLLNamespace.DLLWrapper1.G_Trend(Int32& , Double[] , Double[] )."

I don't think the error is in the way I am call the dll.

So what am I doing in this code that prevents it from being called?

 ...

Can you show the relevant sections of your .NET code i.e., where you prototype the G_Trend procedure and where you call it?

Why do say, "The current problem is that .net can't find the dll"?  Have you confirmed you're getting a  zero value for the dll_handle in G_Trend?  If you think the execution is making it ok to G_trend and something is amiss therein, then here're some suggestions:

  • Following your call to LoadLibrary procedure, you should check for the dll_handle return value and only proceed if it is not zero.  If it is zero, then it is confirmation the DLL was not found or it could not be loaded.
  • For the "not found" situation, check your PATH and ensure the DLL is indeed present in the path
  • For the "could not loaded" possibility, run your DLL through a dependency check utility such as DependencyWalker (www.dependencywalker.com) and see what other DLLs are required by the one in question and whether they are all available on the path.
  • If dll_handle is non-zero (usually greater than zero), then check p_G_Filter return value from GetProcAddress and again only proceed to the next step if it is not zero.  If it is zero, then check whether the G_Filter procedure is exported correctly from the DLL.

 

0 Kudos
Frank_M
Beginner
2,856 Views

The dll is called using netlink in Mathematica.

Here is the code:

Needs["NETLink`"]

ReinstallNET["Force32Bit" -> True];

libName = "C:\\Users\\FrankM\\Documents\\Visual Studio\2012\\Projects\\G_Trend\\G_Trend\\Debug\\G_Trend.dll";

FileExistsQ[libName]

>True

TestSubroutine = DefineDLLFunction["G_Trend", libName, "void", {"int*", "Double[]", "Double[]"}]

>Function[Null,If[NETLink`DLL`Private`checkArgCount["G_Trend", {##1}, 3], Wolfram`NETLink`DynamicDLLNamespace`DLLWrapper1`GUTrend[##1],$Failed], {HoldAll}]

Clear;
Clear[date];
{date, c} = Transpose[FinancialData["GE", "Jan. 1, 2013"]];

pdlen = Length;
fdata = MakeNETObject[Evaluate[Table[0.0, {pdlen}]]];

TestSubroutine[pdlen, c, fdata]

>NET::netexcptn: A .NET exception occurred: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at Wolfram.NETLink.DynamicDLLNamespace.DLLWrapper1.G_Trend(Int32& , Double[] , Double[] ).

$Failed

out = NETObjectToExpression[fdata];

 

Given Repeat Offender's test (Thank You, I should have thought of that) the error must be in the calling.

The above code worked for calling the dll G_Filter.

Needs["NETLink`"]

ReinstallNET["Force32Bit" -> True];(*or InstallNET["Force32Bit"\[Rule]True]*)

libName = "C:\\Users\\FrankM\\Documents\\Visual Studio\2012\\Projects\\G_Filter\\G_Filter\\Debug\\G_Filter.dll";

FileExistsQ[libName]

>True

TestSubroutine = DefineDLLFunction["G_Filter", libName, "void", {"int*", "int*", "int*", "Double[]", "Double[]"}]

>Function[Null,If[NETLink`DLL`Private`checkArgCount["G_Filter", {##1}, 5], Wolfram`NETLink`DynamicDLLNamespace`DLLWrapper1`GUFilter[##1],$Failed], {HoldAll}]

Clear;
Clear[date];
{date, c} = Transpose[FinancialData["IBM", "Jan. 1, 2013"]];

pdlen = Length;
lag = 5;
push = 5;
pdata = c;
fdata = MakeNETObject[Evaluate[Table[0.0, {pdlen}]]];

TestSubroutine[pdlen, lag, push, pdata, fdata]

out = NETObjectToExpression[fdata];

 

There is a copy of G_Filter.dll in the G_Trend/Debug directory, so I would think windows would be able to find it.

Thanks guys. I'll first study and run Repeat Offenders Test using the actual G_Filter, then look into that Dependency Walker.

0 Kudos
Steven_L_Intel1
Employee
2,856 Views

Microsoft's .NET languages give a misleading "DLL not found" error when the problem is that a dependent DLL is not found. Dependency Walker is definitely helpful in this case.

0 Kudos
JVanB
Valued Contributor II
2,856 Views

The interface to G_Filter in your Fortran example and that in your Mathematica example are inconsistent. In Mathematica you are forcing it to use a 32-bit dll (via ReinstallNET) where STDCALL is kind of an oddball calling convention which happens to be the default and then using this default in DefineDLLFunction. Your Fortran example doesn't specify a calling convention so unless a command-line switch is applied it won't be using STDCALL. I have modified my example so it will refuse to compile in 64-bit mode and set up subroutine G_Trend and its interface in program P so that the STDCALL calling convention will be used. Output when compiled with 32-bit ifort was similar to that seen in Quote #5.

 

0 Kudos
JVanB
Valued Contributor II
2,856 Views

Oh, and I should mention: if you are still having problems, do everything over exclusively in 64-bit mode so as to take STDCALL out of the equation. Once you get it running in 64-bit mode, then try to change over to 32-bit STDCALL. This will localize interfacing issues in smaller chunks.

 

0 Kudos
Frank_M
Beginner
2,856 Views

After including a console in G_Trend and adding write statements, windows cannot find G_Filter.dll with the complete path specified.

!works
dll_handle = LoadLibrary (lpLibFileName="C:\Users\FrankM\Documents\Visual Studio 2012\Projects\G_Trend\G_Trend\Debug\G_Filter.dll"//C_NULL_CHAR)


!Fails
dll_handle = LoadLibrary (lpLibFileName="G_Filter.dll"//C_NULL_CHAR)

Is there an elegant solution beyond hard code?

In other news, Mathematica NetLink was not able to find G_Trend with Attribute set to StdCall or C. It likes the default unspecified C in 32bit.

Once again, big thank you to everyone for all the help, especially Repeat Offender for the samples.

0 Kudos
FortranFan
Honored Contributor III
2,856 Views

Frank_M wrote:

After including a console in G_Trend and adding write statements, windows cannot find G_Filter.dll with the complete path specified.

!works
dll_handle = LoadLibrary (lpLibFileName="C:\Users\FrankM\Documents\Visual Studio 2012\Projects\G_Trend\G_Trend\Debug\G_Filter.dll"//C_NULL_CHAR)


!Fails
dll_handle = LoadLibrary (lpLibFileName="G_Filter.dll"//C_NULL_CHAR)

Is there an elegant solution beyond hard code?

..

See https://msdn.microsoft.com/en-us/library/7d83bc18.aspx for how Windows locates DLLs.  If there is a particular folder in Mathematica for "user" DLLs where you can place G_Filter.dll?  Or can you create a new folder for your DLLs and add it your PATH environment?

 

0 Kudos
Reply