@@ -103,6 +103,18 @@ 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+
106118 std::wstring GetInstalledOutlookMAPI (int iOutlook);
107119 std::wstring GetInstalledOutlookMAPI (const std::wstring component);
108120
@@ -262,6 +274,9 @@ namespace mapistub
262274 // Whether or not we should ignore the registry and load MAPI from the system directory
263275 static bool s_fForceSystemMAPI = false ;
264276
277+ // Preference flag for olmapi32.dll, true by default
278+ static bool s_fPreferOlmapi32 = true ;
279+
265280 static volatile HMODULE g_hinstMAPI = nullptr ;
266281 HMODULE g_hModPstPrx32 = nullptr ;
267282
@@ -468,70 +483,63 @@ namespace mapistub
468483 return hkeyMapiClient;
469484 }
470485
471- // Looks up Outlook's path given its qualified component guid
472- std::wstring GetOutlookPath (_In_ const std::wstring& szCategory, _Out_opt_ bool * lpb64 )
486+ // Looks up olmapi32.dll path given a qualified component guid
487+ std::wstring GetOLMAPI32Path (_In_ const std::wstring& szCategory)
473488 {
474- logLoadMapi (L" Enter GetOutlookPath : szCategory = %ws\n " , szCategory.c_str ());
489+ logLoadMapi (L" Enter GetOLMAPI32Path : szCategory = %ws\n " , szCategory.c_str ());
475490 DWORD dwValueBuf = 0 ;
476491 std::wstring path;
477492
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
493+ auto hRes = E_FAIL;
494+ int usedIndex = -1 ;
495+ for (int i = 0 ; i < static_cast <int >(g_OutlookQualifiers.size ()); ++i)
492496 {
497+ logLoadMapi (L" GetOLMAPI32Path: qualifier = %ws\n " , g_OutlookQualifiers[i].c_str ());
498+ dwValueBuf = 0 ;
493499 hRes = MyMsiProvideQualifiedComponent (
494500 szCategory.c_str (),
495- L" outlook.exe " , // STRING_OK
501+ g_OutlookQualifiers[i]. c_str (),
496502 static_cast <DWORD>(INSTALLMODE_DEFAULT),
497503 nullptr ,
498504 &dwValueBuf);
499- LogError (L" GetOutlookPath: MsiProvideQualifiedComponent(x86)" , hRes);
505+ LogError (L" GetOLMAPI32Path: MsiProvideQualifiedComponent" , hRes);
506+ if (hRes == S_OK)
507+ {
508+ usedIndex = i;
509+ break ;
510+ }
500511 }
501512
502- if (hRes == S_OK)
513+ if (hRes == S_OK && usedIndex != - 1 )
503514 {
504515 dwValueBuf += 1 ;
505516 const auto lpszTempPath = std::wstring (dwValueBuf, ' \0 ' );
506-
507517 hRes = MyMsiProvideQualifiedComponent (
508518 szCategory.c_str (),
509- L" outlook.x64.exe " , // STRING_OK
519+ g_OutlookQualifiers[usedIndex]. c_str (),
510520 static_cast <DWORD>(INSTALLMODE_DEFAULT),
511521 const_cast <wchar_t *>(lpszTempPath.c_str ()),
512522 &dwValueBuf);
513- LogError (L" GetOutlookPath : MsiProvideQualifiedComponent(x64 )" , hRes);
514- if (hRes != S_OK)
523+ LogError (L" GetOLMAPI32Path : MsiProvideQualifiedComponent (path )" , hRes);
524+ if (hRes == S_OK && !lpszTempPath. empty () )
515525 {
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- }
526+ WCHAR szDrive[_MAX_DRIVE] = {0 };
527+ WCHAR szOutlookPath[MAX_PATH] = {0 };
528+ const auto errNo = _wsplitpath_s (
529+ lpszTempPath.c_str (), szDrive, _MAX_DRIVE, szOutlookPath, MAX_PATH, nullptr , NULL , nullptr , NULL );
530+ LogError (L" GetOLMAPI32Path: _wsplitpath_s" , errNo);
524531
525- if (hRes == S_OK)
526- {
527- path = lpszTempPath;
528- logLoadMapi (L" Exit GetOutlookPath: Path = %ws\n " , path.c_str ());
532+ if (errNo == ERROR_SUCCESS)
533+ {
534+ path = std::wstring (szDrive) + std::wstring (szOutlookPath) + WszOlMAPI32DLL;
535+ logLoadMapi (L" GetOLMAPI32Path: found %ws\n " , path.c_str ());
536+ }
529537 }
530538 }
531539
532540 if (path.empty ())
533541 {
534- logLoadMapi (L" Exit GetOutlookPath : nothing found\n " );
542+ logLoadMapi (L" Exit GetOLMAPI32Path : nothing found\n " );
535543 }
536544
537545 return path;
@@ -557,23 +565,12 @@ namespace mapistub
557565 {
558566 logLoadMapi (L" Enter GetInstalledOutlookMAPI(%s)\n " , component.c_str ());
559567
560- auto lpszTempPath = GetOutlookPath (component, nullptr );
568+ auto szPath = GetOLMAPI32Path (component);
561569
562- if (!lpszTempPath .empty ())
570+ if (!szPath .empty ())
563571 {
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- }
572+ logLoadMapi (L" GetInstalledOutlookMAPI: found %ws\n " , szPath.c_str ());
573+ return szPath;
577574 }
578575
579576 logLoadMapi (L" Exit GetInstalledOutlookMAPI: found nothing\n " );
@@ -595,42 +592,75 @@ namespace mapistub
595592 return paths;
596593 }
597594
595+ /*
596+ * GetMAPIPaths - Returns a list of possible MAPI DLL paths in order of preference.
597+ *
598+ * Order of preference:
599+ * 1. If ForceSystemMAPI is set, prefer the system directory MAPI32.dll only.
600+ * 2. If ForceOutlookMAPI is set, prefer the Outlook MAPI client registry key.
601+ * 3. DllPathEx registry value from the selected MAPI client.
602+ * 4. All installed Outlook MAPI implementations (from known component GUIDs).
603+ * 5. DllPath registry value from the selected MAPI client.
604+ * 6. MSI-based MAPI path from the selected MAPI client.
605+ * 7. If not forcing Outlook, fallback to system directory MAPI32.dll.
606+ */
598607 std::vector<std::wstring> GetMAPIPaths ()
599608 {
609+ // Holds the ordered list of possible MAPI DLL paths
600610 auto paths = std::vector<std::wstring>();
601611 std::wstring szPath;
612+
613+ // 1. If ForceSystemMAPI is set, only use the system directory MAPI32.dll
602614 if (s_fForceSystemMAPI)
603615 {
604616 szPath = GetMAPISystemDir ();
605617 if (!szPath.empty ()) paths.push_back (szPath);
606618 return paths;
607619 }
608620
621+ // 2. Select the MAPI client registry key: Outlook if forced, otherwise default
609622 auto hkeyMapiClient = HKEY{};
610623 if (s_fForceOutlookMAPI)
611624 hkeyMapiClient = GetHKeyMapiClient (WszOutlookMapiClientName);
612625 else
613626 hkeyMapiClient = GetHKeyMapiClient (L" " );
614627
628+ // 3. Prefer DllPathEx registry value from the selected MAPI client
615629 szPath = RegQueryWszExpand (hkeyMapiClient, WszValueNameDllPathEx);
616630 if (!szPath.empty ()) paths.push_back (szPath);
617631
632+ // 4. Add all installed Outlook MAPI implementations (from known component GUIDs)
618633 auto outlookPaths = GetInstalledOutlookMAPI ();
619634 paths.insert (end (paths), std::begin (outlookPaths), std::end (outlookPaths));
620635
636+ // 5. Prefer DllPath registry value from the selected MAPI client
621637 szPath = RegQueryWszExpand (hkeyMapiClient, WszValueNameDllPath);
622638 if (!szPath.empty ()) paths.push_back (szPath);
623639
640+ // 6. Prefer MSI-based MAPI path from the selected MAPI client
624641 szPath = GetMailClientFromMSIData (hkeyMapiClient);
625642 if (!szPath.empty ()) paths.push_back (szPath);
626643
644+ // 7. If not forcing Outlook, fallback to system directory MAPI32.dll
627645 if (!s_fForceOutlookMAPI)
628646 {
629647 szPath = GetMAPISystemDir ();
630648 if (!szPath.empty ()) paths.push_back (szPath);
631649 }
632650
633651 if (hkeyMapiClient) RegCloseKey (hkeyMapiClient);
652+
653+ // If olmapi32 preference is set, bubble all olmapi32.dll paths to the top (case-insensitive)
654+ if (s_fPreferOlmapi32)
655+ {
656+ auto is_olmapi32 = [](const std::wstring& path) {
657+ std::wstring lower = path;
658+ std::transform (lower.begin (), lower.end (), lower.begin (), ::towlower);
659+ return lower.find (L" olmapi32.dll" ) != std::wstring::npos;
660+ };
661+ auto it = std::stable_partition (paths.begin (), paths.end (), is_olmapi32);
662+ }
663+
634664 return paths;
635665 }
636666
@@ -777,4 +807,10 @@ namespace mapistub
777807 logLoadMapi (L" ForceSystemMAPI: fForce = 0x%08X\n " , fForce );
778808 s_fForceSystemMAPI = fForce ;
779809 }
810+
811+ void PreferOlmapi32 (bool fPrefer )
812+ {
813+ logLoadMapi (L" PreferOlmapi32: fPrefer = 0x%08X\n " , fPrefer );
814+ s_fPreferOlmapi32 = fPrefer ;
815+ }
780816} // namespace mapistub
0 commit comments