From YYpBD's MediaWiki
[강좌] 메모리 맵드 파일 아이오 2.
안녕하세요~ 민성기 입니다.
1편을 올리고 조금 늦어진다는 것이... 흑흑... 무려 다섯달이나
지난 뒤에 2편을 올리는 만행을 저지르고 있습니다. T.T
세미나 전에 마무리를 짓고 싶은 욕심에... 조금 미흡한 점이 있어도
지금까지의 정을 생각해 이쁘게 봐 주시기 바랍니다.
그럼... 강좌 시~작~
-----------------------------
훅킹 강좌 번외편
메모리 맵드 파일 아이오를 이용한 어플리케이션 간의 데이터 교환. 2.
-----------------------------
* 메모리 맵드 파일 아이오용 API들.
이 기법의 흐름을 간단히 설명하면 다음과 같다. 메모리 맵드 파일의
오브젝트를 얻고 맵 뷰를 생성하고 데이터를 읽고 쓰고 맵 뷰를 해제하고
메모리 맵드 파일의 오브젝트를 해제한다. 간단하지~?
이를 위한 API는 CreateFileMapping, OpenFileMapping, MapViewOfFile,
UnmapViewOfFile, CloseFile 등이 있는데… 워낙 위의 설명이 탁월하므로
(~흐뭇) 각 API의 이름만 보아도 뭐하는 녀석인지 짐작이 가리라 믿는다.
메모리 맵드 파일 아이오를 쓰기 위한 가장 중요한 API는 CreateFileMapping
이라는 녀석이다. 생긴것도 무식할 뿐더러 달라는 것도 많아 지래 겁을먹게
만드는 살벌한 놈이지만 이 녀석을 잘 달래 메모리 맵드 파일의 오브젝트를
얻어야만 데이터를 넣고 읽고 지지고 볶고를 할 수 있으므로 싫어도 넘어야
할 산이다. 넘자~!!
function CreateFileMapping(
hFile: THandle;
lpFileMappingAttributes: PSecurityAttributes;
flProtect,
dwMaximumSizeHigh,
dwMaximumSizeLow: DWORD;
lpName: PChar): THandle; stdcall;
첫번째 hFile이라는 인자는 도대체 왜 있어야 하는지 존재 이유를 알 수
없는 희안한 놈이다. 메모리 맵드 파일과 연동될 파일의 핸들을 달라는 건데…
그럴려면 그냥 파일에 쓰지 뭣 때문에 귀찮게 메모리 맵드 파일을 쓰냐~
암튼 무언가 깊은 뜻이 있으니 있겠지 싶기도 하지만, (허긴, 윈도를 만든
사람이 어디 정상적인 사람일까나~) 다행히 도움말에는 여기에 $FFFFFFFF값을
주면 신경쓸 것 없다고 적혀 있다. 글쓰는 놈은 감히 인생은 이렇게 살아야
한다고 생각한다.
두번째 놈은 무슨 보안에 관계된 녀석인데, NT에서만 적용되고 일반적으로
이런 놈들은 무시해도 별 지장 없다. 포인터를 달라고 하니 원하는 대로
'nil'을 줘서 조용히 시키자. 세번째 인자는 메모리 맵드 파일을 읽기만
할껀지 쓰기도 할껀지 결정하는 놈이고 네번째와 다섯번째 인자는 메모리
맵드 파일의 크기를 결정하는 것이다. DWORD를 상/하위로 사용하니~ 크기는…
암튼 무지 크다~ (아~ 이만한 크기의 하드가 갖고 싶다~)
마지막 인자는 맵 오브젝트의 이름을 나타낸다. 이름을 안주면~? 물론 이름
없는 메모리 맵드 파일의 오브젝트가 생성된다. (이건 글쓰는 넘도 안해봤다.
꼭 해 보구 연락 주기 바란다~)
성공적으로 호출되면 이 녀석은 메모리 맵드 파일의 오브젝트를 리턴한다.
만약 메맵 (이거 쓰기도 귀찮다~) 파일 오브젝트가 기존에 존재한다면~?
당연히 기존의 오브젝트를 리턴하면서 새로 정의한 크기값은 무시된다. 이 때
GetLastError를 호출하면 ERROR_ALREADY_EXISTS 값이 나오게 되고, 이런
현상은 가끔씩 마당의 제작자 여덕수님 같은 초 궁극 변태 프로그래머에 의해
다중실행을 막는 방법으로 사용되곤 한다.
기존의 메멥 파일 오브젝트가 존재할 경우는 그 오브젝트를 리턴한다는 이
API의 칭찬해줄 만한 성질 덕분에 OpenFileMapping API 따위는 사용할 필요가
없어지고 (실제로 이 API를 사용한 예는 찾아보기 힘들다.)… 덩달아 설명할
필요도 없어져 버려 글쓰는 넘도 그냥 넘어가겠다. (신난당~)
MapViewOfFile은 생성된 메맵 오브젝트에 대한 '뷰'를 얻는 API이다. 간단히
말해 실제로 저장된 데이터의 포인터 값을 리턴한다.
function MapViewOfFile(
hFileMappingObject: THandle;
dwDesiredAccess: DWORD;
dwFileOffsetHigh,
dwFileOffsetLow,
dwNumberOfBytesToMap: DWORD): Pointer; stdcall;
첫번째 인자는 메멥 오브젝트를 달라는 것이고, 두번째는 읽기만 할래~
쓰기도 할래~ 를 결정하는 값이다. 세번째와 네번째는 맵 뷰의 시작
위치이고 마지막 인자는 맵 뷰의 크기이다. 이 마지막 인자가 0일 경우는
메맵 오브젝트 끝까지 맵 뷰가 설정된다.
메맵파일의 오브젝트를 얻고 이 오브젝트에 대한 맵 뷰를 얻는 API와 달리
해제하는 경우는 상대적으로 간단하다. UnmapViewOfFile은 MapViewOfFile에서
얻은 포인터 값을 주면 되고 메멥 파일 오브젝트를 반환하기 위해서는
CloseFile API에 메멥 파일 오브젝트를 인자로 하여 호출하면 된다.
* 간단한 사용 예.
메모리 맵드 파일을 생성하고 데이터를 읽고 쓰는 간단한 어플리케이션을
만들어 보겠다. 빈 폼에 두 개의 버튼을 올리고 각각 '쓰기'와 '읽기'라고
이름표를 달아준다. '쓰기'에 사용할 정수값을 적어주는 스핀 에디트도 하나
올려두고 다음과 같이 코딩하자.
……
var
// 메모리 맵 파일의 오브젝트.
FileMapObj : THandle;
// 폼 생성시 메모리 맵 파일 만들기.
procedure TForm1.FormCreate(Sender: TObject);
begin
FileMapObj :=
CreateFileMapping(
$FFFFFFFF, // 파일 연동 안함.
nil, // 보안문제 신경 안 씀.
PAGE_READWRITE, // 읽고 쓸 것임.
0, // 크기 상위 DWORD
SizeOf(Integer), // 크기 하위 DWORD
'메모리 맵 테스트를 위한 공유영역' // 메맵 파일의 이름
);
end;
// 폼 해제시 메모리 맵 파일 지우기.
procedure TForm1.FormDestroy(Sender: TObject);
begin
CloseHandle(FileMapObj);
end;
// 메모리 맵 파일의 데이터 읽기.
procedure TForm1.Button1Click(Sender: TObject);
var
IntP : PInteger;
begin
IntP := MapViewOfFile(FileMapObj, FILE_MAP_WRITE, 0, 0, 0);
try
ShowMessage(IntToStr(IntP^));
finally
UnMapViewOfFile(IntP);
end;
end;
// 메모리 맵 파일에 데이터 쓰기.
procedure TForm1.Button2Click(Sender: TObject);
var
IntP : PInteger;
begin
IntP := MapViewOfFile(FileMapObj, FILE_MAP_WRITE, 0, 0, 0);
try
IntP^ := SpinEdit1.Value;
finally
UnMapViewOfFile(IntP);
end;
end;
……
컴파일하고 여러 개 실행시켜 보자. 한 쪽에서 써 둔 정수값을 다른
곳에서도 읽을 수 있다. 물론 이 기법은 DLL 내에서도 사용할 수 있고,
처음 실행시킨 어플리케이션에서 생성한 메맵파일은 특별한 조작을 하지
않아도 마지막에 아무도 참조하지 않을 때 까지 유지된다.
전역 갈구리질에서 이렇게 같은 메맵파일을 공유한다면, 예전처럼 메인
어플리케이션의 핸들을 저장하는 장소나 갈구리의 아이디를 저장하는
장소로 파일을 벗어나 왠지 좀 더 뽀다구 나는 삶을 살 수 있을 것이다.
갈구리질 강좌를 메맵 파일을 사용하도록 뜯어 고치는 일은 당연히 이
글을 읽는 여러분들의 숙제이다.
그리하여 결국 강좌는 마무리 되었다~ 만세~ 만세~ 만만세~~
-------------------------
지금까지 읽어주신 분들게 감사 드립니다.
메모리 맵드 I/O에서 스트링타입으로 쓰기
팁으로 글쓰기는 처음이네요. 쑥쓰..
민성기님이 쓰신 메모리 맵드 I/O 강좌 잘 보았습니다.
예제가 정수로만 되어 있어서 좀 아쉬웠는데 좀더 찾아보니 문자열을 쓰기 위해선 PString 타입을 쓰라고 되어 있었습니다. 그런데 실제로 해보니 잘 안되더군요. 액세스 바이얼레이숑 에러도 나구요.
그래서 제가 다시 해봤습니다. ㅋ. 별 차이는 없고 걍 PChar을 쓰면 됩니다.
처음엔 포인터의 포인터.. 이중 포인터도 생각하고 그랬는데 그림을 그려가며 생각해 보니 그럴 필요가 없더군요. 그냥 PChar 하나면 됩니다.
소스 나갑니다.
////////////////////////////////////////////////////////////////////////////////
// 메모리 맵드 파일에서 읽어온다.
function TForm1.readMMedFile : String;
var
strP : pChar;
begin
new(strP);
StrP := MapViewOfFile(FileMapObj, FILE_MAP_WRITE, 0, 0, 0);
try
result:= String(strP);
finally
UnMapViewOfFile(StrP);
//dispose(strP);
end;
end;
////////////////////////////////////////////////////////////////////////////////
// 메모리 맵드 파일에 쓴다.
procedure TForm1.writeMMedFile(strData : String);
var
strP : pChar;
begin
new(strP);
strP := MapViewOfFile(FileMapObj, FILE_MAP_WRITE, 0, 0, 0);
try
strPCopy(strP, strData);
finally
UnMapViewOfFile(strP);
//dispose(strP);
end;
end;