Media Log

CreateFile은 Wndows Api 들 중 가장 기본적이면서도 중요한 함수이다.
이 함수는 단지 파일을 생성하는 것 뿐만이 아니라 파일을 오픈할 수도 있고 디렉터리를 오픈할 수도 있으며 또한 여러 디바이스들까지 오픈 할 수 있다. 사실 CreateFile에서 File은 꼭 파일만이 아닌 여러 디바이스들을 추상화한 Virtual File을 뜻하는 셈이다.
이 함수를 통해 파일을 여는 순간에 동기 I/O를 할지 비동기 I/O를 할지 결정하게 되며, 내가 어떤 작업을 하려는지 내가 파일을 열고 있는 동안 다른 클라이언트들에게는 어떤 작업을 허용할지도 결정하게 된다.
생성하려는 파일의 읽기 전용, 숨김 파일 등의 속성도 정할 수 있으며, 캐시를 이용 할지 말지, 쓰기를 하는 족족 플러시 하게 할지 또 I/O를 순차적으로 할지 랜덤하게 할지 등의 힌트도 파일 시스템으로 전달해줄 수 있다.

이렇게 중요한 함수이니만큼 MSDN에는 CreateFile에 대한 문서가 아주 잘 나와있는데, 페이지 내의 링크들까지 하나하나  따라가면서 차근 차근 읽다보면 시스템 프로그래밍에 대해 배울 수 있는 것들도 많고 무엇보다도 아주 재밌다.

하지만 아무리 열심히 읽어도 글만 읽고서 지식을 자기 것으로 만들 수는 없는 법이다.
언제나 마지막은 실습으로 끝나야 한다. 글을 다 이해한 것 같아도 막상 진짜로 해보려고 하면 거기서 또 어려운 문제가 닥치기 마련이며, 이 것까지 해결하고 나서야 비로소 완전히 자기 지식으로 만들었다고 할 수 있다.

CreateFile의 파라메터는 몇 개 안되는 것 같지만 엄청난 플래그들의 조합이 가능하기 때문에 사실은 적은 갯수가 아니다.
실습을 해보기 위해서 항상 무거운 비주얼 스튜디오를 켜고 그 지겨운 파라메터들을 매번 입력하는 것은 손가락도 아프고 시간도 많이 들어가는 비효율적인 방식이다.

이런 실습을 위해 누군가가 이미 아주 훌륭한 도구를 만들어서 osronline.com에 올려놓았다.
이는 프로세스 모니터와 함께 내가 가장 즐겨쓰는 도구들 중 하나인데, 병들어가는 내 손가락을 조금이나마 쉴 수 있게 해주는 아주 고마운 친구이다.


위 그림에서 보이는 것 처럼 CreateFile 함수 형태 그대로 UI를 제공하고, 실험해보고 싶은 모든 플래그 조합을 넣어볼 수 있다.
생성뿐만이 아니라 읽기 쓰기도 해볼 수 있으며 조금 더 저수준 함수인 NtCreateFile까지도 다루어볼 수 있다.

우측의 버튼들을 클릭하면 아래처럼 또 다른 대화상자가 나와서 CreateFile의 많은 옵션들을 손쉽게 넣어서 테스트 할 수 있다.



파일 시스템과 관련이 있는 일은 하는 사람들은 두말 할 것도 없고, Wndows 플랫폼에서 개발하는 모든 개발자들이 알아두면 좋을 훌륭한 도구이다.
저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License

