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

LInking a Ftn DLL with a C static library

Richard_S_9
Beginner
2,002 Views

I am updating a 15 year old VB6 project that calls routines in a Ftn DLL. The DLL calls routines in a C++ static library. I have recently purchased the Intel Fortran compiler and have downloaded Visual Studio 2016. I have converted all of the VB, Ftn and C code. I did this by defining three, separate, Visual Studio projects: PcbPrg, PcbDll and PcbStl, a VB Windows forms project, a Ftn DLL project and a C static library project. I have been successful in calling the Ftn DLL routines (after about a week in “DLL Hell”).

My one remaining problem is getting the Ftn DLL routines to find/recognize the C static library. I have spent another week or longer reviewing the relevant information on the internet, including Steve Lionel’s discussions, including the one on August 10,2015, with other users having similar problems. I have also downloaded the Intel Reference Guide(2600 pages) and got a little bit lost and discouraged in that impressive document.

So here is a code snippet that shows where I am. Ignore the unusual line spacing and the occasional red colored underlines which evidently result from copying from MS Word. 

************************************************************************

      subroutine BITMAP_SIZE(bmpfile, bmpwidth, bmpheight, bmperror)

************************************************************************

      implicit real*8 (a-h,o-z)

 

      !DEC$  ATTRIBUTES DLLEXPORT :: BITMAP_SIZE

      !DEC$  ATTRIBUTES ALIAS: 'BITMAP_SIZE' :: BITMAP_SIZE

 

      interface

         subroutine BmpSize(bmpfile,bmpwidth,bmpheight,bmperror)

            !dec$ attributes C, alias:'BmpSize' :: BmpSize            

            !character (len=*) bmpfile

            integer bmpwidth,bmpheight,bmperror

            !dec$ attributes reference :: bmpfile

            !dec$ attributes reference :: bmpwidth

            !dec$ attributes reference :: bmpheight

            !dec$ attributes reference :: bmperror

         end subroutine BmpSize

      end interface

 

      character (len=*) bmpfile

      integer bmpwidth, bmpheight, bmperror

     

      bmperror=0

 

      call BmpSize(bmpfile,bmpwidth,bmpheight,bmperror)

 

       return

 

       end

 

 

 

 

The VB calling routine successfully locates the Ftn called routine, BITMAP_SIZE, but throws an exception at the call to the C static library procedure, BmpSize,

 

      call BmpSize(bmpfile,bmpwidth,bmpheight,bmperror)

 

 

 

The error message is,

 

error LNK2001: unresolved external symbol BmpSize

I guess this is not surprising, since I haven’t indicated, in the Visual Studio IDE for the Ftn project, where to find the C static library.

How do I do that?

I might add that I have an analogous problem trying to tie the VB project to the Ftn DLL project, using the IDE. I have taken the easy way out by putting the Ftn DLL file in the same folder as the VB exe file. This works fine but I am not able to run from within the IDE in either the debug or release configuration.

Finally, let me compliment Intel on the ease and efficiency with which your compiler ate up my old, fixed format, Fortran code and spit it out beautifully. I’ve probably gotten my $850 worth already, but if we can just get the DLL and the static library talking to each other, I would be doubly  happy.

 

Thanks in advance,

Richard Sampson

0 Kudos
21 Replies
Steven_L_Intel1
Employee
1,761 Views

Easiest way to tell your project about the C library is to add the .lib as a "Source file" to the DLL project. You can even "drag and drop" it into the list of Source files in the project's Solution Explorer. Just be aware that this assumes you're building for one platform (32-bit or 64-bit). Otherwise, you will need to go to the Linker > Input property page and give the proper path and name of the .lib for each configuration.

0 Kudos
Richard_S_9
Beginner
1,761 Views

Steve

I added my static library, PcbStl.lib, to the DLL project as a "Source file" as you suggested. It seems a little out of place, snuggled in with the .for source files. Nevertheless, I tried it, and I still get the error message, "error LNK2001: unresolved external symbol BmpSize ".

