Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28629 Discussions

Dereferencing a C++ array descriptor using ALLOCATEABLE array

anthonyrichards
New Contributor III
485 Views

A recent thread has shown how Jugoslav Dujic's wrappers can be used to access MAPI functionality.

A typical call to the MAPIReadMail function returns a pointer to a MAPIMessage structure (code
taken from Jugoslav's MAPI module, with thanks)

type T_MapiMessage
SEQUENCE
integer ulReserved ! Reserved for future use (M.B. 0)
integer(LPSTR) lpszSubject ! Message Subject
integer(LPSTR) lpszNoteText ! Message Text
integer(LPSTR) lpszMessageType ! Message Class
integer(LPSTR) lpszDateReceived ! in YYYY/MM/DD HH:MM format
integer(LPSTR) lpszConversationID ! conversation thread ID
integer flFlags ! unread,return receipt
integer(LPVOID) lpOriginator ! Originator descriptor
integer nRecipCount ! Number of recipients
integer(LPVOID)lpRecips ! Recipient descriptors
integer nFileCount ! # of file attachments
integer(LPVOID) lpFiles ! Attachment descriptors
end type T_MapiMessage

This works fine and message structure can be populated OK.
The main problem next is to recover the list of recipients and the list of
files (which turns out to include the text of the mail message as a temporary file)
using the descriptor pointers.

What the MAPIMessage structure supplies is a pointer to a descriptor which, in the case of multiple
recipients and multiple files, is an array.

For recovering the list of recipients, what I have tried so far is the following:

type(t_MapiRecipDesc) Recipients(5)
integer lprecips
pointer(lprecips, Recipients)
...
character(100) Receiver
pointer(lpreceiver,Receiver)
!
type(t_MapiMessage) :: NewMsg
pointer(lppmessage,NewMsg)
!
character(3) chrecipcount, chfilecount, chir
...
iret = MAPIReadMail(hSession, 0, MsgID, MAPI_BODY_AS_FILE, 0,lppmessage )
...
if(newmsg%lprecips.ne.0) then
if(newmsg%nrecipcount.gt.0) then
lprecips=newmsg%lprecips
do ir=1,newmsg%nrecipcount
write(chir,'(i3)') ir
lpreceiver=Recipients(ir)%lpszname
imess=MessageBox(0,trim(Receiver)//char(0),"Message Receiver no. "//chir//char(0),mb_ok)
end do
endif
endif

Note that I have dimensioned an array Recipients(5) not knowing how many actual recipients there may be
(theoretically I could make it larger than any likely list length, but this seems inefficient storage-wise)
and I use the simplest pointer that I know how to use.

This code works and I can list the recipients (and the file list using a similar dimensioning of an array).
Given that the following is not permitted according to the compiler:

type(t_MapiRecipDesc), allocatable :: Recipients(:)
integer lprecips
pointer(lprecips, Recipients)

what I am enquiring from the forum is whether there is a way of recovering the recipient array using array
allocation, having established the number of recipients using newmsg%nrecipcount ? (the same query
applies to recovering the list of files)

0 Kudos
3 Replies
Jugoslav_Dujic
Valued Contributor II
485 Views

For recovering the list of recipients, what I have tried so far is the following:

type(t_MapiRecipDesc) Recipients(5)
integer lprecips
pointer(lprecips, Recipients)


When you declare a (pointer, pointee) pair as above, the pointee array does not occupy any storage, thus does not waste any memory. You may declare it with a dimension of (1000000) without any consequences (as long as you don't access any undefined members). Thus, they're similar to assumed-size arguments, and indeed, there is the alternate, cleaner, syntax:

type(t_MapiRecipDesc) Recipients(*)
pointer(lprecips, Recipients)


which has an ugly side effect of not being able to watch all of them in the debugger.

These days, I prefer using F2003 C interoperability. With it, you would use a C pointer (type(C_PTR)) and convert it to a standard Fortran pointer to an array, like this:

type(t_MapiRecipDesc), pointer:: Recipients(:)
type(C_PTR):: recips
!Get the value of recips through somehow
...
!convert recips to Fortran pointer of given shape, similar to RESHAPE:
call C_F_POINTER(recips, Recipients, (/
newmsg%nrecipcount/))

Alas, C interoperability does not play nice with IFWIN interfaces (which still use -- and will continue to -- DEC$ATTRIBUTES syntax). In particular, Intel does not support STDCALL + BIND(C) combination (for functions), so your portability in this area is somewhat limited.
0 Kudos
anthonyrichards
New Contributor III
485 Views
Many thanks, JD. I was unaware of the usage

type(t_MapiRecipDesc) Recipients(*)
pointer(lprecips, Recipients)


The compiler likes it!

Regarding your alternative suggestion:

type(t_MapiRecipDesc), pointer:: Recipients(:)
type(C_PTR):: recips
!Get the value of recips through somehow

Getting the value of 'recips' is indeed the crux. Are you implying here that I cannot just use
the integer pointer recovered from the T_MAPIMessage structure?
0 Kudos
Jugoslav_Dujic
Valued Contributor II
485 Views
Well, a type(C_PTR) is by definition interoperable with a C pointer. Thus, it is also interoperable with a Cray (integer) pointer.

Since we (i.e. you and I) have the control over MAPI.f90, we could have used

[bash]type T_MapiMessage
    ...
    integer nRecipCount      ! Number of recipients                   
    type(C_PTR) lpRecips     ! Recipient descriptors                  
[/bash]

If not, we can use the fact that type(C_PTR) contains the integer pointer, so you can "cast" one to another using TRANSFER:

[bash]type T_MapiMessage
...
integer nRecipCount ! Number of recipients
integer(LPVOID) lpRecips ! Recipient descriptors

type(T_MAPIRecipDesc), pointer:: Recipients(:)
type(T_MAPIMessage):: msg
type(C_PTR):: cptr
...
ptr = TRANSFER(msg%lpRecips, cptr)
call C_F_POINTER(cptr, Recipients, [msg%nRecipCount])
[/bash]
or, in one line (though it's ugly:)
[bash]call C_F_POINTER(TRANSFER(msg%lpRecips, cptr), Recipients, [msg%nRecipCount])[/bash]
It's more or less a style choice. I generally prefer standard solutions (C interoperability), but they are 1) a bit more verbose 2) not applicable all the way (because you cannot use both STDCALL and BIND(C)) and 3) Not applied by Intel in IFWIN modules, so we're forced to do mixed-style anyway.
0 Kudos
Reply