MAPISendMail вылетает конвертированное приложение UWP. Альтернативы отправке электронной почты из приложения UWP?

У меня есть приложение Win32, которое также распространяется через Магазин Windows (для этого оно преобразуется в UWP с помощью Настольный мост.)

Приложение использует MAPISendMail функция для автоматизации создания электронной почты в почтовом клиенте по умолчанию с произвольным вложением, как таковая:

BOOL SendEmail(HWND hParentWnd, LPCTSTR pStrEmailAddrTo, LPCTSTR pStrSubject, LPCTSTR pStrMsg, LPCTSTR pFilePathAttachment)
{
BOOL bRes = FALSE;

HINSTANCE hMAPI = ::LoadLibrary(L"MAPI32.DLL");
if(hMAPI)
{
ULONG (WINAPI *pfnMAPISendMailW)(LHANDLE lhSession, ULONG_PTR ulUIParam, lpMapiMessageW lpMessage, FLAGS flFlags, ULONG ulReserved);

(FARPROC&)pfnMAPISendMailW = GetProcAddress(hMAPI, "MAPISendMailW");
if(pfnMAPISendMailW)
{
//Fill out data
MapiMessageW message = {0};
MapiFileDescW fileDesc = {0};
MapiRecipDescW recip = {0};

if(pFilePathAttachment &&
pFilePathAttachment[0])
{
fileDesc.nPosition = (ULONG)-1;
fileDesc.lpszPathName = ::PathFindFileName(pFilePathAttachment);
fileDesc.lpszFileName = (PWSTR)pFilePathAttachment;

message.nFileCount = 1;
message.lpFiles = &fileDesc;

}message.lpszSubject = (PWSTR)pStrSubject;
message.lpszNoteText = (PWSTR)pStrMsg;

if(pStrEmailAddrTo)
{
recip.ulRecipClass = MAPI_TO;
recip.lpszName = L"";
recip.lpszAddress = (PWSTR)pStrEmailAddrTo;

message.nRecipCount = 1;
message.lpRecips = &recip;
}

int nError = pfnMAPISendMailW(0, (ULONG_PTR)hParentWnd, &message, MAPI_LOGON_UI | MAPI_DIALOG, 0);

bRes = nError == SUCCESS_SUCCESS;
}

::FreeLibrary(hMAPI);
}

return bRes;
}

И тогда функция вызывается после нажатия кнопки в диалоговом окне:

SendEmail(this->GetSafeHwnd(), NULL,
L"Email subject message",
L"See attached file",
strFilePath);

Это работает в версии Win32, но конвертированное приложение UWP падает. К сожалению, для преобразованных приложений Магазина не так много диагностики. Все, что я могу видеть из аварийного дампа, это то, что он падает в mmgaclient.dll:

введите описание изображения здесь

и это недопустимое исключение адреса:

введите описание изображения здесь

Глубоко в недрах этой библиотеки:

введите описание изображения здесь

Я также видел другие отчеты о MAPISendMail грохот Другие приложения.

Я начал изучать его и заметил, что MAPISendMail Функция ведет себя странным образом — кажется, что она отключает родительское окно, но в случае, если она показывает какие-либо из своих собственных окон сообщений пользовательского интерфейса, она не делает их модальными. Вот пример. Если щелкнуть родительское окно этого окна сообщений, я смогу взаимодействовать с ним, даже если он не обрабатывает никаких сообщений, что заставляет Windows показать, что «оно не отвечает»:

введите описание изображения здесь

В этом случае (в настольной версии) оно не приводит к сбою приложения, и сообщение «не отвечает» исчезает, когда я нажимаю кнопку ОК.

Так что мне было интересно, сталкивался ли кто-нибудь еще с той же проблемой?

PS. Или есть другой API для отправки электронной почты из преобразованного приложения UWP?


РЕДАКТИРОВАТЬ: Эта вещь оказывается довольно длинной. Я постараюсь сократить это прямо до погони.

Я был в состоянии обратиться к «не отвечающей» родительской части пользовательского интерфейса и сбоя в MAPISendMail со следующими подходами.

A) Сбой может быть обнаружен SEH при вызове самого API (я не буду знать, пока приложение не будет одобрено в Магазине Windows):

ULONG isolated_MAPISendMailW(ULONG (WINAPI *pfnMAPISendMailW)(LHANDLE lhSession, ULONG_PTR ulUIParam, lpMapiMessageW lpMessage, FLAGS flFlags, ULONG ulReserved),
LHANDLE lhSession, ULONG_PTR ulUIParam, lpMapiMessageW lpMessage, FLAGS flFlags, ULONG ulReserved)
{
__try
{
return pfnMAPISendMailW(lhSession, ulUIParam, lpMessage, flFlags, ulReserved);
}
__except(1)
{
//SEH handler for all
ASSERT(NULL);
return -1;
}
}

Б) И по адресу не отвечающего родительского окна UI, я попытался позвонить MAPISendMail из отдельной ветки.

Обновленный код:

struct THREAD_INFO{

HWND hParentWnd;
LPCTSTR pStrEmailAddrTo;
LPCTSTR pStrSubject;
LPCTSTR pStrMsg;
LPCTSTR pFilePathAttachment;

BOOL bRes;
};

