나는 이 함수를 해당 파일을 패치 후 처음 실행시키는건지를
체크하기 위해 이 함수를 사용했다.

bool CUtility::SetRegistFirstStart()
 {
  DWORD dwType = REG_SZ;
  DWORD dwSize = 32;
  HKEY hKey;
  CRegKey RegKey;
  TCHAR szBuffer[32] = {0, };

  LONG lResult = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\생성할 폴더이름", 0, KEY_READ, &hKey);
  if(lResult == ERROR_SUCCESS)
  {
   lResult = RegQueryValueEx(hKey, "생성할 키값 이름", NULL, &dwType, (LPBYTE)szBuffer, &dwSize);

   if(lResult == ERROR_SUCCESS)
    return false;
  }

  RegKey.Create(HKEY_CURRENT_USER, "Software\\FeelOnline");
  RegKey.SetValue("저장할 value값", "생성한 키값이름");
  RegKey.Close();
  return true;
 }

Posted by 명혀니
,

bAutoExec : 등록할건지 해제할건지
lpValueName : 등록이름
lpExeFileName : 시작프로그램으로 등록할 파일의 전체경로

bool CUtility::SetRegistStartUp(bool bAutoExec, LPTSTR lpValueName, LPTSTR lpExeFileName)
 {
  HKEY hKey;
  LONG lResult;
  if(bAutoExec)
  {
   if(lpValueName == NULL || lpExeFileName == NULL)
    return false;

   if(RegOpenKeyEx(
    HKEY_LOCAL_MACHINE,
    "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
    0L,
    KEY_WRITE,
    &hKey
    ) != ERROR_SUCCESS)
    return false;

   lResult = RegSetValueEx(
    hKey,
     lpValueName,
     0,
     REG_SZ,
     (BYTE*)lpExeFileName,
     lstrlen(lpExeFileName));

   RegCloseKey(hKey);

   if(lResult != ERROR_SUCCESS)
    return false;
  }
  else
  {
   if(RegOpenKeyEx(
    HKEY_LOCAL_MACHINE,
    "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
    0L,
    KEY_ALL_ACCESS,
    &hKey) != ERROR_SUCCESS)
    return false;

   lResult = RegDeleteValue(hKey, lpValueName);
   RegCloseKey(hKey);

   if(lResult != ERROR_SUCCESS)
    return false;
  }

  return true;
 }

Posted by 명혀니
,

int CUtility::UTF_8ToAnsi(const char* pSrc, char* pDest, int nOut)
 {
  int ret;
  wchar_t *wc;
  char *Temp;

  wc = (wchar_t*)malloc(sizeof(wchar_t)*(nOut+1));
  memset(wc,0,sizeof(wchar_t)*(nOut+1));

  if(nOut==0)
  {
   free(wc);
   return ::MultiByteToWideChar( CP_UTF8, 0, pSrc, -1, NULL, 0);
  }

  else
  {
   ret = ::MultiByteToWideChar( CP_UTF8, 0, pSrc, nOut, wc, nOut+1); // UTF-8 to UCS-2
   USES_CONVERSION;
   Temp = W2A( wc ); // UCS-2 to ANSI
   memcpy(pDest,Temp,nOut);
   free(wc);
   return ret;
  }
 }

Posted by 명혀니
,
int CUtility::AnsiToUTF_8(const char* pSrc, char* pDest, int nOut)
 {
  USES_CONVERSION;
  wchar_t *wc = A2W( pSrc ); // ANSI to UCS-2
  return ::WideCharToMultiByte( CP_UTF8, 0, wc, -1, pDest, nOut, 0, 0 ); // UCS-2 to UTF-8
 }



 char* CUtility::AnsiToUTF_8(const char* pSrc, int nOut)
 {
  char *out;
  out=(char*)malloc(nOut);
  USES_CONVERSION;
  wchar_t *wc = A2W( pSrc ); // ANSI to UCS-2
  ::WideCharToMultiByte( CP_UTF8, 0, wc, -1, out, nOut, 0, 0 ); // UCS-2 to UTF-8
  return out;
 }
Posted by 명혀니
,
게임 제작상의 루프 코딩은 대부분 아래와 같을 것이다.

  1. MSG msg;  
  2. while (1)  
  3. {  
  4.     if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))  
  5.     { 
  6.         if(msg.message  ==  WM_QUIT) break;  
  7.         DispatchMessage(&msg);  
  8.     } 
  9.     else 
  10.     { 
  11.         Loop();  
  12.     }         
  13. }  


여기서 while(1) 마저도 아깝다면..

  1. LOOP_ING:  
  2.     if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))  
  3.     { 
  4.         if(msg.message  ==  WM_QUIT) goto LOOP_EXIT;  
  5.         DispatchMessage(&msg);  
  6.     } 
  7.     else 
  8.     { 
  9.         Loop();  
  10.     }         
  11.      
  12.     goto LOOP_ING;  
  13. LOOP_EXIT:  

이렇게 해라. 이렇게 하면

  1. 00411AA3  mov         eax,1  
  2. 00411AA8  test        eax,eax  
  3. 00411AAA  je          WinMain+6Fh (411AEFh)  

의 클럭수 (대략 14클럭)를 줄일 수 있게 된다.

단, goto 가 보기 싫은 코드의 예술가가 아니라면.. 말이다.

Posted by 명혀니
,

memory leak

Programming Skill 2007. 12. 31. 20:16
*메모리 누수 잡기
-----------------------------------------------------------------------------------
#include <crtdbg.h> 를 추가한다.
코딩의 마지막 부분에
_CrtDumpMemoryLeaks(); 를 써준다.

