_splitpath() 함수를 사용하면 전체경로에 대해서 드라이브명, 경로, 파일명, 확장자로 구분해준다.

함수의 원형은 아래와 같다...
void _splitpath(
     const char *path,
     char *drive,
     char *dir,
     char *fname,
     char *ext
);

각 인자에 대한 MSDN의 설명을 보면 아래와 같다...
path : Full path
          (분리 시킬 경로의 절대경로...)

drive : Optional drive letter, followed by a colon (:)
            (드라이브...)

dir : Optional directory path, including trailing slash. Forward slashes ( / ), backslashes ( \ ), or both may be used.
        (드라이브와 파일명을 제외한 경로...)

fname : Base filename (no extension)
             (확장자를 제외한 파일명...)

ext : Optional filename extension, including leading period (.)
         ( '.'을 포함한 확장자...)

- 사용예 -
_splitpath("C:/Documents and Settings/gungume/바탕 화면/Console/Console.dsw", drive, dir, fname, ext);
- 결과 -
Drive : C:
Dir : /Documents and Settings/gungume/바탕 화면/Console/
Fname : Console
Ext : .dsw

MSDN 원문 보기...
Posted by Gungume
,

Renamer.exe

파일명 일괄변경 프로그램



개인적으로 사진파일의 이름을 변경할때 쓰려고 급히 만든 파일명 일괄변경 프로그램...ㅡㅡ;

특별한 기능 없이 Drag & Drop를 이용 리스트컨트롤에 이름을 변경할 파일을 추가시키고 에디트박스에 일괄변경할 이름과 일련번호를 붙혀주면 파일명 부분만 변경하는 프로그램...

폴더는 목록에 추가되지 않고 파일의 확장자는 구분없이 목록에 추가되지만 특별한 기능 없이 파일명만 변경시키므로 목적에 따라 그림파일, 음악파일 등 나름데로 분류를 정해서 사용하는게 좋음...

개인사용 목적으로 만든거라서 딱히 rename() 함수에 대한 에러처리는 안함;;;


XP 스타일 버튼 클래스 출처 : http://www.softechsoftware.it/cxpstylebuttonst.html
Source: https://github.com/gungume/Renamer/releases/tag/0.1
(사용언어 및 제작툴 : MFC / VS2003)


최신버전 받기


Posted by Gungume
,




Drag & Drop 되는 리스트컨트롤


서브클래싱을 통해서 Drag&Drop이 되는 리스트컨트롤을 만드는 방법...

일단 서브클래싱을 하기 위해서 CListCtrl을 상속 받은 클래스 생성...

Drag&Drop을 할때 발생되는 메시지인 "WM_DROPFILES"과 관련된 함수인 "OnDropFiles()" 함수를 오버라이딩 한다...

"OnDropFiles()" 안에서 다음 과정을 통해 Drag&Drop된 파일, 폴더의 기본적인 정보를 얻을 수 있다...

일단 사용할 변수를 선언한다...
CHAR  szPath[1024];     // Drag&Drop된 파일, 폴더의 절대경로 저장
UINT  uiFileNum;            // Drag&Drop된 파일, 폴더의 갯수 저장

아래와 같은 방법으로 DragQueryFile() 함수를 이용해서 기본적인 정보를 얻어올 수 있다.
uiFileNum = DragQueryFile(hDropInfo, 0xffffffff, NULL, 0);
for ( UINT i = 0 ; i < uiFileNum ; i++ )
{
     DragQueryFile(hDropInfo, i, (LPTSTR)szPath, 1023);
      // 데이터 처리...
}

소스를 대략적으로 보면 소스의 첫줄처럼 DragQueryFile() 함수의 매개변수를 주면 Drag&Drop된 파일, 폴더의 갯수를 얻어올 수 있다. (MSDN에 그렇게 나온다 ㅡㅡ;)

이렇게 얻어온 갯수로 반복문을 돌면서 반복문 안쪽의 DragQueryFile() 함수처럼 매개변수를 줌으로써 Drag&Drop된 파일, 폴더의 절대경로를 얻어올 수 있다...

이렇게 얻어온 정보를 이용 다양한 데이터 처리가 가능하다...

이렇게 만든 클래스를 CListCtrl로 선언된 객체에서 바꾸고 리스트컨트롤의 속성에서 "Accept Files"를 TRUE로 설정함으로서 사용가능하다...