BOOL SendEmail(HWND hParentWnd, LPCTSTR pStrEmailAddrTo, LPCTSTR pStrSubject, LPCTSTR pStrMsg, LPCTSTR pFilePathAttachment)
{
THREAD_INFO ti;

ti.hParentWnd = hParentWnd;
ti.pStrEmailAddrTo = pStrEmailAddrTo;
ti.pStrSubject = pStrSubject;
ti.pStrMsg = pStrMsg;
ti.pFilePathAttachment = pFilePathAttachment;
ti.bRes = FALSE;

HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc_SendEmail, &ti, 0, NULL);

MSG msg;

for(;;)
{
DWORD dwRw = ::MsgWaitForMultipleObjectsEx(1, &hThread, INFINITE, QS_ALLEVENTS, 0) - WAIT_OBJECT_0;
if(dwRw == 0)
{
//Thread has exited
break;
}
else if(dwRw == 1)
{
//Process some messages
for(int t = 0; t < 8; t++)
{
if(!::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
break;
}
}
else
{
//Error
ASSERT(NULL);
::TerminateThread(hThread, 0xdeadbeef);
break;
}
}

::CloseHandle(hThread);

return ti.bRes;
}DWORD ThreadProc_SendEmail(LPVOID lpParameter)
{
THREAD_INFO* pTI = (THREAD_INFO*)lpParameter;

pTI->bRes = SendEmail_internal(pTI->hParentWnd, pTI->pStrEmailAddrTo,
pTI->pStrSubject, pTI->pStrMsg, pTI->pFilePathAttachment);

return 0;
}

BOOL SendEmail_internal(HWND hParentWnd, LPCTSTR pStrEmailAddrTo, LPCTSTR pStrSubject, LPCTSTR pStrMsg, LPCTSTR pFilePathAttachment)
{
BOOL bRes = FALSE;

HINSTANCE hMAPI = ::LoadLibrary(L"MAPI32.DLL");
if(hMAPI)
{
ULONG (WINAPI *pfnMAPISendMailW)(LHANDLE lhSession, ULONG_PTR ulUIParam, lpMapiMessageW lpMessage, FLAGS flFlags, ULONG ulReserved);

(FARPROC&)pfnMAPISendMailW = GetProcAddress(hMAPI, "MAPISendMailW");
if(pfnMAPISendMailW)
{
//Fill out data
MapiMessageW message = {0};
MapiFileDescW fileDesc = {0};
MapiRecipDescW recip = {0};

if(pFilePathAttachment &&
pFilePathAttachment[0])
{
fileDesc.nPosition = (ULONG)-1;
fileDesc.lpszPathName = ::PathFindFileName(pFilePathAttachment);
fileDesc.lpszFileName = (PWSTR)pFilePathAttachment;

message.nFileCount = 1;
message.lpFiles = &fileDesc;

}message.lpszSubject = (PWSTR)pStrSubject;
message.lpszNoteText = (PWSTR)pStrMsg;

if(pStrEmailAddrTo)
{
recip.ulRecipClass = MAPI_TO;
recip.lpszName = L"";
recip.lpszAddress = (PWSTR)pStrEmailAddrTo;

message.nRecipCount = 1;
message.lpRecips = &recip;
}

int nError = isolated_MAPISendMailW(pfnMAPISendMailW, 0, (ULONG_PTR)hParentWnd, &message, MAPI_LOGON_UI | MAPI_DIALOG, 0);

bRes = nError == SUCCESS_SUCCESS;
}

::FreeLibrary(hMAPI);
}

return bRes;
}

ULONG isolated_MAPISendMailW(ULONG (WINAPI *pfnMAPISendMailW)(LHANDLE lhSession, ULONG_PTR ulUIParam, lpMapiMessageW lpMessage, FLAGS flFlags, ULONG ulReserved),
LHANDLE lhSession, ULONG_PTR ulUIParam, lpMapiMessageW lpMessage, FLAGS flFlags, ULONG ulReserved)
{
__try
{
return pfnMAPISendMailW(lhSession, ulUIParam, lpMessage, flFlags, ulReserved);
}
__except(1)
{
//SEH handler for all
ASSERT(NULL);
return -1;
}
}

Но осталась еще одна часть предыдущего вопроса: даже если родительское окно не зависает, любой пользовательский интерфейс, отображаемый MAPISendMail все еще делается в отдельном окне:

введите описание изображения здесь

Таким образом, пользователь может щелкнуть по родительскому элементу, чтобы активировать его на панели задач или просто в самом окне:

введите описание изображения здесь

это полностью затеняет его и будет очень запутанным для пользователя, так как родительское окно не будет реагировать на нажатия на элементы управления внутри него.

Так что мне было интересно, есть ли способ, которым я могу поместить свое родительское окно в модальная петля сообщений до MAPISendMail отображает свой собственный интерфейс?


EDIT2: Я хочу опубликовать обновление, что мое решение выше Не работал. Мое преобразованное приложение UWP все еще зависало, когда MAPISendMailW был вызван, несмотря на принятые выше меры предосторожности. Таким образом, мое единственное решение до сих пор было серый эта функция пользовательского интерфейса для приложений UWP, если Windows 10, сборка ранее, чем 17107 обнаружен.

1

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]