F5를 눌러 컴파일을 하면 메모리 누수가 일어났을경우
메모리 누수가 일어난 번지(??)를 알려준다.

누수가 일어난 부분을 확인한 후
_CrtSetBreakAlloc(번지); 를 처음 부분에 써주고 F5를 눌러 컴파일 하면 그 부분을 찾아간다.
-----------------------------------------------------------------------------------

*NULL값을 확인하는 방법?
-----------------------------------------------------------------------------------
#include <assert.h>를 추가한다.
확인하고자 하는 변수(?)를 assert(변수); 이렇게 써준다 .
변수가 NULL값일경우 화면에 에러메시지를 표시해준다.
-----------------------------------------------------------------------------------




Visual Studio 에서 개발할 경우 메모리 누수를 확인할려면
디버그 모드에서 F5 (Ctrl+F5 아님) 로 프로그램을 실행하면 되는건 아시죠?
이때 C++ 의 경우는 new 를 사용하고 메모리 반환을 하지 않는경우 해당 위치를 output 창에 보여줍니다.
예를 들어 아래의 코드로 메모리 할당후 어디에서도 반환하지 않았다고 가정해봅시다.
           char *pszTest = new char[100] ;
F5 로 프로그램 실행후 종료하면 Debug 출력창에 아래와 같이 나옵니다.
Detected memory leaks!
Dumping objects ->
D:ProjectMemoryTest.cpp(60) : {73} normal block at 0x00374E58, 100 bytes long.
Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

F4 또는 위의 파일경로명을 더블클릭 하면 해당 위치 소스코드의 포커스를 이동해주기 때문에
메모리누수를 해결하는데 아주 편리합니다.
이렇게 가능한 이유는 비쥬얼스튜디오에서 프로젝트 생성시 자동으로 추가된 아래의 구문 때문입니다

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
보시면 아시겠지만 new 라는 놈을 DEBUG_NEW 로 다시 정의하였습니다.
실제 디버그에선  new 를 사용해도, DEBUG_NEW 가 실행하게 되는것입니다.
그렇다면, 이 DEBUG_NEW 의 정체가 뭘까요?

DEBUG_NEW 가 정의되어있는 소스코드를 가보겠습니다.
DEBUG_NEW 는 <AFX.H> 에 정의되어 있고, 내용은 아래와 같습니다.
#define DEBUG_NEW new(THIS_FILE, __LINE__)
오호라~ new 가 연산자 오버로딩 되어 있는게 확인됩니다.
일반적으로 우리가 생각하는 new 는 인자로 할당할 크기만 주게 되어 있습니다.
하지만, 위의 구문을 보면 해당 위치의 파일명과 라인수를 지정하게 되어 있군요.
다시말해, new 를 사용할때마다 사용된 곳의 파일 경로와 해당 라인수를 메모리에 함께 등록하는 것입니다.

결국 해당 메모리가 제거되지 않았을땐, 메모리에 기록해둔 파일경로와 라인수를 참조해서 디버그 출력창에 뿌려주고
이를 식별자로 사용하여 이동하기 쉽게 해주고 있습니다.
여기까지는 비쥬얼 스튜디오가 자동으로 해주기 때문에 크게 신경쓸 일이 없었습니다.
하지만, 문제는 C 코드일 경우입니다.
비쥬얼스튜디오는 불행하게도 C 코드로 된 프로젝트를 생성하는 마법사가 없습니다.
그러다보니 자동으로 생성해주는 메모리 누수 매크로도 제공하질 않습니다.
실제 C 코드에서 사용하는 malloc 을 사용해서 메모리 해제 하지 않고 결과를 보겠습니다.
char *pszTest2 = (char*)malloc(100*sizeof(char)) ;
위코드를 수행후 종료하면 디버그출력창에 아래와 같이 나옵니다.
etected memory leaks!
Dumping objects ->
{73} normal block at 0x00374E58, 100 bytes long.
Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
도대체 어떻게 하라는건지…
더블클릭해도 아무 반응도 없고, 어디서 메모리 해제가 일어났는지 아무런 정보도 주질 못하고 있습니다.

그렇다고 포기할순 없죠.
C++ 의 new에 사용했던 방식대로 C 의 malloc 에도 똑같이 해주면 될 것 같습니다.
malloc 할때 현재 파일경로와 라인수를 식별자로 함께 등록해주면 되겠군요.
그렇다면 이러한 매크로(DEBUG_NEW와 같은)를 직접 만들어야 할까요?

친절하게도 비쥬얼스튜디오 헤더파일에 미리 다 만들어 놨더군요.
우선 결과 코드를 보겠습니다.
#ifdef _DEBUG
#include <crtdbg.h>
#ifdef malloc
#undef malloc
#endif
#define malloc(s) (_malloc_dbg( s, _NORMAL_BLOCK, __FILE__, __LINE__ ))
#endif

new 를 사용할 때 처럼, malloc 을 다시 다른 함수를 사용하게 정의하였습니다.
위 코드를 C 소스코드의 상단에 위치시킵니다.
그런후 다시 아까의 코드를 수행후 종료하면 친절한 디버그 출력창으로 바뀐걸 볼 수 있습니다.
Detected memory leaks!
Dumping objects ->
D:ProjectMemoryTest.c (68) : {73} normal block at 0x00374E58, 100 bytes long.
Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

