IT_Programming/MFC · API

객체에 툴팁달기 / 하드에서 특정파일찾기 / 단 한개의 프로그램만 실행

JJun ™ 2007. 12. 25. 14:11
LONG
 

<파일 찾아보기 기능 구현>

다름이 아니라 제가 만든 다이얼로그에 파일을 찾아보기라는 있는 버튼을 만들고 싶은데 어디서부터 접근을 하죠? 윈도에서 디폴트로 제공해주는 찾아보기기능이요!  

void CDirectorisFiles::FindFiles(TCHAR* pszPath)
{
     WIN32_FIND_DATA fData;
     TCHAR szDestTempPath[MAX_PATH];

     memset(szDestTempPath, 0x00, MAX_PATH);
     // 선택된 폴더가 루트이면
     SetMakePath(pszPath, szDestTempPath);
     HANDLE hFindFile = FindFirstFile(szDestTempPath, &fData);

     if(hFindFile == INVALID_HANDLE_VALUE) {
          SPB_SystemMessage(GetLastError());
     }

     while(1) {
          if(FindNextFile(hFindFile, &fData)) {
               if(!lstrcmp(fData.cFileName, "..")) continue;
               else if( fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                    TCHAR szPath[MAX_PATH];
                    memset(szPath, 0x00, MAX_PATH);
                    int nLen = lstrlen(pszPath);
                    if(nLen <= 3)
                         wsprintf(szPath, "%s%s", pszPath, fData.cFileName);
                    else
                         wsprintf(szPath, "%s\\%s", pszPath, fData.cFileName);
                         FindFiles(szPath);
               } else {
                    
 // TRACE("File Find Func : %s\\%s\n", pszPath, fData.cFileName);
                     // 디렉토리 및 라인

                     int nLen1 = strlen(pszPath);
                     int nLen2 = strlen(fData.cFileName);

                     m_pFileName[m_nFileCount].szDirName = (TCHAR*)malloc(nLen1+1);
                     m_pFileName[m_nFileCount].szFile = (TCHAR*)malloc(nLen2+1);

                     memset(m_pFileName[m_nFileCount].szDirName, 0x00, nLen1+1);
                     memset(m_pFileName[m_nFileCount].szFile, 0x00, nLen2+1);

                     lstrcpy(m_pFileName[m_nFileCount].szDirName, pszPath);
                     lstrcpy(m_pFileName[m_nFileCount].szFile, fData.cFileName);

                     m_nFileCount++;
               }
          }
          else break;
     }

     FindClose(hFindFile);
}

위 코드를 조금만 수정하면 됩니다.. 

 

 


 

<바탕화면의 월페이퍼 변경하기>

  바탕화면의 월페이퍼를 프로그램 상에서 변경하고자합니다. 레지스트리값을 변경시켜서 월페이퍼를 바꿨지만 실제 리부팅전에는 적용이 되지 않습니다.. 어떻게 해결할 방법이 없을까요..?
그냥 이미지 파일을 특정함수를 사용하면 되지만 월페이퍼로 하면 그게 안되더라구요...

 다음 함수를 쓰시면 됩니다.

BOOL SystemParametersInfo(
UINT uiAction,
// system parameter to query or set
UINT uiParam, // depends on action to be taken
PVOID pvParam,
// depends on action to be taken
UINT fWinIni
// user profile update flag
);

사용법은 다음과 같습니다.

CString szFileName = _T("c:\\windows\\test.bmp");
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0&, szFileName, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);

 

 

 

<CString 형을 char* 형으로 바꾸기>

1.CString 클래스의 GetBuffer()는 CString을 char *로 바꿔줍니다.

ex) CString strTemp = _T("test");
      char *getTemp=NULL;

      getTemp = malloc(strTemp.GetLength()+1);
      strcpy(getTemp, strTemp.GetBuffer(strTemp.GetLength());
      printf("결과:%s\n", getTemp);

      free(getTemp);

 

2. operator LPCTSTR ()도 마찬가지입니다.

ex) CString strTemp = _T("test");
      char *getTemp = (LPSTR)(LPCSTR)strData;

 

 

<CFileDialog 인자 사용법>

아래의 코드는 CFileDialog 함수의 full option 예제입니다. onFileOpen() 함수의 일부분이죠. 확장자가 3ds 인 파일만 표시되도록 하는 예제입니다.

static char BASED_CODE szFilter[] = "3ds 파일 (*.3ds)|*.3ds|";

CFileDialog FileDlg( TRUE, "3ds", NULL, OFN_HIDEREADONLY |
                            OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR,
                            szFilter, NULL );

FileDlg.DoModal();  

 

 

<대용량 파일 읽기 (빠르게...)>

파일 읽기는 자주 사용하실 것인데..
CFile::Read를 사용하면.. 10MB정도 되는 파일을 읽으면 무진장 오래 걸려서..
메모리 맵 파일을 이용한 파일 읽기 방법을 올려 들립니다..
많은 예제가 나온것으로 알지만.. 혹시... 도 몰라서..

