Software Archive
Read-only legacy content
17061 Discussions

passing an LPCSTR or LPCTSTR string to an API routine, CryptAcquireContext()

Intel_C_Intel
Employee
635 Views
Hello,

The general question is how can I pass a string from a Fortran
routine into an API routine?

In some API routines the pointer of the string is passed in;
for example, using the LOC() function, LOC(string_name), will
send the pointer to the string to the API routine, and then
the API routine happily uses the string.

Now, in other API routines that have a string as an argument
the interface definition is indicated as:
character*(*) string_name
and the line before that has:
!DEC$ ATTRIBUTES REFERENCE :: string_name
In this second case it looks like a pointer is not indicated,
rather the string should be passed to the API routine. Since
the API routine will need a null terminated string we can add
the null termination to the string in the Fortran routine by
using:

i = LEN_TRIM(string_name) + 1 ! add 1 character for the null
character
string_name(i:i) = CHAR(0) ! add the null character to
terminate the string
and then pass string_name(:i) to the API routine

If we put the string_name(:i) variable in the call statement
to the API routine we get a compiler error:
Error: The type of the actual argument differs from the type of the
dummy argument.

This seems to say that there is a variable type mismatch
between the character variable type for string_name in the
Fortran routine and the LPCSTR string variable that the API
routine is expecting. The LPCSTR variable type is given in
the help listing for Visual Fortran and is described as a
"pointer to a constant null-terminated string of 8-bit (ANSI)
Windows characters". That is a little confusing since LPCSTR
wants a "pointer", but a character variable is given in the
interface definition (see particular example below).

Is there a way to pass the string_name character variable
from the Fortran routine to the API routine as the needed
LPCSTR type (string or a type of pointer)?
We have tried to use a pointer with the LOC() function but
that also does not work (same compiler error).

Specifically in our case we are trying to use the
CryptAcquireContext() API routine to get a handle to a hash
object. We are getting the compiler error at the third
argument, pszProvider, the CSP name (Cryptographic Service
Provider). Our call statment to the API routine is:

lResult = CryptAcquireContext(m_lProvider, NULL,
CRYPTO_PROVIDER_NAME, CRYPTO_PROV_RSA_FULL, CRYPTO_VERIFY_CONTEXT)

Where m_lProvider will be the handle returned by the API
routine and stored as an integer, NULL is a null string
(for a default argument), CRYPTO_PROVIDER_NAME is the string
that we are having the trouble with, it is declared as
character(len=80) :: CRYPTO_PROVIDER_NAME, and
CRYPTO_PROV_RSA_FULL and CRYPTO_VERIFY_CONTEXT are both
integers (flags we set).

The interface statement from ADVAPI32.f90 (in the DF98
Include folder) is:

INTERFACE
FUNCTION CryptAcquireContext( &
phProv, &
pszContainer, &
pszProvider, &
dwProvType, &
dwFlags)
USE DFWINTY
integer(BOOL) :: CryptAcquireContext ! BOOL
!DEC$ ATTRIBUTES DEFAULT :: CryptAcquireContext
!DEC$IF DEFINED(_X86_)
!DEC$ ATTRIBUTES STDCALL, ALIAS:'_CryptAcquireContextA@20' ::
CryptAcquireContext
!DEC$ ELSE
!DEC$ ATTRIBUTES STDCALL, ALIAS:'CryptAcquireContextA' ::
CryptAcquireContext
!DEC$ ENDIF
!DEC$ ATTRIBUTES REFERENCE :: phProv
integer(ULONG) phProv ! HCRYPTPROV* phProv
!DEC$ ATTRIBUTES REFERENCE :: pszContainer
character*(*) pszContainer ! LPCSTR pszContainer
!DEC$ ATTRIBUTE
0 Kudos
2 Replies
Intel_C_Intel
Employee
635 Views
Your message was also posted to c.l.f. I replied there, but I'll paste it in here too.

Hi,

Whether a string is passed to a Win32 API routine as just 'string_name' or as
'loc(string_name)' depends on how the interface block for that routine defines that string dummy argument. If the dummy argument is defined as an integer passed by value (byval as a result of the stdcall attribute applied to the routine itself) then 'loc(string_name)' is used. If the dummy argument is defined as an assumed length character variable with the reference attribute, then 'string_name' is used to pass the string. How and why this works is discussed in detail in the CVF Programmer's Guide, Programming with Mixed Languages chapter.

The inconsistency in the way strings are passed in different API functions is a by product of backward compatibility with MS FPS. It seems as though the MS generated interface blocks to API functions just punted on the Fortran to C language mapping and passed all pointer variables (including strings and intent(inout) variables) as integers passed by value - hence the use of loc(). Many of the Compaq/Digital generated interface blocks provide a more natural language mapping in most cases (with some notable shortcomings here and there). The desire not to break backward compatibility leaves users without a _consistent_ Fortran to C language mapping. This forces users to always check the declaration of the interface block of each and every API function used to see how to pass things like strings and out parameters. Some time ago I believed that a solution was being evaluated for this issue, but I don't know what its current status is.

As for your specific function call and error message, it looks like you are likely passing the third parameter correctly. But the second parameter looks suspect. In module dfwinty (used in module advapi32), NULL is declared as

integer, parameter :: NULL = 0

but based on the interface block the second parameter should be an assumed length character variable. This is the argument giving you the

Error: The type of the actual argument differs from the type of the dummy argument.

Instead, you can use NULL_CHARACTER, like so:

