[WinAPI] 공유 메모리 – Memory Mapped File

오늘은 프로세스 간 메모리 공유 방법에 대해 알아보겠습니다.

서로 다른 윈도우(프로세스)가 같은 메모리를 참조해야 할 경우엔 파일 매핑 방식을 이용하면 됩니다.
해당 방식에 대한 개념은 구글에 Memory Mapped File라고 쳐도 많이 나옵니다.
공유 메모리를 이용하면 결과적으로 아래와 같은 프로그램을 만들수 있습니다.

프로젝트를 하나 생성 하고 Create, Open Close, Write, Read 5개 버튼과 Edit Box 1개, Static Control 1개를 만듭니다.
아래 그림과 같이 각각의 컨트롤의 ID를 만들어줍니다.

아래 그림과 같이 Edit Box와 Static Control에 컨트롤 변수를 추가합니다.

각각의 버튼들을 더블 클릭하여 버튼 클릭 이벤트 처리기를 추가합니다.

변수형 HANDLE, 변수명 m_hHandle을 멤버 변수로 추가합니다.

그리고 변수형 int *, 변수명 m_piMemory를 멤버 변수로 추가합니다.

HANDLE m_hHandle;
int *m_piMemory;

cpp 파일에 아래 그림과 같이 define을 합니다(메모리 구분용).

#define SHARED_NAME _T("Shared_Memory_Test")

파일 매핑 방식을 이용한 프로세스 공유 메모리를 만드는 방식은 아래와 같습니다.

  1. File Mapping을 만듭니다.
  2. Map View를 만듭니다.

(File Mapping과 Map View가 정확한 개념을 가지고 글을 쓴건 아니지만 그냥 그렇구나 하고 넘어갑시다.)

코드로 구현해 보자면

1. Create 버튼 이벤트 처리기 함수에 아래와 같이 추가합니다.

void CMFCApplication1Dlg::OnBnClickedButtonCreate()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	if ( NULL == m_hHandle )
	{
		m_hHandle = ::CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof( int ), SHARED_NAME );
		if ( NULL == m_hHandle )
			AfxMessageBox( _T("Create Error") );
	}
}

2. Open 버튼 이벤트 처리기 함수에 아래와 같이 추가합니다.

void CMFCApplication1Dlg::OnBnClickedButtonOpen()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	if ( NULL == m_hHandle )
	{
		m_hHandle = ::OpenFileMapping( FILE_MAP_ALL_ACCESS, NULL, SHARED_NAME );

		if ( NULL == m_hHandle )
		{
			AfxMessageBox( _T( "Open Error" ) );
		}
	}

	if ( NULL != m_hHandle )
	{
		m_piMemory = (int *)::MapViewOfFile( m_hHandle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof( int ) );
		if ( NULL == m_piMemory )
		{
			AfxMessageBox( _T( "Open Error" ) );
		}
	}
}

이렇게 추가해서 정상적으로 작동하면 공유 메모리에 쓰기 위해서 아래 Write 버튼 이벤트 처리기 함수에 아래와 같이 추가합니다.

void CMFCApplication1Dlg::OnBnClickedButtonWrite()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	if ( NULL != m_piMemory )
	{
		int iRead;
		CString sRead;
		m_edData.GetWindowText( sRead );
		iRead = _ttoi( sRead );

		memcpy( m_piMemory, &iRead, sizeof( int ) );
	}
	else
	{
		AfxMessageBox( _T( "MapView is NULL" ) );
	}
}

공유 메모리를 읽기 위해서 Read 버튼 이벤트 처리기 함수에 아래와 같이 추가합니다.

void CMFCApplication1Dlg::OnBnClickedButtonRead()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	if ( NULL != m_piMemory )
	{
		int iRead;
		memcpy( &iRead, m_piMemory, sizeof( int ) );

		CString sRead;
		sRead.Format( _T("%d"), iRead );
		m_stRead.SetWindowText( sRead );

	}
	else
	{
		AfxMessageBox( _T( "MapView is NULL" ) );
	}
}

이 상태로만 해도 작동은 되지만, Handle을 받았으면 닫는 것까지 제대로 해야겠죠?

Close 버튼 이벤트 처리기 함수에 아래와 같이 추가합니다.

void CMFCApplication1Dlg::OnBnClickedButtonClose()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	if ( NULL != m_piMemory )
	{
		BOOL bUnMap = ::UnmapViewOfFile( m_piMemory );
		if ( bUnMap )
		{
			m_piMemory = NULL;
		}
		else
		{
			AfxMessageBox( _T( "Unmap Error" ) );
		}
	}

	if ( NULL != m_hHandle )
	{
		BOOL bClose = ::CloseHandle( m_hHandle );
		if ( bClose )
		{
			m_hHandle = NULL;
		}
		else
		{
			AfxMessageBox( _T( "Close Error" ) );
		}
	}
}

여기까지 int형 메모리를 공유 메모리를 만들어 봤습니다.
공유 메모리를 dll로 만들어 사용하는 방법도 있습니다. 그 방법은 나중에 글로 정리하겠습니다.
int형 공유 메모리를 만들어서 그렇지, 구조체로도 공유 메모리를 만들 수가 있습니다.