위 매크로를 보시면 _malloc_dgb 라는 매크로가 보입니다.
우린 그저 가져다 쓴것에 불과하군요.
<crtdbg.h> 를 열어보시면 메모리 관련 함수인  malloc, calloc, realloc, expand, free, msize 에 대한
디버그용 매크로가 정의되어 있는걸 보실 수 있습니다.
그렇다면 _malloc_dgb 에서 사용된 인자들의 의미가 과연 무얼까요?
s 는 실제 할당할 크기(사이즈) 입니다.
_NORMAL_BLOCK 는 시스템에서 메모리를 할당할 때 사용되는 블록의 기본 단위 크기 입니다.
__FILE__ 은 현재 코드의 전체 경로명입니다.
__LINE__ 은 현재 코드의 수행중인 라인수 입니다.
다시,  crtdbg.h 를 보시면 free 라는 놈도 디버그용이 있습니다.
이왕 하는거 위에서 정의된 모든 메모리 관련 함수에 대해서 디버그용으로 변경해 봅시다.
#ifdef _DEBUG
#include <crtdbg.h>
         #ifdef malloc
         #undef malloc
         #endif
         #define malloc(s) (_malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__ ))
         #ifdef calloc
         #undef calloc
         #endif
         #define calloc(c, s) (_calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__ ))
         #ifdef realloc
         #undef realloc
         #endif
         #define realloc(p, s) (_realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__ ))
         #ifdef _expand
         #undef _expand
         #endif
         #define _expand(p, s) (_expand_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__  ))
         #ifdef free
         #undef free
         #endif
         #define free(p) (_free_dbg(p, _NORMAL_BLOCK))
         #ifdef _msize
         #undef _msize
         #endif
         #define _msize(p) (_msize_dbg(p, _NORMAL_BLOCK))
#endif

이제 메모리누수 안녕~~~
Posted by 명혀니
,

<윈도우 접기>

  다이얼로그 베이스에서 보여줘야 할 항목이 많을 때, 약간 지저분해 보이는 경향이 있는데, 단추를 하나 달아서, 안쓰는 항목은 접어 놓고 보면, 상당히 프로그램을 깔끔하게 할 수 있더군요....

 그전에 하셔야 할 작업은 다음과 같습니다.

1) 잘 안쓰이는 항목들은 Static Box를 하나 생성해서 모아 놓는다.

2) StaticBox의 이름을 ID_STATIC이 아닌 다른 이름을 붙인다.(여기서는 ID_STATIC_SEP라는 이름을 붙였습니다. )

3) 그 항목을 보여주거나, 안보여주는 기능을 추가하기 위해서 코맨드 버튼이나 메뉴항목을 추가한다.( 여기서는 IDC_BUTTON_TEST라는 이름입니다. )

 간단하죠..?? 이렇게 하시고 난 후에 윈도우에 다음과 같은 함수를 추가하시면 됩니다.

 코드의 내용은 아까 만든 차일드 윈도우의 영역을 기억해서, 버튼을 한 번 누르면 원래의 윈도우의 모습을, 두번 누르면 차일의 윈도우 영역을 제외한 부분만 보여주는 그런 일을 합니다.

( 아까 만든 버튼의 이벤트 핸들러 입니다. 착오 없으시길... )

void CXXXXDlg::OnExpand()
{
    // TODO: Add your control notification handler code here
    static BOOL bExpand = TRUE;
    Expanding(IDC_STATIC_SEP, bExpand);
    bExpand = !bExpand;
}

void CXXXXDlg::Expanding(int nResourceID, BOOL bExpand)
{
    static CRect rcLarge;
    static CRect rcSmall;
    CString strExpand;
    if (rcLarge.IsRectNull()) {
        CRect rcLandMark;
        CWnd *pWndLandMark = GetDlgItem(nResourceID);
        ASSERT(pWndLandMark);
        GetWindowRect(rcLarge);
        pWndLandMark -> GetWindowRect(rcLandMark);
        rcSmall = rcLarge;
        rcSmall.bottom = rcLandMark.top;
    }
 
    if (bExpand) {
        SetWindowPos(NULL, 0 , 0, rcLarge.Width(), rcLarge.Height(),
                SWP_NOMOVE | SWP_NOZORDER);
        strExpand = " << Test ";
    } else {
        SetWindowPos(NULL, 0, 0, rcSmall.Width(), rcSmall.Height(),
                SWP_NOMOVE | SWP_NOZORDER);
        strExpand = " Test >>";
    }

    SetDlgItemText(IDC_BUTTON_TEST, strExpand);
}


<상태바에 그림 출력>

상태바에 그림 출력하시려면Create함수에

CStatusbarCtrl& m_Status = m_wndStatusBar.GetStatusBarCtrl();

m_Status.SetIcon(페인인덱스,
     (HICON)LoadImage(AfxGetResourceHandle(),
     MAKEINTRESOURCE(그림아디),
     32,32,10));

해주시면 됩니다.

더 자세한 정보는  MSDN찾아 보세요..


<시스템 강제로 다운시키기>

1.요약

 윈도우즈 NT 나 윈도우즈 98 에서 시스템을 강제로 다운시키는 방법에 대해서 알아봅니다.

2.본문

 필요한 경우가 거의 없겠지만(바이러스가 아니고서야. ^^;;), 시스템을 강제로 다운시키고자 하는 경우가 있을 수 있습니다. 이런 경우 아래의 예제에서 처럼 어셈블리 코드를 사용하는 방법과 Thread 를 와장창 만들어서 thread 러쉬(?)를 통해서 다운시키는 방법이 있습니다. 아래의 예제를 참고 하세요.

3.예제

if(g_bIsWinNT) {
     SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
     while(1) {
          DWORD dwTid;
          HANDLE hThread=CreateThread(NULL,0,LockThread,NULL,0,&dwTid);
          SetThreadPriority(hThread,THREAD_PRIORITY_TIME_CRITICAL);
     }
} else {
     lockpoint:
     __asm {
          cli
          jmp lockpoint
     }
}

4.참고

BO2K

 * cli : Clear Interrupt Flag
     --> 이 어셈블리 코드는 현재 시스템이 다운되는 것을 방지하기 위해서 만들어진
            Interrupt Flag 를 제거해버림으로써, 아무도 자신을 건드릴 수 없게 합니다.

           (The Intel Architecture Software Developer's Manual)  


<Win9x VS Win2000,WinNT 시스템 종료하기>

시스템을 관리하는 프로그램에서 자주 쓰이는 기능인데여...

좀 보기 쉽게 정리된 자료가 없는거 같아서 올려봅니다.....

먼저 Win9x Series...

간단하니까...그냥 함수로 구현한 것만 적어 놓으께여...


void CUpsManagerApp::Win9XSystemReboot()
{
     ExitWindowsEx(EWX_REBOOT,0);
}

void CUpsManagerApp::Win9XSystemPowerOff()
{
     ExitWindowsEx(EWX_POWEROFF,0);
}

void CUpsManagerApp::Win9XSystemLogOff()
{
     ExitWindowsEx(EWX_LOGOFF, 0);
}


먼저 WinNT 및 Win2000 Series...

 요건 문제가 쩜 있습니다...

프리빌리지라는 넘을 설정해서 토큰(버스 토큰 아님 --;;;)이라는 넘을 설정줘야 합니다.

뭐하는 넘인지는 MSDN 찾아보면 잘 나오구여... 그냥 시스템 수준의 프로세스 액세스를 할려면... 이거 설정하구 해야한다구 알고 계시면 될겁니다.

글고 이 설정에 관한 방법은 자료실에 이미 계제가 되어 있습니다.

근데.. 또 글을 적어논 이유는 약간의 부연 설명을 달라구여...

지금의 설정대로 하면


ExitWindowsEx(EWX_SHUTDOWN | EWX_REBOOT,0);
ExitWindowsEx(EWX_SHUTDOWN | EWX_POWEROFF,0);

위의 두개의 기능은 당연히 달라야 합니다....

근데... 둘다 리부팅이 됩니다. 원래 하나는 파워 오프되어야 하는데...

아무리 추적해 봐도 왜 그런지 잘 모르겠어여...


전 유피에스 관리 프로그램 지금 짜고 있는데...

바떼리 떨어지면... 시스템을 죽여야 하는데... 이넘이 다시 켜지면 곤란하지여..


그려서... ExitWindowsEx() 함수 대신에 ....

InitiateSystemShutdown() 함수를 써서 구현해봤심더...

엔티4.0하구 윈이천(Professional 버젼)은 잘 동작 하든디...

다른건 아직 테스트 못해봤습니다.


void CUpsManagerApp::WinNTSystemReboot()
{
     HANDLE hToken;
     TOKEN_PRIVILEGES tp;
     LUID luid;

     OpenProcessToken(GetCurrentProcess(),
          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,  
          &hToken);
     LookupPrivilegeVal ue(NULL,"SeShutdownPrivilege",&luid);

     tp.PrivilegeCount = 1;
     tp.Privileges[0].Luid = luid;
     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
 
     AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);

     InitiateSystemShutdown(NULL, NULL, 30, FALSE, TRUE);
     // ExitWindowsEx(EWX_SHUTDOWN | EWX_REBOOT,0);
}

void CUpsManagerApp::WinNTSystemPowerOff()
{
     HANDLE hToken;
     TOKEN_PRIVILEGES tp;
     LUID luid;

     OpenProcessToken(GetCurrentProcess(),
          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
          &hToken);
     LookupPrivilegeVal ue(NULL,"SeShutdownPrivilege",&luid);

     tp.PrivilegeCount = 1;
     tp.Privileges[0].Luid = luid;
     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

     AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);

     InitiateSystemShutdown(NULL, NULL, 30, FALSE, FALSE);
     // ExitWindowsEx(EWX_SHUTDOWN | EWX_POWEROFF,0);
}

void CUpsManagerApp::WinNTSystemLogOff()
{
     ExitWindowsEx(EWX_LOGOFF, 0);
}


<실수 나눗셈 연산을 정수 연산으로 하기>

1.요약

 소수점이 있는 나눗셈 연산을 정수로 계산하여 계산 속도가 빠르고 정확한 숫자를 얻을 수 있는 방법에 대해서 알아보겠습니다.

2.본문

 정수로 계산은 소수점 계산보다 연산 속도가 빠릅니다. 그리고 정확한 숫자값을 얻기 위해서는 나누기 전에 왼쪽으로 16-bit shift하고, 동등한 16-bit의 2진 숫자를 주어야 합니다. 이것은 나누기 전에 100,000으로 곱셈을 한 효과가 있습니다. 이렇게 함으로써 소수점을 계산하지 않아도 됩니다.

3.예제

1) 10 / 85 = x;

  10을 100,000으로 곱하면, 1,000,000이 나온다.

  1,000,000 / 85 = 11764를 먼저 계산하고,

  결과값 11764를 100,000으로 나누면 0.11764가 나온다.


2)

int ratio = ((( dege - px2) < <  16) / (px1 - px2));
px1 = edge;
py1 = py2 + (((py1 - py2) * ratio) >> 16));  

3)

float ratio = (float)((edge - px2) / (px1 - px2));
px1 = edge;
py = py2 + (int)(((float)(py1 - py2) * ratio));  

[결과]

2)연산속도가 3)연산속도보다 약 2배정도 빠른결과가 나옵니다.


4.참고

Real-Time Strategy Game Programming using Ms DirectX  p.318  


<연결 프로그램 찾기 다이얼로그 띄우기>

[요약]

Explorer 에서 파일 아이콘을 더블클릭할 경우 파일의 확장명에 따라 연결되어 있는 프로그램이 동작하게 되어 있다. 하지만 연결되어 있는 프로그램이 없을 경우 "연결 프로그램 찾기" 다이얼로그가 화면에 나타난다.