http://www.benjaminlog.com/trackback/171 관련글 쓰기

  1. 재호님 팬 at 2010/12/29 12:14 [edit/del]

    재호님~ 파일시스템에 관심이 많으신거 같네요!
    저도 파일시스템에 관심있어요 ^^
    이병오님의 "윈도우 파일시스템" 책과 정명수님의 커널관련글 읽으면서 공부하고 있는데 좋은거 같습니다. 재호님도 화이팅! ^^

    Reply
    • Favicon of http://www.benjaminlog.com BlogIcon 김재호 at 2010/12/29 12:37 [edit/del]

      윈도 파일시스템 책은 저도 가지고 있는데 정명수님 커널 관련글은 잘 모르겠어요. 혹시 글들 정리되어 있는 URL이 있으면 좀 가르쳐주세요.^^

  2. 재호님 팬 at 2010/12/29 20:19 [edit/del]

    이런 제가 센스가 없어서 ㅋㅋ
    www.swblog.net 입니다. 저도 마이크로소프트 잡지를 통해서 알게 되었구요~ 이분 글로 공부하고 있어요 ^^

    Reply

submit
Basic Grammar in Use with Answers (Full Color, 3rd Edition) - 9점
Raymond Murphy 외 지음/Cambridge University Press

Grammar in Use Intermediate (Paperback, 3rd Edition, with Answers) - 10점
/Cambridge University Press(케임브리지)

어느 날 문득 영어공부를 좀 해야겠다는 생각이 들어서 인터넷으로 바로 이 책을 주문했었는데, 그게 2008년 10월의 일이었다.
점심시간이나 잠들기 전의 짜투리 시간을 활용해서 이 책을 공부했다. 2년의 시간이나 지나서야 이 책들을 다 끝냈으니 얼마나 게으르게 공부했는가. 그래도 주기적으로 꼬박꼬박 봐오면서 끝까지 포기하지 않은게 너무 너무 기쁘다.

1장부터 마지막 장까지 한번도 순서를 뒤집지 않고, 한글자 한글자 조심조심 읽어보고 문제를 풀고 또 답을 맞췄다.
보라색 책인 Grammar in Use Intermediate는 올해 내내 가방에 넣고 다녀서 책이 떡이 되어버렸는데, 그래서 오히려 더 애착이 간다.

당연히 이 책을 처음 샀을 당시에 비해서 영어 실력은 상당히 많이 좋아졌다.
내가 영어 공부를 시작한 목적은 프로그래밍을 조금 더 잘하고 싶어서 였다. 기술 문서들을 읽기도 너무 어렵고, 특히 뉴스 그룹 같은 곳에서 무엇을 물어 볼 때 한 문장도 제대로 못 써서 쩔쩔매는 나를 발견하고는 이대로는 실력이 많이 안늘겠구나 하고 생각을 했다.

이 책들 덕분에 지금은 이런 문서들을 별다른 거부감 없이 읽을 수 있고, stackoverflow 같은 사이트에서 프로그래밍 하다가 궁금한 것들을 물어볼 수 있을 정도가 되었으니 목적은 달성한 셈이다.

사실 처음에는 이 두 권을 꼼꼼히만 다 보면 실력이 엄청 많이 늘 것이라 생각했는데, 아직도 2~3줄 짜리 긴 문장을 볼 때 숨이 턱턱 막히는 것을 보면 역시 세상에 만만하게 되는 일이 없구나 싶다. 그래도 기초는 잘 닦았으니 꾸준히만 계속 공부하면 점점 좋아질 것이라 생각한다.

나는 Basic이나 Intermediate나 2판으로 샀었는데, 내가 구입한 직후 Intermediate의 3판이 나왔고 바로 얼마전에 Basic Grammar In Use의 3판이 나왔다. 새로 구입한다면 당연히 3판을 사는 것이 좋을 것이다.
당시 이 책 2권을 사기 위해 책 값 3만원 정도를 썼는데, 얻은 것에 비하면 책 값은 아주 뽕을 뽑았다고 할 수 있겠다.
아, 뽕을 뽑는다는 것은 얼마나 기분 좋은 일인가.

