2019/03/06

[MFC] Document/View Architecture를 사용하지 않고 FormView 사용하기

CFormView를 이용한 간단한 프로그램을 만들 때 Visual C++ 에서 제공하는 Document/View Architecture를 사용할 경우 불필요한 작업이 많은 것 같아 이를 사용하지 않고 만드는 방법을 알아보았다. 

SDI 프로젝트를 만들때 Document/View Architecture 옵션을 제거하고 프로젝트를 만든 후 기본적으로 만들어지는 View외에 추가로 CFormView를 상속받는 View을 하나 만들어 기존 View와 교체를 하면 가능하였다.

하지만 새로운 View를 만들어 교체를 할 때 일반 변수를 이용하여 Stack에 만들경우 프로그램 종료시 에러가 발생하였다. 이미 해제된 View를 다시한 번 삭제하려 하기때문인 것 같았다. Heap 메모리에 만들어지도록 하면 문제가 없었다.

//CChildFormView.h
class CChildFormView : public CFormView
{
public:  // Change constructor to public. It is private by default
 CChildFormView();           // protected constructor used by dynamic creation
 virtual ~CChildFormView();
};




//CMainFrm.h
#include "ChildFormView.h"

class CMainFrame : public CFrameWnd
{
protected:  // control bar embedded members
 CChildFormView    *m_pWndView;
};




//CMainFrm.cpp
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
 if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
  return -1;

 // First, build the view context structure
 CCreateContext ccx;

 // Designate the class from which to build the view
 ccx.m_pNewViewClass = RUNTIME_CLASS(CChildFormView);

 // Using the structure, create a view
 m_pWndView = DYNAMIC_DOWNCAST(CChildFormView, this->CreateView(&ccx));

 // Did we succeed ?
 if (!m_pWndView)
 {
  TRACE0("Creation of view failed\n");
  return -1;
 }

 // Do layout recalc
 RecalcLayout();

 // Show the view and do an initial update
 m_pWndView->ShowWindow(SW_SHOW);
 m_pWndView->OnInitialUpdate();

 // Set this view active
 SetActiveView(m_pWndView);

 // Order it to resize the parent window to fit
 m_pWndView->ResizeParentToFit(FALSE);

 // create a view to occupy the client area of the frame
//  if (!m_wndView.Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, NULL))
//  {
//   TRACE0("Failed to create view window\n");
//   return -1;
//  }

 if (!m_wndStatusBar.Create(this))
 {
  TRACE0("Failed to create status bar\n");
  return -1;      // fail to create
 }
 m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));

 return 0;
}


https://www.codeproject.com/Articles/5445/Using-views-effectively-without-the-doc-view-overh

Visual C++ 디버깅 메모리 상태

0xcccccccc (3435973836)
할당된 Stack 메모리를 초기화 하지 않을 경우 채워지는 값
해당 값은 어셈블리 __asm int 3(break)와 동일하여 이 영역을 접근하면 break point에 적중된다.
When the code is compiled with the /GZ option, uninitialized variables are automatically assigned to this value (at byte level).

0xcdcdcdcd (3452816845)
할당된 Heap 메모리를 초기화 하지 않을 경우 채워지는 값
Clean Memory: Allocated memory via malloc or new but never written by the application.

0xdddddddd (3722304989)
0xfeeefeee (4277075694)
Free 된 Heap 메모리에 채워지는 값
Dead Memory: Memory that has been released with delete or free. It is used to detect writing through dangling pointers.

0xfdfdfdfd (4261281277)
할당된 Heap 메모리의 경계(전,후)에 채워지는 값
Fence Memory: Also known as "no mans land." This is used to wrap the allocated memory (like surrounding it with fences) and is used to detect indexing arrays out of bounds.

0xABABABAB (‭2880154539‬)
HeapAlloc으로 메모리 할당 후 가드 바이트에 채워진 값
(Allocated Block?): Memory allocated by LocalAlloc().

0xBAADF00D (‭3131961357‬)
LocalAlloc(LMEM_FIXED)으로 메모리를 할당한 후 초기화하지 않음
Bad Food: Memory allocated by LocalAlloc() with LMEM_FIXED, but not yet written to.

https://www.codeguru.com/cpp/w-p/win32/tutorials/article.php/c9535/Inside-CRT-Debug-Heap-Management.htm

2019/01/29

[C++] OS 버전 알아내기

