C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 팁&트릭
C++Builder Programming Tip&Tricks
[79] 탐색기로부터 파일 리스트 Drag&Drop / Paste 기능 구현하기
박지훈.임프 [cbuilder] 9675 읽음    2001-02-28 02:29
임펠리테리입니다.

이 내용은, 사실 작년 10월에 임한섭님의 질문에 대해 답변했던 내용입니다. 바로 팁으로 올리겠다고
했었는데, 지금까지 팁을 하나도 올리지 못했네요.

파일 리스트를 드래그해서 드랍으로 받아오는 기능에 대해서는 꽤 많이 알려져 있습니다. 먼저 드래그된
파일리스트를 받아오는 기능부터 알아봅시다.

드래그된 파일 리스트를 받아오기 위해서는, 먼저 "이 윈도우에서는 파일리스트 드롭을 받겠다"하고 등록해
주는 과정이 필요합니다. 여기에 쓰이는 API 함수가 DragAcceptFiles() 입니다. 이 함수는 두개의 인자를
갖는데, 첫번째는 파일리스트를 받을 윈도우의 핸들이고, 두번째는 불린 값으로서 true로 설정하면 됩니다.
이 함수는 처음에 단 한번만 실행되면 되므로, 폼의 OnCreate 이벤트 핸들러에 적어주면 되겠죠.

빈 프로젝트를 하나 만들고, 리스트박스를 하나 놓습니다. 이 리스트박스는 받아온 파일들의 리스트를
표시하기 위해 사용될 겁니다. 그리고 폼의 OnCreate 핸들러를 다음과 같이 만듭시다.
void __fastcall TForm1::FormCreate(TObject *Sender)
{
    DragAcceptFiles(Handle, true);
}


그 다음 단계는, 실제로 파일이 드랍되었을 때를 감지하는 것입니다. 드래그된 파일리스트가 드랍되면
WM_DROPFILES 메시지가 발생하므로, 폼에다 메시지핸들러를 만들고 이 메시지에 대한 핸들러를 추가하면
됩니다. 메시지핸들러에 대한 자세한 내용은 43번 팁 "메시지가 vcl에서 처리되는 방법" 을 참고하시구요.
http://www.borlandforum.com/impboard/impboard.dll?action=read&db=tip&no=43
여기서는 구현만 해보겠습니다.

폼의 헤더파일을 열고 폼 클래스 선언부의 private 섹션에 다음과 같이 추가하세요.
void __fastcall AddFilesToList(HANDLE hDrop); 
void __fastcall WMDropFiles(TWMDropFiles &Msg); 

그리고 폼 클래스의 선언부가 끝나기 직전, 그러니까 }; 바로 앞에 다음과 같이 추가하세요.
BEGIN_MESSAGE_MAP 
    VCL_MESSAGE_HANDLER(WM_DROPFILES, TWMDropFiles, WMDropFiles); 
END_MESSAGE_MAP(TForm) 

다음으로 cpp파일의 마지막에 다음과 같이 추가합니다.
void __fastcall TForm1::WMDropFiles(TWMDropFiles &Msg) 
{ 
    AddFilesToList((HANDLE)Msg.Drop); 
} 


여기서 AddFilesToList() 함수는 지금부터 만들 함수입니다. WM_DROPFILES의 LParam으로 드랍 핸들이 넘어
오는데, 이것을 그대로 AddFilesToList() 함수로 넘기고 이 함수 내에서 처리하도록 하겠습니다. 여기서
드랍 핸들이란, 드랍되는 파일 리스트에 대한 핸들로서, Win32 API에서 파일 드래그를 나타내는 객체입니다.

이제 AddFilesToList() 함수를 만들어봅시다. 앞에서 말한 드랍 핸들로부터 실제로 파일 이름들을 받아와야
하는데, 이때 사용되는 API 함수가 DragQueryFile() 입니다. 이 함수는 모두 네개의 인자를 필요로 하는데,
첫번째 인자는 드랍 핸들, 두번째 인자는 드랍된 파일리스트중에서 읽어올 파일이름의 인덱스, 그리고
세번째, 네번째 인자는 각각 파일이름을 받아올 버퍼와 그 버퍼의 사이즈입니다. 그러니까, 인덱스를
0, 1, 2, ... 이런 식으로 차례로 넘겨주면 모든 파일들의 이름을 차례로 알아낼 수가 있습니다.

그런데, 총 갯수를 알아야 인덱스를 어디까지 반복할지 결정할 수 있겠지요? DragQueryFile() 함수의
두번째 인자로 인덱스 대신 0xFFFFFFFF 값을 넘겨주면 이 함수는 드랍된 파일들의 총 갯수를 리턴합니다.
그러니, 먼저 갯수를 알아내고 그 횟수만큼 루프를 돌면서 파일 이름들을 알아내면 되겠지요?
void __fastcall TForm1::AddFilesToList(HANDLE hDrop) 
{ 
    AnsiString FilePath; 
    FilePath.SetLength(MAX_PATH); 
    int FileCount = DragQueryFile(hDrop, 0xFFFFFFFF, FilePath.c_str(), MAX_PATH); 
    for(int i=0; iItems->Add(ExtractFileName(FilePath)); 
    } 
    DragFinish(hDrop); 
} 