하지만, 이 책을 끝까지 공부한다는 것은 그리 쉬운 일이 아니다.
사실 이 책이 얼마나 유명한 책인지는 말 안해도 영어에 관심이 있는 사람들이라면 다 알고 있는 사실 아닌가. 내 주위에도 많은 사람들이 이 책을 가지고 있지만 끝까지 다 풀어낸 사람을 한 명도 보지 못했다.
나는 내가 얼마나 의지가 약한 인간인지 알기 때문에, 처음 책을 사서 아주 느슨하게 목표를 잡았다. 1년에 한 권씩.
3일에 한 챕터씩만 하면 딱 1년이 걸린다. 이 정도면 할만하지 않은가?
사람마다 성향도 다르고 처해있는 상황들도 각기 다르니, 자신에게 잘 맞추어서 목표를 정하고 시작하는 것이 도움이 될 것이다. 중요한 것은 중간에 포기하지 않고 끝까지 하는 것이다.

뒤돌아보면 내가 군대에 있었을 때 이 책들을 공부하지 않았던 것은 정말 어리석었다. 나는 그 때 왜 내무반 방구석에 누워서 쓸데없는 소설책들만 읽고 있었을까.
영어 공부는 어렸을 때 부터 하는 것이 훨씬 이득이다. 23-4살 때 영어를 어느 정도만 했어도 30살이 될 때까지 훨씬 더 많은 지식을 머리 속에 넣을 수 있었을텐데.

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License

http://www.benjaminlog.com/trackback/170 관련글 쓰기

  1. 재호님 팬 at 2010/12/29 12:08 [edit/del]

    재호님의 의지가 대단하신거 같네요!
    저도 보라책으로 공부하고 있는데 반정도 본거 같네요~ 저도 기술문서 읽을때 꼭 2~3번씩 읽는지라 더 분발해야 겠습니다.

    Reply
  2. lovelybandy at 2011/01/16 17:10 [edit/del]

    잘 읽었습니다.
    저도 꼭 끝까지 다 해내고 싶어지네요.

    Reply
  3. ㄷㄷㄷ at 2011/01/26 12:18 [edit/del]

    엮인글이되었네욤 ㅋㅋ
    글쓴것 지워졌어용 ㅋㅋ

    Reply

submit
프로그램을 짜다보면, 특정 디렉토리 내에서 파일 혹은 디렉터리가 변경되었음을 감지해내야 하는 경우가 가끔 생긴다.
윈도우즈에서는 FindFirstChangeNotification과 그 패밀리 함수들을 통해서 이를 쉽게 확인할 수 있다.
감시하고 싶은 디렉터리의 바로 하위 디렉터리 뿐만 아니라, 모든 하위 디렉터리까지 알림을 받을 수 있도록 API가 설계되어져 있다.
FindFirstChangeNotification은 파일 변경 알림을 위한 커널 오브젝트를 만들어서 돌려주는 함수이며, 다른 여느 커널 오브젝트들을 사용하듯이, 그저 생성한 뒤 시그널 되기를(파일이 변경되기를) 기다리면 된다. -WaitForSingleObject 따위의 함수들을 이용해서 말이다.

그럼 디렉터리의 어떤 파일이 어떻게 변경되었는지도 알 수 있을까?
FindFirstChangeNotification 함수로는 이를 알 수 없지만 ReadDirectoryChangesW 함수를 이용하면 알 수 있다.

ReadDirectoryChangesW 함수는 다른 함수들과는 다르게 이름 뒤에 W가 붙은 유니코드용 함수만 제공된다.
처음에 막상 이 함수를 써보려고 하면 몇 가지 어려움에 부딪치게 되는데, 알고 나면 그렇게 어렵지 않은 함수이다.

2가지의 지식만 알고 있으면 되는데 첫번째는 디렉터리의 핸들을 얻는 방법이고, 두번째는 FILE_NOTIFY_INFORMATION 데이터 구조를 이해하는 것이다.

CreateFile 함수는 파일을 생성하는 것 뿐만아니라, 파일을 열 수도 있으며 디렉터리를 열 수도 있다. 사실 CreateFile에서 File이란 의미는 VirtualFile을 의미하며, 실제 파일이 아닌 장치들도 CreateFile을 통해 열어서 I/O를 하게 된다.
CreateFile을 통해 디렉터리를 열 때는 꼭 FILE_FLAG_BACKUP_SEMANTICS 플래그를 넣어주어야 한다.