[추가 정보]

 파일 아이콘에 연결된 프로그램이 없을 경우 연결 프로그램을 지정하도록 "연결 프로그램 찾기" 다이얼로그를 프로그램적으로 띄우는 방법은 ShellExecuteEx API 를 Call 할 때 "Openas" 를 파라미터로 사용하면 된다.

우선 ShellExecuteEx를 Call하기 전에 FindExecutable API를 사용하여 파일의 프로그램 연결여부를 우선 알아보도록 한다. 만약 연결된 응용프로그램이 있을 경우 SHELLEXECUTEINFO structure에 "Open" 파라미터를 사용하고, 그렇지 않을 경우 "Openas"를 사용하면 된다. 이 파라미터에 의해 ShellExecuteEx 가 "연결 프로그램 찾기" 다이얼로그를 화면에 나타내게 된다.

 다음은 위 내용에 대한 Sample code이다.

// Get the name of the file to be openend.
CFileDialog dlg(TRUE);
if (dlg.DoModal() == IDOK ) {
     CString strFile = dlg.GetPathName();

     // See if there is an association for this file.
     char strExecutable[FILENAME_MAX];
     int result = (int)FindExecutable((LPCTSTR)strFile, NULL,
          (LPTSTR)&strExecutable);
     if (result == 31) {        // No Associated .EXE file
          AfxMessageBox("There is no association for the specified file type.
               The 'Open With...' dialog box will now be displayed.");
          SHELLEXECUTEINFO sei = {sizeof(sei), 0, m_hWnd, "Openas",
               (LPCSTR)strFile, NULL, NULL, SW_SHOWNORMAL,
               AfxGetApp()->m_hInstance};

          // Invoke the "Open With..." dialog box before opening the selected file.
               ShellExecuteEx(&sei);
     
     } else if (result >= 32 ) {               // Found an Associated .EXE file
          AfxMessageBox("The file will now be opened with the associated program...");
          SHELLEXECUTEINFO sei = {sizeof(sei), 0, m_hWnd, "Open",
               (LPCSTR)strFile, NULL, NULL, SW_SHOWNORMAL,
               AfxGetApp()->m_hInstance};

          // Open the selected file.
         ShellExecuteEx(&sei);

     } else {          // Error condition.
         AfxMessageBox("Some other problem has caused 'FindExecutable' to fail.");
     }
}


<Window에서 콘솔 얻기(API)>

보통 윈도우 Application은 화려한 GUI를 자랑하며 Application이 만들어 집니다.

그러나 서버에서 실행되는 프로그램인 경우하나의 콘솔 화면에서 여러 GUI를 띄어 작업을 하게 되지요.

보통은 하나의 Application에 하나의 콘솔에 여러개의 윈도우를 생성해 프로그램 됩니다. 물론 콘솔도 하나의 윈도우지만요.

그럼 여기서 간단히 윈도우 Application에서 콘솔을 띄어 Text Edit에 있는 문자를 콘솔에 찍는 API 함수를 소개합니다.

비교적 간단하고 여러군데서 사용가능하니 많은 참조 바랍니다.

1. 윈도우에서 콘솔 띄우기

    BOOL AllocConsole()

2. 생성된 콘솔에 문자열 보내기

    CString str;
    str = "문자열"

    DWORD dwWrite;
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    WriteFile(hOut, str, str.GetLength(), &dwWrite, NULL);

3. 생성된 콘솔없애기

    BOOL FreeConsole()


함수의 자세한 사용은 MSDN에서 참고하세요.


<제어판에서 큰글꼴로 설정했을경우에도 일정한 크기의 글꼴 지정>


CDC* pDC = GetDC();
int nSize1 = 100;         // 작은 글꼴에서 보여지는 글꼴 크기
int nSize2 = MulDiv(nSize1, 96, pDC->GetDeviceCaps(LOGPIXELSY));
                                // nSize1의 크기를 현재 디스플레이의 인치당 픽셀수로 재계산한다.

m_Font.CreatePointFont(nSize2, "굴림체", pDC);
                                // 단순히 폰트명과 폰트크기로 생성할때

m_Button.SetFont(&m_Font);

이렇게 해서 앞에 올린 코드를 대신하면 제어판에서 설정한 큰글꼴, 작은글꼴의 값에 상관없이 일관된 크기의 폰트를 지정할 수 있습니다. 큰글꼴로 설정하면 글자가 잘리는 현상을 막을 수 있죠.


<투명 256 비트맵>

256 칼라의 비트맵을 화면에 뿌려줍니다. 물론 투명색을 지정하는 방식이구요.

그럼 소스 나갑니다.


int CCISBitmap::Width()
{
    BITMAP bm;
    GetBitmap(&bm);
    return bm.bmWidth;
}

int CCISBitmap::Height()
{
    BITMAP bm;
    GetBitmap(&bm);

    return bm.bmHeight;
}

void CCISBitmap::DrawTransparent(HDC hDC, int x, int y, COLORREF crColour)
{
    COLORREF crOldBack = SetBkColor(hDC,m_crWhite);
    COLORREF crOldText = SetTextColor(hDC,m_crBlack);
    CDC dcImage, dcTrans;
 
    // Create two memory dcs for the image and the mask
   
    dcImage.m_hDC = CreateCompatibleDC(hDC);
    dcTrans.m_hDC = CreateCompatibleDC(hDC);
   
    // Select the image into the appropriate dc
    CBitmap* pOldBitmapImage = dcImage.SelectObject(this);
 
    // Create the mask bitmap
    CBitmap bitmapTrans;
    int nWidth = Width();
    int nHeight = Height();
    bitmapTrans.CreateBitmap(nWidth, nHeight, 1, 1, NULL);

    // Select the mask bitmap into the appropriate dc
    CBitmap* pOldBitmapTrans = dcTrans.SelectObject(&bitmapTrans);
 
    // Build mask based on transparent colour
    dcImage.SetBkColor(crColour);
    dcTrans.BitBlt(0, 0, nWidth, nHeight, &dcImage, 0, 0, SRCCOPY);
 
    // Do the work - True Mask method - cool if not actual display
    BitBlt(hDC, x, y, nWidth, nHeight, dcImage.m_hDC , 0, 0, SRCINVERT);
    BitBlt(hDC, x, y, nWidth, nHeight, dcTrans.m_hDC , 0, 0, SRCAND);
    BitBlt(hDC, x, y, nWidth, nHeight, dcImage.m_hDC , 0, 0, SRCINVERT);
 
    // Restore settings
    dcImage.SelectObject(pOldBitmapImage);
    dcTrans.SelectObject(pOldBitmapTrans);
    pOldBitmapImage->DeleteObject();
    pOldBitmapTrans->DeleteObject();
    SetBkColor(hDC,crOldBack);
    SetTextColor(hDC,crOldText);
}

입니다.

음.. 그런데 이게 좀 느려요. --;.

투명색 없이 출력하는 루틴은 아래껍니다.


void CCISBitmap::DrawBitmap(HDC hDC, int x, int y)
{
    CDC MemDC;
    MemDC.m_hDC = CreateCompatibleDC (hDC);
 
    int nWidth = Width();
    int nHeight = Height();
 
    CBitmap *pBitmap = (CBitmap *)MemDC.SelectObject (this);
    BitBlt (hDC,x,y,nWidth,nHeight,MemDC.m_hDC,0,0,SRCCOPY);
    MemDC.SelectObject (pBitmap);
    pBitmap->DeleteObject();
    pBitmap = NULL;
}

그리고 헤더는 이렇게 해주세요.

public:
    void DrawBitmap(HDC hDC, int x, int y);
    CCISBitmap();
    virtual ~CCISBitmap();
 
    // Functions
    int Height();
    int Width();    
    virtual void DrawTransparent(HDC hDC, int x, int y, COLORREF crColour);    
   
private:
    COLORREF    m_crBlack;
    COLORREF    m_crWhite;

그리고요.. 또.. 생성자에 COLORREF 를 초기화 해주심 되구요

이건 CBitmap 을 상속받았습니다.


<트레이의 아이콘이 사라지지 않게>

IE가 죽을 때 트레이의 아이콘이 모두 사라지는 경우가 있습니다.

특히 트레이에 숨어버리는 프로그램이 그렇게 되면 참으로 난감해지죠.

IE 4이상에서는 그런 일을 막기 위해서 죽은 후 태스크바가 만들어질 때 모든 top-level 윈도우에 메시지를 보내줍니다. 그 메시지를 받으면 트레이에 새로 아이콘을 넣어주면 됩니다.

MSDN에 있는 내용이지만 모르시는 분들이 많더군요.

소스를 보면 다음과 같습니다.


LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
     static UINT s_uTaskbarRestart;

     switch(uMessage) {
     case WM_CREATE:
          s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
          break;

     default:
          if(uMessage == s_uTaskbarRestart)
               AddTaskbarIcons();
          break;
    }
   
    return DefWindowProc(hWnd, uMessage, wParam, lParam);
}


