Skip to content

Commit 9f41d04

Browse files
Merge pull request #122 from microsoft/u/sgriffin/monarch
Better fallbacks for finding olmapi32
2 parents 33740da + c9e47d6 commit 9f41d04

File tree

2 files changed

+93
-54
lines changed

2 files changed

+93
-54
lines changed

library/stubutils.cpp

Lines changed: 90 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,20 @@ namespace mapistub
103103
L"{BC174BAD-2F53-4855-A1D5-1D575C19B1EA}", // O11_CATEGORY_GUID_CORE_OFFICE (debug) // STRING_OK
104104
};
105105

106+
std::vector<std::wstring> g_OutlookQualifiers = {
107+
// Possible qualifiers for MsiProvideQualifiedComponent
108+
L"outlook.x64.exe",
109+
L"outlook.exe",
110+
L"excel.exe",
111+
L"winword.exe",
112+
L"wwlib.dll",
113+
L"powerpnt.exe",
114+
L"msaccess.exe",
115+
L"onenote.exe",
116+
L"mspub.exe",
117+
L"visio.exe",
118+
L"mspub.exe"};
119+
106120
std::wstring GetInstalledOutlookMAPI(int iOutlook);
107121
std::wstring GetInstalledOutlookMAPI(const std::wstring component);
108122

@@ -262,6 +276,9 @@ namespace mapistub
262276
// Whether or not we should ignore the registry and load MAPI from the system directory
263277
static bool s_fForceSystemMAPI = false;
264278

279+
// Preference flag for olmapi32.dll, true by default
280+
static bool s_fPreferOlmapi32 = true;
281+
265282
static volatile HMODULE g_hinstMAPI = nullptr;
266283
HMODULE g_hModPstPrx32 = nullptr;
267284

@@ -468,70 +485,63 @@ namespace mapistub
468485
return hkeyMapiClient;
469486
}
470487