FILE_NOTIFY_INFORMATION 구조체는 다음처럼 생겼다. 한 개의 파일 변경에 대한 정보를 담을 수 있는 구조체이며, 내가 넣어준 버퍼에 여러 개의 아래 구조체가 담겨온다.
첫번째 필드인 NextEntryOffset을 통해 다음 구조체의 오프셋을 가르쳐주는데. 다음 엔트리가 없을 때까지(NextEntryOffset이 0) 하나씩 쭉쭉 읽어오면 되는 것이다.
typedef struct _FILE_NOTIFY_INFORMATION {
  DWORD NextEntryOffset;
  DWORD Action;
  DWORD FileNameLength;
  WCHAR FileName[1];
} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;

마지막에 FileName[1] 이라고 적혀있는 것은 가변 크기 데이터를 한 덩어리로 메모리를 할당해서 쓰기 위해 C언어에서 종종 사용되는 기법이다. 이런 경우 항상 가변 길이 변수(여기서는 FileName[1])의 크기를 나타내는 변수가 하나 더 존재한다.(여기서는 FileNameLength이다)

커널 모드의 많은 서비스 함수들과 유저모드로 노출된 몇몇 API 들에서 저런 데이터 구조를 사용하는데, 이상하게 생기고 어려워 보인다고 그냥 넘어가면 꼭 필요할 때 효율적인 데이터 구조를 만들 수 없을 뿐만 아니라, 남이 만들어 놓은 함수들조차 사용할 수 없다.

아래 블로그 포스트에 이에 대한 약간의 설명이 더 있으니 참고하자.
char data[1]의 역할은?

SetFileInformationByHandle 함수는 비스타 부터 제공되는 강력한 파일 조작 API인데 위와 같은 데이터 구조를 알아야 사용할 수 있다. 이 함수를 통해서 Rename을 하는 부분만 살펴보자. FIELD_OFFSET 매크로를 어떻게 사용하는지 주목해서 봐야한다.

이 함수에서 입력으로 사용되는 FILE_RENAME_INFO 구조체는 다음과 같이 생겼다.
typedef struct _FILE_RENAME_INFO {
  BOOL   ReplaceIfExists;
  HANDLE RootDirectory;
  DWORD  FileNameLength;
  WCHAR  FileName[1];
} FILE_RENAME_INFO, *PFILE_RENAME_INFO;

