@@ -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
0 commit comments