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

Breaking a DLL into separate DLLs

Ibrahim_A_
Novice
1,474 Views

Hello Community,

I currently have two Projects that are connected and work perfectly. There's a static library Project (exe) that calls a dynamic library project (dll). The DLL project consists of many Modules. I would like to break the DLL project into separate DLLs by compiling each module as an individual DLL.

Would you guide me where to find instructions on this? I am using Visual Studio to build the projects.

0 Kudos
25 Replies
Steve_Lionel
Honored Contributor III
1,189 Views

Do you really want to have each module as its own DLL? That seems unnecessarily complex. It may also be impossible if you have calls between the modules in both directions.

Open the VS solution containing your DLL. Select File > Add > New Project. Select Fortran DLL as the project type. In the Solution Explorer window, drag the module source file(s) from the current project to Source Files in the new project. Repeat this for each module.

Now open the executable project that, ideally, has as dependent projects the static library and DLL. Add all of the DLL projects you created to this one. Right click on the executable project, select Dependencies > Project Dependencies. Check the boxes for each DLL project.

This will most likely fail because your modules are not independent. But maybe you'll get lucky.

0 Kudos
Ibrahim_A_
Novice
1,189 Views

I know it may sound unnecessary to break up the DLL but I'm going to experiment with separating one DLL.

I am following your instructions. The "main" DLL module is calling a subroutine that is calling another. I am trying to separate the latter subroutine as a DLL project. Then, it is called from the subroutine that is inside the "main" DLL Is that possible? Or does the separated subroutine have to be called directly from the "main" DLL? Or, does both DLLs have to be called by the static project?

Thanks!

0 Kudos
Steve_Lionel
Honored Contributor III
1,189 Views

Build a tree diagram of how your calls go. If you have a routine in DLL B that is used only by DLL A, then DLL B needs to be a dependent of DLL A.

The static library doesn't really enter into this at all, as you never link it on its own. All that matters is that all of the DLL routines it references can be found when the EXE is linked.

0 Kudos
mecej4
Honored Contributor III
1,189 Views

You said that in your current single DLL you have several modules. Do those modules contain data objects? If so, breaking up the DLLs will entail doing quite a bit of additional coding to ensure that the new sets of DLLs have a consistent view of the data. If you do not do so, each DLL may have its own copies of the variables and any pair of DLLs that are supposed to share data may end up with separate and uncoordinated copies of the data.

0 Kudos
Ibrahim_A_
Novice
1,189 Views

Thank you all for your comments.

I have tried to separate a single module into a separate DLL B from the main DLL A. In DLL A, there's a module that uses and calls DLL B . After creating a new project for DLL B and building the projects, I can't debug the code. I set DLL A to be dependent on DLL B.

In DLL B, I used these commands to export DLL B (this is a simplified example code):

subroutine DLL_B(x,y,z)
implicit none
!DEC$ ATTRIBUTES DLLEXPORT, STDCALL :: DLL_B
!DEC$ ATTRIBUTES ALIAS:'DLL_B' :: DLL_B
!DEC$ ATTRIBUTES REFERENCE :: x,y,z
real(8) :: x,y,z
! the body of the code
end subroutine DLL_B

In the module that calls DLL B in the main DLL A, I added these lines:

module example

!DEC$ ATTRIBUTES DLLIMPORT,ALIAS: 'DLL_B' :: DLL_B

subroutine example(q)
implicit none
real(8)::q,x,y,z

call DLL_B(x,y,z)

end subroutine example
end module example

During the debug I get exception thrown error at the statement {call DLL_B(x,y,z)}. I am trying to figure out why. All the lib, exp and pdb files are output to the same debug directory. What am I missing? 

0 Kudos
Steve_Lionel
Honored Contributor III
1,189 Views

You didn't say STDCALL in the caller.  If you used modules for everything, all the interfaces and calling method would get handled automatically.

That the program linked means your file locations are fine. I assume that the exception was either an access violation or a stack issue.

0 Kudos
Ibrahim_A_
Novice
1,189 Views

I added STDCALL to the caller as follows

module example

!DEC$ ATTRIBUTES DLLIMPORT,STDCALL,ALIAS: 'DLL_B' :: DLL_B

subroutine example(q)
implicit none
real(8)::q,x,y,z

call DLL_B(x,y,z)

end subroutine example
end module example

I still get the same error and yes it's access violation.

0 Kudos
Steve_Lionel
Honored Contributor III
1,189 Views

Since you're only showing us pseudocode here, and not your actual code, it's hard for us to provide useful help. Do you recognize that simply saying STDCALL makes the arguments pass-by-value, and thus any attempt to assign to them in the subroutine will fail?

0 Kudos
Ibrahim_A_
Novice
1,189 Views

Thanks, Steve!

It's working now. The reason was that there were a lot of pointer variables in the actual DLL_B code. I had to assign a fixed size to them. I also removed the STDCALL from the caller.

0 Kudos
Steve_Lionel
Honored Contributor III
1,189 Views

So these were POINTER dummy arguments that you passed non-pointers to? That is supported in some contexts (not yours), but only when there is an explicit interface visible, which your psuedocode did not provide.