BOOL OpenFiles(LPCSTR lpszPathName)
{
     DWORD dwFileSize;
     HANDLE hFile, hFileMap;
     LPVOID lpvFile;

     hFile = ::CreateFile(lpszPathName, GENERIC_READ , 0, NULL
                OPEN_EXISTING, FILE_ATTRIBUTTE_NORMAL, NULL);
     if(hFile == INVALID_HANDLE_VALUE) {
           //여기에서 에러 메세지 처리..
     }
     dwFileSize = ::GetFileSize(hFile, NULL);

     hFileMap = CreateFileMapping(hFile, NULL, PAGE_WRITECOPY, 0, dwFileSize, NULL);

     if(hFileMap == NULL) {
          CloseHandle(hFile);
          
//여기에서 에러 메세지 처리..
     }

     lpFile = MapViewOfFile(hFileMap, FILE_MAP_COPY, 0,0,0);

     if(lpFile == NULL) {
          CloseHandle(hFile);
          CloseHandle(hFileMap);
          
//여기에서 에러 처리
     }
}

이렇게 하면.. 대용량의 파일을 빠르게 읽을 수 있습니다..

제가 시간을 한번 제어 봤는데.. 4초 안에 끊나더군요..

흠.. 그리고 여기에 나온 함수는 Help에서 정확한 내용을 보세요..

 

 

 

<대용량 파일 빠르게 읽기 2>

메모리 맵파일을 이용한 방법외에 간단한 방법이 있어서 말씀드려볼까 합니다.

어려운 루틴은 아니고요, 그냥 도스용 시절에 사용했던 fread함수를 사용한 것입니다.
물론 fread대신 다른 파일 읽기 함수를 사용해도 됩니다.
다만 사용자 편의를 위해서 추가로 만들어진 파일함수들은(파일함수뿐만 아니라 다른 것들도 마찬가지...) 사용하긴 편하겠지만, 속도가 무척 느린 문제가 있습니다.

char *ReadFile( char *FileName )
{
     FILE *fp;
     int FileSize;
     char *buffer;

     try {
          fp = fopen( FileName, "rb" );
          if( !fp ) throw "File Not Found!";

          FileSize = filelength( fileno(fp) );
          buffer = new char [FileSize+1];
          fread( buffer, FileSize, 1, fp );
          *(buffer + FileSize) = 0;
          fclose( fp );
          return buffer;
     }
     catch( char *msg ) {
          printf( msg );
          return NULL;
     }
}

위의 try ~ catch구분은 중요한것은 아니고요, 대부분의 프로그래머분들이 예외처리를 함에 있어서 일반적인 C스타일로 일일히 에러메시지를 코딩하더군요.
그래서 혹시나 도움이 될까 해서 try ~ catch구분을 사용해보았습니다.
어려운건 아니니깐 try ~ catch를 사용하면 코딩이 훨씬 간단해질꺼예요. ^^

그리고 위의 파일읽기 함수를 fopen/fread등을 사용했는데, 이건 C를 시작하는 분들께 조금이나마 이해가 쉽도록 도스에서 사용하던 함수를 사용했습니다.
물론 이 함수들은 윈도우즈에서도 그대로 사용할수 있습니다.

원래 도스에서 fread는 한블럭최대크기가 64k로 제한됩니다.
따라서 64k씩 나누어서 파일을 읽어들여야 하는데, 윈도우즈에서는 그냥 한번에 읽을수 있더군요. ^^

10메가 정도 읽어들이는데 있어서, 펜133의 컴에서 약 3초미만으로 걸리는 것 같습니다.

ARTICLE

<객체에 툴팁달기>

우선 툴팁을 달기 위해, 툴팁을 알아서 처리해 주는 CToolTipCtrl이라는 클래스로 객체를 하나 만듭니다.

즉, 다이얼로그의 헤더 파일에서, 다이얼로그 클래스 안에 다음과 같은 변수를 만들면  되겠죠.

CToolTipCtrl m_ToolTip;

그리고 다이얼로그의 onInitDialog()에서 원하시는 버튼이나, 컨트롤들을 이 툴팁 변수에 입력하시면

됩니다. 이때, CToolTipCtrl의 AddTool()이라는 함수를 사용하게됩니다.

이 함수를 도움말에서 찾아보면 다음과 같이 나옵니다.

BOOL AddTool( CWnd* pWnd, LPCTSTR lpszText = LPSTR_TEXTCALLBACK,
              LPCRECT lpRectTool = NULL, UINT nIDTool = 0 );

여기서 다른 건 볼 거 없구, 첫번째 것과 두번째 것만 보시면 됩니다.
첫번째 인자는, 툴팁을 달고 싶은 컨트롤의 윈도우 포인터입니다.
두번째 인자는, 툴팁으로 출력하고 싶은 텍스트예요.

