ももらぼっ!にっき


2006年05月02日 [長年日記]

_ [W-ZERO3][WM5] IMailSyncHandlerを使ったメールの受信。

掲示板でと言ったきり、ずーっとほっぽっていたのですが。 ちょこちょこと調べながらやってみたところ、IMailSyncHandlerを使ったメールの受信に成功したっぽいです。

まぁ、詳細は後述のサンプルソースを見てもらうとして。

そもそも、IMailSyncHandlerってのはMAPIのメールの送受信部分(とか)を請け負うインターフェースのようです。 POP3とかIMAPとかのプロトコルの実装は(多分)このインターフェースを実装したdllで実現してます。 「HKLM\SOFTWARE\Microsoft\Inbox\Svc\IMAP4\DLL」とか「HKLM\SOFTWARE\Microsoft\Inbox\Svc\POP3\DLL」に書かれている「mailtrns.dll」でもこのIMailSyncHandlerの実装を持っています。

で、そのDLLには、IMailSyncHandlerを生成する関数「OneStopFactory」が必ず含まれていますので、その関数をLoadLibrary&GetProcAddressして取得します。

取得したIMailSyncHandlerではまず、Initializeメンバ関数を呼んであげます。で、その前に、IMailSyncCallBackとIMsgStoreが必要ですのでそれを用意します。 IMailSyncCallBackはIMailSyncHandlerで行われた操作の状態を取得するコールバック用インターフェースで*1、これを実装したクラスを作成し、そのインスタンスを用意すればOKです。 IMsgStoreは送受信したいアカウントのIMsgStoreをIMAPISession.GetMsgStoresTableあたりから取ってくればOKです。

んで、後はこのIMailSyncHandlerをごにょごにょすればOKのようです。

ということで試したときのサンプルです。 IMsgStoreを取るところは、今まで作ってきたMAPI.cpp/hをそのまま使っています。 実装を見たい方は、いぬそふとからソースをダウンロードしてくださいな。

//IMailSyncCallBackの実装…空っぽだけど。
class CMailSyncCallBack : public IMailSyncCallBack
{
  HRESULT QueryInterface(REFIID, void **){return S_OK;};
  ULONG AddRef(void){return 0;};
  ULONG Release(void){return 0;};

  HRESULT RequestSync(LPCWSTR, DWORD, LPBYTE){return S_OK;};
  HRESULT Progress(LPCWSTR, SYNCPROGRESSITEM*){return S_OK;};
  HRESULT GetGlobalSetting(LPCWSTR, LPSPropValue){return S_OK;};
  HRESULT LogEvent(TRANSPORTEVENT*){return S_OK;};
  UINT DisplayMessageBox(LPCWSTR, LPCWSTR, LPCWSTR, UINT){return 0;};
  HRESULT RequestCredentials(LPCWSTR, SYNCCREDENTIALS*, SYNCCREDENTIALS**){return S_OK;};
  HRESULT AllocateMem(DWORD, LPBYTE*){return S_OK;};
  HRESULT FreeMem(LPVOID){return S_OK;};
};

void sample(void)
{
  IMailSyncHandler *plHandler = NULL;
  MAPI::CMAPI mapi;
  MAPI::CSession session;
  CMailSyncCallBack callback;
  HRESULT hr = 0;

  //OneStopFactory関数を取得
  HINSTANCE hDll = LoadLibrary(_T("mailtrns.dll"));
  if (!hDll) return;

  ONESTOPFACTORYFUNC pOneStopFactory = (ONESTOPFACTORYFUNC) GetProcAddress(hDll, _T("OneStopFactory"));
  if (!pOneStopFactory) goto EXIT_FUNC;

  //POP3のIMailSyncHandlerを取得
  hr = pOneStopFactory(_T("POP3"), &plHandler);
  if (FAILED(hr)) goto EXIT_FUNC;

  //IMailSyncHandler.Initializeを呼ぶ
  MAPI::CMsgStore &msgStore = session.GetAccount(_T("あかうんと"));
  LPMDB pMsgStore = msgStore.GetLPMDB();
  hr = plHandler->Initialize(&callback, _T("あかうんと"), pMsgStore);
  if (FAILED(hr)) goto EXIT_FUNC;

  //IMailSyncHandler.Connectを呼ぶ
  //このタイミングで「ログイン...」相当が行われているっぽい。
  SYNCCREDENTIALS sc;
  ZeroMemory(&sc, sizeof(SYNCCREDENTIALS));
  sc.cbSize = sizeof(SYNCCREDENTIALS);
  sc.pszUsername = _T("ゆーざめい");
  sc.pszPassword = _T("ぱすわーど");
  sc.pszDomain = _T("どめいん");
  hr = plHandler->Connect(0, &sc);
  if (FAILED(hr)) goto EXIT_FUNC;

  //IMailSyncHandler.Synchronizeを呼ぶ
  //このタイミングでメールが受信されている。
  MAILSYNCREQUEST msr;
  ZeroMemory(&msr, sizeof(MAILSYNCREQUEST));
  msr.cbSize = sizeof(MAILSYNCREQUEST);
  msr.ffFlags = SYNC_NORMAL;
  hr = plHandler->Synchronize(&msr);
  if (FAILED(hr)) goto EXIT_FUNC;

  //IMailSyncHandler.Disconnectを呼ぶ
  //「ログオフ...」相当をやっているのかな。
  hr = plHandler->Disconnect(0);

EXIT_FUNC:
  if (plHandler) plHandler->ShutDown(0);
  if (hDll) FreeLibrary(hDll);
}

*1  多分ね。まだ試してないので…

_ [WM5][W-ZERO3] で、IMailSyncHandlerを使うと何が嬉しいの?

画面制御を完全に自分でコントロール可能になると思われます。

今のSandRでは、PDXメールを送受信するときは邪魔なバルーンが毎回出てきちゃいます。 その他のアカウントのメールだとメールソフトが起動しちゃいます。

それが、この方法を使うと、完全に裏で処理をすることが可能になりそうです。(いや、まだちゃんと試してないのですよ)

また、これまたあんまり試してないですけど、IMailSyncCallBackで細かい状況の取得ができそうですので、邪魔にならないようにそれを表示させることもできるかもしれません。

ということで、SandRとSendNowはこの手法を使った方式に切り替えていこうかなーと思っています。

あ、でも。 送受信するときにアカウント名&パスワードが必要になっちゃうんですよね。 PDXメールだけを使っている人には敷居が高くなっちゃうかもなので、今まで通りの方式で送受信する方法も残しておきたいとは思っています。

で、ここまで書いて思ったんですけど。

ここで知った技術をベースに、mailtrns.dllをラップしたdllを作って置き換えれば、メールが受信されるタイミングを正確に取れる気がします。 tmail.exeの置き換えで実現しているtmail_qmail3をもっと違った実装で実現したり、 メールの着信鳴り分けを実現したりできるかもしれません。

思っただけなので私が実装する可能性はものすごく低いんですがorz 技術共有はするので誰か作ってくれないかなー。