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

return values in dll array problem

Robert_B_4
Beginner
2,595 Views
I have a Fortran DLL that is called from a C# program does some calculations and then updates an array which was passed as a parameter. I have written several DLLs in the past and they work as expected but this new routine when called updates the wrong parameters. Here is the DLL function-- FUNCTION ModComp7( dll_units, dll_output, dll_alloads, dll_precision, dll_tol3, dll_maxitn, dll_ns, dll_type, dll_model, dll_dassign, dll_e, dll_v, dll_den, dll_k0, dll_thknes, dll_k1, dll_k2, dll_k3, dll_nloads, dll_ar, dll_wgt, dll_psi, dll_ndef, dll_inprad, dll_evalue, dll_rmse, dll_msg, dll_inpdef) implicit none !DEC$ ATTRIBUTES ALIAS:'ModComp7' :: ModComp7 !DEC$ ATTRIBUTES DLLEXPORT :: ModComp7 !DEC$ ATTRIBUTES REFERENCE :: dll_units, dll_output, dll_alloads, dll_precision !DEC$ ATTRIBUTES VALUE :: dll_tol3, dll_maxitn, dll_ns !DEC$ ATTRIBUTES REFERENCE :: dll_type, dll_model, dll_dassign, dll_e !DEC$ ATTRIBUTES REFERENCE :: dll_v, dll_den, dll_k0, dll_thknes, dll_k1, dll_k2 !DEC$ ATTRIBUTES REFERENCE :: dll_k3, dll_nloads, dll_ar, dll_wgt, dll_psi, dll_ndef, dll_inprad !DEC$ ATTRIBUTES REFERENCE :: dll_evalue !DEC$ ATTRIBUTES REFERENCE :: dll_rmse !DEC$ ATTRIBUTES REFERENCE :: dll_msg, dll_inpdef CHARACTER*4, INTENT(IN) :: dll_units, dll_output LOGICAL, INTENT(IN) :: dll_alloads CHARACTER*1, INTENT(IN) :: dll_precision REAL(8), INTENT(IN) :: dll_tol3 INTEGER, INTENT(IN) :: dll_maxitn, dll_ns CHARACTER*1, dimension(7), INTENT(IN) :: dll_type INTEGER, dimension(7), INTENT(IN) :: dll_model, dll_dassign REAL(8), dimension(7), INTENT(IN) :: dll_e, dll_v, dll_den, dll_k0, dll_thknes REAL(8), dimension(7), INTENT(IN) :: dll_k1, dll_k2, dll_k3 INTEGER, INTENT(IN) :: dll_nloads REAL(8), INTENT(IN) :: dll_ar REAL(8), dimension(6), INTENT(IN) :: dll_wgt, dll_psi !maxlod = 5 INTEGER, INTENT(IN) :: dll_ndef REAL(8), dimension(9), INTENT(IN) :: dll_inprad ! return values REAL(8), dimension(7), INTENT(OUT) :: dll_evalue REAL(8), INTENT(OUT) :: dll_rmse CHARACTER*5, INTENT(OUT) :: dll_msg REAL(8), dimension(6,9), INTENT(OUT) :: dll_inpdef INTEGER :: ModComp7, i !Some calculations here ... then we update the parameter ddl_evalue with the solution... dll_evalue(1) = 1 dll_evalue(3) = 3 dll_evalue(4) = 4 dll_evalue(6) = 6 ModComp7 = 1 RETURN END FUNCTION When I inspect the arrays in the C# calling program I find that instead of having the evalue array updated the array listed just before (dll_inprad) is receiving the values. I have re-ordered some of the parameters only to find a different parameter is being updated in error. Any idea what is going on? Thanks.
0 Kudos
16 Replies
jimdempseyatthecove
Honored Contributor III
2,595 Views

Try making a test call to ModComp7 using unique values for the arguments passed, then verify that the arguments are what you think they are.

Jim Dempsey

0 Kudos
Robert_B_4
Beginner
2,595 Views

I did a variation on your suggestion by trying to change the values of the passed in data on in the DLL only to find the wrong parameters were being updated.  I believe they are being passed in just fine but making changes to the values is not working correctly.

Robert

0 Kudos
Robert_B_4
Beginner
2,595 Views

In the DLL's I had written before, the parameters all consisted of variables or arrays that were just integer or real(8) types. So I rewrote the DLL to just pass numeric values, thinking that perhaps the strings/characters were causing a problem; however that did not work either.

0 Kudos
Steven_L_Intel1
Employee
2,595 Views

Please show the C# declaration of the Fortran routine and the actual call.  Can you reproduce the problem with a Fortran caller? Which argument(s) are being updated incorrectly and with the values of which other arguments? You know about 1-origin vs. 0-origin arrays?

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,595 Views

Build a test program in C++ and make a similar call (use same arguments if possible).

If that fails, then you have an interface issue. Look in the IVF documentation under "C interoperability". Get the C++ call working.