Richard 

0 Kudos
FortranFan
Honored Contributor II
1,761 Views

Richard S. wrote:

Steve

I added my static library, PcbStl.lib, to the DLL project as a "Source file" as you suggested. It seems a little out of place, snuggled in with the .for source files. Nevertheless, I tried it, and I still get the error message, "error LNK2001: unresolved external symbol BmpSize ".

Richard 

See this other thread: https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/610588

Your title says C static library, but your description indicates C++ library - maybe you have the same issue as suggested by Steve in the other thread and BmpSize needs the "extern C" attribute?

As Steve suggested, telling the linker explicitly the library dependence may be better for you:

lib.png 

 

0 Kudos
Richard_S_9
Beginner
1,761 Views

Steve

As you recommended I put the following line in Linker>Input>Additional Dependencies:

   c:\users\richard\documents\visual studio 2015\projects\pcbstl\release\pcbstl.lib

The linker error was rather strange. I don't understand the partial listings of the path.
The linker error listing is shown below:

   1>Linking...
   1>ipo: error #11018: Cannot open C:\Users\richard\Documents\Visual
   1>ipo: error #11018: Cannot open Studio
   1>ipo: error #11018: Cannot open 2015\Projects\pcbstl\Release\pcbstl.lib
   1>LINK : fatal error LNK1181: cannot open input file 'C:\Users\richard\Documents\Visual.obj'
   1>
   1>Build log written to  file://C:\Users\richard\Documents\Visual Studio 2015\Projects\pcbdll\pcbdll\Release\
                        

I have also listed the contents of the build log, BuildLog.htm, in case that gives you any clues:

c:\users\richard\documents\visual studio 2015\projects\pcbstl\pcbstl\release\pcbstl.pch
c:\users\richard\documents\visual studio 2015\projects\pcbstl\pcbstl\release\pcbstl.pdb
c:\users\richard\documents\visual studio 2015\projects\pcbstl\pcbstl\release\stdafx.obj
c:\users\richard\documents\visual studio 2015\projects\pcbstl\pcbstl\release\pcbstl.obj
c:\users\richard\documents\visual studio 2015\projects\pcbstl\release\pcbstl.lib
c:\users\richard\documents\visual studio 2015\projects\pcbstl\pcbstl\release\pcbstl.tlog\cl.command.1.tlog
c:\users\richard\documents\visual studio 2015\projects\pcbstl\pcbstl\release\pcbstl.tlog\cl.read.1.tlog
c:\users\richard\documents\visual studio 2015\projects\pcbstl\pcbstl\release\pcbstl.tlog\cl.write.1.tlog
c:\users\richard\documents\visual studio 2015\projects\pcbstl\pcbstl\release\pcbstl.tlog\lib-link.read.1.tlog
c:\users\richard\documents\visual studio 2015\projects\pcbstl\pcbstl\release\pcbstl.tlog\lib-link.write.1.tlog
c:\users\richard\documents\visual studio 2015\projects\pcbstl\pcbstl\release\pcbstl.tlog\lib.command.1.tlog

Richard

0 Kudos
Richard_S_9
Beginner
1,761 Views

Steve

I forgot to mention that each of the subroutine declarations in the static library already have extern "C" just prior to each  declaration. I wasn't sure if you meant putting extern "C" before or in the Interface declaration in the DLL so I tried that. The compiler threw exceptions at that maneuver so I guess you meant to put it in the static library. 

Richard

0 Kudos
FortranFan
Honored Contributor II
1,761 Views

Richard S. wrote:

Steve

As you recommended I put the following line in Linker>Input>Additional Dependencies:

   c:\users\richard\documents\visual studio 2015\projects\pcbstl\release\pcbstl.lib  ..

