简体   繁体   中英

How to add unmanaged dll to c# project

This question has been asked before, and I have gained much from reviewing them. Nevertheless, I am still missing some key step in the process. I have developed a Windows Forms application consisting of several Fortran executables operating under the control of a gui written in C#. The Fortran programs are compiled with Intel Visual Fortran and the C# code by Visual Studio 2015 using .NET 4.5. I am now attempting to convert one of the Fortran programs to a DLL. This is my first attempt to develop and implement a DLL and am having troubles getting everything right. Hopefully, someone in this community can steer me in the correct direction.

The only element in the DLL to be exposed to the public is a subroutine with the following interface:

subroutine PlanetState(path, n, date, id, mu, s, errmsg)
   character(n), intent(in)    :: path      ! the path name to an ephemeris file
   integer, intent(in)         :: n     ! the length of path
   real(8), intent(in)         :: date      ! a Julian date
   integer, intent(in)         :: id        ! a planet number
   real(8), intent(out)        :: emu       ! a parameter used in the calculations
   real(8), intent(out)        :: s(9)      ! an array of state variables
   character(256), intent(out) :: errmsg    ! 256-byte error message that may be returned
end subroutine PlanetState

This and several supporting routines are embedded in a Fortran module. A separate Fortran main program was developed to call and test the PlanetState subroutine to assure the code works correctly in a direct call. Once this was accomplished, compiler directives for the intel compiler were added at the top of the declarative section of the subroutine, as follows:

   !DEC$ ATTRIBUTES DLLEXPORT :: PlanetState
   !DEC$ ATTRIBUTES ALIAS: 'PlanetState' :: PlanetState
   !DEC$ ATTRIBUTES REFERENCE :: Path, N, Date, PlanID, Emu, S, ErrMsg

The module was then re-compiled and successfully linked to a dll file named DEeph.dll.

In the C# application, a class was added as a wrapper for the P/Invoke code, as follows:

using System.Runtime.InteropServices;
namespace myNamespace
{
    public static class FortranDlls
    {
        [DllImport("DEeph.dll", EntryPoint = "PlanetState", CallingConvention = CallingConvention.Cdecl)]
        extern public static void PlanetState(char[] path, ref int n, ref double date, ref int planId, 
                           out double emu, out double[] s, out char[] errMsg);
   }
}

After declaring and initializing the in parameters and declaring the out parameters, a C# call is made to the subroutine PlanetState as follows:

   try
   {
       FortranDlls.PlanetState(dePath, ref n, ref date, ref id, out emu, out state, out errorMsg);
   }
   catch (Exception ex)
   {
       MessageBox.Show(ex.Message, "DLL Error", MessageBoxButtons.OK);
   }

Finally, the DLL itself is added to the project solution with the property 'Copy if newer' assigned as a build action. I then successfully rebuilt the solution, but upon execution the program breaks at the call to PlanetState with the error message 'FatalExecutionEngineError occurred'. An error code of 0xc0000005 is given, which I believe is the code for AccessViolationException. The catch clause following the call was not executed.

Obviously, I screwed up or overlooked some step(s) in properly incorporating the dll in the project. Can anyone point to the cause of the problem. Thanks for any help you are able to provide.

After several days of further experimentation, I have solved most of my issues. An early key to solving the problem was the realization that I needed to set a debugger property to allow native code debugging. This property is set in the VS project properties/debug window. With this setting, I was able to follow execution in the debugger as it transitioned from the main (C#) program to the dll. This allowed me to trace the error to the read of the first record of a direct access file. The read statement appeared as follows:

read (3, rec = 1) ttl, ((Ipt(I, J), I = 1, 3), J = 1, 12), ...

where ttl is a doubly dimensioned array of integers and Ipt is a 3 x 13 array of integers. On examining the values after the break occurred, I found that ttl contained the correct values and Ipt did not. The implication here is that the implied do-loop is failing. To test this I declared a new array Jpt dimensioned 3 x 12 and changed the read statement as follows:

read (3, rec = 1) ttl, Jpt, ...

which did work correctly. I believe this may represent a bug in the Intel run-time system because the code with the implied do-loops works when called directly from a Fortran main program. After eliminating all implied do loops in the DLL, the code works correctly until the end of the subroutine was reached. On the step to return to the calling program, an AccessViolationException was raised. I suspected this problem was caused by the out character array ErrMsg, so to defer that issue to later, I revised the program to write any error messages to a file on disk and removed the parameter from the subroutine signature. Once that was done, the program runs to completion. However, it was then noticed that the values in the array S of doubles were all zero even though they held the correct values at the end of the subroutine in the DLL. On further experimentation, it was learned that by initializing the values of S in the caller program and removing the out keyword in the subroutine signature, the correct values were returned to the main program. This would also seem to be an error in the Intel RT or its integration with VS 2015.

So I now have a working DLL that writes error messages to an external file. I hope to find a way to return messages through the calling arguments, but after several attempts with differing approaches I have found no success. I intend to post a separate question to the community for help on this. Anyway, I hope this discussion helps others who run into similar problems.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM