Calling Release() for IStream after OpenProperty

  • Thread starter Thread starter Jim
  • Start date Start date
J

Jim

Hello,

I'm still working on my Extended MAPI wrapper code to avoid the
Outlook security warning when reading objMailItem.Body.

This approach seems to work:

HrMAPIFindDefaultMsgStore
OpenMsgStore
OpenEntry
HrGetOneProp(.., PR_BODY, ..)
if MAPI_E_NOT_FOUND
no body text -- return ok
if ! MAPI_E_NOT_ENOUGH_MEMORY
return body text from Value.lpszA
OpenProperty(PR_BODY, ..)
IStream->Read(bf, ..)
return body text in bf

But I ran into a problem where after quite a lot of processing
(induced by scrolling through 1000 Inbox items several times, reading
each Inbox item via the Extended MAPI wrapper code above), the call
from Outlook to the Extended MAPI wrapper logic above would suddenly
slow down. Each call would suspend for about five seconds and then
succeed. It was as if I ran into some kind of Windows internal
resource timeout and recovery logic.

I tried a lot of different things, including disabling different parts
of the cleanup code (not shown above). The problem finally went away
when I *stopped* calling Release() on the IStream used to read PR_BODY
after the call to OpenProperty.

I went back and reviewed several web examples of Extended MAPI source,
and found that some call Release() for the IStream and some don't.

Here is one that calls Release():

http://www.wischik.com/lu/programmer/mapi_utils.html

char *bodybuf=0; unsigned int bodysize=0;
IStream *istream;
hr = imsg->OpenProperty(PR_BODY, &IID_IStream, STGM_READ,
0, (IUnknown**)&istream);
if (hr==S_OK)
{
AvailableOffline = true;
STATSTG stg = {0};
hr = istream->Stat(&stg,STATFLAG_NONAME);
if (hr==S_OK)
{
bodysize = stg.cbSize.LowPart;
bodybuf = new char[bodysize+1];
ULONG red; hr = istream->Read(bodybuf, bodysize, &red);
if (hr!=S_OK) bodysize=0;
else if (red<bodysize) bodysize=red;
bodybuf[bodysize]=0;
}
istream->Release();
}

Whereas Microsoft in this source example does *not* call Release() for
the IStream:

http://support.microsoft.com/?kbid=239795

Yet Microsoft states elsewhere, in a more general discussion, that
Release() should be called:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
exchserv/html/perform_0vjn.asp

Use the Win32 IStream interface functions. This interface is
used by all the preceding functions to return a Stream object
and a pointer which should be used for memory allocation and
deallocation. To free the Stream object, the application or
provider must call the OLE IStream::Release method.

I can only guess that calling Release() for the message returned by
OpenProperty() implicitly calls Release() for the IStream, and it is
an error for my application to also call Release() for the IStream.

Without calling Release() for the IStream, my application is working.
I can read PR_BODY via my Extended MAPI wrapper for *many* iterations
without any apparent problems. But I am nervous that I really should
be calling Release() for the IStream.

What do you do in your Extended MAPI code? Do you call Release() for
the IStream?

A second quick question. The example code above from www.wischik.com
calls Stat() after OpenProperty(), before calling Read() on the
IStream. Is there any benefit to this? Is it necessary for any
reason? Microsoft does not call Stat() in their KB239795 sample code.

Thanks.

Jim
 
1. Yes, you *must* call Release. I suspect that because you do not call
IStream::Release, the message (and hence the message store) are still
referenced, hence on the next access they need not be reopened from the
scratch. You can achieve the same effect by caching the message store.
2. In most cases IStream::Stat() is called to figure out the size of the
stream so that you can allocate a sufficiently large buffer dynamically to
use in IStream::Read().

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool


Jim said:
Hello,

I'm still working on my Extended MAPI wrapper code to avoid the
Outlook security warning when reading objMailItem.Body.

This approach seems to work:

HrMAPIFindDefaultMsgStore
OpenMsgStore
OpenEntry
HrGetOneProp(.., PR_BODY, ..)
if MAPI_E_NOT_FOUND
no body text -- return ok
if ! MAPI_E_NOT_ENOUGH_MEMORY
return body text from Value.lpszA
OpenProperty(PR_BODY, ..)
IStream->Read(bf, ..)
return body text in bf