You should always have explicit interfaces visible. /warn:interface can help identify where these are required but not used, but I might be inclined nowadays to suggest /warn:external, which will give you errors for any call where an explicit interface or EXTERNAL attribute is not provided.

0 Kudos
Ibrahim_A_
Novice
1,189 Views

These were pointer dummies that I passed pointer dummies to. Their size is determined inside DLL_B, and they're supposed to be allocated inside DLL_B as well. Would INTERFACE solve this issue?

0 Kudos
Steve_Lionel
Honored Contributor III
1,189 Views

Yes - if you have a POINTER dummy, an explicit interface is required. See Doctor Fortran Gets Explicit – Again! -warn interface would likely complain to you about this, if you compiled things in the right order.

0 Kudos
Ibrahim_A_
Novice
1,189 Views

I have added a reference block in the calling subroutine (DLL_A) where I added the pointer arrays and all the parameters that are called from DLL_B. I built the codes with warn:interfaces on and the solution was compiled without errors. Also, debugging is working and DLL_B is executed without errors.

However, some parameters from the caller subroutine (DLL_A) took some random values while I was debugging the code. Those parameters are not in the called subroutine (DLL_B), and they took those random values without being modified. Any idea of what to check in the code?

0 Kudos
kolber__Michael
New Contributor I
1,190 Views

I tried code as above and got an error code of 6530.

0 Kudos
Steve_Lionel
Honored Contributor III
1,190 Views

Please show the actual code you used and the actual error message. Few if any of us have memorized all of the error numbers, though if I have it right this one is "The array spec for this component must be of explicit shape and each bound must be an initialization expression." Since I see no arrays in the code posted in this thread, I am perplexed.

0 Kudos
mecej4
Honored Contributor III
1,190 Views

kolber, Michael wrote:

I tried code as above and got an error code of 6530.

There is no complete body of code in this thread that you can designate as "code as above". There are many combinations and variations that could be tried, and that one such variation (with modifications by you, perhaps) produced an error message is not something that one would consider to be interesting or worth investigating.

0 Kudos
JohnNichols
Valued Contributor III
1,187 Views

not something that one would consider to be interesting or worth investigating.

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

I thought something was worth investigating once, but then thought better and felt much better for my failure to perform and therefore perform at the level of the average human bean on the average day. or as my Dad used to say when I asked for a beer, leave your money on the frig. By the way, by the time you are old and tall enough to leave your money on the frig, you are old enough to have earned the money mowing lawns. 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,190 Views

>>...perform at the level of the average human bean...

Just how large is a human bean? As big as a Lima bean, green bean?

Jim Dempsey

0 Kudos
mecej4
Honored Contributor III
1,190 Views

Typo, intended "being", perhaps?

0 Kudos
Ibrahim_A_
Novice
986 Views

Steve Lionel (Ret.) (Blackbelt) wrote:

Please show the actual code you used and the actual error message. Few if any of us have memorized all of the error numbers, though if I have it right this one is "The array spec for this component must be of explicit shape and each bound must be an initialization expression." Since I see no arrays in the code posted in this thread, I am perplexed.

Steve,

I have been investigating the issue and I decided to pass the arrays in a data structure (derived type) so that the memory allocation for all variables won't be interrupted during the execution of the code. I am attaching the subroutine for the dll I'm using as an experiment. The dll receives arrays x and y and return the summation of them. The dll works fine with a fortran caller. However, when C# is used to call the dll, I get an error message that an allocatable array is already allocated. I think it's the sumt array. C# wouldn't pass an array to the dll unless it was allocated. So, it was allocated before calling the dll. What should be done to pass that array without prior allocation in C#? The main objective is to get that array allocated inside the dll. I'm also attaching the C# caller.

fortran dll:

subroutine sum_type(sum_obj)
    
    !DEC$ ATTRIBUTES DLLEXPORT, STDCALL :: sum_type
    !DEC$ ATTRIBUTES ALIAS:'sum_type' :: sum_type
    !DEC$ ATTRIBUTES REFERENCE :: sum_obj
    
    
    implicit none
    
    type sum_data
        real(8),allocatable:: x(:),y(:),sumt(:)
    end type sum_data
    
    type(sum_data):: sum_obj
    integer(8) l
    
    l=size(sum_obj%x)
    allocate(sum_obj%sumt(l))
    sum_obj%sumt=sum_obj%x+sum_obj%y
    
    open(1,file='sum_type.out')
    write(1,*)sum_obj%x
    write(1,*)sum_obj%y
    write(1,*)sum_obj%sumt
    close(1)
    
end subroutine sum_type

C# caller:

//Here is where I define the dll:
        [DllImport("sum_type.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void sum_type(ref SumStruct data);

//Here is where I define the struct:
        [StructLayout(LayoutKind.Sequential, Pack = 8)]
        public struct SumStruct
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
            public double[] x;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
            public double[] y;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
            public double[] prod;
        }

//Here is where I call the dll:
            SumStruct sum;
            sum.x = new double[3] { 1, 2, 3 };
            sum.y = new double[3] { 2, 4, 6 };
            sum.prod = new double[3];

            sum_type(ref sum);

 

0 Kudos
Reply