std::wstring newFileName = L"D:\\newfilename";
HANDLE h = CreateFileW(L"D:\\originfilename", GENERIC_READ|GENERIC_WRITE|DELETE,
    FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
 
DWORD cbBuffer = FIELD_OFFSET(FILE_RENAME_INFO, FileName[newFileName.size() + 1]);
 
PFILE_RENAME_INFO pRenameInfo = (PFILE_RENAME_INFO)malloc(cbBuffer);
pRenameInfo->ReplaceIfExists = FALSE;
pRenameInfo->FileNameLength = newFileName.size() * sizeof(WCHAR);
pRenameInfo->RootDirectory = 0;
 
StringCchCopyNW(pRenameInfo->FileName,
    newFileName.size() + 1, newFileName.c_str(), newFileName.size());
 
SetFileInformationByHandle(h, FileRenameInfo, pRenameInfo, cbBuffer);

이제 ReadDirectoryChangesW 함수도 이해할 수 있다. 바로 코드를 살펴보자. 잡스런 처리는 하지 않았다.

HANDLE hDir = CreateFileW(L"D:\\", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
    0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
CONST DWORD cbBuffer = 1024*1024;
BYTE* pBuffer = (PBYTE)malloc(cbBuffer);
BOOL bWatchSubtree = FALSE;
DWORD dwNotifyFilter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME |
    FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE |
    FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION;
DWORD bytesReturned;
WCHAR temp[MAX_PATH] = { 0 };
 
for(;;)
{
    FILE_NOTIFY_INFORMATION* pfni;
    BOOL fOk = ReadDirectoryChangesW(hDir, pBuffer, cbBuffer,
        bWatchSubtree, dwNotifyFilter, &bytesReturned, 0, 0);
    if(!fOk)
    {
        DWORD dwLastError = GetLastError();
        printf("error : %d\n", dwLastError);
        break;
    }
 
    pfni = (FILE_NOTIFY_INFORMATION*)pBuffer;
 
    do {
        printf("NextEntryOffset(%d)\n", pfni->NextEntryOffset);
        switch(pfni->Action)
        {
        case FILE_ACTION_ADDED:
            wprintf(L"FILE_ACTION_ADDED\n");
            break;
        case FILE_ACTION_REMOVED:
            wprintf(L"FILE_ACTION_REMOVED\n");
            break;
        case FILE_ACTION_MODIFIED:
            wprintf(L"FILE_ACTION_MODIFIED\n");
            break;
        case FILE_ACTION_RENAMED_OLD_NAME:
            wprintf(L"FILE_ACTION_RENAMED_OLD_NAME\n");
            break;
        case FILE_ACTION_RENAMED_NEW_NAME:
            wprintf(L"FILE_ACTION_RENAMED_NEW_NAME\n");
            break;
        default:
            break;
        }
        printf("FileNameLength(%d)\n", pfni->FileNameLength);
 
        StringCbCopyNW(temp, sizeof(temp), pfni->FileName, pfni->FileNameLength);
 
        wprintf(L"FileName(%s)\n", temp);
 
        pfni = (FILE_NOTIFY_INFORMATION*)((PBYTE)pfni + pfni->NextEntryOffset);
    } while(pfni->NextEntryOffset > 0);
}

위와 같이 변경된 파일의 이름과 어떤 식으로 변경되었는지(파일이 새로 생성되었는지, 시간이 바뀐건지)등의 정보를 모두 얻어낼 수 있다.

함수를 사용하는 법 이외에도 몇 가지 더 알고 있어야 하는 것들이 있다.

ReadDirectoryChangesW를 호출해서 한번 통지를 받은 후 다시 루프를 도는 동안 파일들이 변경된다면 그 사이 변경된 파일들은 모두 놓치게 되는 것인가?
함수를 통해 통지를 받을 때, 꼭 하나의 파일(혹은 디렉터리)만 튀어나오는 것은 아니라는 점을 명심해야 한다.
파일 시스템 드라이버는 내부에서 버퍼를 따로 할당해서 이 버퍼에 그 동안 변경된 파일들을 계속 모아둔다. 그리고 사용자 쪽에서 통지를 기다리면, 이 내부 버퍼에 쌓인 것들을 전부 사용자 버퍼로 복사 한뒤 I/O를 완료시켜서 사용자 쪽으로 돌려주게 된다. 따라서 혹시 루프가 천천히 돌더라도 그 사이에 변경되는 파일들은 다음 번 호출시에 모두 받을 수 있게된다. 그렇기 때문에 두번째 인자로 제공되는 버퍼에 FILE_NOTIFY_INFORMATION 구조를 여러개 담아 주도록 설계한 것이다.

또한 이 파일 시스템 드라이버의 내부 버퍼는 핸들을 닫을 때까지 유지된다. 즉, 한번 ReadDirectoryChangesW 함수를 호출하고 핸들을 닫지 않은채 그 다음 호출을 안하고 멍하니 있는다면 그 동안 드라이버 내의 내부 버퍼에 변경된 파일 정보들이 계속 쌓이게 될 것이다. 물론 얼마나 쌓이느냐는 파일 시스템 드라이버의 구현에 달려있을 것이고 NTFS가 어떻게 구현했는지는 모른다.

ReadDirectoryChangesW 함수의 모양을 보면 알 수 있지만 이 함수는 비동기 I/O도 지원을 한다.
디렉터리를 1개만 감시하고 싶을 때는 위에서 한 것 처럼 동기적으로 호출해도 되겠지만, 1개의 쓰레드만 사용하면서 여러 개의 디렉터리들을 감시하고 싶다면 비동기 I/O를 사용하는 것을 고려해봐야 할 것이다.
비동기로 함수를 호출하는 방법은 따로 설명하지 않는다.

파일 시스템 드라이버나 네트워크 리디렉터를 만들 때는 위 기능을 직접 구현해주어야 하는데 필수적으로 구현해야 하는 것은 아니다. 물론 구현하지 않으면 파일이 변경되었을 때 애플리케이션들이 보여주는 UI에서, 변경되는 파일들이 자동으로 갱신되지 않을 것이므로(ReadDirectoryChangesW가 실패할 것이다) 구현 하는 쪽이 더 나은 사용자 경험을 제공해 줄 수 있는 파일 시스템 드라이버가 될 것이다.
저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License

http://www.benjaminlog.com/trackback/169 관련글 쓰기

  1. 재호님 팬 at 2010/12/29 12:11 [edit/del]

    재호님~ 소스코드 폰트색상이 굉장히 예쁘네요
    폰트 rgb 값좀 알려주시면 안되나요?

    Reply
  2. Ji at 2011/02/10 17:48 [edit/del]

    여기저기 쓸데없는 포스트들만 엄청 찾아보다가 드디어 오아시스 같은 글을 만나네요.
    잘 배우고 갑니다. 고맙습니다.

    Reply
  3. 오곡 at 2012/12/05 00:57 [edit/del]

    정말 좋은 내용 잘보고 갑니다 ㅠㅠ

    Reply
  4. at 2013/07/21 21:49 [edit/del]

    저기 비동기식으로 감시한다는게 무슨 뜻인가요?

    Reply
  5. Mr.K at 2014/06/26 11:37 [edit/del]

    감사합니다. 파일과 관련된 처리 하다가 찾았습니다. 유용할거 같네요

    Reply

submit
거인과 싸우는 법 - 8점
이기형 지음/링거스그룹

블루문님의 블로그에서 이 책을 알게 되었다.
'별로' 라는 내용의 리뷰였는데, 나는 그 리뷰를 읽으면서도 우와 재밌겠다 싶었고, 실제로도 기대했던 만큼 재밌었다.

아이리버의 전성기 때 나는 군대에 있었기 때문에 얼마나 큰 열풍이 불었는지 전혀 몰랐는데, 이 책을 읽어보니 엄청 났었구나 싶었다.

나는 2007년인가 생전 처음으로 MP3 플레이어를 하나 샀었는데 그게 아이리버 제품이었다. 상당히 마음에 들게 잘 쓰고 있었는데 어머니께서 탐내셔서 어머니를 드리고는 삼성 YEPP으로 다시 샀었다.
그 당시에는 이미 아이리버보다 삼성이 더 많이 팔릴 때였는데, 나는 YEPP을 사고 나서 이 빌어먹을 꼬물딱지를 다시는 안사겠다고 결심했던 기억이 난다. 다음에 혹시 MP3를 사면 꼭 다시 아이리버를 사야지 생각했는데, 이제는 제품도 몇 개 없는게 괜히 내가 다 슬프다.

임직원들이 다들 365일 사무실에서 살았다는데(물론 뻥이 좀 섞였겠지만) 불쌍하다기 보다는 젊은 기업의 열정을 느낄 수 있어서 너무 좋았고, 해이해진 내 모습도 돌아볼 수 있었다.

책의 내용은 양덕준 사장의 예찬론에 가깝다. 정말 그대로라면 그는 그 이름처럼 참으로 덕장이다. 나는 그만한 사람을 여태 한 번도 만나보지 못한 것 같다. 자기 돈을 다 퍼부어 직원들 인센티브와 월급을 주는 사장이 몇 명이나 있을까.

가끔씩 나오는 이용현 이사의 이야기는 더욱 재밌었다. 최고 실력의 엔지니어라고 하는데, 얼마나 잘하는 사람일까 너무 궁금했다. 책을 다 읽고 검색을 해서 좀 찾아봤는데, 별 다른 정보를 얻을 수가 없어서 실망을 했다.
빌어먹을, 세상에는 왜 이렇게 천재들이 많은가. 내게는 참 부럽고 만나보고 싶은 존재들이다.

빨리 빨리 실력이 늘어야 하는데, 어째 프로그램은 하면 할 수록 더 어려워진다.

양덕준 사장님은 지금 몸이 아주 많이 안좋으시다고 한다. 부디 완쾌해서 그가 아이리버를 꼭 다시 찾을 수 있기를 바란다.
저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License

http://www.benjaminlog.com/trackback/168 관련글 쓰기

  1. 거인과 싸우는 법 (벤처신화 아이리버의 끝나지 않은 혁명)
    // 알통 네트워크 2011/01/06 22:42 x

submit
윈도우즈 API를 사용하다보면 종종 reserved라는 파라메터를 접하게 된다.

LONG WINAPI RegQueryValueEx(
  __in         HKEY hKey,
  __in_opt     LPCTSTR lpValueName,
  __reserved   LPDWORD lpReserved, //This parameter is reserved and must be NULL.
  __out_opt    LPDWORD lpType,
  __out_opt    LPBYTE lpData,
  __inout_opt  LPDWORD lpcbData
);

도대체 마이크로소프트는 이딴 수법을 왜 쓰는 것일까?

첫번째는 나중에 함수의 기능이 추가되거나 쉽게 확장될 수 있도록 하기 위해서이다. 이 추가적인 파라메터로 인해 인터페이스를 변경하지 않고도 쉽게 기능을 넣을 수 있다.
문서상에서 많은 reserved 파라메터들이 반드시 NULL을 넣어야 한다고 쓰여있는데, 이렇게 해두면 추후 함수가 변경될 때 이전 클라이언트 코드들과 구분을 하기가 용이해진다.

두번째는 윈도우즈 내부에서 호출하는 경우이다. 외부에서 노출된 winapi를 사용하는 클라이언트들에게는 NULL을 넣도록 하고, 내부에서는 다른 용도로 특별한 값을 넣어서 사용하는 것이다.

세번째 이유는 첫번째 이유와 반대이다.
처음 해당 함수가 생길 당시에는 reserved 파라메터는 실제 다른 어떤 용도로 쓰이고 있었다. 시간이 한참 지나고 해당 필드의 의미가 퇴색되고 더 이상 필요 없게 되어 버리자, 파라메터를 제거하는 대신 이름을 reserved로 바꾸어 버렸다. 물론 이전 코드들과의 호환성을 지켜주기 위함이다.

또 다른 이유는 구조체에 불필요한 패딩 데이터를 포함시키지 않고 차라리 reserved용도로 사용하려는 것이다.
struct IconDirectoryEntry {
    BYTE  bWidth;
    BYTE  bHeight;
    BYTE  bColorCount;
    BYTE  bReserved;
    WORD  wPlanes;
    WORD  wBitCount;
    DWORD dwBytesInRes;
    DWORD dwImageOffset;
};
위 구조체에서는 4번째 필드인 bReserved가 패딩 데이터를 채우는 대신 그 자리에 차라리 reserved용 데이터를 집어 넣었다는 것이 명백히 드러난다.

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License

http://www.benjaminlog.com/trackback/167 관련글 쓰기

submit

VirtualBox 4.0 베타

2010/12/07 19:59 | Softwares

업데이트 되는 것이 가장 기다려지는 소프트웨어를 꼽으라 하면 첫번째는 우분투이고 그 다음은 버추얼박스이다.
회사에서나 집에서나 참 잘 쓰는 프로그램인데, VMware에 비하면 꼬진 점도 많지만 그래도 공짜로 이 정도 기능을 제공해주는 것이 너무 예뻐서 사랑할 수 밖에 없다.

이번엔 드디어 메이저 업데이트다.
이제야 호스트에서 게스트로 바로 파일을 복사하는 것이 지원되는 것 같다. VMware는 드래그드랍으로 한번에 복사가 잘되는데 버추얼박스는 ShareFolder를 설정하고 게스트에서 NET USE로 네트워크 드라이브를 잡아서 사용해야 하는 불편함이 있었다. 드라이버를 개발하면서 하루에도 몇십번씩 복사를 해대는데 손가락이 얼마나 아프던지.

정식이 나오려면 한달 쯤 더 기다려야 하지 않을까.

릴리즈 노트는 아래 링크에서 볼 수 있다.

다운로드는 여기에서.

Edit:
정식버전이 릴리즈 되었음.
저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License

http://www.benjaminlog.com/trackback/166 관련글 쓰기

submit
SysInternals의 대부분의 유틸리티들은 하나의 실행파일로 배포된다.

프로세스 모니터 같은 도구는 시스템 내에서 일어나는 모든 I/O등을 잡아채서 보여주는데 이 역시 EXE 파일 하나로 배포된다. 그렇다면 마크 러시노비치가 이것들을 유저모드 애플리케이션에서 구현했다는 말인가? 아니, 심지어 이런 것들을 유저모드에서 구현할 수나 있는 것일까?
물론 그렇지는 않다. 프로세스 모니터는 파일 시스템 레이어 상위에 장착되는 필터 드라이버와 응용 프로그램으로 구성되어져 있다. 아마도 필터 드라이버가 열심히 I/O를 엿본 다음에 그 정보를 잘 정리해서 응용 프로그램에게 전달한 뒤 응용 프로그램이 GUI로 출력하도록 구현되어져 있을 것이다.

그렇다면 프로세스 모니터의 드라이버 이미지는 어디에 숨어있는 것일까?
드라이버는 바로 EXE 파일 내의 리소스에 있다.


응용 프로그램 내에 커스텀 리소스를 하나 만들어서 그 곳에 바이너리 파일을(여기서는 프로세스 모니터 드라이버 이미지) 쑤셔 넣어둔다.
응용 프로그램이 처음 실행되면 FindResource, LoadResource, LockResource, SizeofResource 함수들을 사용해서 이 리소스를 읽어 오고 CreateFile과 WriteFile 같은 함수를 통해서 새로운 파일을(바로 그 프로세스 모니터 드라이버) 디스크 상에 만들어낸다.
이런 식으로 말이다.
HRSRC h = FindResourceW(0, MAKEINTRESOURCEW(IDR_BIN), L"BIN");
HANDLE hRes = LoadResource(0, h);
CONST CHAR* p = (CONST CHAR*)LockResource(hRes);
HANDLE hFile = CreateFileW(L"D:\\bin", GENERIC_ALL, 0, 0, CREATE_ALWAYS, 0, 0);
DWORD cb = SizeofResource(0, h);
DWORD dw;
WriteFile(hFile, p, cb, &dw, 0);
UnlockResource(hRes);
너무 간단하지 않은가?

이제 드라이버 파일이 생겼으므로 응용 프로그램 코드에서는 CreateServiceStartService 함수들을 통해서 드라이버를 설치하고 구동시킬 수 있다.
응용 프로그램이 종료 될 때는 물론 드라이버를 잘 멈추고 제거하고 파일을 지워버리는 등의 작업 또한 해주어야 할 것이다.

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License

http://www.benjaminlog.com/trackback/165 관련글 쓰기

  1. 관우 at 2011/01/05 23:15 [edit/del]

    너무 궁금했는데 이렇게 간단할 줄은 몰랐네요. ㅎㅎ 잘 배워 갑니다.

    Reply

submit