기본 내용 출처 : 데브피아 -> [짱!!선임] 파일 드래그&드롭 리스트 컨트롤입니다.
(사용언어 및 제작툴 : MFC / VS2003)

Posted by Gungume
,

- 스타일 -
CListCtrl.SetExtendedStyle( LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT );

- 칼럼 초기화 -
CListCtrl.InsertColumn(0, "칼럼명", LVCFMT_LEFT, 100, -1);
(인덱스, 칼람명, 정렬방향, 칼럼길이, 서브아이템 갯수)

- 데이터 삽입 -
CListCtrl.InsertItem(nCnt, "문자열");
(인덱스, 삽입될 문자열)

- 서브아이템 추가 -
CListCtrl.SetItem(nCnt, 1, LVIF_TEXT, "문자열", NULL, NULL, NULL, NULL);
(인덱스, 서브아이템 인덱스, 데이터 형식, 삽입될 문자열, ~~~~)

Posted by Gungume
,

시스템 이미지 얻기

MFC/TIP 2006. 8. 25. 13:03




시스템 이미지 얻기

탐색기 등을 제작하다보면 각각의 파일이나 폴더에 대한 아이콘이 필요하게 된다.
이런 아이콘은 프로그램 제작시 리소스 등을 이용해서 미리 준비한 아이콘을 사용할 수도 있지만 현재 윈도우에서 사용하고 있는 각각의 파일과 폴더에 대한 아이콘을 얻어와서 사용할 수도 있다.
아래의 예는 시스템의 이미지를 얻어와서 리스트컨트롤에 데이터를 뿌려주는 것이다.

1. 우선 이미지 리스트 제작에 관련된 변수를 생성한다.
    CImageList * m_SmallImage;     // SMALL 이미지 리스트
    CImageList * m_LargeImage;     // LARGE 이미지 리스트
    HIMAGELIST hSystemSmall;     // SMALL 시스템 이미지 리스트
    HIMAGELIST hSystemLarge;     // LARGE 시스템 이미지 리스트
    SHFILEINFO shFileInfo;            // 선택된 파일 및 시스템 아이콘 정보

2. 다음으로는 CImageList 변수를 동적 할당 및 해제를 해준다...
    (물론 해제는 프로그램이 종료될 때 사용...)
    // 동적 할당 (초기화 관련 함수에서 코딩)
    m_SmallImage = new CImageList;
    m_LargeImage = new CImageList;

    // 메모리 해제 (OnClose() 함수 등에서 코딩)
    if(m_SmallImage != NULL)
         delete m_SmallImage;
    if(m_LargeImage != NULL)
         delete m_LargeImage;