프로그래밍 중 OS 버전을 확인해야 하는 상황이 있어 내용을 찾아보았다.
검색을통해서 GetVersion, GetVersionEx 함수를 이용하는 방법과 VerifyVersionInfo 또는 Version Helper functions 를 사용하는 방법 그리고 시스템 파일(Kernel32.dll)의 파일 정보(GetFileVersionInfo)를 읽어들이는 방법이 있었다.
GetVersion, GetVersionEx 의 경우는 호환성 문제로 MS에서도 이 함수보다 VerifyVersionInfo 또는 Version Helper functions 를 사용하기를 권장하고 있었다.
VerifyVersionInfo은 사용법이 복잡해서 귀찮고 오래된 윈도우에서 동작하지 않을 수 있을 것 같아 다른 방법을 찾아보았다.

간단하게 도스 명령 Ver를 실행하여 결과를 해석하는 방법을 사용하였다.
Ver 명령어는 윈도우의 버전을 보여주는 명령어다. SystemInfo 라는 명령어도 있지만 많은 데이터를 보여주다보니 실행시간 다소 길다.
Ver 명령은 오래전부터있던 명령어로 윈도우의 버전만 간단하게 보여준다.
https://en.wikipedia.org/wiki/Ver_(command)

Windows 10의 경우
C:\>ver

Microsoft Windows [Version 10.0.17763.253]

Windows 7의 경우
C:\>ver

Microsoft Windows [Version 6.1.7601]

도스 명령의 결과를 받아오기 위해 _popen 함수를 사용하였다.

//////////////////////////////////////////////////////////////////////////
// OS Version
FILE* file = nullptr;
char szOutput[300];

file = _popen("ver", "r");
if ( file != nullptr )
{
 fread(szOutput, 1, sizeof(szOutput), file);
 fclose(file);

 std::string strVer(szOutput);
 std::regex reVer("[0-9]+\\.[0-9]+\\.?(\\*|[0-9]+)\\.?([0-9]+)?");
 std::smatch m;
 if( regex_search(strVer, m, reVer) )
 {
  m_strWinVersion = CString(szOutput);
  m_strWinVerNumber = CString(m.str().c_str());

  int n1stDot = m_strWinVerNumber.Find(L".");
  int n2ndDot = m_strWinVerNumber.Find(L".", n1stDot + 1);
  int n3rdDot = m_strWinVerNumber.Find(L".", n2ndDot + 1);
  if ( n1stDot >= 0 && n2ndDot > n1stDot )
  {
   m_nWinVerMajor = _tstoi(m_strWinVerNumber.Left(n1stDot));
   m_nWinVerMinor = _tstoi(m_strWinVerNumber.Mid(n1stDot + 1, n2ndDot - n1stDot - 1));
   if ( n3rdDot == -1 && n2ndDot != -1 )
   {
    m_nWinVerBuild = _tstoi(m_strWinVerNumber.Mid(n2ndDot + 1));
   }
   else if ( n3rdDot > n2ndDot )
   {
    m_nWinVerBuild = _tstoi(m_strWinVerNumber.Mid(n2ndDot + 1, n3rdDot - n2ndDot - 1));
   }
  }
 }
}
// OS Version
//////////////////////////////////////////////////////////////////////////

2019/01/15

Windows Boot Manager 옮기기

여러 HDD를 사용하다가 없애야 하는 상황이 발생하였다.
운영체제가 설치된 HDD를 제외하고 다른 HDD를 제거하려 하였으나 부팅이 되지 않는 상황에 부딪쳤다.
확인을 해 보니 Window Boot Manager 가 C드라이브가 아닌 Z드라이브에 설치되어 있었다.

$ bcdedit

Windows Boot Manager
--------------------
identifier              {bootmgr}
device                  partition=Z:
:

Windows Boot Loader
-------------------
identifier              {current}
device                  partition=C:
:

C 드라이브를 제외한 모든 HDD를 제거하기위해 Windows Boot Manager 를 C 드라이브로 옮겨야 했다.
우선 bcdboot 프로그램으로 C드라이브에 Boot 파일을 생성하였다.

$ bcdboot C:\Windows /s C:
Boot files successfully created.

그리고 난 후 diskpart 프로그램을 이용하여 C 드라이브의 파티션을 활성화 해 주었다.

$ diskpart
DISKPART> select disk 0
DISKPART> select partition 1
DISKPART> active

그리고 나서 C 드라이브를 제외한 모든 드라이버를 제거하고 부팅에 성공을 하였다.
bcdedit를 이용하여 확인을 해보니 Window Boot Manager 가 C드라이브 옮겨졌다.

$ bcdedit

Windows Boot Manager
--------------------
identifier              {bootmgr}
device                  partition=C:
: