Hi: Is it possible to make use of the return value of the windows API call GetEnvironmentStrings()? It appears to return integer(LPSTR); but, I can't figure out how to convert this to a Fortran CHARACTER variable.
Use TRANSFER to convert the result to TYPE(C_PTR). Then compute the length via strlen() from MSVCRT.DLL and call a subroutine with those two results: call sub(ptr,length). The subroutine sub will have a local variable declared as character(length), pointer :: envstr and then call C_F_POINTER(ptr, envstr). Envstr now points at your string. You may want to consider GetEnvironmentVariable(); I think ifort does not yet support the GET_ENVIRONMENT_VARIABLE intrinsic.
(What RO said, but note that strlen will just give you the length of the first environment variable/value pair - you need to write your own variant that goes looking for the double null terminator. You can do that in Fortran using BIND(C) tricks.)
Thanks RO and IanH! With your help, I was able to access the contents. Even FreeEnvironmentStrings() appears to work with the coerced envptr variable.
I have made a small example how to obtain a string from the GetEnvironmentString function. The determination of the length of this string, in advance the C_F_POINTER operation, is unclear to me. Any idea how to solve this in a standard-comforming ways?
The GET_ENVIRONMENT_VARIABLE subroutine in Intel works fine (most recent update.)
I would recommend using the Fortran standard intrinsic GET_ENVIRONMENT_VARIABLE, as Robert suggests.
Just for fun, I made up an example that uses strlen to determine the length of the environment strings. Tested it with gfortran; I don't know what modifications env.f90 requires to function on ifort.
Robert: Thanks! That's more or less what my code looks like. I don't like having to limit the size of the string; according to the MS documentation, the size of the evironment in windows 7 and later is essentially unlimited. I don't know what else to do without adopting RO's scheme.
Steve: Thanks, too! For diagnostic purposes, I need to see all the environment variables.
RO: Thanks again, too! Very clever. I think your declaration of GetEnvrionmentStrings as returning a C_PTR makes a bit more sense than the compiler's translation to integer(LPSTR).
The declaration as returning an integer predates Fortran 2003. Can't change it now, unfortunately. TRANSFER is your friend here.
Allen: the reason to be worried on the length of the string and thus asks for a way to determine its length in advance is that I do not like to have arrays, strings etc. larger than needed. But it is not critical. And if you cannot find a \0\0 string, it is surely too short and must be reallocated.
Steve: I played some more with the intrinsic get_environment_variable and I found that problems arise if the length of the string that gets the value (second argument) is too short to store that value. I expect in that case the value argument to be truncated and the status argument (4th argument) to be set to -1, but I got severe error 408. Setting different compiler options does not resolve it. What is going wrong here? (See att. file)
Sorry, I made an error in the example added with the previous post..
I wanted to print the result (value as a string) and in that line the error occurred because I tried to print that string with the length as given by the intrinsic. And that went wrong. It thus has nnoting to do with any error of get_environment_variable.
My previous example was less elegant than it could have been for two reasons. First, C_F_POINTER doesn't directly allow you to point at an assumed-size array, in fact there is no way to do so. However, the C function strlen() has the interface
size_t strlen(const char *str)
so you could write two styles of interface body for it:
type(C_PTR), value :: str
end function strlen1
character(kind=C_CHAR), intent(in) :: str(*)
end function strlen2
end interface strlen
Similarly one could write an implementation using either form. Accordingly, we have created a subroutine with the second form for implementation (but in Fortran, not C) and have written out an interface block that allows the compiler to invoke it either way at our discretion.
As a result, we have converted a type(C_PTR) value in the caller to an assumed-size array in the callee.
Another way would have been to use cray pointers, as they can point at an assumed-size array.
Second, we would like to point a deferred-length pointer at the memory pointed at by a variable of type(C_PTR), but again there is no syntax for achieving this directly.
Here, a three step process is necessary:
1) create a scope where the desired length is available as a specification expression and declare a scalar pointer of this length
2) point the pointer alluded to above at the memory via C_F_POINTER
3) point the deferred-length pointer at the previous pointer's target via pointer assignment.
I have created two examples, env3.f90 uses the multiple interface method to get the assumed-size array and a subroutine to create the new scope, while env4.f90 uses cray pointers to point at an assumed-size array and a BLOCK construct to create the new scope.
I don't know whether either will compile in ifort, you will just have to try.