471-
// Looks up Outlook's path given its qualified component guid
472-
std::wstring GetOutlookPath(_In_ const std::wstring& szCategory, _Out_opt_ bool* lpb64)
488+
// Looks up olmapi32.dll path given a qualified component guid
489+
std::wstring GetOLMAPI32Path(_In_ const std::wstring& szCategory)
473490
{
474-
logLoadMapi(L"Enter GetOutlookPath: szCategory = %ws\n", szCategory.c_str());
491+
logLoadMapi(L"Enter GetOLMAPI32Path: szCategory = %ws\n", szCategory.c_str());
475492
DWORD dwValueBuf = 0;
476493
std::wstring path;
477494

478-
if (lpb64) *lpb64 = false;
479-
480-
auto hRes = MyMsiProvideQualifiedComponent(
481-
szCategory.c_str(),
482-
L"outlook.x64.exe", // STRING_OK
483-
static_cast<DWORD>(INSTALLMODE_DEFAULT),
484-
nullptr,
485-
&dwValueBuf);
486-
LogError(L"GetOutlookPath: MsiProvideQualifiedComponent(x64)", hRes);
487-
if (hRes == S_OK)
488-
{
489-
if (lpb64) *lpb64 = true;
490-
}
491-
else
495+
auto hRes = E_FAIL;
496+
int usedIndex = -1;
497+
for (int i = 0; i < static_cast<int>(g_OutlookQualifiers.size()); ++i)
492498
{
499+
logLoadMapi(L"GetOLMAPI32Path: qualifier = %ws\n", g_OutlookQualifiers[i].c_str());
500+
dwValueBuf = 0;
493501
hRes = MyMsiProvideQualifiedComponent(
494502
szCategory.c_str(),
495-
L"outlook.exe", // STRING_OK
503+
g_OutlookQualifiers[i].c_str(),
496504
static_cast<DWORD>(INSTALLMODE_DEFAULT),
497505
nullptr,
498506
&dwValueBuf);
499-
LogError(L"GetOutlookPath: MsiProvideQualifiedComponent(x86)", hRes);
507+
LogError(L"GetOLMAPI32Path: MsiProvideQualifiedComponent", hRes);
508+
if (hRes == S_OK)
509+
{
510+
usedIndex = i;
511+
break;
512+
}
500513
}
501514

502-
if (hRes == S_OK)
515+
if (hRes == S_OK && usedIndex != -1)
503516
{
504517
dwValueBuf += 1;
505518
const auto lpszTempPath = std::wstring(dwValueBuf, '\0');
506-
507519
hRes = MyMsiProvideQualifiedComponent(
508520
szCategory.c_str(),
509-
L"outlook.x64.exe", // STRING_OK
521+
g_OutlookQualifiers[usedIndex].c_str(),
510522
static_cast<DWORD>(INSTALLMODE_DEFAULT),
511523
const_cast<wchar_t*>(lpszTempPath.c_str()),
512524
&dwValueBuf);
513-
LogError(L"GetOutlookPath: MsiProvideQualifiedComponent(x64)", hRes);
514-
if (hRes != S_OK)
525+
LogError(L"GetOLMAPI32Path: MsiProvideQualifiedComponent (path)", hRes);
526+
if (hRes == S_OK && !lpszTempPath.empty())
515527
{
516-
hRes = MyMsiProvideQualifiedComponent(
517-
szCategory.c_str(),
518-
L"outlook.exe", // STRING_OK
519-
static_cast<DWORD>(INSTALLMODE_DEFAULT),
520-
const_cast<wchar_t*>(lpszTempPath.c_str()),
521-
&dwValueBuf);
522-
LogError(L"GetOutlookPath: MsiProvideQualifiedComponent(x86)", hRes);
523-
}
528+
WCHAR szDrive[_MAX_DRIVE] = {0};
529+
WCHAR szOutlookPath[MAX_PATH] = {0};
530+
const auto errNo = _wsplitpath_s(
531+
lpszTempPath.c_str(), szDrive, _MAX_DRIVE, szOutlookPath, MAX_PATH, nullptr, NULL, nullptr, NULL);
532+
LogError(L"GetOLMAPI32Path: _wsplitpath_s", errNo);
524533

525-
if (hRes == S_OK)
526-
{
527-
path = lpszTempPath;
528-
logLoadMapi(L"Exit GetOutlookPath: Path = %ws\n", path.c_str());
534+
if (errNo == ERROR_SUCCESS)
535+
{
536+
path = std::wstring(szDrive) + std::wstring(szOutlookPath) + WszOlMAPI32DLL;
537+
logLoadMapi(L"GetOLMAPI32Path: found %ws\n", path.c_str());
538+
}
529539
}
530540
}
531541

532542
if (path.empty())
533543
{
534-
logLoadMapi(L"Exit GetOutlookPath: nothing found\n");
544+
logLoadMapi(L"Exit GetOLMAPI32Path: nothing found\n");
535545
}
536546

537547
return path;
@@ -557,23 +567,12 @@ namespace mapistub
557567
{
558568
logLoadMapi(L"Enter GetInstalledOutlookMAPI(%s)\n", component.c_str());
559569

560-
auto lpszTempPath = GetOutlookPath(component, nullptr);
570+
auto szPath = GetOLMAPI32Path(component);
561571

562-
if (!lpszTempPath.empty())
572+
if (!szPath.empty())
563573
{
564-
WCHAR szDrive[_MAX_DRIVE] = {0};
565-
WCHAR szOutlookPath[MAX_PATH] = {0};
566-
const auto errNo = _wsplitpath_s(
567-
lpszTempPath.c_str(), szDrive, _MAX_DRIVE, szOutlookPath, MAX_PATH, nullptr, NULL, nullptr, NULL);
568-
LogError(L"GetOutlookPath: _wsplitpath_s", errNo);
569-
570-
if (errNo == ERROR_SUCCESS)
571-
{
572-
const auto szPath = std::wstring(szDrive) + std::wstring(szOutlookPath) + WszOlMAPI32DLL;
573-
574-
logLoadMapi(L"GetInstalledOutlookMAPI: found %ws\n", szPath.c_str());
575-
return szPath;
576-
}
574+
logLoadMapi(L"GetInstalledOutlookMAPI: found %ws\n", szPath.c_str());
575+
return szPath;
577576
}
578577

579578
logLoadMapi(L"Exit GetInstalledOutlookMAPI: found nothing\n");
@@ -595,42 +594,75 @@ namespace mapistub
595594
return paths;
596595
}
597596

597+
/*
598+
* GetMAPIPaths - Returns a list of possible MAPI DLL paths in order of preference.
599+
*
600+
* Order of preference:
601+
* 1. If ForceSystemMAPI is set, prefer the system directory MAPI32.dll only.
602+
* 2. If ForceOutlookMAPI is set, prefer the Outlook MAPI client registry key.
603+
* 3. DllPathEx registry value from the selected MAPI client.
604+
* 4. All installed Outlook MAPI implementations (from known component GUIDs).
605+
* 5. DllPath registry value from the selected MAPI client.
606+
* 6. MSI-based MAPI path from the selected MAPI client.
607+
* 7. If not forcing Outlook, fallback to system directory MAPI32.dll.
608+
*/
598609
std::vector<std::wstring> GetMAPIPaths()
599610
{
611+
// Holds the ordered list of possible MAPI DLL paths
600612
auto paths = std::vector<std::wstring>();
601613
std::wstring szPath;
614+
615+
// 1. If ForceSystemMAPI is set, only use the system directory MAPI32.dll
602616
if (s_fForceSystemMAPI)
603617
{
604618
szPath = GetMAPISystemDir();
605619
if (!szPath.empty()) paths.push_back(szPath);
606620
return paths;
607621
}
608622

623+
// 2. Select the MAPI client registry key: Outlook if forced, otherwise default
609624
auto hkeyMapiClient = HKEY{};
610625
if (s_fForceOutlookMAPI)
611626
hkeyMapiClient = GetHKeyMapiClient(WszOutlookMapiClientName);
612627
else
613628
hkeyMapiClient = GetHKeyMapiClient(L"");
614629

630+
// 3. Prefer DllPathEx registry value from the selected MAPI client
615631
szPath = RegQueryWszExpand(hkeyMapiClient, WszValueNameDllPathEx);
616632
if (!szPath.empty()) paths.push_back(szPath);
617633

634+
// 4. Add all installed Outlook MAPI implementations (from known component GUIDs)
618635
auto outlookPaths = GetInstalledOutlookMAPI();
619636
paths.insert(end(paths), std::begin(outlookPaths), std::end(outlookPaths));
620637

638+
// 5. Prefer DllPath registry value from the selected MAPI client
621639
szPath = RegQueryWszExpand(hkeyMapiClient, WszValueNameDllPath);
622640
if (!szPath.empty()) paths.push_back(szPath);
623641

642+
// 6. Prefer MSI-based MAPI path from the selected MAPI client
624643
szPath = GetMailClientFromMSIData(hkeyMapiClient);
625644
if (!szPath.empty()) paths.push_back(szPath);
626645

646+
// 7. If not forcing Outlook, fallback to system directory MAPI32.dll
627647
if (!s_fForceOutlookMAPI)
628648
{
629649
szPath = GetMAPISystemDir();
630650
if (!szPath.empty()) paths.push_back(szPath);
631651
}
632652

633653
if (hkeyMapiClient) RegCloseKey(hkeyMapiClient);
654+
655+
// If olmapi32 preference is set, bubble all olmapi32.dll paths to the top (case-insensitive)
656+
if (s_fPreferOlmapi32)
657+
{
658+
auto is_olmapi32 = [](const std::wstring& path) {
659+
std::wstring lower = path;
660+
std::transform(lower.begin(), lower.end(), lower.begin(), ::towlower);
661+
return lower.find(L"olmapi32.dll") != std::wstring::npos;
662+
};
663+
static_cast<void>(std::stable_partition(paths.begin(), paths.end(), is_olmapi32));
664+
}
665+
634666
return paths;
635667
}
636668

@@ -777,4 +809,10 @@ namespace mapistub
777809
logLoadMapi(L"ForceSystemMAPI: fForce = 0x%08X\n", fForce);
778810
s_fForceSystemMAPI = fForce;
779811
}
812+
813+
void PreferOlmapi32(bool fPrefer)
814+
{
815+
logLoadMapi(L"PreferOlmapi32: fPrefer = 0x%08X\n", fPrefer);
816+
s_fPreferOlmapi32 = fPrefer;
817+
}
780818
} // namespace mapistub

library/stubutils.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ namespace mapistub
3737
std::wstring GetComponentPath(const std::wstring& szComponent, const std::wstring& szQualifier, bool fInstall);
3838
extern std::vector<std::wstring> g_pszOutlookQualifiedComponents;
3939
std::vector<std::wstring> GetMAPIPaths();
40-
// Looks up Outlook's path given its qualified component guid
41-
std::wstring GetOutlookPath(_In_ const std::wstring& szCategory, _Out_opt_ bool* lpb64);
40+
// Looks up olmapi32.dll path using a qualified component guid
41+
std::wstring GetOLMAPI32Path(_In_ const std::wstring& szCategory);
4242
std::wstring GetInstalledOutlookMAPI(int iOutlook);
4343
std::wstring GetMAPISystemDir();
4444

@@ -49,4 +49,5 @@ namespace mapistub
4949
void UnloadPrivateMAPI();
5050
void ForceOutlookMAPI(bool fForce);
5151
void ForceSystemMAPI(bool fForce);
52+
void PreferOlmapi32(bool fPrefer);
5253
} // namespace mapistub

0 commit comments

Comments
 (0)