Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
6 Views

return values in dll array problem

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
Highlighted
6 Views

Try making a test call to

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
Highlighted
Beginner
6 Views

I did a variation on your

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
Highlighted
Beginner
6 Views

In the DLL's I had written

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
Highlighted
6 Views

Please show the C#

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?

Retired 12/31/2016
0 Kudos
Highlighted
6 Views

Build a test program in C++

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
Highlighted
Beginner
6 Views

First off, you got me about 1

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
Highlighted
Beginner
6 Views

One more thing I just noticed

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
Highlighted
6 Views

I was referring to Fortran

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?

Retired 12/31/2016
0 Kudos
Highlighted
Beginner
6 Views

No, I don't think the array

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
Highlighted
6 Views

Could you please provide a

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.

Retired 12/31/2016
0 Kudos
Highlighted
6 Views

I will let Steve experiment

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
Highlighted
New Contributor III
6 Views

Hello,

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
Highlighted
6 Views

Again this may be my

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.

Retired 12/31/2016
0 Kudos
Highlighted
Beginner
6 Views

Steve, Greg & Jim

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
Highlighted
6 Views

The fundamental problem is as

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.

Retired 12/31/2016
0 Kudos
Highlighted
Beginner
6 Views

Steve,

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