If (when) the C++ call succeeds, have C# call a C++ wrapper that calls the Fortran DLL. This may give you a hint as to where the issue resides.

Jim Dempsey

0 Kudos
Robert_B_4
Beginner
2,595 Views
First off, you got me about 1-origin vs. 0-origin arrays, I am not familiar with the concept. Here is how I am calling the routine. Please note, I changed the character string parameters to integers from my first post so there is some differences. In my C# program I have [DllImport("ModCompDLL.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int ModComp7( int units, int output, int alloads, int precision, double tol3, int maxitn, int ns, int[] mc_type, int[] mc_model, int[] mc_dassign, double[] mc_e, double[] mc_v, double[] mc_den, double[] mc_k0, double[] mc_thknes, double[] mc_k1, double[] mc_k2, double[] mc_k3, int mc_nloads, double mc_ar, double[] mc_wgt, double[] mc_psi, int mc_ndef, double[] mc_inprad, [Out] double[] mc_evalue, [Out] double mc_rmse, [Out] int[] mc_msg, [Out] double[,] mc_inpdef); //return values from Modcomp7 double[] mc_evalue = new double[] { 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7 }; double mc_rmse = 5.0; int[] mc_msg = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; (The return values are declared early to a avoid a scope problem writing out the results later in the program) Then I make the call with the code below. In the DLL I try changing the value of mc_rmse from 5.0 to .9. The value ends up in the mc_evalue array (first element). I also try to push some values onto the mc_evalue array too but these values end up in the mc_inpdef array. Quite strange. Appreciate your time and thoughts. Robert //Create test variables & arrays for one test point int units = 1; int output = 1; int alloads = 0; int precision = 3; //high precision double tol3 = 1.00; //tolerance int maxitn = 15; //max iterations int ns = 5; //number of layers max = 7 int[] mc_type = new int[] { 1, 1, 1, 1, 1, 1, 1 }; int[] mc_model = new int[] { 0, 0, 0, 0, 0, 0, 0 }; int[] mc_dassign = new int[] { 0, 0, 0, 0, 0, 0, 0 }; double[] mc_e = new double[] { 500000, 50000, 10000, 5000, 500000, 0.0, 0.0 }; double[] mc_v = new double[] { .35, .40, .45, .45, .20, 0.0, 0.0 }; double[] mc_den = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; double[] mc_k0 = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; double[] mc_thknes = new double[] { 6.0, 15.0, 24.0, 102.0, 0.0, 0.0, 0.0 }; double[] mc_k1 = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; double[] mc_k2 = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; double[] mc_k3 = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; int mc_nloads = 4; double mc_ar = 5.9100; double[] mc_wgt = new double[] { 5854, 8913, 12490, 16443, 0, 0 }; //MOXLOD set to 6 double[] mc_psi = new double[] { 53.35, 81.23, 113.83, 149.85, 0, 0 }; int mc_ndef = 9; //number of sensors double[] mc_inprad = new double[] { 0.00, 8.00, 12.00, 18.00, 24.00, 36.00, 48.00, 60.00, 72.00 }; double[,] mc_inpdef = new double[6,9] { { 9.213, 6.975, 5.743, 4.290, 3.145, 1.778, 1.055, 0.720, 0.463}, { 13.795, 10.655, 8.870, 6.723, 5.023, 2.918, 1.740, 1.138, 0.790}, { 18.690, 14.365, 12.028, 9.228, 6.980, 4.135, 2.513, 1.685, 1.190}, { 23.865, 18.353, 15.393, 11.868, 9.068, 5.460, 3.390, 2.283, 1.665}, { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }}; try { int result = ModComp7(units, output, alloads, precision, tol3, maxitn, ns, mc_type, mc_model, mc_dassign, mc_e, mc_v, mc_den, mc_k0, mc_thknes, mc_k1, mc_k2, mc_k3, mc_nloads, mc_ar, mc_wgt, mc_psi, mc_ndef, mc_inprad, mc_evalue, mc_rmse, mc_msg, mc_inpdef); dataGrid.ItemsSource = LoadResultsData(); } catch (Exception ex) { MessageBox.Show(ex.Message.ToString()); } }
0 Kudos
Robert_B_4
Beginner
2,595 Views
One more thing I just noticed, the DLL declares these values INTEGER, INTENT(IN) :: dll_units, dll_output INTEGER, INTENT(IN) :: dll_alloads INTEGER, INTENT(IN) :: dll_precision REAL(8), INTENT(IN) :: dll_tol3 INTEGER, INTENT(IN) :: dll_maxitn, dll_ns INTEGER, dimension(7), INTENT(IN) :: dll_type INTEGER, dimension(7), INTENT(IN) :: dll_model, dll_dassign REAL(8), dimension(7), INTENT(IN) :: dll_e, dll_v, dll_den, dll_k0, dll_thknes REAL(8), dimension(7), INTENT(IN) :: dll_k1, dll_k2, dll_k3 INTEGER, INTENT(IN) :: dll_nloads REAL(8), INTENT(IN) :: dll_ar REAL(8), dimension(6), INTENT(IN) :: dll_wgt, dll_psi !maxlod = 5 INTEGER, INTENT(IN) :: dll_ndef REAL(8), dimension(9), INTENT(IN) :: dll_inprad ! return values REAL(8), dimension(7), INTENT(OUT) :: dll_evalue REAL(8), INTENT(OUT) :: dll_rmse INTEGER, dimension(10), INTENT(OUT) :: dll_msg REAL(8), dimension(6,9), INTENT(OUT) :: dll_inpdef INTEGER :: ModComp7, i I just noticed I have two warnings when this is compiled stating that #6843: A dummy argument with an explicit INTENT(OUT) declaration is not given an explicit value. [DLL_MSG] and #6843: A dummy argument with an explicit INTENT(OUT) declaration is not given an explicit value. [DLL_INPDEF] I did not see this before and I am not sure what I am missing. Thanks.
0 Kudos
Steven_L_Intel1
Employee
2,595 Views

