PSCAD Support
unknown
fortran
21 days ago
12 kB
7
Indexable
Never
!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ! DLL-Course Demo !------------------------------------------------------------------------------ ! This is a demo that is part of the DLL-Course for creating EMTDC components ! that are primarily executed through a DLL. ! ! Created By: ! ~~~~~~~~~~~ ! PSCAD Design Team <pscad@hvdc.ca> ! Manitoba HVDC Research Centre Inc. ! Winnipeg, Manitoba. CANADA ! !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !----------------------------------------------------------------------------------------------- ! This code is for linking to PSCAD for an example of how to create wrapper function that are ! exported. A DLL may import and call those function. ! ! Instructions: ! 1. write a wrapper function that will be decorated (exported) that will wrap any ! functionality (function calls / subroutine calls) required by this DLL ! ! 2. Imported the decorated functions from the DLL ! ! 3. Call the DLL functions through the wrapper !----------------------------------------------------------------------------------------------- !====================================================================== ! DynamicLoad !---------------------------------------------------------------------- ! This module interfaces definitions of each of the functions to be ! imported and a copy of the handle of the DLL and all the function ! pointers !---------------------------------------------------------------------- MODULE DynamicLoad ! Module holds all of the pointers/interfaces for accessing our DLL USE, INTRINSIC::ISO_C_BINDING ! provides access to required HANDLEs and Pointer types IMPLICIT NONE ! Implicit none will make debugging easier, by preventing complication of variables/types we did not declare. !------------------------------------------------------------ ! INTERFACEs ! ! This should contain a list of interfaces for each of the ! function/subroutines that are to be imported from the ! DLL. ! ! In this example We are importing the function that has ! 2 Integers as inputs and 1 Integer as an output. ! ! It should be noted that multiple functions can share a ! single interface. ! !------------------------------------------------------------ ABSTRACT INTERFACE ! We define an abstract interface as there is no concrete function behind it untill we load the DLL SUBROUTINE rt_onestep(freq, mod, Ia, Ib, Ic) ! We must provide what the function would look like if calling normally REAL, INTENT(IN) :: freq ! Defining each of the parameters of this function. REAL, INTENT(IN) :: mod ! Defining each of the parameters of this function. REAL, INTENT(OUT) :: Ia ! Defining each of the parameters of this function. REAL, INTENT(OUT) :: Ib ! Defining each of the parameters of this function. REAL, INTENT(OUT) :: Ic ! Defining each of the parameters of this function. END SUBROUTINE rt_onestep END INTERFACE ! End of the interface !------------------------------------------------------------ ! Function Pointers ! ! This will require the list of function pointers that point ! to the function imported from the DLL ! ! In this example we are importing Dll Function !------------------------------------------------------------ PROCEDURE(rt_onestep), POINTER :: rt_onestepPointer => NULL() !------------------------------------------------------------ ! DLL Handler ! ! This is the handle pointer to the DLL !------------------------------------------------------------ INTEGER(C_INTPTR_T) :: DLL_HANDLE = 0 ! This variable will hold the handle to the DLL, that we can can see if the DLL is loaded. CONTAINS ! Denotes the module subroutines start here !====================================================================== ! IMPORT_ROUTINES !---------------------------------------------------------------------- ! This function will import the DLL. ! Note: this is standard DLL importing. Not specific to this example !---------------------------------------------------------------------- SUBROUTINE IMPORT_ROUTINES ! We need to define a routing that will load the DLL and extract the functions. ! Required for library import functions for Intel ! This will provided the LoadLibrary and GetProcAddress function automatically for Intel Fortran ! #ifdef __INTEL_COMPILER USE KERNEL32 #endif USE, INTRINSIC :: ISO_C_BINDING ! Required for ISO Binding definitions IMPLICIT NONE ! GFortran Requires using Interfaces to import the LoadLibrary and GetProcAddress ! Functions. ! #ifdef __GFORTRAN__ INTERFACE FUNCTION LoadLibrary(lpFileName) BIND(C,NAME='LoadLibraryA') USE, INTRINSIC :: ISO_C_BINDING IMPLICIT NONE CHARACTER(KIND=C_CHAR) :: lpFileName(*) !GCC$ ATTRIBUTES STDCALL :: LoadLibrary INTEGER(C_INTPTR_T) :: LoadLibrary END FUNCTION LoadLibrary FUNCTION GetProcAddress(hModule, lpProcName) & BIND(C, NAME='GetProcAddress') USE, INTRINSIC :: ISO_C_BINDING IMPLICIT NONE !GCC$ ATTRIBUTES STDCALL :: GetProcAddress TYPE(C_FUNPTR) :: GetProcAddress INTEGER(C_INTPTR_T), VALUE :: hModule CHARACTER(KIND=C_CHAR) :: lpProcName(*) END FUNCTION GetProcAddress END INTERFACE #endif ! Import the DLL, We provide a relative path to the DLL that is going to be loaded. The path ! is relative to where the EXE will reside. ! ! #ifdef __INTEL_COMPILER #ifdef _WIN64 DLL_HANDLE = LoadLibrary(C_CHAR_'../DLLSource/x64/CInterface0_win64.dll'//C_NULL_CHAR) #else DLL_HANDLE = LoadLibrary(C_CHAR_'../DLLSource/x86/CInterface0_win64.dll'//C_NULL_CHAR) #endif #endif #ifdef __GFORTRAN__ #if __SIZEOF_POINTER__ == 8 DLL_HANDLE = LoadLibrary(C_CHAR_'../DLLSource/CInterface0_win64.dll'//C_NULL_CHAR) #endif #endif IF (DLL_HANDLE .EQ. 0) THEN CALL print_error ('loading DLL') STOP ENDIF ! Extract the function pointers by name. Each of the function is extracted using the GetProcAddress ! then converted to the correct Function Pointer type. The conversion of function pointers is ! compiler dependent so we must use the correct function to perform it by detecting which type of ! compiler is being used. ! #if defined (__INTEL_COMPILER) CALL C_F_POINTER(TRANSFER(GetProcAddress(DLL_HANDLE, C_CHAR_'rt_onestep'//C_NULL_CHAR), C_NULL_PTR), rt_onestepPointer) IF (.NOT. ASSOCIATED(rt_onestepPointer)) THEN CALL print_error ('looking up routine') STOP ENDIF #elif defined (__GFORTRAN__) CALL C_F_PROCPOINTER(GetProcAddress(DLL_HANDLE, C_CHAR_'rt_onestep'//C_NULL_CHAR), rt_onestepPointer) IF (.NOT. ASSOCIATED(rt_onestepPointer)) THEN CALL print_error ('looking up routine') STOP ENDIF #endif END SUBROUTINE IMPORT_ROUTINES ! After Extracting the function the subroutine is done. ! Error processing routine. Gets the system error and ! its corresponding string, prints a message, then stops ! execution ! SUBROUTINE print_error (string) #ifdef __INTEL_COMPILER USE KERNEL32 #endif USE, INTRINSIC :: ISO_C_BINDING IMPLICIT NONE #ifdef __GFORTRAN__ INTERFACE FUNCTION GetLastError() BIND(C,NAME='GetLastError') USE, INTRINSIC :: ISO_C_BINDING IMPLICIT NONE !GCC$ ATTRIBUTES STDCALL :: GetLastError INTEGER(C_LONG) :: GetLastError END FUNCTION GetLastError END INTERFACE #endif character(*), intent(IN) :: string write (*,'(3A,Z8.8)') "An Error occurred while ", string, ": error status = ", GetLastError() ! INTEGER(C_LONG) :: last_error ! integer(C_LONG) :: nTchars ! integer(HANDLE) :: ret ! type(C_PTR) :: message_buffer_cptr ! character, pointer :: message_buffer(:) ! Get the actual system error code ! ! last_error = GetLastError () ! Get the string corresponding to this error ! Use the option to have Windows allocate the message buffer - it puts the ! address in the lpBuffer argument. Here we pass it the C_PTR message_buffer_cptr, ! using TRANSFER to cast the address to an LPVOID. FORMAT_MESSAGE_IGNORE_INSERTS ! is used so that it doesn't try looking for arguments - a possible security violation. ! Again, we're using C interoperability features. ! ! nTchars = FormatMessage ( & ! dwFlags=IANY([FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS, FORMAT_MESSAGE_ALLOCATE_BUFFER]), & ! lpSource=NULL, & ! ignored ! dwMessageId=last_error, & ! dwLanguageId=0, & ! will use a default ! lpBuffer=TRANSFER(C_LOC(message_buffer_cptr), 0_LPVOID), & ! nSize=100, & ! minimum size to allocate ! arguments=NULL) ! if (nTchars == 0) then ! write (*,'(A,Z8.8,3A,Z8.8)') "Format message failed for status ", last_error, " while ", & ! string, ": error status = ", GetLastError() ! else ! ! message_buffer_cptr is now pointing to the message. Use C_F_POINTER to convert ! ! this to an array of characters. Note the use of the Fortran 2008 unlimited ! ! repeat count specifier. ! call C_F_POINTER (message_buffer_cptr, message_buffer, [nTchars]) ! write (*,'(3A,*(A))') "Error while ", string, ": ", message_buffer ! ! Free the memory Windows allocated for the message string ! ret = LocalFree (hMem=TRANSFER(message_buffer_cptr, 0_HANDLE)) ! end if ! stop END SUBROUTINE print_error END MODULE DynamicLoad ! We have finished defining everything that we know about the DLL, we can end the module definition. SUBROUTINE rt_onestep_begin USE DynamicLoad CAll IMPORT_ROUTINES() END SUBROUTINE rt_onestep_begin SUBROUTINE RT_ONESTEP_DYNAMICS(freq,mod,Ia,Ib,Ic) USE DynamicLoad INCLUDE "nd.h" INCLUDE "s0.h" INCLUDE "branches.h" INCLUDE "CInterface0.h" REAL freq,mod REAL Ia,Ib,Ic ! Now call the function in a loop ! ! write (*,'(/A, G0)') "Calling USERFUNC with argument ", i CALL rt_onestepPointer(freq, mod, Ia, Ib, Ic) ! write (*,'(A,G0)') "USERFUNC returned ", ret ! End of main program RETURN END SUBROUTINE RT_ONESTEP_DYNAMICS
Leave a Comment