3. 다음으로는 실제로 시스템 이미지를 얻어오는 과정이다...
  간략히 설명하면 일단 시스템이미지를 얻어온 후 그 정보를 이미지리스트에 등록하고 다시그 정보를 리스트 컨트롤에 등록하는 과정을 거친다.

    // 선택한 폴더내 "파일 & 폴더" 시스템 이미지 리스트 얻기
       (예제 소스는 단순히 'C:\'를 대상으로 했다...

    // 시스템 이미지 얻기
    ZeroMemory(&shFileInfo, sizeof(SHFILEINFO));
    hSystemSmall = (HIMAGELIST)SHGetFileInfo( (LPCTSTR)"C:\\", 0, &shFileInfo,
                     sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON );
    hSystemLarge = (HIMAGELIST)SHGetFileInfo( (LPCTSTR)"C:\\", 0, &shFileInfo,
                     sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_LARGEICON );

    // 시스템 이미지 이미지리스트에 등록
    m_SmallImage->Attach( hSystemSmall );
    m_LargeImage->Attach( hSystemLarge );

    // 이미지리스트 리스트컨트롤에 등록
    m_listCtrl.SetImageList(m_SmallImage, LVSIL_SMALL);
    m_listCtrl.SetImageList(m_LargeImage, LVSIL_NORMAL);

여기서 Small 이미지랑 Large 이미지가 있는데 리스트컨트롤 속성에 따라서 작은아이콘이랑 큰아이콘 두가지 경우가 존재하므로 두가지 모두 얻어오는것이다...
한가지만 필요하면 해당되는것 하나만 얻어오면 된다...
4. 위의 과정을 거치면 시스템의 이미지 리스트가 확보된다...
  이제 이것을 사용하려면 리스트컨트롤에 데이터를 삽입할 때 얻어온 이미지 리스트의 인덱스만을 넣어주면 된다...
   아래와 같이 사용을 한다...

    "LV_ITEM" 구조체를 이용해서 데이터를 삽입할 때 이미지 관련 부분에서 아래와 같이 사용
    ZeroMemory(&shFileInfo, sizeof(SHFILEINFO));
    SHGetFileInfo( (char*)(LPCTSTR)m_fileFinder.GetFilePath(), NULL,
                        &shFileInfo, sizeof(shFileInfo),
                        SHGFI_DISPLAYNAME | SHGFI_ICON | SHGFI_SMALLICON);    
    lvItem.iImage = shFileInfo.iIcon;

(char*)(LPCTSTR)m_fileFinder.GetFilePath() -> 이 부분은 데모소스상에서 CFileFind 클래스를 이용한 부분으로서 얻어오고 싶은 파일이나 폴더의 경로가 된다....
반복문 등에서 지속적으로 호출해서 사용을 하면 된다...

5. 마지막으로 이미지 리스트를 해지해준다..
    m_SmallImage->Detach();
    m_LargeImage->Detach();

이 작업은 동적할당된 메모리 해지와 달리 시스템 이미지를 얻을때마다 해지를 시켜줘야 한다...
얻어온 이미지리스트를 해지하지 않은 상태로 다시 얻어오려고 하면 에러가 발생한다.

(사용언어 및 제작툴 : MFC / VS2003)

Posted by Gungume
,

CFileFind 클래스는 매개변수로 넘겨준 경로의 파일 및 폴더 등의 목록을 얻고 각각의 기본적인 정보를 얻어올 수 있는 클래스이다.

일단 CFileFind 클래스의 객체랑 결과값을 받을 변수를 선언한다.
CFileFind m_fileFinder;
BOOL bWorking;

파일과 폴더의 목록을 얻어올 때 매개변수로 넘겨준 경로부터 시작해서 FindNextFile() 멤버함수를 이용해서 계속 다음 파일로 접근하는 방식으로 코딩을 하면 되는데 보통 while()을 이용해서 무한루프를 사용한다.
이때 탈출조건이 필요하므로 BOOL형 변수가 필요하다.(자세한 것은 아래 소스를 통해서...)

가장 기본적인 사용법은 아래와 같다.
bWorking = m_fileFinder.FindFile("경로");
while (bWorking)
{
    bWorking = m_fileFinder.FindNextFile();
    // 얻어온 파일에 대해서 처리...
}
m_fileFinder.Close();

위의 소스를 설명하면 일단 매개변수로 경로가 들어간다...
예를 들면 "C:\\*.*" 이런식이다...
일단 경로를 표시하기 위해서 '\'가 아닌 '\\'임을 주의해야 되고, 와일드카드를 사용할 수 있으므로 보통 해당 경로내의 모든 목록을 얻고 싶으면 예제와 같이 원하는 경로 끝에 '*.*'를 써주면 된다...
당연히 특정 확장자만 얻고 싶으면 '*.bmp' 이런식으로 경로를 지정해주면 된다....

위의 예처럼 경로를 "C:\\*.*" 이렇게 설정해주면 'C:\' 의 모든 파일과 폴더 목록을 얻어온다....
이때 주의점은 기본 기능만으로는 일단 해당 경로만 접근이 되고 하위경로는 들어가지 않는다는 점(하위경로를 들어가려면 재귀함수 등 필요...)과 파일과 폴더를 따로 구분해서 불러오지는 않는다...(그냥 이름순으로 얻어온다...)

어쨌든 위와 같이 쓰면 FindNextFile() 멤버함수에 의해 지정된 경로의 모든 파일과 폴더의 정보를 얻어올 수 있다...
정보를 얻어오면 CFileFind 객체인 m_fileFinder을 이용해서 원하는 작업을 바로 바로 처리해 줄수 있다...
파일명, 액세스 타임 얻기 등의 작업이 가능한데....CRecordSet 클래스를 이용해서 DB를 처리하는것과 비슷하다...

모든 작업이 끝나고 해당 경로의 마지막 파일까지 접근했다면 FindNextFile() 멤버함수의 리턴값에 의해 while() 반복문을 종료한다...

이 클래스로 처리할 수 있는 대략적인 정보는 해당 파일이나 폴더의 이름, 전체경로, 크기, 최근 수정일 등의 기본적인 정보를 얻을 수 있다.

또한 현재 얻어온 정보가 폴더인지, 시스템파일인지, 읽기전용, 숨김파일 등등의 속성을 알수도 있다....

이런 정보를 얻어올 수 있는 멤버함수를 적절히 사용해서 얻어온 파일 목록에 대한 처리가 가능하다.

Posted by Gungume
,



숫자 3자리 단위로 컴마(',') 찍기

일반적인 숫자를 알아보기 쉽도록 3자리 단위로 컴마를 찍어주는 소스...
예전에 프로그램 짜다가 필요해서 직접 구현해보려다가 구찮아서 어디선가 퍼온 소스 ㅡㅡ
소스는 기본적으로 아래와 같다....매개변수, 리턴값 등 적절히 수정해서 사용하면 될듯....

CString CCommaDlg::Comma(double nData) 
{ 
	CString str, strReturn=_T(""); 
	str.Format("%.0f", nData); 
	
	for(int i=0; i<str.GetLength(); i++) 
	{
		strReturn += str.GetAt(i); 
		if( (str.GetLength() - i) != 1 && (str.GetLength() - i) % 3 == 1)
			strReturn += ','; 
	} 
	return strReturn; 
}


(사용언어 및 제작툴 : MFC / VC++ 6.0)
Posted by Gungume
,



예제 프로그램
실제 드라이브 목록

현재 시스템의 드라이브 정보를 얻기 위해서 GetLogicalDrives()라는 함수를 사용한다...
이 함수를 사용하면 DWORD형으로 값을 리턴해주는데 각각의 비트마다 해당 드라이브의 존재유무를 2진수로 알려준다.

기본적으로 아래와 같이 초기화를 해준다.
int m_nDrivePos = 0;
CString m_strDrive = "?:\\";
DWORD dwDriveList = ::GetLogicalDrives();

각각의 변수의 의미는 m_nDrivePos는 "a, c, d, e" 등 드라이브의 실제 이름을 정해줄때 아스키값 이용해서 사용할 변수이다...
m_strDrive는 "a:\", "c:\"와 같이 드라이브명을 지어주기 위해 사용한다...
위의 두 변수는 GetLogicalDrives() 함수는 현 시스템의 드라이브 정보를 2진수로만 알려주기 때문에 이것을 적절히 변경하여 알아보기 쉽게 바꾸기 위해서 사용한다.
마지막으로 dwDriveList는 GetLogicalDrives() 함수를 이용 실제로 드라이브 정보를 얻어오는 부분이다...2진수가 들어간다.

이렇게 얻어온 정보를 아래의 소스를 이용해서 실제적인 현 시스템의 드라이브명을 얻을수 있다
while(dwDriveList)
{
    // DWORD형으로 넘어온 값 하나씩 '&' 연산으로 드라이브 존재 유무 판단
    if(dwDriveList & 1)  
    {
         // "C:\"과 같이 드라이브를 표시하는 문자열로 만듦
         // ‘A' 값 기준으로 아스키코드 값 이용
         m_strDrive.SetAt(0,'A'+m_nDrivePos);
    }

    // 우로 1비트 이동(= 다음 드라이브 체크)
    dwDriveList >>= 1;
    m_nDrivePos++;
}

위의 소스를 설명하기 전에 dwDriveList의 값이 어떻게 설정되어 있는지 알아보면 다음과 같다.
예를 들어 현재 시스템에 드라이브가  "A, C, D, E" 가 존재한다고 하면 dwDriveList의 값은 11101이 된다.
이것을 좀더 분석해보면 저 수치를 오른쪽부터 읽어들이면 된다...
즉 오른쪽부터 "A, B, C, D, E" 드라이브의 존재 유무를 표시해준다...
값은 0이면 드라이브가 없음을...1이면 드라이브가 존재함을 의미한다...
즉 가장 오른쪽 값은 A드라이브의 존재유무를 알려주는데 값이 1로 설정되있으므로 현 시스템에는 A 드라이브가 있음을 알려준다.

이제 다시 소스 부분으로 돌아가서 분석하면...
일단 while 문에 dwDriveList를 넣었다....dwDriveList 를 계속 비트연산하다보면 결국에는 0이 된다...즉 루프탈출 조건은 모든 드라이브 목록을 구하면이다...

그 다음에 if 문을 보면 값 1을 가지고 "&연산"을 한다...
1로 "&연산"을 하면 피연산자가 1이면 1을 리턴하고 0이면 0을 리턴한다...
즉 if문의 조건이 참이라면 해당되는 곳은 드라이브가 존재한다는 뜻이다...
일단 1로 계산을 했으므로 앞에는 모두 0으로 계산하므로 가장 뒷자리만 계산을 한다...

위의 "11101"을 가지고 예를 들면....첫 반복문에서는 "11101 & 00001 = 1"이라는 결과가 나오므로 현 시스템에서 A드라이브가 존재함을 알수 있다.
그 다음에는 단순히 'A' 문자부터 시작하는 아스키 값을 이용해서 "A:\" -> 이런 형식으로 문자열을 바꿔준다...

이렇게하면 하나의 드라이브 목록을 구하는데 다음 드라이브 목록을 구하기 위해서 비트를 한단계 이동 시킨다...
그러면 11101 이라는 값이 1110으로 변경되므로 다음에는 1110이랑 1이랑 & 연산을 시켜서 드라이브 유무를 판단한다...

이런식으로 현 시스템의 모든 드라이브 목록을 얻고 원하는 형식으로 드라이브명을 변경시켜줘서 사용할 수 있다.

(사용언어 및 제작툴 : MFC / VS2003)

Posted by Gungume
,




삭제할 번호 선택하는 로또 프로그램


친구들과 임의의 로또 번호를 먼저 뽑아서 그 번호는 제끼고 로또를 할때 편하게 하려고 만든 프로그램...

특별한 로직 없이 그냥 rand() 함수 이용함...

(사용언어 및 제작툴 : MFC / VC++ 6.0)

Posted by Gungume
,




다이얼로그 등에서 WM_KEYDOWN 처리

MFC 프로젝트 생성시 다이얼로그기반이나...기본 뷰클래스를 폼뷰로 설정하면 WM_KEYDOWN 메시지 핸들러를 오버라이딩해도 메시지 처리가 안된다...(나머지 경우는 모름;;;)

이유는....잘모름 ㅡㅡ;   처음 MFC 배울때 다이얼로그 기반에서 WM_KEYDOWN 메시지를 처리하려고 했는데 잘안되서 인터넷을 뒤져본 결과... PreTranslateMessage() 함수를 오버라이딩 해서 그곳에서 처리해야 한다는 것을 알게되었다....

다이얼로그나 폼뷰 기반에서 바로 KeyDown 메시지가 처리 안되는 이유는 정확하지는 않지만 컨트롤쪽으로 바로 메시지가 가서 그런것 같다...

어쨌든 위에서 말했듯이 위와 같은 상황에서는 PreTranslateMessage() 함수를 오버라이딩 한 후에 아래와 같은 식으로 처리하면 된다...

if(pMsg->wParam == VK_RETURN)
    AfxMessageBox("엔터키를 눌렀습니다.");

위와 같은 식으로 PreTranslateMessage() 함수의 매개변수를 이용해서 가상키코드를 직접 비교해서 사용하는 방법이 있고....

아래의 소스와 같이 WM_KEYDOWN 메시지를 일단 잡은 후에 사용자 정의 함수를 통해서 나머지 메시지를 처리하는 방법도 있다...

if(pMsg->message == WM_KEYDOWN)
{
    KeyDown(*pMsg);
}

// 가상키코드 처리할 사용자 정의 함수
void CPnameDlg::KeyDown(MSG pMsg)
{
    switch(pMsg.wParam)
  {
         case VK_LEFT :
                  AfxMessageBox("왼쪽 방향키를 눌렀습니다.");
                  break;
         case VK_RIGHT :
                  AfxMessageBox("오른쪽 방향키를 눌렀습니다.");
                  break;
    }
}

위에서 처리 내용을 보면 알겠지만 PreTranslateMessage() 함수는 함수명 그대로 어떤 메시지를 도중에 가로채서 처리하고자 할때 쓰일수 있는 함수이다...

데모프로그램을 보면 다이얼로그 기반에서는 ESC키나 Enter키를 누르면 다이얼로그가 닫혀버리는데(=프로그램 종료) 이런 경우에 위의 예제를 좀만 수정하면 ESC키나 Enter키에 대한 메시지를 중간에 처리함으로서 프로그램이 바로 종료되는 문제 등을 처리할 수 있다.

또한 PreTranslateMessage() 함수는 단순히 키보드 메시지외에도 다른 메시지도 오므로 매개변수를 잘 이용해서 다양한 메시지 처리를 할수 있다.

(사용언어 및 제작툴 : MFC / VC++ 6.0)

Posted by Gungume
,