위에서 구현한 AddFilesToList() 함수의 코딩에는 팁속의 작은 팁이 있습니다.
DragQueryFile()에서 파일 이름을 받아오기 위해 넘겨주는 버퍼는 char *형이어야 하는데, char 배열으로
만들거나 동적으로 생성하는 대신, 안시스트링을 이용했습니다. 안시스트링의 SetLength() 메소드를 이용하여
필요한 만큼 메모리를 미리 확보하도록 한 후에, c_str() 로 내부에서 사용하는 char 포인터 값을 버퍼로
사용한 것입니다.

마지막 라인에 있는 DragFinish()는 드랍 핸들을 다 썼으므로 해제하겠다는 의미입니다.
자, 그러면 위에서 임의로 만들어준 여기까지 코딩하고 AddFilesToList() 함수의 프로토타입을 폼 헤더파일에
폼 클래스의 멤버함수로 추가합시다. 이제 컴파일하고 실행한 후에 파일 리스트를 드래그해서 드랍해보면
파일 리스트가 리스트박스에 추가되는 것을 볼 수 있습니다.

여기까지는.. 흔히 알려진 내용입니다. API 헬프만 잘 뒤져보면 금방 찾을 수 있는 팁이죠. 다음 단계로서,
잘 알려지지 않은, 탐색기에서 컨트롤+C 키를 눌러 복사한 파일리스트를 폼에서 컨트롤+V 키를 누름으로서
붙여넣기 기능이 되도록 해봅시다.

사실, 아주 간단한 내용입니다. VCL의 TClipboard는 API의 클립보드의 기능을 모두 다 구현해놓았으므로
Clipboard() 객체를 이용하면 되지요. 그런데, 아무리 뒤져봐도 TClipboard에서는 파일리스트의 복사에
대한 내용은 언급되어 있지 않습니다. CF_TEXT(텍스트), CF_BITMAP(비트맵), CF_METAFILEPICT(메타파일),
CF_PICTURE(TPicture), CF_COMPONENT(컴퍼넌트)에 대해서만 설명이 되어있지요.   

그런데, 사실은 이 데이터타입들은 VCL에서 정의한 것이 아니라 API에서 정의된 것이므로 API에서 지원하는
타입이라면 VCL에서도 그대로 사용할 수 있습니다. 이 데이터타입들은 winuser.h 헤더에 정의되어 있고,
MSDN의 다음 페이지를 보면 자세히 설명되어 있습니다.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ipc/hh/winbase/clipbrd_0cxf.asp

VCL에 소개되지 않은 타입들로 CF_WAVE(웨이브파일)이나 CF_PALETTE(팔레트), 그리고 여기서 필요한
CF_HDROP 타입 등이 있습니다. 이 CF_HDROP 타입이, 이름에서도 알 수 있다시피 파일리스트를 가지고 있는
드랍 핸들을 가지고 있을 때의 포맷입니다.

자, 여기까지 알았으니, 구현은 간단하지요? 리스트박스의 OnKeyDown 이벤트 핸들러를 다음과 같이
작성합시다.
void __fastcall TForm1::ListBox1KeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
{
    if(Shift.Contains(ssCtrl) && Key == 'V')
        if(Clipboard()->HasFormat(CF_HDROP))
            AddFilesToList((HANDLE)Clipboard()->GetAsHandle(CF_HDROP));
}


두번째 줄에서, 먼저 클립보드에 파일리스트 객체가 있는지를 검사한 후, 세번째 줄에서
Clipboard()->GetAsHandle(CF_HDROP)을 이용해서 CF_HDROP 타입의 핸들을 얻습니다. 이 CF_HDROP 타입
핸들이 드랍 핸들이므로, 이것을 앞에서 만들었던 AddFilesToList() 함수에 넘기면 됩니다.

이렇게 해서 파일 리스트의 드래그&드랍과 붙여넣기 기능이 모두 끝났습니다. 원칙적으로 따지자면,
붙여넣기란 WM_PASTE 메시지에 반응하는 것으므로 WM_PASTE 메시지핸들러를 만들어서 거기서 검사하는 것이
당연하지만, 여기서는 코딩을 간단히 하기 위해 단순히 리스트박스의 OnKeyDown 이벤트 핸들러를 이용했습니다.
만약 드래그&드랍과 Paste를 지원하는 새로운 리스트박스 컴퍼넌트를 만든다면 당연히 WM_PASTE 메시지의
핸들러를 구현해서 해야겠지요.

그럼 도움되시길...

+ -

관련 글 리스트
79 탐색기로부터 파일 리스트 Drag&Drop / Paste 기능 구현하기 박지훈.임프 9675 2001/02/28
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.