I had a customer recently who was reporting odd behavior when he used SeekRow on an attachment table. If the cursor was at the end of the table and he passed –1 in to SeekRow, a subsequent QueryRows would not return the last row of the table. Instead, it would return no rows. If he tried this at any other location in the table, the SeekRow moved the cursor as expected. This problem started happening when he ran his code against Exchange 2010 and he wanted to know what had changed and what he could do to avoid the problem.

As we dug in to his code and the reasons for the calls he was using, we found that he had a need for QueryRows to return the same row on two consecutive calls. Years ago, he had tried something like this:

QueryRows(1,TBL_NOADVANCE)
QueryRows(1,0)

But testing against attachment tables showed that QueryRows with the TBL_NOADVANCE flag still advanced the cursor on reading the last row of the table. So he switched to the following code to accomplish his goal:

QueryRows(1,0)
SeekRow(BOOKMARK_CURRENT,-1)
QueryRows(1,0)
And this worked, until we got to Exchange 2010, when the SeekRow call stopped moving the cursor if we’re at the end of the table. Note that SeekRow is now working incorrectly in the same way and same place that QueryRows was working incorrectly. Could they be related?

As it happens, yes, the two issues are related. Remember that Exchange 2010 introduced RPC Client Access, a new layer that acts as the endpoint for MAPI client calls. RPC Client Access takes the inbound MAPI calls and proxies them back to the appropriate Exchange mailbox server. Along the way, it can introduce some new logic, which is what happened here. Previously, if you made a SeekRow call, that was handled by Store directly. Now, RPC Client Access submits the call to Store for you, and then does a little something extra. In certain circumstances, if the seek was backwards, it does a check to ensure that the cursor is on the row we expect it to be on. It does this in part by issuing a QueryRows call to the store, passing the TBL_NOADVANCE flag. This is the same scenario the customer had originally noticed on earlier versions of Exchange!

Now – in this case, the customer wasn’t tied to any specific code – they just wanted something that worked. So now they’re doing this:

QueryPosition(&ulRowCount)
QueryRows(1,0)
SeekRow(BOOKMARK_BEGINNING,ulRowCount)
This bypasses the end of the table bug and gets their code working again.

My recommended solution, by the way, was if you know you’re going to query for the same row twice, just hang on to the results of the first call and don’t make the second. They agreed this would work, but weren’t able to make that change in a small patch.

Since they were able to work around the issue, and there is evidence that attachment tables in Exchange may have behaved like this for a long time (without customer complaint), we didn’t pursue a bug on this issue. If you encounter it, hopefully you’ll be able to work around it in a similar fashion, but if you can’t, you can always open a case.