I was referring to Fortran arrays numbering elements starting at 1 whereas C and C-like languages start at zero. That doesn't seem to matter here but I couldn't tell earlier.

When the Fortran routine is entered, are the input values what you expect? You should be able to see which Fortran statement changes the value incorrectly. Can you provide a cut down but complete example that demonstrates the problem?

0 Kudos
Robert_B_4
Beginner
2,595 Views
No, I don't think the array indexing differences are a factor. I am attaching the DLL code which is just a stripped down test and the C# calling program.
0 Kudos
Steven_L_Intel1
Employee
2,595 Views

Could you please provide a complete solution folder for the C# code? I am unfamiliar with C# and am not sure which of the many project types to select from.

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,595 Views

I will let Steve experiment with your uploads.

Your C# code is provisioning for ModComp7 to throw an exception. Fortran does not throw exceptions. Though LoadResultsData() may throw an exception.

Jim Dempsey

0 Kudos
Greg_T_
Valued Contributor I
2,595 Views

Hello,

The data value problem with the passed arrays could be due to one of the arrays being 2D:

REAL(8), dimension(6,9), INTENT(OUT) :: dll_inpdef

I've found that 2D and multi-dimensional array order is different for Fortran and C languages.  I think that Fortran arrays are stored in column order and C# arrays are stored in row order.  I need to transpose the array in C# (or declare as the transposed size) before calling Fortran so that the data is arranged as expected.  I'm passing arrays by reference between C# and Fortran.

I've found this link useful in the past:

http://www.nag.co.uk/numeric/csharpinfo.asp

Regards,
Greg

0 Kudos
Steven_L_Intel1
Employee
2,595 Views

Again this may be my inexperience with C# showing, but I am confused by your calling out dll_tol3, dll_maxitn and dll_maxns as being passed by value in the Fortran code, but on the C# side you also have mc_nloads, mc_ar and mc_ndef specified without [] suggesting to me that those are also passed by value. mc_ar in particular is a double, so if C# passes that by value then that will take up two argument list slots and shift the addresses of the following arguments.

0 Kudos
Robert_B_4
Beginner
2,583 Views

Steve, Greg & Jim

Thank you for taking the time to look at my problem and offer suggestions.  Regarding Steve's question about the variables, perhaps I am handling these wrong.  The intent of the DLL is to pass in all parameter values to the DLL, do some computations, and return the results to the C# program with the parameters dll_evalue, dll_rmse, & dll_msg.  So to my thinking only these values need to be passed by reference. 

Attached is the entire VS2015 project to test the dll.  When the button on the window is pressed the DLL is called and the "results" of dll_evalue is loaded into a datagrid.

Again, thanks for all of the help!

--Robert

0 Kudos
Steven_L_Intel1
Employee
2,583 Views

The fundamental problem is as I described. You have several arguments declared in the C# code as being passed by value, but on the Fortran side they are declared as passed by reference. This will corrupt the argument passing for any arguments that aren't the same size as an address.

dll_tol3, dll_maxitn, dll_ns, dll_nloads, dll_ar, and dll_ndef all need to be declared as VALUE, not REFERENCE.

The alternative is to use [ref] in the C# declaration for these arguments.

This solved most of the problems. But I am left with dll_rmse (mc_rmse in the C# code.) The argument is declared [out] in C#, which should mean pass by reference, but the address being passed is null. I can't figure out why that is and will leave that as an exercise for a C# expert.

0 Kudos
Robert_B_4
Beginner
2,583 Views

Steve,

Thanks for the analysis.  I have been in meetings so I will implement your suggestions and hopefully it will resolve my problems.  I will check out the dll_rmse issue on my end, hopefully it will be a minor problem.

--Robert

0 Kudos
Reply