But I ran into a problem where after quite a lot of processing
(induced by scrolling through 1000 Inbox items several times, reading
each Inbox item via the Extended MAPI wrapper code above), the call
from Outlook to the Extended MAPI wrapper logic above would suddenly
slow down. Each call would suspend for about five seconds and then
succeed. It was as if I ran into some kind of Windows internal
resource timeout and recovery logic.

I tried a lot of different things, including disabling different parts
of the cleanup code (not shown above). The problem finally went away
when I *stopped* calling Release() on the IStream used to read PR_BODY
after the call to OpenProperty.

I went back and reviewed several web examples of Extended MAPI source,
and found that some call Release() for the IStream and some don't.

Here is one that calls Release():

http://www.wischik.com/lu/programmer/mapi_utils.html

char *bodybuf=0; unsigned int bodysize=0;
IStream *istream;
hr = imsg->OpenProperty(PR_BODY, &IID_IStream, STGM_READ,
0, (IUnknown**)&istream);
if (hr==S_OK)
{
AvailableOffline = true;
STATSTG stg = {0};
hr = istream->Stat(&stg,STATFLAG_NONAME);
if (hr==S_OK)
{
bodysize = stg.cbSize.LowPart;
bodybuf = new char[bodysize+1];
ULONG red; hr = istream->Read(bodybuf, bodysize, &red);
if (hr!=S_OK) bodysize=0;
else if (red<bodysize) bodysize=red;
bodybuf[bodysize]=0;
}
istream->Release();
}

Whereas Microsoft in this source example does *not* call Release() for
the IStream:

http://support.microsoft.com/?kbid=239795

Yet Microsoft states elsewhere, in a more general discussion, that
Release() should be called:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
exchserv/html/perform_0vjn.asp

Use the Win32 IStream interface functions. This interface is
used by all the preceding functions to return a Stream object
and a pointer which should be used for memory allocation and
deallocation. To free the Stream object, the application or
provider must call the OLE IStream::Release method.

I can only guess that calling Release() for the message returned by
OpenProperty() implicitly calls Release() for the IStream, and it is
an error for my application to also call Release() for the IStream.

Without calling Release() for the IStream, my application is working.
I can read PR_BODY via my Extended MAPI wrapper for *many* iterations
without any apparent problems. But I am nervous that I really should
be calling Release() for the IStream.

What do you do in your Extended MAPI code? Do you call Release() for
the IStream?

A second quick question. The example code above from www.wischik.com
calls Stat() after OpenProperty(), before calling Read() on the
IStream. Is there any benefit to this? Is it necessary for any
reason? Microsoft does not call Stat() in their KB239795 sample code.

Thanks.

Jim
 
Dmitry,

