| Server IP : 209.209.40.120 / Your IP : 216.73.217.112 Web Server : Microsoft-IIS/10.0 System : Windows NT NEWWWW 10.0 build 17763 (Windows Server 2019) i586 User : NEWWWW$ ( 0) PHP Version : 8.3.30 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0/um/ |
Upload File : |
//+---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved
//
//
// File: mapiUnicodeHelp.h
//
// Contents: This file provides a function MAPISendMailHelper which takes mail message information
// as Unicode and will pass to the registered mail provider, converting to ANSI/UTF8 if necessary.
// This function will work on Windows 7 and below, on later platforms mapi32!MAPISendMailW can be called directly.
//
//----------------------------------------------------------------------------
#pragma once
#include <winapifamily.h>
#include <Shellapi.h>
#pragma region Desktop Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
// Main helper function declaration:
ULONG MAPISendMailHelper(_In_ LHANDLE lhSession, _In_ ULONG_PTR ulUIParam, _In_ lpMapiMessageW lpMessage, _In_ FLAGS flFlags, _In_ ULONG ulReserved);
// Declarations of other functions used:
HMODULE LoadDefaultMailProvider(_Out_ bool *pfSupportUTF8);
HRESULT ConvertMessageFromUnicode(_In_ const MapiMessageW *pMessageIn, _Outptr_ MapiMessage **ppMessageOut, bool fSupportUTF8);
void FreeMessage(_Inout_opt_ MapiMessage* pMessage);
__inline ULONG MAPISendMailHelper(
_In_ LHANDLE lhSession,
_In_ ULONG_PTR ulUIParam,
_In_ lpMapiMessageW lpMessage,
_In_ FLAGS flFlags,
_In_ ULONG ulReserved)
{
ULONG ulRet = MAPI_E_FAILURE; // Default return code
// Load the MAPI stub dll, which itself delegates to the default mail provider:
HMODULE hMapi32 = LoadLibraryW(L"mapi32.dll");
if (hMapi32)
{
LPMAPISENDMAILW pfnMapiSendMailW = (LPMAPISENDMAILW)GetProcAddress(hMapi32, "MAPISendMailW");
if (pfnMapiSendMailW)
{
// Mapi32 supports MAPISendMailW directly - call it:
ulRet = pfnMapiSendMailW(lhSession, ulUIParam, lpMessage, flFlags, ulReserved);
}
else
{
// Mapi32 does not support MAPISendMailW, see if the mapi provider {mail client} does:
bool fMadeCall = false;
bool fSupportsUTF8 = false;
HMODULE hMapiProvider = LoadDefaultMailProvider(&fSupportsUTF8);
if (hMapiProvider)
{
LPMAPISENDMAILW pfnMapiSendMailProviderW = (LPMAPISENDMAILW)GetProcAddress(hMapiProvider, "MAPISendMailW");
if (pfnMapiSendMailProviderW)
{
// Mapi provider supports MAPISendMailW - call it:
ulRet = pfnMapiSendMailProviderW(lhSession, ulUIParam, lpMessage, flFlags, ulReserved);
fMadeCall = true;
}
FreeLibrary(hMapiProvider);
}
// If we failed to load the provider or it did not support MAPISendMailW, then convert to ansi and call the mapi32 MAPISendMail directly.
if (!fMadeCall && ((flFlags & MAPI_FORCE_UNICODE) != MAPI_FORCE_UNICODE))
{
MapiMessage *pMapiMessageA;
if (SUCCEEDED(ConvertMessageFromUnicode(lpMessage, &pMapiMessageA, fSupportsUTF8)))
{
LPMAPISENDMAIL pfnMapiSendMailA = (LPMAPISENDMAIL)GetProcAddress(hMapi32, "MAPISendMail");
if (pfnMapiSendMailA)
{
ulRet = pfnMapiSendMailA(lhSession, ulUIParam, pMapiMessageA, flFlags, ulReserved);
}
FreeMessage(pMapiMessageA);
}
}
}
FreeLibrary(hMapi32);
}
return ulRet;
}
// Allocates space for nFileDesc MapiFileDesc structures and zeros out their memory.
__inline HRESULT AllocFileDesc(ULONG nFileDesc, _Outptr_result_buffer_(nFileDesc) MapiFileDesc** ppFileDesc)
{
HRESULT hr = E_INVALIDARG;
if (ppFileDesc && (nFileDesc > 0))
{
*ppFileDesc = NULL;
MapiFileDesc* pNewFileDesc = new MapiFileDesc[nFileDesc];
if (pNewFileDesc == NULL)
{
hr = E_OUTOFMEMORY;
}
else
{
ZeroMemory(pNewFileDesc, nFileDesc * sizeof(MapiFileDesc));
*ppFileDesc = pNewFileDesc;
hr = S_OK;
}
}
return hr;
}
// Frees memory in embedded pointers in a MapiFileDesc array.
__inline void FreeFileDesc(_Inout_updates_opt_(nFileDesc) MapiFileDesc* pFileDesc, ULONG nFileDesc)
{
if (pFileDesc)
{
for (ULONG i = 0; i < nFileDesc; i++)
{
delete[] pFileDesc[i].lpszPathName;
delete[] pFileDesc[i].lpszFileName;
pFileDesc[i].ulReserved = 0;
pFileDesc[i].flFlags = 0;
pFileDesc[i].nPosition = 0;
pFileDesc[i].lpszPathName = 0;
pFileDesc[i].lpszFileName = 0;
pFileDesc[i].lpFileType = NULL; // Don't free this as not allocated initially
}
}
}
// Allocates space for nRecipDesc MapiRecipDesc structures and zeros out their memory.
__inline HRESULT AllocRecipDesc(ULONG nRecipDesc, _Outptr_result_buffer_(nRecipDesc) MapiRecipDesc** ppRecipDesc)
{
HRESULT hr = E_INVALIDARG;
if (ppRecipDesc && (nRecipDesc > 0))
{
*ppRecipDesc = NULL;
MapiRecipDesc* pNewRecipDesc = new MapiRecipDesc[nRecipDesc];
if (pNewRecipDesc == NULL)
{
hr = E_OUTOFMEMORY;
}
else
{
ZeroMemory(pNewRecipDesc, nRecipDesc * sizeof(MapiRecipDesc));
*ppRecipDesc = pNewRecipDesc;
hr = S_OK;
}
}
return hr;
}
// Frees memory in embedded pointers in a MapiRecipDesc array.
__inline void FreeRecipDesc(_Inout_updates_opt_(nRecipDesc) MapiRecipDesc* pRecipDesc, ULONG nRecipDesc)
{
if (pRecipDesc)
{
for (ULONG i = 0; i < nRecipDesc; i++)
{
delete[] pRecipDesc[i].lpszName;
delete[] pRecipDesc[i].lpszAddress;
delete[] pRecipDesc[i].lpEntryID;
pRecipDesc[i].ulReserved = 0;
pRecipDesc[i].ulRecipClass = 0;
pRecipDesc[i].lpszName = 0;
pRecipDesc[i].lpszAddress = 0;
pRecipDesc[i].ulEIDSize = 0;
pRecipDesc[i].lpEntryID = 0;
}
}
}
// Frees MapiMessage object and embedded pointers. Can be passed NULL.
__inline void FreeMessage(_Inout_opt_ MapiMessage* pMessage)
{
if (pMessage)
{
FreeRecipDesc(pMessage->lpOriginator, 1);
FreeRecipDesc(pMessage->lpRecips, pMessage->nRecipCount);
FreeFileDesc(pMessage->lpFiles, pMessage->nFileCount);
delete[] pMessage->lpszSubject;
delete[] pMessage->lpszNoteText;
delete[] pMessage->lpszMessageType;
delete[] pMessage->lpszDateReceived;
delete[] pMessage->lpszConversationID;
delete[] pMessage->lpOriginator;
delete[] pMessage->lpRecips;
delete[] pMessage->lpFiles;
pMessage->ulReserved = 0;
pMessage->lpszSubject = 0;
pMessage->lpszNoteText = 0;
pMessage->lpszMessageType = 0;
pMessage->lpszDateReceived = 0;
pMessage->lpszConversationID = 0;
pMessage->flFlags = 0;
pMessage->lpOriginator = 0;
pMessage->nRecipCount = 0;
pMessage->lpRecips = 0;
pMessage->nFileCount = 0;
pMessage->lpFiles = 0;
delete pMessage;
}
}
// Convert Unicode string to ANSI/UTF8.
__inline HRESULT ConvertStringToMultiByteEx(PCWSTR pszIn, _Outptr_ PSTR *ppszOut, bool fSupportUTF8, _Out_opt_ bool *pfUsedDefaultChar)
{
HRESULT hr = S_OK;
*ppszOut = NULL;
if (pfUsedDefaultChar)
{
*pfUsedDefaultChar = false;
}
if (pszIn)
{
BOOL fUsedDefaultChar = FALSE;
int iLen = WideCharToMultiByte(fSupportUTF8 ? CP_UTF8 : CP_ACP, fSupportUTF8 ? WC_ERR_INVALID_CHARS : 0, pszIn, -1, NULL, 0, NULL, fSupportUTF8 ? NULL : &fUsedDefaultChar);
if (iLen)
{
*ppszOut = new char[iLen];
if (*ppszOut)
{
if (0 == WideCharToMultiByte(fSupportUTF8 ? CP_UTF8 : CP_ACP, fSupportUTF8 ? WC_ERR_INVALID_CHARS : 0, pszIn, -1, *ppszOut, iLen, NULL, fSupportUTF8 ? NULL : &fUsedDefaultChar))
{
delete[] *ppszOut;
*ppszOut = NULL;
hr = HRESULT_FROM_WIN32(GetLastError());
}
else if (pfUsedDefaultChar)
{
*pfUsedDefaultChar = (fUsedDefaultChar != FALSE);
}
}
else
{
hr = E_OUTOFMEMORY;
}
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
return hr;
}
// Convert Unicode string to ANSI.
__inline HRESULT ConvertStringToMultiByte(PCWSTR pszIn, _Outptr_result_maybenull_ PSTR *ppszOut)
{
return ConvertStringToMultiByteEx(pszIn, ppszOut, false, NULL);
}
// Convert a file path from Unicode to ANSI.
// If the conversion cannot be performed then try converting to the short-filename representation first.
__inline HRESULT ConvertFilePathFromUnicode(PCWSTR pszIn, _Outptr_result_maybenull_ PSTR *ppszOut)
{
*ppszOut = NULL;
bool fUsedDefaultChar = false;
HRESULT hr = ConvertStringToMultiByteEx(pszIn, ppszOut, false, &fUsedDefaultChar);
if (SUCCEEDED(hr) && fUsedDefaultChar)
{
// Invalid conversion - try using GetShortPathName:
delete[] *ppszOut;
*ppszOut = NULL;
wchar_t wzTemp[MAX_PATH];
DWORD dwRes = GetShortPathNameW(pszIn, wzTemp, ARRAYSIZE(wzTemp));
if (dwRes >= ARRAYSIZE(wzTemp))
{
hr = E_INVALIDARG;
}
else if (dwRes == 0)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
else
{
hr = ConvertStringToMultiByteEx(wzTemp, ppszOut, false, &fUsedDefaultChar);
if (SUCCEEDED(hr) && fUsedDefaultChar)
{
// Invalid conversion - fail here rather than passing an invalid path:
delete[] *ppszOut;
*ppszOut = NULL;
hr = E_INVALIDARG;
}
}
}
return hr;
}
__inline HRESULT ConvertFileDescArrayFromUnicode(_In_reads_(ulFileDesc) const MapiFileDescW *pFileIn, ULONG ulFileDesc, _Outptr_result_buffer_(ulFileDesc) MapiFileDesc **ppFileOut)
{
HRESULT hr = S_OK;
*ppFileOut = NULL;
if (pFileIn && ulFileDesc)
{
MapiFileDesc *pFileOut = NULL;
hr = AllocFileDesc(ulFileDesc, &pFileOut);
if (SUCCEEDED(hr))
{
for (ULONG ul = 0; SUCCEEDED(hr) && ul < ulFileDesc; ul++)
{
hr = ConvertFilePathFromUnicode(pFileIn[ul].lpszPathName, &pFileOut[ul].lpszPathName);
if (SUCCEEDED(hr))
{
hr = ConvertStringToMultiByte(pFileIn[ul].lpszFileName, &pFileOut[ul].lpszFileName);
}
if (SUCCEEDED(hr))
{
pFileOut[ul].ulReserved = pFileIn[ul].ulReserved;
pFileOut[ul].flFlags = pFileIn[ul].flFlags;
pFileOut[ul].nPosition = pFileIn[ul].nPosition;
pFileOut[ul].lpFileType = pFileIn[ul].lpFileType; // This entry has no parts which need converting, copy pointer directly
}
}
if (SUCCEEDED(hr))
{
*ppFileOut = pFileOut;
}
else
{
FreeFileDesc(pFileOut, ulFileDesc); // Will delete partially constructed MapiFileDesc's
delete[] pFileOut;
}
}
}
return hr;
}
__inline HRESULT ConvertRecipDescArrayFromUnicode(_In_reads_(ulRecipDesc) const MapiRecipDescW *pRecipIn, ULONG ulRecipDesc, _Outptr_result_buffer_(ulRecipDesc) MapiRecipDesc **ppRecipOut)
{
HRESULT hr = S_OK;
*ppRecipOut = NULL;
if (pRecipIn && ulRecipDesc)
{
MapiRecipDesc *pRecipOut = NULL;
hr = AllocRecipDesc(ulRecipDesc, &pRecipOut);
if (SUCCEEDED(hr))
{
for (ULONG ul = 0; SUCCEEDED(hr) && ul < ulRecipDesc; ul++)
{
hr = ConvertStringToMultiByte(pRecipIn[ul].lpszName, &pRecipOut[ul].lpszName);
if (SUCCEEDED(hr))
{
hr = ConvertStringToMultiByte(pRecipIn[ul].lpszAddress, &pRecipOut[ul].lpszAddress);
}
if (SUCCEEDED(hr) && pRecipIn[ul].ulEIDSize && pRecipIn[ul].lpEntryID)
{
pRecipOut[ul].lpEntryID = new BYTE[pRecipIn[ul].ulEIDSize];
if (pRecipOut[ul].lpEntryID)
{
memcpy(pRecipOut[ul].lpEntryID, pRecipIn[ul].lpEntryID, pRecipIn[ul].ulEIDSize);
}
else
{
hr = E_OUTOFMEMORY;
}
}
if (SUCCEEDED(hr))
{
pRecipOut[ul].ulReserved = pRecipIn[ul].ulReserved;
pRecipOut[ul].ulRecipClass = pRecipIn[ul].ulRecipClass;
pRecipOut[ul].ulEIDSize = pRecipIn[ul].ulEIDSize;
}
}
if (SUCCEEDED(hr))
{
*ppRecipOut = pRecipOut;
}
else
{
FreeRecipDesc(pRecipOut, ulRecipDesc); // Will delete partially constructed MapiRecipDesc's
delete[] pRecipOut;
}
}
}
return hr;
}
// Convert a complete Unicode MapiMessageW to a MapiMessage.
// Take account of whether the mapi provider supports the message body being passed as UTF8 in this scenario.
__inline HRESULT ConvertMessageFromUnicode(_In_ const MapiMessageW *pMessageIn, _Outptr_ MapiMessage **ppMessageOut, bool fSupportUTF8)
{
HRESULT hr = S_OK;
*ppMessageOut = NULL;
if (pMessageIn)
{
MapiMessage *pMessageOut = new MapiMessage;
if (pMessageOut == NULL)
{
hr = E_OUTOFMEMORY;
}
else
{
ZeroMemory(pMessageOut, sizeof(MapiMessage));
hr = ConvertStringToMultiByteEx(pMessageIn->lpszSubject, &pMessageOut->lpszSubject, fSupportUTF8, NULL); // Can be passed as UTF8 if provider supports it.
if (SUCCEEDED(hr))
{
hr = ConvertStringToMultiByteEx(pMessageIn->lpszNoteText, &pMessageOut->lpszNoteText, fSupportUTF8, NULL); // Can be passed as UTF8 if provider supports it.
if (SUCCEEDED(hr))
{
hr = ConvertStringToMultiByte(pMessageIn->lpszMessageType, &pMessageOut->lpszMessageType);
if (SUCCEEDED(hr))
{
hr = ConvertStringToMultiByte(pMessageIn->lpszDateReceived, &pMessageOut->lpszDateReceived);
if (SUCCEEDED(hr))
{
hr = ConvertStringToMultiByte(pMessageIn->lpszConversationID, &pMessageOut->lpszConversationID);
if (SUCCEEDED(hr))
{
hr = ConvertRecipDescArrayFromUnicode(pMessageIn->lpOriginator, 1, &pMessageOut->lpOriginator);
if (SUCCEEDED(hr))
{
hr = ConvertRecipDescArrayFromUnicode(pMessageIn->lpRecips, pMessageIn->nRecipCount, &pMessageOut->lpRecips);
if (SUCCEEDED(hr))
{
hr = ConvertFileDescArrayFromUnicode(pMessageIn->lpFiles, pMessageIn->nFileCount, &pMessageOut->lpFiles);
if (SUCCEEDED(hr))
{
pMessageOut->ulReserved = fSupportUTF8 ? CP_UTF8 : pMessageIn->ulReserved;
pMessageOut->flFlags = pMessageIn->flFlags;
pMessageOut->nRecipCount = pMessageIn->nRecipCount;
pMessageOut->nFileCount = pMessageIn->nFileCount;
}
}
}
}
}
}
}
}
if (SUCCEEDED(hr))
{
*ppMessageOut = pMessageOut;
}
else
{
FreeMessage(pMessageOut);
}
}
}
return hr;
}
// Methods used to obtain the default MAPI provider, including looking this up in Windows Installer:
// Function pointer typedefs for Windows Installer MsiProvideQualifiedComponentW function.
typedef UINT (WINAPI MSIPROVIDEQUALIFIEDCOMPONENTW)(PCWSTR, PCWSTR, DWORD, PWSTR, DWORD*);
typedef MSIPROVIDEQUALIFIEDCOMPONENTW * PMSIPROVIDEQUALIFIEDCOMPONENTW;
static const wchar_t s_wszLcid[] = L"Software\\";
static const wchar_t s_wszPolicy[] = L"Software\\Policy\\";
__inline bool GetComponentPathOrInstall(HINSTANCE hinstMSI, PCWSTR szCategory, DWORD dwLcid, _Out_writes_(*pcchPath) PWSTR szPath, _Inout_ DWORD *pcchPath, bool fInstall)
{
UINT uiRet = ERROR_INDEX_ABSENT; // Default value, means did not find component
wchar_t szQualifier[16]; // "{lcid}\NT"
PMSIPROVIDEQUALIFIEDCOMPONENTW pfnMsiProvideQualifiedComponentW;
const DWORD cchPathOrig = *pcchPath;
// Get MsiProvideQualifiedComponentW()
pfnMsiProvideQualifiedComponentW = (PMSIPROVIDEQUALIFIEDCOMPONENTW)GetProcAddress(hinstMSI, "MsiProvideQualifiedComponentW");
if (pfnMsiProvideQualifiedComponentW)
{
szPath[0] = L'\0';
szQualifier[0] = L'\0';
// 1. Try "dddd\NT" qualifier
swprintf_s(szQualifier, ARRAYSIZE(szQualifier), L"%lu\\%s", dwLcid, L"NT");
uiRet = pfnMsiProvideQualifiedComponentW(szCategory, szQualifier, static_cast<DWORD>(-1), szPath, pcchPath);
if (uiRet == ERROR_FILE_NOT_FOUND || uiRet == ERROR_INDEX_ABSENT)
{
*pcchPath = cchPathOrig;
if (fInstall && uiRet == ERROR_FILE_NOT_FOUND)
{
// If component not found, try installing it:
uiRet = pfnMsiProvideQualifiedComponentW(szCategory, szQualifier, 0, szPath, pcchPath);
}
else
{
// 2. Try "dddd" qualifier
swprintf_s(szQualifier, ARRAYSIZE(szQualifier), L"%lu", dwLcid);
uiRet = pfnMsiProvideQualifiedComponentW(szCategory, szQualifier, static_cast<DWORD>(-1), szPath, pcchPath);
if (fInstall && uiRet == ERROR_FILE_NOT_FOUND)
{
*pcchPath = cchPathOrig;
uiRet = pfnMsiProvideQualifiedComponentW(szCategory, szQualifier, 0, szPath, pcchPath);
}
}
}
}
return (uiRet != ERROR_INDEX_ABSENT); // Continue searching if we didn't find the component
}
__inline bool GetComponentPath(_In_opt_ PWSTR szComponent, _In_opt_ PZZWSTR szzQualifier, _Out_writes_(cchDllPath) PWSTR szDllPath, DWORD cchDllPath, bool fInstall)
{
szDllPath[0] = L'\0';
HINSTANCE hinstMSI = NULL;
size_t cb;
PWSTR szLcid = NULL;
HKEY hkeyLcid = NULL;
PWSTR szPolicy = NULL;
HKEY hkeyPolicy = NULL;
bool fDone = false;
PWSTR szName;
DWORD dwLcid;
const DWORD cchDllPathOrig = cchDllPath;
hinstMSI = ::LoadLibraryW(L"MSI.DLL");
if (hinstMSI)
{
// Use defaults if no szzQualifier
if (szzQualifier == NULL || szzQualifier[0] == L'\0')
{
fDone = true;
if (!GetComponentPathOrInstall(hinstMSI, szComponent, GetUserDefaultLCID(), szDllPath, &cchDllPath, fInstall))
{
cchDllPath = cchDllPathOrig;
if (!GetComponentPathOrInstall(hinstMSI, szComponent, GetSystemDefaultLCID(), szDllPath, &cchDllPath, fInstall))
{
cchDllPath = cchDllPathOrig;
if (!GetComponentPathOrInstall(hinstMSI, szComponent, 1033, szDllPath, &cchDllPath, fInstall))
{
fDone = false;
}
}
}
}
else
{
// Open the Policy key
cb = (wcslen(s_wszPolicy) + wcslen(szzQualifier) + 1) * sizeof(wchar_t);
szPolicy = (PWSTR) HeapAlloc(GetProcessHeap(), 0, cb);
if (szPolicy)
{
wcscpy_s(szPolicy, cb / sizeof(wchar_t), s_wszPolicy);
wcscat_s(szPolicy, cb / sizeof(wchar_t), szzQualifier);
RegOpenKeyExW(HKEY_CURRENT_USER, szPolicy, 0, KEY_READ, &hkeyPolicy);
}
// Open the Lcid key
cb = (wcslen(s_wszLcid) + wcslen(szzQualifier) + 1) * sizeof(wchar_t);
szLcid = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, cb);
if (szLcid)
{
wcscpy_s(szLcid, cb / sizeof(wchar_t), s_wszLcid);
wcscat_s(szLcid, cb / sizeof(wchar_t), szzQualifier);
RegOpenKeyExW(HKEY_CURRENT_USER, szLcid, 0, KEY_READ, &hkeyLcid);
}
// Get first registry value name
szName = &szzQualifier[wcslen(szzQualifier) + 1];
// Loop till component found or we're out of registry value names
while (szName[0] != L'\0' && !fDone)
{
DWORD dwType;
DWORD dwSize = sizeof(dwLcid);
DWORD dwSize1 = dwSize;
if ((hkeyPolicy && RegQueryValueExW(hkeyPolicy, szName, 0, &dwType, (LPBYTE) &dwLcid, &dwSize) == ERROR_SUCCESS && dwType == REG_DWORD) ||
(hkeyLcid && RegQueryValueExW(hkeyLcid, szName, 0, &dwType, (LPBYTE) &dwLcid, &dwSize1) == ERROR_SUCCESS && dwType == REG_DWORD))
{
cchDllPath = cchDllPathOrig;
fDone = GetComponentPathOrInstall(hinstMSI, szComponent, dwLcid, szDllPath, &cchDllPath, fInstall);
}
szName = &szName[wcslen(szName) + 1]; // Next registry value name
}
}
}
if (hkeyPolicy) { RegCloseKey(hkeyPolicy); }
if (szPolicy) { HeapFree(GetProcessHeap(), 0, (LPVOID) szPolicy); }
if (hkeyLcid) { RegCloseKey(hkeyLcid); }
if (szLcid) { HeapFree(GetProcessHeap(), 0, (LPVOID) szLcid); }
if (hinstMSI) { ::FreeLibrary(hinstMSI); }
return fDone;
}
// Applications or DLLs can specify they must always use a certain mail provider in the registry.
// Check this for the current process and see if a certain provider is required:
__inline bool AlwaysNeedsMSMAPI(_Out_writes_opt_(cchDllPath) PWSTR szDllPath, DWORD cchDllPath)
{
bool fNeedsMSMAPI = false;
HKEY hkeyRoot = NULL;
long lResult = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows Messaging Subsystem\\MSMapiApps", 0, KEY_READ, &hkeyRoot);
if (ERROR_SUCCESS == lResult)
{
wchar_t szValueName[256];
DWORD dwSize;
DWORD dwIndex = 0;
DWORD dwType;
DWORD cbBuffer;
// Loop through the list of entries specified in the registry:
do
{
dwSize = ARRAYSIZE(szValueName);
// Either check for the mail provider specified for each entry, or just check for the existence of the entry:
if (NULL == szDllPath || 0 == cchDllPath)
{
lResult = ::RegEnumValueW(hkeyRoot, dwIndex++, szValueName, &dwSize, NULL, &dwType, NULL, NULL);
}
else
{
szDllPath[cchDllPath - 1] = L'\0';
cbBuffer = (cchDllPath - 1) * sizeof(wchar_t);
lResult = ::RegEnumValueW(hkeyRoot, dwIndex++, szValueName, &dwSize, NULL, &dwType, (LPBYTE) szDllPath, &cbBuffer);
}
if (ERROR_SUCCESS == lResult)
{
// Check if the registry entry is the current process, or if it is a dll loaded in the current process:
if (NULL != GetModuleHandleW(szValueName))
{
fNeedsMSMAPI = true;
break;
}
}
} while (ERROR_SUCCESS == lResult);
::RegCloseKey(hkeyRoot);
}
return fNeedsMSMAPI;
}
__inline bool IsTerminalServer(void)
{
OSVERSIONINFOEXW osVersionInfo = {0};
DWORDLONG dwlConditionMask = 0;
osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
osVersionInfo.wSuiteMask = VER_SUITE_TERMINAL;
VER_SET_CONDITION(dwlConditionMask, VER_SUITENAME, VER_AND);
return (VerifyVersionInfoW(&osVersionInfo, VER_SUITENAME, dwlConditionMask) != 0);
}
// Find the registry key to the default Simple MAPI provider, from either HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE.
__inline LRESULT GetMailClientRegKey(HKEY hkeyRoot, _Out_ HKEY* hkeyDefaultMail)
{
HKEY hkey = nullptr;
// Open the key to find out what the default mail program is:
LRESULT lResult = ::RegOpenKeyExW(hkeyRoot, L"Software\\Clients\\Mail", 0, KEY_READ, &hkey);
if (ERROR_SUCCESS == lResult)
{
wchar_t mailAppName[MAX_PATH] = L"";
wchar_t defaultMailAppName[MAX_PATH];
if (AlwaysNeedsMSMAPI(mailAppName, ARRAYSIZE(mailAppName)))
{
if (mailAppName[0] != L'\0')
{
// Mail client supplied
wcscpy_s(defaultMailAppName, ARRAYSIZE(defaultMailAppName), mailAppName);
lResult = ERROR_SUCCESS;
}
else
{
// No mail client, use mapi32x.dll
lResult = ERROR_PATH_NOT_FOUND;
}
}
else
{
// Find out what the default mail program is.
DWORD sizeInBytes = (ARRAYSIZE(defaultMailAppName) - 1) * sizeof(wchar_t);
defaultMailAppName[ARRAYSIZE(defaultMailAppName) - 1] = L'\0';
lResult = ::RegQueryValueExW(hkey, nullptr, nullptr, nullptr, (LPBYTE)defaultMailAppName, &sizeInBytes);
}
if (ERROR_SUCCESS == lResult)
{
// Open the key for the default mail program to see where the dll is:
lResult = ::RegOpenKeyExW(hkey, defaultMailAppName, 0, KEY_READ, hkeyDefaultMail);
//In case of getting default mail client value from HKCU, look at both HKCU/HKLM
//for finding the dllPath for the mail client
if (ERROR_SUCCESS != lResult && hkeyRoot == HKEY_CURRENT_USER)
{
wchar_t DefaultMailAppKeyPath[MAX_PATH];
wcscpy_s(DefaultMailAppKeyPath, ARRAYSIZE(DefaultMailAppKeyPath), L"Software\\Clients\\Mail\\");
wcscat_s(DefaultMailAppKeyPath, ARRAYSIZE(DefaultMailAppKeyPath), defaultMailAppName);
lResult = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, DefaultMailAppKeyPath, 0, KEY_READ, hkeyDefaultMail);
}
}
::RegCloseKey(hkey);
}
return lResult;
}
// Find the path to the default Simple MAPI provider, from either HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE.
// If the provider was installed by Windows Installer then the MSI itself can be checked for the location,
// and if neccessary install on demand:
__inline bool GetMapiDllPath(HKEY hkeyRoot, _Out_writes_(cchDllPath) PWSTR szDllPath, DWORD cchDllPath, _Out_ bool *pfSupportUTF8)
{
szDllPath[0] = L'\0';
*pfSupportUTF8 = false;
DWORD cbBufferSizeT;
DWORD dwType;
HKEY hkeyDefaultMail = NULL;
// Open the key for the default mail program to see where the dll is:
LRESULT lResult = GetMailClientRegKey(hkeyRoot, &hkeyDefaultMail);
if (ERROR_SUCCESS == lResult)
{
// See if provider supports messages as UTF8:
DWORD dwSupportUTF8 = 0;
cbBufferSizeT = sizeof(dwSupportUTF8);
if (ERROR_SUCCESS == ::RegQueryValueExW(hkeyDefaultMail, L"SupportUTF8", NULL, &dwType, (LPBYTE)&dwSupportUTF8, &cbBufferSizeT))
{
if (REG_DWORD == dwType && dwSupportUTF8 != 0)
{
*pfSupportUTF8 = true;
}
} // If reg value not present *pfSupportUTF8 remains as false, not an error
wchar_t szComponent[39 + 1] = {0}; // wcslen(GUID)
DWORD dwMSIInstallOnWTS;
PWSTR szMSIOfficeLCID = NULL;
PWSTR szMSIApplicationLCID = NULL;
// Get MSIInstallOnWTS, 0 means don't demand-install on terminal server machines:
cbBufferSizeT = sizeof(dwMSIInstallOnWTS);
lResult = ::RegQueryValueExW(hkeyDefaultMail, L"MSIInstallOnWTS", NULL, &dwType, (LPBYTE) &dwMSIInstallOnWTS, &cbBufferSizeT);
if (ERROR_SUCCESS == lResult && REG_DWORD == dwType)
{
// Use what is returned
}
else
{
dwMSIInstallOnWTS = 1; // Default is true
}
// Get MSIApplicationLCID
lResult = ::RegQueryValueExW(hkeyDefaultMail, L"MSIApplicationLCID", NULL, &dwType, NULL, &cbBufferSizeT);
if (ERROR_SUCCESS == lResult && REG_MULTI_SZ == dwType)
{
szMSIApplicationLCID = (PWSTR)HeapAlloc(GetProcessHeap(), 0, cbBufferSizeT + sizeof(wchar_t));
if (szMSIApplicationLCID)
{
szMSIApplicationLCID[cbBufferSizeT / sizeof(wchar_t)] = L'\0';
// Note: Could return ERROR_MORE_DATA if the size of the value changes between here
// and the call above to get the size.
lResult = ::RegQueryValueExW(hkeyDefaultMail, L"MSIApplicationLCID", NULL, &dwType, (LPBYTE) szMSIApplicationLCID, &cbBufferSizeT);
}
}
// Get MSIOfficeLCID
if (ERROR_SUCCESS == lResult)
{
lResult = ::RegQueryValueExW(hkeyDefaultMail, L"MSIOfficeLCID", NULL, &dwType, NULL, &cbBufferSizeT);
}
if (ERROR_SUCCESS == lResult && REG_MULTI_SZ == dwType)
{
szMSIOfficeLCID = (PWSTR)HeapAlloc(GetProcessHeap(), 0, cbBufferSizeT + sizeof(wchar_t));
if (szMSIOfficeLCID)
{
szMSIOfficeLCID[cbBufferSizeT / sizeof(wchar_t)] = L'\0';
// Note: Could return ERROR_MORE_DATA if the size of the value changes between here
// and the call above to get the size.
lResult = ::RegQueryValueExW(hkeyDefaultMail, L"MSIOfficeLCID", NULL, &dwType, (LPBYTE) szMSIOfficeLCID, &cbBufferSizeT);
}
}
// Find out what the component is.
bool fDone = false;
cbBufferSizeT = (ARRAYSIZE(szComponent) - 1) * sizeof(wchar_t);
szComponent[ARRAYSIZE(szComponent) - 1] = L'\0';
if (ERROR_SUCCESS == lResult)
{
lResult = ::RegQueryValueExW(hkeyDefaultMail, L"MSIComponentID", NULL, &dwType, (LPBYTE) szComponent, &cbBufferSizeT);
}
if (ERROR_SUCCESS == lResult && REG_SZ == dwType)
{
bool fInstall = dwMSIInstallOnWTS || !IsTerminalServer();
// First try Application's LCID(s)
if (szMSIApplicationLCID && GetComponentPath(szComponent, szMSIApplicationLCID, szDllPath, cchDllPath, fInstall))
{
fDone = true;
}
else if (szMSIOfficeLCID && GetComponentPath(szComponent, szMSIOfficeLCID, szDllPath, cchDllPath, fInstall))
{
// Then try Office's LCID(s)
fDone = true;
}
else if (GetComponentPath(szComponent, NULL, szDllPath, cchDllPath, fInstall))
{
// Finally try the defaults
fDone = true;
}
}
if (!fDone)
{
// Find out what the dll is from direct registry lookup:
cbBufferSizeT = (cchDllPath - 1) * sizeof(wchar_t);
szDllPath[cchDllPath - 1] = L'\0';
lResult = ::RegQueryValueExW(hkeyDefaultMail, L"DLLPath", NULL, &dwType, (LPBYTE) szDllPath, &cbBufferSizeT);
if (ERROR_SUCCESS != lResult)
{
szDllPath[0] = L'\0';
}
else if (REG_EXPAND_SZ == dwType)
{
wchar_t szExpandedPath[MAX_PATH];
lResult = ExpandEnvironmentStringsW(szDllPath, szExpandedPath, MAX_PATH);
if (lResult > 0 && lResult < MAX_PATH)
{
wcscpy_s(szDllPath, cchDllPath, szExpandedPath);
}
else
{
szDllPath[0] = L'\0';
}
}
}
if (szMSIApplicationLCID)
{
HeapFree(GetProcessHeap(), 0, (LPVOID) szMSIApplicationLCID);
}
if (szMSIOfficeLCID)
{
HeapFree(GetProcessHeap(), 0, (LPVOID) szMSIOfficeLCID);
}
::RegCloseKey(hkeyDefaultMail);
}
return (L'\0' != szDllPath[0]); // return true if we found a dll path
}
// Find the path of the executable for the Default Mail client. Note it's the responsibility of the caller
// to free exePath using CoTaskMemFree when it's no longer needed.
__inline bool GetDefaultMailClientExePath(HKEY hkeyRoot, _Out_ PWSTR* exePath)
{
*exePath = nullptr;
HKEY hkeyDefaultMail = nullptr;
// Open the key for the default mail program to see where the EXE is:
LRESULT lResult = GetMailClientRegKey(hkeyRoot, &hkeyDefaultMail);
if (ERROR_SUCCESS == lResult)
{
// Find out what the exe is from direct registry lookup:
HKEY hkeyShellCommand = nullptr;
lResult = ::RegOpenKeyExW(hkeyDefaultMail, L"shell\\open\\command", 0, KEY_READ, &hkeyShellCommand);
if (ERROR_SUCCESS == lResult)
{
wchar_t shellCommand[MAX_PATH];
wchar_t expandedPath[MAX_PATH];
DWORD bufferSizeInBytes = ARRAYSIZE(shellCommand) * sizeof(wchar_t);
DWORD valueType;
lResult = ::RegQueryValueExW(hkeyShellCommand, L"", NULL, &valueType, (LPBYTE)shellCommand, &bufferSizeInBytes);
if (ERROR_SUCCESS == lResult)
{
if (REG_EXPAND_SZ == valueType)
{
lResult = ExpandEnvironmentStringsW(shellCommand, expandedPath, ARRAYSIZE(expandedPath));
if (lResult > 0 && lResult <= ARRAYSIZE(shellCommand))
{
wcscpy_s(shellCommand, ARRAYSIZE(shellCommand), expandedPath);
}
}
if (FAILED(SHEvaluateSystemCommandTemplate(shellCommand, exePath, nullptr, nullptr)))
{
*exePath = nullptr;
lResult = ERROR_PATH_NOT_FOUND;
}
}
::RegCloseKey(hkeyShellCommand);
}
::RegCloseKey(hkeyDefaultMail);
}
return (lResult == ERROR_SUCCESS); // return true if we found an exe path
}
__inline HMODULE LoadDefaultMailProvider(_Out_ bool *pfSupportUTF8)
{
HMODULE hMod = NULL;
wchar_t szDllPath[MAX_PATH] = L"";
*pfSupportUTF8 = false;
// Get path to Simple MAPI provider implementation and whether it supports UTF8 messages.
// Try both HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE:
if (!GetMapiDllPath(HKEY_CURRENT_USER, szDllPath, ARRAYSIZE(szDllPath), pfSupportUTF8) &&
!GetMapiDllPath(HKEY_LOCAL_MACHINE, szDllPath, ARRAYSIZE(szDllPath), pfSupportUTF8))
{
// Otherwise default to trying to load mapi32x.dll:
wcscpy_s(szDllPath, ARRAYSIZE(szDllPath), L"mapi32x.dll");
}
OFSTRUCT ofs;
// If the proposed dll is mapi32.dll, then don't load as this would lead to an infinite loop
if (_wcsicmp(szDllPath, L"mapi32.dll") != 0)
{
if (_wcsicmp(szDllPath, L"mapi32x.dll") != 0)
{
// Specific mapi client dll has been specified - load it
hMod = LoadLibraryW(szDllPath);
}
else if ((OpenFile("mapisvc.inf", &ofs, OF_EXIST) != HFILE_ERROR) || AlwaysNeedsMSMAPI(NULL, 0))
{
// Fallback to mapi32x.dll if no mapi client has been specified - ensure always loading from system directory
UINT uiRes = GetSystemDirectoryW(szDllPath, ARRAYSIZE(szDllPath));
if ((uiRes > 0) && (uiRes < ARRAYSIZE(szDllPath)))
{
if (wcscat_s(szDllPath, ARRAYSIZE(szDllPath), L"\\mapi32x.dll") == 0)
{
hMod = LoadLibraryW(szDllPath);
}
}
}
}
return hMod;
}
#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */
#pragma endregion