Try putting quotes around your path: 'c:\users\richard\documents\visual studio 2015\projects\pcbstl\release\pcbstl.lib'

0 Kudos
Richard_S_9
Beginner
1,761 Views

Steve

As you suggested I put the path in quotes and that cleaned up the linker message which follows:

1>Linking...

1>Creating library C:\Users\richard\Documents\Visual Studio 2015\Projects\pcbdll\pcbdll\Release\pcbdll.lib and object C:\Users\richard\Documents\Visual Studio 2015\Projects\pcbdll\pcbdll\Release\pcbdll.exp

1>pcbdll.obj : error LNK2019: unresolved external symbol BmpSize referenced in function BITMAP_SIZE

1>LINK : MSIL .netmodule or module compiled with /GL found; restarting link with /LTCG; add /LTCG to the link command line to improve linker performance

1> Creating library C:\Users\richard\Documents\Visual Studio 2015\Projects\pcbdll\pcbdll\Release\pcbdll.lib and object C:\Users\richard\Documents\Visual Studio 2015\Projects\pcbdll\pcbdll\Release\pcbdll.exp

1>pcbdll.obj : error LNK2001: unresolved external symbol BmpSize

1>Release\pcbdll.dll : fatal error LNK1120: 1 unresolved externals

 

As you can see I still get the link error.

Let me ask a related question.Isn't it true that the "unresolved external symbol" message might be due to an incompatibility between the calling and the called routines - for example a difference in the type of a parameter in the two routines?  I will review my code to see if there is such an error while you guys are coming up with new things for me to try.

You know you don't have to work on Sunday evening. This can wait till tomorrow morning, but I do appreciate your efforts.

Richard  

0 Kudos
IanH
Honored Contributor II
1,761 Views

Please show the relevant part of the function definition for the called C++ function.  Are you using any special C++ compile options?

I speculate that you need the DECORATE attribute also applied in the compiler directive that gives BmpSize its alias.  C and C++ compilers typically generate linker names with a leading underscore, but your use of ALIAS without DECORATE means that the Fortran compiler isn't expecting such an underscore.