RegisterWindowMessage(TEXT("TaskbarCreated")); 는 TaskbarCreated라는 이름의 메시지를 시스템에 대해서 유일하게 만들어주는 함수입니다. 이미 이 이름으로 메시지가 등록되어 있을 경우에는 그 값을 돌려줍니다.

따라서 프로그램이 시작할 때 한번 실행해서 그 값을 저장해줘야 합니다.

그리고 해당 메시지가 오면 그 때 트레이에 아이콘을 새로 넣으면 되겠죠.


MFC의 경우는 메시지의 값이 상수로 정해져 있지 않기 때문에 메시지 맵에 붙이는 건 안될 것 같습니다. CWnd::WindowProc이나 PretranslateMessage에서 메시지 값을 확인해서 처리해주면 되겠죠.


- MFC의 경우

  윈도우는 tray icon에 있는 아이콘들을 보여줄때. "TaskbarCreated" 라는 윈도우 메세지를 보여줍니다...

 따라서 우리는 이 윈도우 메세지를 등록해서 사용하면 됩니다.

UINT g_uShellRestart;
g_uShellRestart = RegisterWindowMessage(__Text("TaskbarCreated"));

이렇게 해서..

Message Map 에서..


ON_REGISTERED_MESSAGE(g_uShellRestart, OnTrayShow)


이런식으로 메세지 핸들러를 설치하시고..


LRESULT CMyDlg::OnTrayShow(WPARAM wParam, LPARAM lParam)
{
    m_Tray.ShowTray();        // TrayIcon을 다시 보여줍니다. ShowTray 는 임시.
}


이런식으로 해주시면 됩니다..

 테스트를 해보시고 싶으시다면..Ctrl+Alt+Del 을 눌러서..Explorer 를 죽이세요..

그러면 tray icon들이 사라질텐데..

이 메세지를 등록한 프로그램들은 ..여전히 남아있는 모습을 볼 수 있습니다.


<특정한 다이알로그 박스에 버튼을 누른 효과를 내기>

일단 다이알로그 박스의 핸들을 얻어야겠지요?

HWND hWnd;