Thanks for your comments. Based on your urging, I resumed calling
Release for the IStream. This broke my Add-In again, but it forced me
to take a hard look at my Release code (I can't see anything wrong)
and then to step back and look at some of the code that leads up to my
calls to OpenProperty, IStream::Read, and IStream::Release. That is
where I may have found the problem.

My code was calling HrMAPIFindDefaultMsgStore and OpenMsgStore prior
to each read, followed by suitable Release where required. I changed
my code to not call these prior to each read, but to just reuse the
existing handles after the initial OpenMsgStore.

This seems to have eliminated the failure that seemed related to the
use of IStream::Release. That was probably a side-effect rather than
a direct cause.

I see now that there is Q265514 that describes a memory leak in
OpenMsgStore for Outlook 2000, which is where I am seeing the failure.
I also see a few newsgroup postings about this leak, with the
recommended workaround that the application just not call OpenMsgStore
repeatedly.

I am a little worried about that, because I wonder if other problems
will crop up with handles held for hours and days when Outlook is not
closed. My motivation to open and close each time was to avoid
holding handles over long periods of time. But it appears that there
isn't much choice.

Tomorrow I will do some more work to fully isolate the failure and
determine for certain if it is the known OpenMsgStore leak. I'll post
the outcome in a follow-up message for the benefit of any future
readers.

Thanks for your continued guidance in response to my several recent
posts.

Jim


Dmitry Streblechenko \(MVP\) said:
1. Yes, you *must* call Release. I suspect that because you do not call
IStream::Release, the message (and hence the message store) are still
referenced, hence on the next access they need not be reopened from the
scratch. You can achieve the same effect by caching the message store.
2. In most cases IStream::Stat() is called to figure out the size of the
stream so that you can allocate a sufficiently large buffer dynamically to
use in IStream::Read().

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool


Jim said:
Hello,

I'm still working on my Extended MAPI wrapper code to avoid the
Outlook security warning when reading objMailItem.Body.

This approach seems to work:

HrMAPIFindDefaultMsgStore
OpenMsgStore
OpenEntry
HrGetOneProp(.., PR_BODY, ..)
if MAPI_E_NOT_FOUND
no body text -- return ok
if ! MAPI_E_NOT_ENOUGH_MEMORY
return body text from Value.lpszA
OpenProperty(PR_BODY, ..)
IStream->Read(bf, ..)
return body text in bf

But I ran into a problem where after quite a lot of processing
(induced by scrolling through 1000 Inbox items several times, reading
each Inbox item via the Extended MAPI wrapper code above), the call
from Outlook to the Extended MAPI wrapper logic above would suddenly
slow down. Each call would suspend for about five seconds and then
succeed. It was as if I ran into some kind of Windows internal
resource timeout and recovery logic.

I tried a lot of different things, including disabling different parts
of the cleanup code (not shown above). The problem finally went away
when I *stopped* calling Release() on the IStream used to read PR_BODY
after the call to OpenProperty.

I went back and reviewed several web examples of Extended MAPI source,
and found that some call Release() for the IStream and some don't.

Here is one that calls Release():

http://www.wischik.com/lu/programmer/mapi_utils.html

char *bodybuf=0; unsigned int bodysize=0;
IStream *istream;
hr = imsg->OpenProperty(PR_BODY, &IID_IStream, STGM_READ,
0, (IUnknown**)&istream);
if (hr==S_OK)
{
AvailableOffline = true;
STATSTG stg = {0};
hr = istream->Stat(&stg,STATFLAG_NONAME);
if (hr==S_OK)
{
bodysize = stg.cbSize.LowPart;
bodybuf = new char[bodysize+1];
ULONG red; hr = istream->Read(bodybuf, bodysize, &red);
if (hr!=S_OK) bodysize=0;
else if (red<bodysize) bodysize=red;
bodybuf[bodysize]=0;
}
istream->Release();
}

Whereas Microsoft in this source example does *not* call Release() for
the IStream:

http://support.microsoft.com/?kbid=239795

Yet Microsoft states elsewhere, in a more general discussion, that
Release() should be called:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
exchserv/html/perform_0vjn.asp

Use the Win32 IStream interface functions. This interface is
used by all the preceding functions to return a Stream object
and a pointer which should be used for memory allocation and
deallocation. To free the Stream object, the application or
provider must call the OLE IStream::Release method.

I can only guess that calling Release() for the message returned by
OpenProperty() implicitly calls Release() for the IStream, and it is
an error for my application to also call Release() for the IStream.

Without calling Release() for the IStream, my application is working.
I can read PR_BODY via my Extended MAPI wrapper for *many* iterations
without any apparent problems. But I am nervous that I really should
be calling Release() for the IStream.

What do you do in your Extended MAPI code? Do you call Release() for
the IStream?

A second quick question. The example code above from www.wischik.com
calls Stat() after OpenProperty(), before calling Read() on the
IStream. Is there any benefit to this? Is it necessary for any
reason? Microsoft does not call Stat() in their KB239795 sample code.

Thanks.

Jim
 
The OpenMsgStore leak under Outlook 2000 was the problem.

I now call MAPILogonEx, HrMAPIFindDefaultMsgStore, and OpenMsgStore at
Add-In startup, and hold the handles open until the Add-In terminates
at Outlook shutdown. I can read PR_BODY text thousands of times
without failure using OpenEntry, HrGetOneProp, and where needed for
long messages, OpenProperty, IStream Stat, Read, and (now) Release.
It works great.

On a non-Exchange client platform where the message store is on the
local disk, I am not worried about holding the handles open for the
hours and days that the user might keep Outlook (and my Add-In) open.

But what happens if the user is in an Exchange environment, and the
Exchange server is reboot while the Outlook client is open? Will the
original handles held by my Add-In from MAPILogonEx and OpenMsgStore
still work, or will they be invalidated by the Exchange reboot?

Does Outlook itself detect this condition and recover under the
covers? If it does, I probably need to do the same. If Outlook in an
Exchange environment doesn't recover from this, then there is no
benefit to my Add-In trying to recover.

I would just try this myself to see what happens, but I don't have
Exchange available.

Thanks.

Jim


Dmitry,

Thanks for your comments. Based on your urging, I resumed calling
Release for the IStream. This broke my Add-In again, but it forced me
to take a hard look at my Release code (I can't see anything wrong)
and then to step back and look at some of the code that leads up to my
calls to OpenProperty, IStream::Read, and IStream::Release. That is
where I may have found the problem.

My code was calling HrMAPIFindDefaultMsgStore and OpenMsgStore prior
to each read, followed by suitable Release where required. I changed
my code to not call these prior to each read, but to just reuse the
existing handles after the initial OpenMsgStore.

This seems to have eliminated the failure that seemed related to the
use of IStream::Release. That was probably a side-effect rather than
a direct cause.

I see now that there is Q265514 that describes a memory leak in
OpenMsgStore for Outlook 2000, which is where I am seeing the failure.
I also see a few newsgroup postings about this leak, with the
recommended workaround that the application just not call OpenMsgStore
repeatedly.

I am a little worried about that, because I wonder if other problems
will crop up with handles held for hours and days when Outlook is not
closed. My motivation to open and close each time was to avoid
holding handles over long periods of time. But it appears that there
isn't much choice.

Tomorrow I will do some more work to fully isolate the failure and
determine for certain if it is the known OpenMsgStore leak. I'll post
the outcome in a follow-up message for the benefit of any future
readers.

Thanks for your continued guidance in response to my several recent
posts.

Jim


Dmitry Streblechenko \(MVP\) said:
1. Yes, you *must* call Release. I suspect that because you do not call
IStream::Release, the message (and hence the message store) are still
referenced, hence on the next access they need not be reopened from the
scratch. You can achieve the same effect by caching the message store.
2. In most cases IStream::Stat() is called to figure out the size of the
stream so that you can allocate a sufficiently large buffer dynamically to
use in IStream::Read().

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool


Jim said:
Hello,

I'm still working on my Extended MAPI wrapper code to avoid the
Outlook security warning when reading objMailItem.Body.

This approach seems to work:

HrMAPIFindDefaultMsgStore
OpenMsgStore
OpenEntry
HrGetOneProp(.., PR_BODY, ..)
if MAPI_E_NOT_FOUND
no body text -- return ok
if ! MAPI_E_NOT_ENOUGH_MEMORY
return body text from Value.lpszA
OpenProperty(PR_BODY, ..)
IStream->Read(bf, ..)
return body text in bf

But I ran into a problem where after quite a lot of processing
(induced by scrolling through 1000 Inbox items several times, reading
each Inbox item via the Extended MAPI wrapper code above), the call
from Outlook to the Extended MAPI wrapper logic above would suddenly
slow down. Each call would suspend for about five seconds and then
succeed. It was as if I ran into some kind of Windows internal
resource timeout and recovery logic.

I tried a lot of different things, including disabling different parts
of the cleanup code (not shown above). The problem finally went away
when I *stopped* calling Release() on the IStream used to read PR_BODY
after the call to OpenProperty.

I went back and reviewed several web examples of Extended MAPI source,
and found that some call Release() for the IStream and some don't.

Here is one that calls Release():

http://www.wischik.com/lu/programmer/mapi_utils.html

char *bodybuf=0; unsigned int bodysize=0;
IStream *istream;
hr = imsg->OpenProperty(PR_BODY, &IID_IStream, STGM_READ,
0, (IUnknown**)&istream);
if (hr==S_OK)
{
AvailableOffline = true;
STATSTG stg = {0};
hr = istream->Stat(&stg,STATFLAG_NONAME);
if (hr==S_OK)
{
bodysize = stg.cbSize.LowPart;
bodybuf = new char[bodysize+1];
ULONG red; hr = istream->Read(bodybuf, bodysize, &red);
if (hr!=S_OK) bodysize=0;
else if (red<bodysize) bodysize=red;
bodybuf[bodysize]=0;
}
istream->Release();
}

Whereas Microsoft in this source example does *not* call Release() for
the IStream:

http://support.microsoft.com/?kbid=239795

Yet Microsoft states elsewhere, in a more general discussion, that
Release() should be called:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
exchserv/html/perform_0vjn.asp

Use the Win32 IStream interface functions. This interface is
used by all the preceding functions to return a Stream object
and a pointer which should be used for memory allocation and
deallocation. To free the Stream object, the application or
provider must call the OLE IStream::Release method.

I can only guess that calling Release() for the message returned by
OpenProperty() implicitly calls Release() for the IStream, and it is
an error for my application to also call Release() for the IStream.

Without calling Release() for the IStream, my application is working.
I can read PR_BODY via my Extended MAPI wrapper for *many* iterations
without any apparent problems. But I am nervous that I really should
be calling Release() for the IStream.

What do you do in your Extended MAPI code? Do you call Release() for
the IStream?

A second quick question. The example code above from www.wischik.com
calls Stat() after OpenProperty(), before calling Read() on the
IStream. Is there any benefit to this? Is it necessary for any
reason? Microsoft does not call Stat() in their KB239795 sample code.

Thanks.

Jim
 
If you encounter an error, you can always reset the cached objects and
retrieve them again.

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool


Jim said:
The OpenMsgStore leak under Outlook 2000 was the problem.

I now call MAPILogonEx, HrMAPIFindDefaultMsgStore, and OpenMsgStore at
Add-In startup, and hold the handles open until the Add-In terminates
at Outlook shutdown. I can read PR_BODY text thousands of times
without failure using OpenEntry, HrGetOneProp, and where needed for
long messages, OpenProperty, IStream Stat, Read, and (now) Release.
It works great.

On a non-Exchange client platform where the message store is on the
local disk, I am not worried about holding the handles open for the
hours and days that the user might keep Outlook (and my Add-In) open.

But what happens if the user is in an Exchange environment, and the
Exchange server is reboot while the Outlook client is open? Will the
original handles held by my Add-In from MAPILogonEx and OpenMsgStore
still work, or will they be invalidated by the Exchange reboot?

Does Outlook itself detect this condition and recover under the
covers? If it does, I probably need to do the same. If Outlook in an
Exchange environment doesn't recover from this, then there is no
benefit to my Add-In trying to recover.

I would just try this myself to see what happens, but I don't have
Exchange available.

Thanks.

Jim


(e-mail address removed) (Jim) wrote in message
Dmitry,

Thanks for your comments. Based on your urging, I resumed calling
Release for the IStream. This broke my Add-In again, but it forced me
to take a hard look at my Release code (I can't see anything wrong)
and then to step back and look at some of the code that leads up to my
calls to OpenProperty, IStream::Read, and IStream::Release. That is
where I may have found the problem.

My code was calling HrMAPIFindDefaultMsgStore and OpenMsgStore prior
to each read, followed by suitable Release where required. I changed
my code to not call these prior to each read, but to just reuse the
existing handles after the initial OpenMsgStore.

This seems to have eliminated the failure that seemed related to the
use of IStream::Release. That was probably a side-effect rather than
a direct cause.

I see now that there is Q265514 that describes a memory leak in
OpenMsgStore for Outlook 2000, which is where I am seeing the failure.
I also see a few newsgroup postings about this leak, with the
recommended workaround that the application just not call OpenMsgStore
repeatedly.

I am a little worried about that, because I wonder if other problems
will crop up with handles held for hours and days when Outlook is not
closed. My motivation to open and close each time was to avoid
holding handles over long periods of time. But it appears that there
isn't much choice.

Tomorrow I will do some more work to fully isolate the failure and
determine for certain if it is the known OpenMsgStore leak. I'll post
the outcome in a follow-up message for the benefit of any future
readers.

Thanks for your continued guidance in response to my several recent
posts.

Jim


"Dmitry Streblechenko \(MVP\)" <[email protected]> wrote in message
1. Yes, you *must* call Release. I suspect that because you do not call
IStream::Release, the message (and hence the message store) are still
referenced, hence on the next access they need not be reopened from the
scratch. You can achieve the same effect by caching the message store.
2. In most cases IStream::Stat() is called to figure out the size of the
stream so that you can allocate a sufficiently large buffer dynamically to
use in IStream::Read().

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool


Hello,

I'm still working on my Extended MAPI wrapper code to avoid the
Outlook security warning when reading objMailItem.Body.

This approach seems to work:

HrMAPIFindDefaultMsgStore
OpenMsgStore
OpenEntry
HrGetOneProp(.., PR_BODY, ..)
if MAPI_E_NOT_FOUND
no body text -- return ok
if ! MAPI_E_NOT_ENOUGH_MEMORY
return body text from Value.lpszA
OpenProperty(PR_BODY, ..)
IStream->Read(bf, ..)
return body text in bf

But I ran into a problem where after quite a lot of processing
(induced by scrolling through 1000 Inbox items several times, reading
each Inbox item via the Extended MAPI wrapper code above), the call
from Outlook to the Extended MAPI wrapper logic above would suddenly
slow down. Each call would suspend for about five seconds and then
succeed. It was as if I ran into some kind of Windows internal
resource timeout and recovery logic.

I tried a lot of different things, including disabling different parts
of the cleanup code (not shown above). The problem finally went away
when I *stopped* calling Release() on the IStream used to read PR_BODY
after the call to OpenProperty.

I went back and reviewed several web examples of Extended MAPI source,
and found that some call Release() for the IStream and some don't.

Here is one that calls Release():

http://www.wischik.com/lu/programmer/mapi_utils.html

char *bodybuf=0; unsigned int bodysize=0;
IStream *istream;
hr = imsg->OpenProperty(PR_BODY, &IID_IStream, STGM_READ,
0, (IUnknown**)&istream);
if (hr==S_OK)
{
AvailableOffline = true;
STATSTG stg = {0};
hr = istream->Stat(&stg,STATFLAG_NONAME);
if (hr==S_OK)
{
bodysize = stg.cbSize.LowPart;
bodybuf = new char[bodysize+1];
ULONG red; hr = istream->Read(bodybuf, bodysize, &red);
if (hr!=S_OK) bodysize=0;
else if (red<bodysize) bodysize=red;
bodybuf[bodysize]=0;
}
istream->Release();
}

Whereas Microsoft in this source example does *not* call Release() for
the IStream:

http://support.microsoft.com/?kbid=239795

Yet Microsoft states elsewhere, in a more general discussion, that
Release() should be called:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
exchserv/html/perform_0vjn.asp

Use the Win32 IStream interface functions. This interface is
used by all the preceding functions to return a Stream object
and a pointer which should be used for memory allocation and
deallocation. To free the Stream object, the application or
provider must call the OLE IStream::Release method.

I can only guess that calling Release() for the message returned by
OpenProperty() implicitly calls Release() for the IStream, and it is
an error for my application to also call Release() for the IStream.

Without calling Release() for the IStream, my application is working.
I can read PR_BODY via my Extended MAPI wrapper for *many* iterations
without any apparent problems. But I am nervous that I really should
be calling Release() for the IStream.

What do you do in your Extended MAPI code? Do you call Release() for
the IStream?

A second quick question. The example code above from www.wischik.com
calls Stat() after OpenProperty(), before calling Read() on the
IStream. Is there any benefit to this? Is it necessary for any
reason? Microsoft does not call Stat() in their KB239795 sample code.

Thanks.

Jim
 
Back
Top