The linker babbling about MSIL makes me think that you have set the C++ project up to use what is known as the common language runtime (CLR, i.e. what sits behind Microsoft's .NET stuff).  If so, then I don't think you want that - you want native code.

If you have control of the source, I suggest you look at Fortran 2003's C interoperability feature.

Note that you have commented out the declaration of the bmpfile dummy argument in your original post.  Fortran's implicit typing rules, active given the absence of IMPLICIT NONE in that scope, mean that dummy argument will be default REAL.  That is unlikely to be what you want.

0 Kudos
Lorri_M_Intel
Employee
1,761 Views

The other thing to look at; you've got the Win32 configuration.  
By default, C prepends an underscore to the name, as _BmpSize.  
Let me suggesting adding "DECORATE" to the list of attributes on the declaration, as:

!dec$ attributes C, decorate, alias:'BmpSize' :: BmpSize            

This will tell the compiler to prepend the underscore, even though you gave an explicit alias name.

You can confirm the names generated by the C compiler by using the dumpbin utility on the generated library, and searching for the name string.  (This, by the way, is my very favorite tool when trying to debug Fortran <-> C name mismatches)

I use dumpbin from one of the Intel Fortran command line prompts.

       Good luck!

                           --Lorri

0 Kudos
Richard_S_9
Beginner
1,761 Views

Lorri,

As you requested, the following lines of code are snippets from the C++ static library.

This code is at the beginning of the library:

// pcbstl.cpp

#pragma once

#include "stdafx.h"

#include "pcbstl.h"

#include <windows.h>

#include <stdio.h>

 

This code is the complete declaration of BmpSize:

extern "C"

void BmpSize(LPCWSTR bmpfile,

       int *pwidth,

       int *pheight,

       int *pbmperror);

 

This code is the complete definition of BmpSize:

void BmpSize(LPCWSTR bmpfile,

       int *pwidth,

       int *pheight,

       int *pbmperror)

{

       extern int width;

       extern int height;

       extern int bmperror;

      RdBmpHdr(bmpfile);

      *pwidth = width;

       *pheight = height;

       *pbmperror = bmperror;

       return;

}

 

Note: I thought that possibly the type definition, LPCWSTR, which is necessary for some of the functions used in the library, might be the culprit. So I temporarily revised BmpSize by removing the parameter, bmpfile, from the  call and received parameter lists, leaving only three integer parameters. I still got the linker error.

I will try the suggestions in your most recent reply.

Richard

0 Kudos
Richard_S_9
Beginner
1,761 Views

Lorri

Putting "decorate" in the attribute list, as you recommended, did the trick. The call to bmpsize no longer throws an exception. Leave it to a woman to know things about decorating. Sorry, I couldn't resist. At age 87 I should know better. 

Can you recommend a good book on Intel Fortran (and Visual Studio too, for that matter)? I have downloaded your 2600 page Used Guide.

Thanks very much for the help. Please thank Steve and the others for me.

I am so happy!

Richard

0 Kudos
Richard_S_9
Beginner
1,761 Views

Lorri

Again, I want to thank you and your team members for solving my problem related to calling a C++ static library from a Fortran DLL. As I indicated early on in this Post, I have an analogous problem trying to tie the VB project to the  DLL project, from within the IDE. I have taken the easy way out by putting the  DLL file in the same folder as the VB exe file and running while external to the IDE. This works fine, but I am not able to run or debug from within the IDE in either the debug or release configuration. I guess this is not surprising, since I haven’t indicated, in the IDE for the VB project, where to find the DLL.

How do I do that?

As a possible clue to solving this problem, I note that I am unable to debug the DLL from within the IDE DLL project. I get the following message:

      "Visual Studio cannot debug because a debug target has not been specified"

When I initially installed the DLL as an IDE project, I was not getting this error message. I have evidently punched the wrong buttons somewhere along the line.

These are probably two separate issues but, perhaps, related issues.

Can you give me some things to try?

Richard

0 Kudos
Steven_L_Intel1
Employee
1,761 Views

Richard,

If your VB project is in the same solution as the DLL project, you would simply start the VB project in the debugger. You could then set breakpoints in the DLL or even just "step in" to it. However, before you do that, you have to set the VB project's option Debugging > Enable Unmanaged Code Debugging.

If you are using separate solutions, then in your DLL project set the property Debugging > Command to be the full path to your VB executable. This will resolve the "debug target has not been specified" error.

I generally prefer to have my VB project and Fortran DLL project share a VS solution. You can look at the two VB-Fortran samples we provide for inspiration.

0 Kudos
Richard_S_9
Beginner
1,761 Views

Steve

I am using separate solutions, for no compelling reason. Your suggestion to add the full path to Debugging>Command got rid of the error:

  "Visual Studio cannot debug because a debug target has not been specified"

Thank you.

Now when I run in debug mode from the DLL I get an access violation at a call from VB to one of the routines in the DLL:

Exception thrown at 0x160F5544 (pcbdll.dll) in pcboard.exe: 0xC0000005: Access violation writing location 0x8FC4F543.

However when I run from the VB project in debug mode I get:

Unable to load DLL 'pcbdll.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

I have read some of your posts on this type of behavior but I have not retained the fix in my memory. I think I remember you saying that this could be due to either a missing .dll file or due to a mismatch between calling and called parameters. I can play with the parameter definitions. What do you think?

Richard

0 Kudos
Steven_L_Intel1
Employee
1,761 Views

The access violation is probably a mismatch between how you've declared the DLL routine in VB and how it is declared in the DLL. I just noticed that your code excerpts in the original post use ATTRIBUTES C. This is wrong for calls from VB - you should say ATTRIBUTES STDCALL instead. This error causes stack corruption.

As for the "unable to load", either the file path you give in VB is wrong or there are DLLs that your DLL depends on which are not found in the various locations given in the PATH system environment variable. If you're running this on the same system where you installed Intel Parallel Studio XE, that is not likely to be the issue.

0 Kudos
FortranFan
Honored Contributor II
1,761 Views

Richard S. wrote:

.. Now when I run in debug mode from the DLL I get an access violation at a call from VB to one of the routines in the DLL:

Exception thrown at 0x160F5544 (pcbdll.dll) in pcboard.exe: 0xC0000005: Access violation writing location 0x8FC4F543.

However when I run from the VB project in debug mode I get:

Unable to load DLL 'pcbdll.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E) ..

@Richard,

Firstly, I'm not Steve!  He's Dr Fortran!  https://software.intel.com/en-us/blogs/2013/10/02/has-it-really-been-35-years

Secondly, if you're using Visual Basic from Visual Studio, additional considerations need to be made for the "managed code" aspect of .NET that then comes into play.  See the thread below, especially Message #2 - there is a worked out example in the attached zip file for VB-Fortran mixed language solution:

https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/509148

 

0 Kudos
Richard_S_9
Beginner
1,761 Views

Steve

First

The ATTRIBUTES C you refer to (near the top of the original post is in the interface to a routine, BmpSize, in a C++ static library. The DEC$'s  related to the Fortran DLL contain only EXPORT and ALIAS  attributes. Should they be modified?

Second

You showed me, above, how to put a path  in the DLL project using the property Debugging>Command. but  there is no such property in the VB project. I do see the property, References. Is that where the path goes? You state above:

"As for the "unable to load", either the file path you give in VB is wrong or".

You see, my problem is that I haven't yet specified any path in the VB project. I don't see where I do that to tell the VB project where to find the DLL.

Richard 

0 Kudos
Steven_L_Intel1
Employee
1,761 Views

Richard,

Any Fortran routines called from VB must have the STDCALL attribute specified. Typically you'd use something like this:

!DEC$ ATTRIBUTES STDCALL, REFERENCE, ALIAS:"my_sub" :: my_sub

where "my_sub" would be replaced by the name of your routine. Note that the exact spelling and case of the ALIAS value is what you would specify in VB.

You don't put DLL paths in the VB project settings, they go in the VB source. Here's an example from the VB-Calls-Fortran sample we provide:

Module Module1
    REM Use ByVal to pass strings unless the called routine expects BSTR structures
    Public Declare Auto Sub DLL_ROUT Lib "FCALL.DLL" _
   (ByVal DBL_IN() As Double, ByVal STR_IN As String, ByVal DBL_OUT() As Double)
End Module

Here it would be the string "FCALL.DLL" that you would modify to give the full path to your DLL. (The sample doesn't need it because it copies the DLL to the same folder as the VB executable.)

I suggest that you unpack the MixedLanguage.zip samples collection onto your desktop and study the VB-Calls-Fortran sample.

0 Kudos
Richard_S_9
Beginner
1,761 Views

Steve

I looked for the documents you recommended:

       "two VB-Fortran samples"

       "MixedLanguage.zip samples collection"

Where do I find them? Can you provide a link(s)?

Richard

0 Kudos
Richard_S_9
Beginner
1,536 Views

Steve

I did have the Ftn subroutine declaration in the VB source code , but I specified only the file name, pcbdll.dll instead of the full path. (This was the way it appeared in the VB 6 code.) I specified the full path as you recommended and the VB code now finds the DLL. I believe the reason I specified only the file name, pcbdll.dll, in the VB 6 code was because the user was instructed to place the executable and the DLL in his working directory along with other files germane to his use. If I had included my full path in the disseminated code, the program would never have found the DLL. 

Anyway, everything is working as it should now.

Thanks very much for the help.

Richard

0 Kudos
Reply