Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Область применения: Outlook 2013 | Outlook 2016
В рамках URL-адреса универсального указателя ресурсов MAPI поставщик хранилища отправляет хэш-номер хранилища обработчику протокола MAPI для идентификации объекта, готового к индексации. Обработчик протокола MAPI использует этот хэш-номер хранилища для идентификации хранилища. Как правило, поставщик хранилища вычисляет хэш-номер хранилища на основе сигнатуры сопоставления хранилища, если в хранилище есть свойство PR_MAPPING_SIGNATURE , определенное в разделе глобального профиля. В противном случае поставщик хранилища использует идентификатор записи в хранилище. Алгоритм вычисления хэш-номера хранилища должен свести к минимуму неоднозначность, определяющую хранилища.
В этом разделе описывается алгоритм, который Microsoft Office Outlook использует для вычисления хэш-номера магазина на основе подписи или идентификатора записи и имени файла хранилища.
Двоичный BLOB-объект для кодирования в большинстве случаев является PR_ENTRYID хранилища, но для кэшированных хранилищ Exchange, как общедоступных, так и частных, двоичный BLOB-объект должен быть PR_MAPPING_SIGNATURE, который находится в профиле.
После вычисления хэша для двоичного BLOB-объекта хранилища общедоступных папок, но перед хэшированием в пути OST константа 0x2E505542, которая представляет строку ". PUB", хэширован в, чтобы гарантировать, что он уникален, то есть, отличается от хэша частного магазина.
Код поддержки вычисляет соответствующие биты из профиля, с помощью которых можно определить, является ли хранилище общедоступным или частным, если оно кэшировано, а также путь к OST. Чтобы включить этот код в проект, вызовите функцию ComputeStoreHash, которая принимает в качестве входных данных указатель сеанса, а также PR_ENTRYID, PR_SERVICE_UID и PR_MDB_PROVIDER из таблицы хранилища сообщений. Остальная часть необходимых сведений, которые она получает из профиля. Для выходных данных эта функция возвращает хэш, вычисленный из PR_MAPPING_SIGNATURE, если хранилище является кэшируемым хранилищем Exchange, или хэш, вычисляемый из PR_ENTRYID.
Примечание.
Функция поддержки HrEmsmdbUIDFromStore — это замена с поддержкой нескольких учетных записей Exchange для использования pbGlobalProfileSectionGuid для открытия раздела профиля для почтового ящика Exchange.
#define PR_PROFILE_OFFLINE_STORE_PATH_A PROP_TAG(PT_STRING8, 0x6610)
#define PR_PROFILE_OFFLINE_STORE_PATH_W PROP_TAG(PT_UNICODE, 0x6610)
#define CONFIG_OST_CACHE_PRIVATE ((ULONG)0x00000180)
#define CONFIG_OST_CACHE_PUBLIC ((ULONG)0x00000400)
HRESULT HrEmsmdbUIDFromStore(IMAPISession* pmsess,
MAPIUID* puidService,
MAPIUID* pEmsmdbUID)
{
if (!puidService) return MAPI_E_INVALID_PARAMETER;
HRESULT hRes = S_OK;
SRestriction mres = {0};
SPropValue mval = {0};
SRowSet* pRows = NULL;
SRow* pRow = NULL;
LPSERVICEADMIN spSvcAdmin = NULL;
LPMAPITABLE spmtab = NULL;
enum { eEntryID = 0, eSectionUid, eMax };
static const SizedSPropTagArray(eMax, tagaCols) =
{
eMax,
{
PR_ENTRYID,
PR_EMSMDB_SECTION_UID,
}
};
hRes = pmsess->AdminServices(0, (LPSERVICEADMIN*)&spSvcAdmin);
if (SUCCEEDED(hRes) && spSvcAdmin)
{
hRes = spSvcAdmin->GetMsgServiceTable(0, &spmtab);
if (spmtab)
{
hRes = spmtab->SetColumns((LPSPropTagArray)&tagaCols, TBL_BATCH);
mres.rt = RES_PROPERTY;
mres.res.resProperty.relop = RELOP_EQ;
mres.res.resProperty.ulPropTag = PR_SERVICE_UID;
mres.res.resProperty.lpProp = &mval;
mval.ulPropTag = PR_SERVICE_UID;
mval.Value.bin.cb = sizeof(*puidService);
mval.Value.bin.lpb = (LPBYTE)puidService;
(void) spmtab->Restrict(&mres, 0);
(void) spmtab->QueryRows(10, 0, &pRows);
if (SUCCEEDED(hRes) && pRows && pRows->cRows)
{
pRow = &pRows->aRow[0];
if (pEmsmdbUID && pRow)
{
if (PR_EMSMDB_SECTION_UID == pRow->lpProps[eSectionUid].ulPropTag &&
pRow->lpProps[eSectionUid].Value.bin.cb == sizeof(*pEmsmdbUID))
{
memcpy(pEmsmdbUID, pRow->lpProps[eSectionUid].Value.bin.lpb, sizeof(*pEmsmdbUID));
}
}
}
FreeProws(pRows);
}
if (spmtab) spmtab->Release();
}
if (spSvcAdmin) spSvcAdmin->Release();
return hRes;
} // HrEmsmdbUIDFromStore
bool FExchangePrivateStore(LPMAPIUID lpmapiuid)
{
if (!lpmapiuid) return false;
return IsEqualMAPIUID(lpmapiuid, (LPMAPIUID)pbExchangeProviderPrimaryUserGuid);
} // FExchangePrivateStore
bool FExchangePublicStore(LPMAPIUID lpmapiuid)
{
if (!lpmapiuid) return false;
return IsEqualMAPIUID(lpmapiuid, (LPMAPIUID)pbExchangeProviderPublicGuid);
} // FExchangePublicStore
DWORD ComputeHash(ULONG cbStoreEID, LPBYTE pbStoreEID, LPCSTR pszFileName, LPCWSTR pwzFileName, BOOL bPublicStore)
{
DWORD dwHash = 0;
ULONG cdw = 0;
DWORD* pdw = NULL;
ULONG cb = 0;
BYTE* pb = NULL;
ULONG i = 0;
if (!cbStoreEID || !pbStoreEID) return dwHash;
// Shouldn't see both of these at the same time.
if (pszFileName && pwzFileName) return dwHash;
// Get the Store Entry ID
// pbStoreEID is a pointer to the Entry ID.
// cbStoreEID is the size in bytes of the Entry ID.
pdw = (DWORD*)pbStoreEID;
cdw = cbStoreEID / sizeof(DWORD);
for (i = 0; i < cdw; i++)
{
dwHash = (dwHash << 5) + dwHash + *pdw++;
}
pb = (BYTE *)pdw;
cb = cbStoreEID % sizeof(DWORD);
for (i = 0; i < cb; i++)
{
dwHash = (dwHash << 5) + dwHash + *pb++;
}
if (bPublicStore)
{
// Augment to assure it is unique, else could be same as private store.
dwHash = (dwHash << 5) + dwHash + 0x2E505542; // this is '.PUB'
}
// To also include the store file name in the hash calculation,
// pszFileName and pwzFileName are NULL terminated strings with the path and filename of the store.
if (pwzFileName)
{
while (*pwzFileName)
{
dwHash = (dwHash << 5) + dwHash + *pwzFileName++;
}
}
else if (pszFileName)
{
while (*pszFileName)
{
dwHash = (dwHash << 5) + dwHash + *pszFileName++;
}
}
// dwHash now contains the hash to be used. It should be written in hex when building a URL.
return dwHash;
} // ComputeHash
void ComputeStoreHash(LPMAPISESSION lpMAPISession, LPSBinary lpEntryID, LPSBinary lpServiceUID, LPSBinary lpProviderUID, DWORD* lpdwSigHash, DWORD* lpdwEIDHash)
{
HRESULT hRes = S_OK;
MAPIUID emsmdbUID = {0};
LPPROFSECT lpProfSect = NULL;
BOOL fPublicExchangeStore = FExchangePublicStore((LPMAPIUID)lpProviderUID->lpb);
BOOL fPrivateExchangeStore = FExchangePrivateStore((LPMAPIUID)lpProviderUID->lpb);
BOOL fCached = false;
LPSPropValue lpConfigProp = NULL;
LPSPropValue lpPathPropA = NULL;
LPSPropValue lpPathPropW = NULL;
LPSPropValue lpMappingSig = NULL;
LPSTR szPath = NULL; // Do not free.
LPWSTR wzPath = NULL; // Do not free.
// Get profile section.
if (lpServiceUID)
{
hRes = HrEmsmdbUIDFromStore(lpMAPISession,
(LPMAPIUID) lpServiceUID->lpb,
&emsmdbUID);
if (SUCCEEDED(hRes))
{
hRes = lpMAPISession->OpenProfileSection(&emsmdbUID, NULL, 0, &lpProfSect);
}
}
if (!lpServiceUID || FAILED(hRes))
{
// For Outlook 2003/2007, HrEmsmdbUIDFromStore may not succeed,
// so use pbGlobalProfileSectionGuid instead.
hRes = lpMAPISession->OpenProfileSection((LPMAPIUID)pbGlobalProfileSectionGuid, NULL, 0, &lpProfSect);
}
if (lpProfSect)
{
hRes = HrGetOneProp(lpProfSect, PR_PROFILE_CONFIG_FLAGS, &lpConfigProp);
if (SUCCEEDED(hRes) && PROP_TYPE(lpConfigProp->ulPropTag) != PT_ERROR)
{
if (fPrivateExchangeStore)
{
fCached = ((lpConfigProp->Value.l & CONFIG_OST_CACHE_PRIVATE) != 0);
}
if (fPublicExchangeStore)
{
fCached = ((lpConfigProp->Value.l & CONFIG_OST_CACHE_PUBLIC) == CONFIG_OST_CACHE_PUBLIC);
}
}
if (fCached)
{
hRes = HrGetOneProp(lpProfSect, PR_PROFILE_OFFLINE_STORE_PATH_W, &lpPathPropW);
if (FAILED(hRes))
{
hRes = HrGetOneProp(lpProfSect, PR_PROFILE_OFFLINE_STORE_PATH_A, &lpPathPropA);
}
if (SUCCEEDED(hRes))
{
if (lpPathPropW && lpPathPropW->Value.lpszW)
{
wzPath = lpPathPropW->Value.lpszW;
}
else if (lpPathPropA && lpPathPropA->Value.lpszA)
{
szPath = lpPathPropA->Value.lpszA;
}
}
// If this is an Exchange store with an OST path, it's an OST, so get the mapping signature.
if ((fPrivateExchangeStore || fPublicExchangeStore) && (wzPath || szPath))
{
hRes = HrGetOneProp(lpProfSect, PR_MAPPING_SIGNATURE, &lpMappingSig);
}
}
}
DWORD dwSigHash = NULL;
if (lpMappingSig && PT_BINARY == PROP_TYPE(lpMappingSig->ulPropTag))
{
dwSigHash = ComputeHash(lpMappingSig->Value.bin.cb, lpMappingSig->Value.bin.lpb, NULL, NULL, fPublicExchangeStore);
}
DWORD dwEIDHash = ComputeHash(lpEntryID->cb, lpEntryID->lpb, szPath, wzPath, fPublicExchangeStore);
if (lpdwSigHash) *lpdwSigHash = dwSigHash;
if (lpdwEIDHash) *lpdwEIDHash = dwEIDHash;
MAPIFreeBuffer(lpMappingSig);
MAPIFreeBuffer(lpPathPropA);
MAPIFreeBuffer(lpPathPropW);
MAPIFreeBuffer(lpConfigProp);
if (lpProfSect) lpProfSect->Release();
} // ComputeStoreHash
Совет
Функция HrEmsmdbUIDFromStore работает без фактического открытия хранилища, поэтому это хороший подход общего назначения. Однако если у вас уже есть указатель на объект store, вы также можете получить GUID раздела профиля непосредственно из хранилища сообщений, прочитав свойство PR_EMSMDB_SECTION_UID.