Problem with attachments in Outl

  • Thread starter Thread starter Vladimir Chtchetkine
  • Start date Start date
V

Vladimir Chtchetkine

I'm dealing with attachments on MAPI level (i.e. I'm adding/deleting
attachments using IMessage and attachment table). When I add an attachment
(via IMessage's CreateAttach) I save (SaveChanges) both, attachment and
message object. And after that I can see that Outl recognizes that there is
an attachment added to the massage (attachment icon appears in the column)
but. But OutlSpy on this item reports that its Attachment list empty (Count
prop is zero). Moreover, when I try to delete my attachment (again, on MAPI
level, using IMessage's DeleteAttach) that call fails with "not found" error
(10f) even though attachment is there and "number" that I passed to
DeleteAttach is correct attachment number. In addition to the puzzle, if I
1. add attachment, 2. unselect that item in Outl, 3 select it again and then
try to delete attachment everything works just fine.



What am I messing here?



TIA,



Vladimir
 
Do you deselect the message and then select the message again to force
Outlook to refresh its cache? What if you close and reopen Outlook?

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool
 
I pool back my statement about reselecting item. I saw this behavior couple
of times but now I can't reproduce it. Even when item is reselected or
unselected I still get "not found" error when trying to delete attachment.
And it doesn't matter whether I restart Outlook or not. No matter what I do
attachment stays there. I 've also veryfied that I don't hold (myself) any
references to that attachment. Unless something else holds it, attachment is
not opened.
 
Can you delete the attachmeent using OutlookSpy (click Imessage, go to teh
GetAttachmeentTable tab)?

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool
 
I would be happy to but it's not that simple... Oh, well... :-) Here are two
basic routines that run within the scope of the problem:
(see additional comments below)

//

// List attachments

//

// m_spMessage is declared as CComPtr<IMessage> m_spMessage

//

HRESULT CBrMAPIMessage::GetAttachments( CBrMAPIAttachmentList& aList )

{

ASSERT(IsMessageInitialized());

if( !IsMessageInitialized() ) return OLE_E_BLANK;



CComPtr<IMAPITable> spAttachmentTable;

ULONG nAttachments = 0;



HRESULT hr = m_spMessage -> GetAttachmentTable( 0,
&spAttachmentTable );

ASSERT(spAttachmentTable.p!=NULL);

if( spAttachmentTable.p == NULL ) return FAILURE(hr);



hr = spAttachmentTable -> GetRowCount( 0, &nAttachments);

ASSERT(hr==S_OK);

if( FAILED(hr) ) return hr;



if( !nAttachments ) return S_OK;



//

// Make sure that table is positioned at the begining

//



hr = spAttachmentTable -> SeekRow( BOOKMARK_BEGINNING, 0, NULL);

ASSERT(hr==S_OK);

if( FAILED(hr) ) return hr;



//

// Enum

//



while( nAttachments && SUCCEEDED(hr) )

{

LPSRowSet pRows = NULL;



//

// Try to get all of them at once

//



hr = spAttachmentTable -> QueryRows( nAttachments, 0, &pRows);

ASSERT(hr==S_OK);

ASSERT(pRows!=NULL);

if( FAILED(hr) ) return hr;

if( pRows == NULL ) return FAILURE(hr);

ASSERT(pRows -> cRows!=0);

if( !pRows -> cRows )

{

MAPIFreeBuffer(pRows);

break;

}



for( ULONG nAtt = 0; nAtt < pRows -> cRows; nAtt++ )

{

//

// We will be looking for our PR_ATTACH_NUM property that

// allows us to open attachment object

//



SRow* pRow = pRows -> aRow + nAtt;

LPSPropValue pProps = pRow -> lpProps;

ASSERT(pProps!=NULL);

if( pProps == NULL ) continue;

ASSERT(pRow -> cValues!=0);



for( ULONG nProp = 0; nProp < pRow -> cValues; nProp++ )

{

//

// Look for PR_ATTACH_NUM

//



if( pProps[nProp].ulPropTag != PR_ATTACH_NUM )
continue;



//

// Try to open it

//



CComPtr<IAttach> spAttachment;



HRESULT hrc = m_spMessage -> OpenAttach(
(ULONG)pProps[nProp].Value.l, &IID_IAttachment, 0/*MAPI_BEST_ACCESS*/,
&spAttachment);

REPORTHR(hrc);

ASSERT(spAttachment.p!=NULL);

if( spAttachment.p == NULL ) continue;



//

// Instantiate object and add it to the list

// What gets added here is just attachment info, not
IAttach. All references to IAttach

// will be released as soon as we step out of that
loop...

//



CBrMAPIAttachment* pAttach = NULL;



try

{

CBrMAPIAttachmentObject aObj(spAttachment.p);

pAttach = new CBrMAPIAttachment( &aObj );

aList.push_back(pAttach);

}

catch( ... )

{

DBGREPORTEXCEPTION();

}



break;

}



MAPIFreeBuffer(pProps);

}



nAttachments -= pRows -> cRows;



MAPIFreeBuffer(pRows);

}




return S_OK;

}



//

// Delete an attachment

//

HRESULT CBrMAPIMessage::DeleteAttachment( ULONG nAttachNum )

{

ASSERT(IsMessageInitialized());

if( !IsMessageInitialized() ) return OLE_E_BLANK;




HRESULT hr = m_spMessage -> DeleteAttach( nAttachNum, 0, NULL, 0);



if( SUCCEEDED(hr) )

{

Save();

}

else

{

CBrMAPIMessage aWrk;



if( InitializeCopy( aWrk ) == S_OK )

{

hr = aWrk.m_spMessage -> DeleteAttach( nAttachNum, 0,
NULL, 0);



if( SUCCEEDED(hr) )

{

aWrk.Save();

}

}

}



return hr;

}



So, all together they work like this. First I call GetAttachments to enum
all the attachments for an item.

This works just fine. Then I select some attachments to delete and call
DeleteAttachment passing "number" that

I got from PR_ATTACH_NUM property when enumerating. And that fails.
Everything is going on within the same

instance of CBrMAPIMessage (thus, the same instance of IMessage that this
object holds). Now, the code (in Delete

routine) related to aWrk is my recent experiment. What I've done is I've
created a completely new instance of

IMessage (by getting my message EntryID and opening new item via
IMAPISession:OpenEntry) and (surprise, surprise!)

deleting attachment using that "copy" instance succeeds! But... Although
"DeleteAttach" succeeds, attachment is still

remains in the attachment table.



I'm so completely lost here that "Blair Witch Project" seems just as
innocent as "Cinderella"
 
OK, some more clues :-)
It turned out that PR_ATTACH_NUM is different when I get it from the table
(when enuming rows) and from the attachment (IAttach) object itself. And
that's what drives the system nuts. I trusted PR_ATTACH_NUM from IAttach and
passed that value to DeleteAttach -> not found. But when I've chaged it to
the value I get from the table it succeeds and attachment gets deleted.
Voila! Now I have to understand why those numbers are different at the first
place and which one should I use for what?
 
Well, this has closed the issue. As soon as I stopped "trusting"
PR_ATTACH_NUM for the attachment object and started relying upon the one
that I've got from the table enum everything started to fly... But still I'm
curious what makes those numbers to differ? And differ significantly (0 in
table -> 0x4b545 in the object). I checked with the regular mail items and
those numbers are equal for them... Strange. Has anyone any explanations for
that?

TIA,

Vladimir
 
I do remember seeing something like this a long while back, but only if the
attachments were modified. If you reopen the message, both attach nums will
be the same.

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool
 
Hi. I was experiencing similar, but slightly different problem as you when
using DeleteAttach (). When I would create an attachment, and then delete
the old ones from a message, the original attachment would stay and the new
one would be gone. The behavior was crazy, and not neccesarily worth
explaining here. I was writing an exchange addin the would modify the
attachments in the OnSubmit () event. This is where my problem was. After
looking an example that kinda did was I was doing (http://www.codeproject.
com/com/outlookaddinzip.asp), I tried placing my code in OnWriteComplete ()
and Whala! It worked. So the moral of the story is: Don't modify the
attachments unless your in OnWriteComplete ().

Sorry if this was too unrelated, but I found this post while trying to solve
my problem, and so I thought maybe others would too.

Have a great day!

-Joel
 
Back
Top