만약 다이얼로그에 IDC_TIPBUTTON이라는 버튼이 있고, 이 버튼에 "툴팁"이라는 툴팁을 넣고 싶다면 아래와 같이 하시면 되겠죠.

m_ToolTip.AddTool(GetDlgItem(IDC_TIPBUTTON), "툴팁");
....

이렇게 한 다음 한가지 더 해줘야 할게 있습니다.
마우스의 움직임을 툴팁 변수에게 알려줘야 하거든요.
그래야 툴팁을 출력해야 하는 지 판단할거 아닙니까?

그래서 다음과 같이 PreTranslateMessage()라는 함수에 코드를 넣으시면 됩니다.
 
PreTranslateMessage(MSG* pMsg)
{
   
// TODO: Add your specialized code here and/or call the base class
   switch(pMsg->message)
   {
      case WM_LBUTTONDOWN:
      case WM_LBUTTONUP:
      case WM_MOUSEMOVE:
             m_ToolTip.RelayEvent(pMsg);
             break;
      default:
             break;
   }

   return CDialog::PreTranslateMessage(pMsg);
}

 

 

<하드에서 특정파일찾기>

//[*]-----------------------------------------------------[*]
#include "direct.h"
//[*]-----------------------------------------------------[*]

void ViewFileInfo(struct _finddata_t &info)
{
    char   CurrentDir[200];
    GetCurrentDirectory(200,CurrentDir);
    printf("%s\\%s ---- Size : %9ld Attrib : ",CurrentDir, info.name, info.size);
    printf((info.attrib & _A_RDONLY ) ? "R":"-");
    printf((info.attrib & _A_SYSTEM ) ? "S":"-");
    printf((info.attrib & _A_HIDDEN ) ? "H":"-");
    printf((info.attrib & _A_ARCH   ) ? "A":"-");
    printf("\n");
}

//[*]-----------------------------------------------------[*]
void SearchFile(char *Ext, bool SubDirSearch = false)
{
    struct _finddata_t c_file;    
    long    hFile;
        
    
//  화일 검사
    if((hFile = _findfirst( Ext, &c_file )) != -1L )   {
        ViewFileInfo(c_file);
        while(!_findnext( hFile, &c_file ))   ViewFileInfo(c_file);
       _findclose( hFile );
    }
        
    if(!SubDirSearch)   return;
    //  서브 디렉토리 검사
    if((hFile = _findfirst("*.*", &c_file )) != -1L )  {
        if(c_file.attrib & _A_SUBDIR)   {
            if(strcmp(c_file.name,".")!=0 && strcmp(c_file.name, "..")!=0)  {
               _chdir(c_file.name);
                SearchFile(Ext);        
// 재귀적 호출 ...
               _chdir("..");
            }
        }
        while(!_findnext( hFile, &c_file ))  {
            if(c_file.attrib & _A_SUBDIR)        {
                if(strcmp(c_file.name,".")!=0 && strcmp(c_file.name, "..")!=0)   {
                   _chdir(c_file.name);
                    SearchFile(Ext);    // 제귀적 호출 ...
                   _chdir("..");
                }
            }
        }
       _findclose( hFile );
    }
}

//[*]-----------------------------------------------------[*]
void test()
{
    _chdir("F:\\");
    SearchFile("*.cpp", true);
}
//[*]-----------------------------------------------------[*]

 

 

 

<단 한개의 프로그램만 실행하기>

    대부분의 윈도 프로그램은 동시에 여러 개의 인스턴스가 수행될 수 있습니다. 하지만 단지 한 개의 프로그램만 실행해야 하는 경우도 있습니다. 이미 자신이 실행되고 있는지의 여부는 InitInstance()함수에서 체크하고 그 결과에 대한 처리를 할 수 있습니다.

    BOOL CMyApp::InitInstance()
    {
        HANDLE hMutex = NULL;
        hMutex = CreateMutex(NULL,TRUE,"PreventSecondInstanceRunning");
        if (GetLastError() == ERROR_ALREADY_EXISTS)
        {
            AfxMessageBox("이미 실행되고 있습니다.");
        }
    }

    윈도에서는 CreateMutex() 라는 간단한 함수가 있습니다.
    여기서 3번째 인자인 문자열이 중요합니다.
    "PreventSecondInstanceRunning"을 비교하여 같은 프로그램인지 확인하게 됩니다. 주의할 사항은

    "Running"과 같은 단순한 문자열을  이용한다면...  다른 프로그램도 자기 자신인 것처엄 오인할 수

    있습니다.

 

[↓Click! 파일 찾아보기 기능 구현 / 바탕화면의 월페이퍼 변경하기 / CString 형을 char* 형으로

             바꾸기 / CFileDialog 인자 사용법 / 대용량 파일 읽기 (빠르게...)1 / 대용량 파일 빠르게

             읽기 2 ]