[WinAPI] MailSlot, 메일 슬롯 사용법

오늘은 프로세스간 통신 방법인 메일 슬롯 사용법에 대해서 알아보겠습니다.

메일 슬롯에 대해 약간의 설명을 하자면, 우체통과 비슷하다고 볼 수 있습니다.
아래 그림과 같이 A가 B에게 편지를 보낸다고 할 때, A는 편지를 우체통에 넣고, B는 우체통에서 편지를 찾아가면 됩니다.

이 방식은 매우 편리해서 아래와 같이 A는 우체통에 편지를 여러번 집어 넣을 수 있고, B가 나중에 한번에 찾아갈 수 도 있습니다.

물론 B가 편지를 찾아가지 않는데 A가 편지를 계속 집어 넣기만 한다면 우체통이 터지겠죠?

잘 유의해서 사용하면 편리하게 사용가능합니다.
MFC로 예제를 해보겠습니다. MFC 프로젝트를 생성해주시고, 아래와 같이 버튼 4개를 만들어 줍니다.

대화상자 헤더파일에 메일슬롯 핸들과 전송 데이터를 선언해줍니다.

HANDLE m_hMailSlot; // 메일슬롯 핸들
BYTE m_byData; // 전송 데이터

Create 버튼을 더블 클릭하여 이벤트 처리 함수에 아래와 같이 코딩합니다.

void CMFCApplication1Dlg::OnBnClickedButtonCreate()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	int iMailSize = 40000;

	m_hMailSlot = ::CreateMailslot( _T("\\\\.\\mailslot\\MailSlot"), iMailSize, 0, (LPSECURITY_ATTRIBUTES)NULL);
	
	if (INVALID_HANDLE_VALUE == m_hMailSlot)
	{
		m_hMailSlot = NULL;
		AfxMessageBox(_T("CreateMailslot Fail"));
	}
}

Send 버튼을 더블 클릭하여 이벤트 처리 함수에 아래와 같이 코딩합니다.

void CMFCApplication1Dlg::OnBnClickedButtonSend()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	BOOL bResult(FALSE);
	DWORD dwWrLen(0);

	HANDLE hFile;
	hFile = ::CreateFile(_T("\\\\.\\mailslot\\MailSlot"),
		GENERIC_WRITE,
		FILE_SHARE_READ,
		(LPSECURITY_ATTRIBUTES)NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		(HANDLE)NULL);

	if (INVALID_HANDLE_VALUE != hFile)
	{
		bResult = ::WriteFile(hFile, &m_byData, sizeof(m_byData), &dwWrLen, (LPOVERLAPPED)NULL);
		m_byData++;
		if ( FALSE == bResult )
			AfxMessageBox(_T("WriteFile Fail"));

		::CloseHandle(hFile);
	}
	else
	{
		AfxMessageBox(_T("CreateFile Fail"));
	}
}

Receive 버튼을 더블 클릭하여 이벤트 처리 함수에 아래와 같이 코딩합니다.

void CMFCApplication1Dlg::OnBnClickedButtonReceive()
{
	DWORD dwNextSize(0), dwMsgCnt(0);
	BOOL bResult(FALSE);
	BYTE byData(0);
	DWORD dwReadSize(0);

	if (INVALID_HANDLE_VALUE != m_hMailSlot)
	{
		bResult = GetMailslotInfo(m_hMailSlot, ///< mailslot handle
			(LPDWORD)NULL, ///< no maximum message size
			&dwNextSize, ///< size of next message
			&dwMsgCnt, ///< number of messages
			(LPDWORD)NULL); ///< no read time-out

		if (TRUE == bResult)
		{
			if (MAILSLOT_NO_MESSAGE != dwNextSize)
			{
				if (0 != dwMsgCnt) ///< retrieve all messages
				{
					bResult = ::ReadFile(m_hMailSlot, &byData, dwNextSize, &dwReadSize, (LPOVERLAPPED)NULL);

					if (TRUE == bResult)
					{
						CString sMsg;
						sMsg.Format(_T("Receive Message(%d)"), byData);
						AfxMessageBox(sMsg);
					}
					else
					{
						AfxMessageBox(_T("ReadFile Fail"));
					}
				}
			}
		}
	}
	else
	{
		AfxMessageBox(_T("CreateFile Fail"));
	}
}

Close 버튼을 더블 클릭하여 이벤트 처리 함수에 아래와 같이 코딩합니다.

void CMFCApplication1Dlg::OnBnClickedButtonClose()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	::CloseHandle(m_hMailSlot);
	m_hMailSlot = NULL;
}

위 상태로 실행해 보시면 아래와 같이 작동합니다.

서로 다른 프로세스에서 메일 슬롯으로 통신할 경우, CreateMailslot을 한 곳에서 Send, Receive가 가능하지만 그렇지 않은 곳에서는 Send만 해야합니다.