! ...
nullify(NULL_CHARACTER) ! sure wish this was initialised with null()
lResult = CryptAcquireContext(m_lProvider, NULL_CHARACTER, &
CRYPTO_PROVIDER_NAME, CRYPTO_PROV_RSA_FULL, RYPTO_VERIFY_CONTEXT)
! ...

or alternatively you can override the interface block with the %val(), although this working may depend on what version of CVF you're using - probably 6.0+ will do.

lResult = CryptAcquireContext(m_lProvider, %val(0), &
CRYPTO_PROVIDER_NAME, CRYPTO_PROV_RSA_FULL, RYPTO_VERIFY_CONTEXT)

hth,
John
0 Kudos
Steven_L_Intel1
Employee
635 Views
A hint as to the status can be seen in the new ALLOW_NULL and IGNORE_LOC attributes we added in CVF 6.5. In the next update of CVF, we will be replacing the current Win32 interfaces with a completely new set, automatically generated from the current MS .h files. For arguments that are "strings", they will be consistently declared as CHARACTER in the interfaces. Where appropriate, the new ALLOW_NULL and/or IGNORE_LOC attributes will be included to provide backward compatibility with existing sources. For example, consider the pesky routine RegCreateKeyEx, whose interface currently looks like this (ignore the wrapping, please):

integer(4) function  RegCreateKeyEx  (hKey ,lpSubKey ,Reserved ,lpClass ,dwOptions ,samDesired ,
lpSecurityAttributes ,phkResult ,lpdwDisposition  ) 
!DEC$ ATTRIBUTES DEFAULT :: RegCreateKeyEx
!DEC$ IF DEFINED(_X86_)
!DEC$ ATTRIBUTES STDCALL, ALIAS : '_RegCreateKeyExA@36'   :: RegCreateKeyEx  
!DEC$ ELSE
!DEC$ ATTRIBUTES STDCALL, ALIAS :  'RegCreateKeyExA'      :: RegCreateKeyEx  
!DEC$ ENDIF
!DEC$ ATTRIBUTES REFERENCE :: lpSubKey
!DEC$ ATTRIBUTES REFERENCE :: lpClass
!DEC$ ATTRIBUTES REFERENCE :: lpSecurityAttributes
use dfwinty
integer                     hKey
character*(*)               lpSubKey
integer                     Reserved
character*(*)               lpClass
integer                     dwOptions
integer                     samDesired
type(T_SECURITY_ATTRIBUTES) lpSecurityAttributes
integer                     phkResult
integer                     lpdwDisposition   
end function RegCreateKeyEx


Note that lpSecurityAttributes, phkResult and lpdwDisposition are just integers (passed by value, implied by the explicit STDCALL attribute.) If you want to pass an actual variable, you have to pass LOC(variable), and if you want to omit lpSecurityAttributes, you have to use the predeclared NULL_SECURITY_ATTRIBUTES.

Here's what it will look like in the future:

INTERFACE 
FUNCTION RegCreateKeyEx( &
        hKey, &
        lpSubKey, &
        Reserved, &
        lpClass, &
        dwOptions, &
        samDesired, &
        lpSecurityAttributes, &
        phkResult, &
        lpdwDisposition)
USE DFWINTY
  integer(LONG) :: RegCreateKeyEx ! LONG
    !DEC$ ATTRIBUTES DEFAULT, STDCALL, DECORATE, ALIAS:'RegCreateKeyExA' :: RegCreateKeyEx
  integer(HANDLE) hKey ! HKEY hKey
!DEC$ ATTRIBUTES REFERENCE, IGNORE_LOC, ALLOW_NULL :: lpSubKey
  character*(*) lpSubKey ! LPCSTR lpSubKey
  integer(DWORD) Reserved ! DWORD Reserved
!DEC$ ATTRIBUTES REFERENCE, IGNORE_LOC, ALLOW_NULL :: lpClass
  character*(*) lpClass ! LPSTR lpClass
  integer(DWORD) dwOptions ! DWORD dwOptions
  integer(ULONG) samDesired ! REGSAM samDesired
!DEC$ ATTRIBUTES REFERENCE, IGNORE_LOC, ALLOW_NULL :: lpSecurityAttributes
  TYPE (T_SECURITY_ATTRIBUTES) lpSecurityAttributes ! LPSECURITY_ATTRIBUTES lpSecurityAttributes
!DEC$ ATTRIBUTES REFERENCE, IGNORE_LOC :: phkResult
  integer(HANDLE)  phkResult ! PHKEY phkResult
  integer(LPDWORD) lpdwDisposition ! LPDWORD lpdwDisposition
 END FUNCTION
END INTERFACE


The interesting differences are:


  1. Explicit specification of KINDs (the KIND values are declared in DFWINTY.)
  2. The last two arguments are now specified as passed by reference, so no LOC() is needed. The IGNORE_LOC attribute provides backwards-compatibility for code that uses LOC.
  3. The arguments which can be omitted also have ALLOW_NULL, allowing use of NULL or NULL() (your choice), rather than NULL_CHARACTER, NULL_SECURITY_ATTRIBUTES, etc.
  4. The DECORATE attribute replaces the conditionalization on platform - this says "apply this platform's default decoration rules to the ALIAS specified."


We've been doing extensive testing of these new modules, and found that there are very few problems with old sources. Of the few problems we've encountered, nearly all deal with updated derived type declarations where MS redefined fields - some code may need minor changes for such cases. Of course, we'll document all such changes we're aware of.

Look for this update in August or September - it will be free to CVF 6.5 users. (If you're currently at 6.0 or 6.1, I suggest taking advantage now of the special $149 upgrade price for registered users, as it will probably end at about the same time. For details, see our web site.)
0 Kudos
Reply