hWnd = ::FindWindow("클래스 명", "타이틀 명");

  보통 다이알로그박스는 클래스명이 "#32770" 이죠,

  따라서  ::FindWindow("#32770", NULL); 이렇게하면 타이틀에 관계없이 첫번째 다이알로그 박스를 찾아 핸들을 넘겨주죠 그담엔 아래와 같이 해보길...


  ::PostMessage(hWnd, WM_COMMAND, IDOK, 0L);   //"OK" 버튼 누름
  ::PostMessage(hWnd, WM_COMMAND, IDNO, 0L);   //"NO" 버튼 누름
  ::PostMessage(hWnd, WM_COMMAND, IDCANCEL, 0L);   //"CANCEL" 버튼 누름


<프로그램내에서 한영전환을 하는 방법>

해당 헤더파일 : imm.h  해당 라이브러리 파일 : imm32.lib

한글모드와 영문모드를 전환하는 함수는 다음 두가지입니다.


1. 한글모드로 전환하는 방법

void CHanengClass::SetHanMode(HWND hWnd)
{
    HIMC hIMC = ImmGetContext(hWnd);
    DWORD dwConv, dwSent;
    DWORD dwTemp;

    ImmGetConversionStatus(hIMC,&dwConv,&dwSent);
 
    dwTemp = dwConv & ~IME_CMODE_LANGUAGE;
    dwTemp |= IME_CMODE_NATIVE;
    dwConv = dwTemp;
    ImmSetConversionStatus(hIMC,dwConv,dwSent);
    ImmReleaseContext(hWnd,hIMC);
}


2. 영문모드로 전환하는 방법

void CHanengClass::SetEngMode(HWND hWnd)
{
    HIMC hIMC = ImmGetContext(hWnd);
    DWORD dwConv, dwSent;
    DWORD dwTemp;
 
    ImmGetConversionStatus(hIMC,&dwConv,&dwSent);
 
    dwTemp = dwConv & ~IME_CMODE_LANGUAGE;
    dwTemp |= IME_CMODE_ALPHANUMERIC;
    dwConv = dwTemp;
    ImmSetConversionStatus(hIMC,dwConv,dwSent);

    ImmReleaseContext(hWnd,hIMC);
}


<Dialog의 Min/Max/Close Box를 Run Time Show/Hide>

// 아래의 세 메세지 핸들러는
// Dialog Client Area에 만든 각 Button을 위한것입니다.
// 이 각 Button들을 클릭하면 Title Bar상의 해당 Box가 Show/Hide되죠.

void CTestDlg::OnBtnMin()
{
     // TODO: Add your control notification handler code here
     static BOOL bEnable = TRUE;
     bEnable = bEnable ? FALSE : TRUE;    
     if(bEnable)
           ModifyStyle(0,WS_MINIMIZEBOX);
     else
           ModifyStyle(WS_MINIMIZEBOX,0);

     PostMessage(WM_NCACTIVATE,TRUE);
}

void CTestDlg::OnBtnMax()
{
     // TODO: Add your control notification handler code here
     static BOOL bEnable = TRUE;
     bEnable = bEnable ? FALSE : TRUE;    
     if(bEnable)
         ModifyStyle(0,WS_MAXIMIZEBOX   );
     else
         ModifyStyle(WS_MAXIMIZEBOX   ,0);
 
     PostMessage(WM_NCACTIVATE,TRUE);
}

void CTestDlg::OnBtnClose()
{
      // TODO: Add your control notification handler code here
      static BOOL bEnable = TRUE;
      CMenu* pSysMenu = GetSystemMenu(FALSE);
      if (pSysMenu != NULL) {
           bEnable = bEnable ? FALSE : TRUE;
           if(bEnable)
                pSysMenu->EnableMenuItem(SC_CLOSE,  MF_ENABLED | MF_BYCOMMAND);
           else
                pSysMenu->EnableMenuItem(SC_CLOSE, MF_DISABLED | MF_BYCOMMAND);
      }
}


<error LNK2001: unresolved external symbol _main>

error LNK2001: unresolved external symbol _main

이 에러가 발생하는 경우는 크게 두가지가 있습니다.


1. API로 프로그래밍을 처음 하시는 분들이 흔히 하는 실수 같습니다.

 윈도우에서 프로그램은 크게 두가지로 분류할 수 있습니다.

도스처럼 console 창에 텍스트만을 출력하는 console 프로그램과 흔히 쓰는 GUI를 가진 프로그램이 있죠.  

Visual C++에서 프로젝트를 만들 때

Win32 Application : WinMain 함수에서 시작하게 되는 GUI 프로그램

Win32 Console Application : main 함수에서 시작하는 console 프로그램

이 두가지가 있습니다.

 제목에 적은 것 같은 에러가 나는 분들은 대부분 GUI 프로그램을 만드시려고 하고 WinMain은 만드셨는데, 프로젝트를 만들 때 console application을 선택하신겁니다. 반대로 WinMain이 없다고 에러가 나오면 GUI 프로젝트를 선택하고서 main 함수를 작성하신 것이겠죠.

자, 그럼 어떻게 해결하느냐. 설정을 바꿔주면 됩니다. 프로젝트 세팅(Alt + F7)에서 Link탭을 누르면 제일 아래에 에디트 박스가 보이죠? 잘 보면 /subsystem:console 이라고 써있을 겁니다. 이걸 /subsystem:windows로 바꿔주세요. (또는 상황에 따라서 그 반대로 해줄 수도 있겠죠) 이 옵션의 역할은 링커에게 있어서 이 프로그램이 어떤 함수로부터 시작해야 하는지를 알려주는 겁니다. 이 옵션이 console이라면 main을, windows라면 WinMain을 찾아서 그 함수에서부터 프로그램이 시작하게 하는거죠.


2. ATL에서 컨트롤을 만들 때 이 에러가 날 수 있습니다.

디버그에서는 잘 컴파일이 되던게 릴리즈로 빌드하면 저 에러가 날 수 있습니다. ATL에서는 생성되는 코드의 크기를 최소화 하기 위해서 CRT (C RunTime) 함수의 일부를 제한하고 있기 때문이죠(정확히 말하자면 start up 코드를 넣지 않습니다). 그런데 프로젝트에서 특정 C 함수를 사용하면 ATL에서 제한한 CRT의 기능이 필요해지고 따라서 에러가 나는겁니다.


이 해결책도 간단합니다.

a. 프로젝트 세팅에서 predefined keyword를 찾아서 _ATL_MIN_CRT를 지워주세요.

b. 초기화가 필요한 CRT 함수를 사용하지 마세요. -_-; MSDN에 따르면 strcmp대신 lstrcmp를 사용하는 식으로 피해갈 수 있다고 합니다.


자세한건 MSDN에서 _ATL_MIN_CRT를 찾아보세요. (색인에서 입력하면 나옵니다.)


<친절한 메시지 WM_NULL>

1.요약

특정 Thread 나 Window 에 아무런 영향을 끼치지 않으면서 단순히 그 Thread 나 Window 가 살아있는지를 확인하고 싶을때 '친절한 메세지' 를 사용하시면 됩니다.  

2.본문

 MSDN 의 '색인' 에는 나와있지 않지만(MSDN 에서 특이하다 싶은것들은 일반적으로 '색인'에 없죠. ^^), 윈도우 메세지 종류에 WM_NULL 이라는 메세지가 존재합니다. MSDN 이나 다른 책에서 WM_NULL 메세지를 benign message 라고 소개하고 있습니다.

WinUser.H 파일에 아래와 같이 정의되어 있습니다.

 #define WM_NULL                         0x0000

 이 메세지는 메세지 큐에 날라가긴 하지만, 어느 윈도우에서도 이 메세지를 처리하지 않기 때문에 그냥 메세지 큐에 들어가기만 하는 메세지입니다. 그럼 윈도우는 왜 이런 메세지를 만들어 놓은 걸까요? Debugging Application 에서 보면 이 메세지를 정지해있는 혹은 메세지큐가 idle 상태에 있는 thread 를 깨우기 위해서 ( 마치 술 먹기 전에 밥먹는 것처럼..) 사용하고 있습니다. 또한 해당 윈도우가 정상적으로 메세지를 받을 수 있는 상태인지 단순히 확인만 하고 싶을때 ( 이런 경우가 종종있는데, 어떤 메세지를 보내야 할지 난감할때가 있습니다.

3.예제

    // 일반 메세지 보내는것과 같습니다.
    PostMessage(m_hwndTarget, WM_NULL, 0,0);

Posted by 명혀니
,

MSDN URL :  http://msdn2.microsoft.com/en-us/library/2c6ye477(vs.71).aspx



Call ResizeParentToFit to let the size of your view dictate the size of its frame window.

void ResizeParentToFit(
   BOOL bShrinkOnly = TRUE 
);

This is recommended only for views in MDI child frame windows. Use ResizeParentToFit in the OnInitialUpdate handler function of your derived CScrollView class. For an example of this member function, see CScrollView::SetScrollSizes.





주로 VIEW 클래스의

OnInitialUpdate 함수에서 쓴다.


이 함수를 통해 view 의 크기를 그때 상황에 맞게 조절할수 있다.


다음은 Open CV 라이브러리를 사용해서 그림 파일을 읽어 올경우 그그림 사이즈에 맞기 view 를

크기 조정하는 사용예이다.



void COpenMFCView::OnInitialUpdate()
{

 COpenMFCDoc * pDoc = GetDocument();  // doc 를 가져온다.
 ASSERT_VALID(pDoc);


 CScrollView::OnInitialUpdate();

 CSize sizeTotal;

 if(pDoc->m_cvvImage.GetImage() )
 {
  int height = pDoc->m_cvvImage.Height();
  int width = pDoc->m_cvvImage.Width();
  sizeTotal = CSize(width,height);

 }
 else
 {
  sizeTotal.cx = sizeTotal.cy = 100;
 }
 
 SetScrollSizes(MM_TEXT, sizeTotal);
 ResizeParentToFit(TRUE);
}

Posted by 명혀니
,
CWnd::DragAcceptFiles 
 
 
 

Call this member function from within a window, using a CWnd pointer, in your application's CWinApp::InitInstance function to indicate that the window accepts dropped files from the Windows File Manager or Windows Explorer.

void DragAcceptFiles(
   BOOL bAccept = TRUE   // 디폴트가 트루다 !
);
 

Parameters

BAccept

Flag that indicates whether dragged files are accepted.

 
 
 
사용 예제 ..
 

사용자 삽입 이미지


 


CxxApp 에서


InitInstance 함수내에서


위의 그림과 같이


PMainFrame->DragAcceptFiles() 함수를 호출하면


됩니다..



한줄 코딩 ...

Posted by 명혀니
,



보통 한글코드는 최상위비트가 1로 시작하는 연속된 두바이트로 구성된다. 따라서 스트링의 시작부터 스트링의 길이만큼 바이트 단위로 비교하면서 해당 바이트의 최상위비트가 1이면 해당 연속 2바이트는 한글코드로 판단하면 된다.

char* p_string = "abc한글";
for(int i = 0; i < strlen(p_string); i++){
    if(p_string[i] & 0x80){
        //한글임
        i++; //한글은 2바이트이기 때문에 1바이트 더 증가시킴
    }
    else{
        //영문임
    }
}




Posted by 명혀니
,