비트연산자

2012. 4. 9. 21:10 from PROGRAMMING/MFC(C++)

출처: http://www.winapi.co.kr/clec/cpp1/5-2-3.htm

 

아직도 헷갈리는 비트 연산, 시프트 연산...

 

5-2-다.비트 연산자

비트 연산자는 논리 연산자와 비슷하지만 비트를 연산 대상으로 한다는 점이 조금 다르다. 비트(bit)란 기억 장치의 최소 단위로서 1 또는 0을 기억하며 8개의 비트가 모여야 1바이트가 된다. 32비트의 정수 1234는 16진수로는 0x4d2이며 메모리에 다음과 같이 기억된다.

 

32비트이므로 32개의 비트가 있고 이 각각의 비트가 1이나 0을 기억함으로써 1234라는 숫자 하나를 저장하는 것이다. 비트 연산자는 이 그림에서 각 격자인 비트들을 대상으로 조작을 한다. 비트가 연산대상이라는 말은 두 피연산자의 대응되는 비트끼리 연산해서 그 결과를 리턴한다는 뜻이다. 일상 생활에서 쓰는 십진수와는 다른 이진수 차원의 연산이라 다소 어려운 것처럼 보이겠지만 막상 다 이해하고 나면 이진수만큼이나 간단하다.

과거 프로그램이 비디오 메모리를 직접 액세스할 때는 비트 연산이 굉장히 중요했으며 섬세한 처리를 하고자 할 때마다 꼭 사용되는 중요한 연산이었다. 비트를 잘 조작하면 반전, 스크롤, 투명 처리 등이 가능했으며 일반적인 산술 연산보다 훨씬 더 빠른 속도로 복잡한 연산을 할 수 있었다. 그러나 윈도우즈 환경에서는 비디오 메모리를 직접 액세스하는 것이 금지되었고 별로 그럴 필요도 없기 때문에 요즘은 비트 연산자가 많이 사용되지 않는다.

하지만 게임이나 중요한 시스템 소프트웨어에서는 아직도 비트 연산이 꼭 필요하며 활용 범위가 넓다. 스타일값 중 원하는 값을 추출하거나 액세스 권한 같은 플래그를 다룰 때 비트 연산자가 사용된다. 일단 비트 연산자의 종류에 대해 표로 간단하게 정리해 보자. 다음 여섯 가지가 있다.

 

연산자

설명

~

비트를 반전시킨다.

&

대응되는 비트가 모두 1 1이다.

|

대응되는 비트가 모두 0 0이다.

^

개의 비트가 달라야 1이다.

<<

지정한 수만큼 왼쪽으로 비트들을 이동시킨다.

>>

지정한 수만큼 오른쪽으로 비트들을 이동시킨다.

 

~만 단항 연산자이고 나머지는 모두 두 개의 피연산자를 취하는 이항 연산자이다. 비트 연산은 정수 수준에서만 의미가 있기 때문에 피연산자는 모두 정수형이거나 또는 정수로 자동 변환될 수 있는 타입이어야 한다. 실수나 포인터 등은 비트 연산자와 함께 사용할 수 없다. 다음은 비트 연산자들의 진리표인데 다 알고 있겠지만 도표로 정리해 보도록 하자.

 

b1

b2

b1 & b2

b1 | b2

b1 ^ b2

~b1

0

0

0

0

0

1

0

1

0

1

1

1

1

0

0

1

1

0

1

1

1

1

0

0

 

단항 연산자 ~는 가장 이해하기 쉬운 연산자이다. 비트가 1이면 0으로 0이면 1로 바꾸어 1의 보수로 만든다. a가 0x59라고 할 때 ~a가 어떻게 연산되는지 보자. 32비트 환경에서 정수는 32비트이지만 설명의 편의상(사실은 그림 그리기 귀찮으니까) a가 8비트 정수타입(unsigned char)이라고 하자.

 

0x59의 ~연산 결과는 비트를 모두 뒤집은 0xa6이 되는데 이 두 수는 1의 보수 관계이며 더하면 전체 비트가 모두 1인 0xff(이진수로 11111111)가 된다. 이렇게 그림으로 비트들이 어떻게 변하는지 보면 ~연산을 쉽게 이해할 수 있을 것이다. 만약 10진수로 이 연산자의 동작을 살펴보면 89의 ~연산 결과가 166이 되는데 89가 어떻게 166이 되었는지 직감적으로 이해하기 어렵다. 그래서 비트 연산자를 설명할 때는 2진수나 16진수를 쓸 수밖에 없으며 이 동작을 잘 이해하기 위해서는 2진수와 16진수 사이를 암산으로 신속하게 변환할 수 있어야 한다.

 

반전 연산자는 이미지 처리에 많이 사용되는데 이미지의 각 픽셀값을 반대로 뒤집으면 역상의 이미지를 얻을 수 있다. 흰색은 검정색이 되고 검정색은 흰색이 되기 때문에 역상 이미지가 만들어지는 것이다. Win32 환경에서는 API 함수들이 이런 처리를 대신해 주기 때문에 이 연산자를 직접 쓸 경우는 드물다.

 

&, | 연산자의 동작도 이해하기 쉬운데 특정 비트만 0으로 만들거나 또는 1로 만들 때 이 연산자들이 사용된다. a가 0x59일 때 a & 0xf(이진수 00001111)가 어떻게 연산되는지 보자.

 

 

&연산의 진리표를 보면 0과 &되는 비트는 그 값에 상관없이 무조건 0이 되며 1과 &되는 비트는 원래 비트값을 그대로 유지하는 특성이 있다. 이진수 00001111과 &연산을 하면 상위 4비트는 0이 되며 하위 4비트만 값을 유지한다. 이런 식으로 특정 비트를 강제로 0으로 만드는 연산을 마스크 오프(mask off)라고 한다. | 연산은 이와는 반대의 연산을 한다.

 

 

1과 |되는 비트는 무조건 1이 되고 0과 |되는 비트는 원래 값을 유지하는데 이렇게 특정 비트를 강제로 1로 만드는 연산을 마스크 온(mask on)이라고 한다. 마스크 연산이란 특정 비트에 덮개(mask)를 씌워 놓고 전부 0(off)이나 1(on)로 만든 후 덮개를 벗긴다고 생각하면 된다. 덮개가 씌워져 있던 비트는 원래 값을 유지하고 나머지 비트는 0이나 1로 강제 변환된다. & 연산에서는 1이 마스크이고 OR 연산에서는 0이 마스크이다.

&, | 연산자는 일부 비트만 제한적으로 읽거나 변경할 때 흔히 사용된다. 기억 공간을 절약하기 위해 하나의 정수값을 비트별로 잘라 여러 가지 값을 같이 기억시키는 방법이 많이 사용되는데 예를 들어 한글 조합형 코드는 16비트 길이를 가지며 다음과 같이 구성되어 있다.

 

 

최상위 비트는 항상 1인데 이 값은 이 코드가 한글임을 표시한다. 영문 알파벳은 모두 128보다 작기 때문에 이 비트가 0으로 되어 있어 한글과 구분된다. 16비트의 정수값을 5비트씩 잘라서 초성, 중성, 종성 코드를 기억시킨다. 한글 낱글자인 ㄱ,ㄴ,ㄷ,ㄹ,... 은 총 개수가 32개가 안되기 때문에 5비트면 낱글자 하나를 기억할 수 있고 이런 글자 세 개가 모이면 한글 1음절을 표현할 수 있다. 초성, 중성, 종성 코드를 각각의 정수에 기억하는 방법에 비해 훨씬 더 기억 공간이 절약된다. 이런 조합된 값에서 일부만 추출해 내거나 일부만 변경하려면 &, | 비트 연산이 필요하다. 다음은 한글 1음절의 값을 가지는 변수 Han을 비트 조작하는 예이다.

 

Han & 0x1f // 종성만 분리한다.

Han & 0x7c00 // 초성만 분리한다.

Han & 0xffe0 | 2 // 종성만 ㄱ으로 바꾼다.

 

윈도우의 스타일도 32비트의 정수에 각 스타일 비트들이 조합되어 있는데 이런 값들을 조작할 때도 비트 연산자가 사용된다. 다음에 API를 배울 때 보게 되겠지만 간단히 예만 보이자면 다음과 같다. style 변수에 32개나 되는 스타일 비트가 기억되어 있는데 다른 스타일값은 무시하고 WS_CHILD 값만 조사하거나 변경하고자 할 때 마스크 연산을 해야 한다.

 

if (style & WS_CHILD) // WS_CHILD 스타일을 가지고 있으면

style |= WS_CHILD // WS_CHILD 스타일 지정

 

XOR 연산자인 ^ 는 배타적 논리합이라고 부르며 ~연산자와 마찬가지로 비트를 반전시키는 기능을 하는데 ~연산자가 전체 비트를 반전시키는 반면 ^는 지정한 비트만을 반전시킨다. 배타적 논리합은 비트가 서로 다를 때만 1이 되고 같으면 0이 되기 때문에 1과 ^되는 비트는 반전되고 0과 ^되는 비트는 원래 값을 유지한다. 그래서 반전시키고자 하는 부분만 1로 만든 값과 ^연산을 취하면 원하는 부분만 반전된다. 이름하여 마스크 반전이라고 할 수 있다.

 

 

반전된 값은 다시 반전시키면 원래대로 돌아오는 특성이 있다. 1을 0으로 만들었다가 다시 반전하면 원래값 1이 되기 때문이다. 그래서 XOR 연산은 이미지의 이동이나 반복적인 점멸 처리에 사용된다. 캐럿이 깜박거리거나 텍스트의 선택 블록을 보여주는 처리가 모두 이 연산을 사용하는데 반복적으로 XOR 연산을 하면 원형을 손상하지 않고 복구 가능하기 때문이다.

 

Posted by 마마필로 :

출처: http://www.dolba.net/tt/k2club/entry/CString%EA%B3%BC-char%ED%98%95%EC%9D%98-%EC%83%81%ED%98%B8-%ED%98%95%EB%B3%80%ED%99%98

 

1. CString => char* 변환

char* ch;

CString *str;

1) ch = (LPSTR)(LPCSTR)str;

2) ch = str.GetBuffer(str.GetLength());

3) wsprintf(ch, "%s", str);

 

2. char* => CString 변환

1) str = (LPCSTR)(LPSTR)ch;

2) str = ch;

 

LPSTR: char string의 32비트 포인터, char*와 같다.

LPCSTR: Constant character String의 32비트 포인터

UNIT: 32비트 unsigned형 정수

DWORD: unsigned long int형

BYTE: 8비트 unsigned 정수

 

참고: CString을 const char* 형태로 변경 -> (LPTSTR)(LPCSTR)Cstring

 

 

Posted by 마마필로 :

1. CString::Left

http://msdn.microsoft.com/en-us/library/aa300477(v=vs.60).aspx

// example for CString::Left
CString s( _T("abcdef") );
ASSERT( s.Left(2) == _T("ab") );
 
 
2. CString::Mid
http://msdn.microsoft.com/en-us/library/aa300543(v=vs.60).aspx
// example for CString::Mid
CString s( _T("abcdef") );
ASSERT( s.Mid( 2, 3 ) == _T("cde") );
 
 
3. CString::Right
http://msdn.microsoft.com/en-us/library/aa300592(v=vs.60).aspx
 
// example for CString::Right
CString s( _T("abcdef") );
ASSERT( s.Right(2) == _T("ef") );
Posted by 마마필로 :

출처: http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=50&MAEULNo=20&no=14876&ref=14876

 컨트롤에 멤버 변수를 추가할때 value 와 control 형의 차이점이 무엇이죠?   | VC++ 일반 2000-02-15 오후 6:46:00
박대근 번호: 14876  / 읽음:144
MFC를 공부하기 시작한 초보자 에요.
요즘 여러가지 컨트롤들을 공부하면서 이해하기 힘든게 있어요.
컨트롤의 아이디를 지어주고, 그것에 멤버 변수를 연결하잖아요?
그때, category에 value 와 control 이 있는데, 어떤때 value를 
선택하고, 어떤때 control을 선택해야 하는거죠?
처음에는 value를 선택할때는 멤버변수를 값으로 필요할때 선택하는줄
알았는데, 리스트박스 컨트롤을 생성할때는 value를 선택해도 type에는 CString
클래스가 나오더라구요. int 같은 정수형이 아닌 클래스가요...
제가 잘못 이해하고 있는것 같기도 하고, 
어째든 value 와 control 을 사용할 시기에 대해서 설명해 줄실분 없나요?
이 글에 답변 등록하기

         [답변][답변]컨트롤에 멤버 변수를 추가할때 value 와 contr 2000-02-15 오후 6:59:00
김진수 번호: 14877  
control로 선택하는 것은 Window자체를 사용하겠다는 것입니다.
가령 ListCtrl을 control로 변수선언하면
그 ListCtrl의 모든것을 이용할 수 있습니다.
가령, Window를 옮긴다거나, 크기를 변경한다거나

그러나 value로 선택하는 것은 control로 선언할 때 처럼
window 자체로 사용하지 않고 value로만 사용한다는 것입니다.

하나의 Window Control로 두가지 형태 모두 선언할 수도 있습니다.

UpdateData()함수를 더 공부해 보시길...

이렇게 봐도 아직 잘 이해가 안된다.

control형은 어떤것에 대한 컨트롤이 필요할때?

 

value형은 연결된 값만 가지고 사용할때?

대충 이해한건 이정도...

 

 

Posted by 마마필로 :


1. OnOK() 함수 재정의 후, OnOK() 호출 주석처리

void CSocketTestDlg::OnOK()
{
 // TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.

 //CDialog::OnOK();
}


2. PreTranslateMessage() 재정의

if(pMsg->message == WM_KEYDOWN)
{

if(pMsg->wParam == VK_RETURN)
{

return TRUE;

}

}

//Enter키를 아예 막아버리는 코드. Enter키 입력을 받을 수 없음.

Posted by 마마필로 :

[CAsyncSocket 클래스의 주요 함수]

 

생성자

CAsyncSocket : CAsyncSocket 객체를 생성한다.

 

메서드

Create : 소켓을 생성한다.

Accept : 소켓에 연결을 받아들인다.

Bind : 소켓에 로컬 주소를 연결한다.

Close : 소켓을 닫는다.

Connect : 상대방 소켓에 연결을 맺는다.

Listen : 연결 요구를 기다린다.

Receive : 소켓으로부터 데이터를 받는다.

Send : 연결된 소켓으로 데이터를 전송한다.

 

*CSocket클래스는 CAsyncSocket 클래스의 파생 클래스이므로 CAsyncSocket 클래스의 주요 함수를 모두 사용할 수 있다.

 

 

 

출처: 성윤정,<<Visual C++ MFC 윈도우프로그래밍>>, 인피니티북스, 2009


Posted by 마마필로 :

Windows Service Process

주로 시스템 트레이에 등록되어 있는 프로그램들로

화면(창)이 사라져도 프로그램이 종료되지 않고 계속 실행되고 있는 것들.

 

Shell Interface: 데이타나 파일이나 디렉토리등을 보여줄 수 있는 화면

 

1. 프로그램 실행시 트레이에 아이콘이 들어가게 함

-트레이에 아이콘이 박히는 시점 결정. 함수가 호출되는 시점

 

1.1 함수 구현(트레이에 아이콘이 박히는 시점과 관련된)

응용프로그램에서의 편리한 제어를 위해 MainFrame에 구현

 

MainFrame.h파일에 원형 선언

void ViewTray(void);

 

MainFrame.cpp파일에 코드 구현

 // CMainFrame 메시지 처리기

 

 void CMainFrame::ViewTray(void)
  {
  NOTIFYICONDATA nid;
  ::ZeroMemory(&nid, sizeof(nid));
 
  nid.cbSize     =sizeof(nid);
  nid.hWnd     =m_hWnd;
  nid.uID      =IDR_MAINFRAME;
  nid.uFlags     =NIF_MESSAGE | NIF_ICON | NIF_TIP;
  nid.uCallbackMessage =UM_TRAY;
  nid.hIcon     =AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  wsprintf(nid.szTip, _T("%s"), _T("메롱!"));
 
  ::Shell_NotifyIcon(NIM_ADD, &nid);  // 트레이 아이콘에 넣음
 }

 

1.2 전역헤더(stdafx.h)에 UM_TRAY 메시지 선언

#define UM_TRAY WM_USER + 100

 

ViewTray함수 구현만 하고 아직 호출하지는 않았으므로 이 단계까지는 아직 트레이에 들어가지 않는다.

프로그램이 실행되면 바로 트레이에 들어가도록 하기 위해

이 함수가 호출되는 시점은... 메인 프레임이 다 완성된 후에 시스템 트레이에 올려주기 위해

OnCreate함수에서 리턴되기 직전에 호출해 주는 것이 좋다.

 

트레이 영역에 아이콘을 넣는 것은 윈도우의 쉘서비스가 하는 일.

프로그래머는 구조체를 채워 던져주는 역할만.

 

2. 창을 닫아도 시스템트레이에서 사라지지 않도록.

 Onclose메시지 재정의

-프로그램이 종료되지 않도록 OnClose함수가 실해되지 않게 해주고

창은 숨겨준다.

void CMainFrame::OnClose()
{
 ShowWindow(SW_HIDE);
 // CFrameWndEx::OnClose(); // 응용 프로그램의 종료를 막기위해 주석처리.
}

 

3. 아이콘을 더블클릭하면 프로그램이 다시 실행되도록.

트레이 아이콘이 메시지를 받을 수 있도록 한다.

 

 

윈도우를 종료할 때

 

WM_Close

WM_Destory

WM-Quit

 

순서로 메시지 큐에 쌓이면서 윈도우가 종료됨

Quit은 응용프로그램을 완전히 종료하므로 개발자가 재정의 할 수 없음.

 

Onclose에 넣었을 경우 응용프로그램이 종료되는것 위에서 확인했음

 

따라서, WM_Destory를 이용해서 창을 닫아도 트레이에서 아이콘이 사라지지 않도록 해준다.

 

void CMainFrame::OnDestroy()
{
 NOTIFYICONDATA nid;
 nid.cbSize    =sizeof(nid);
 nid.hWnd    =m_hWnd;
 nid.uID     =IDR_MAINFRAME;

 ::Shell_NotifyIcon(NIM_DELETE, &nid);

 CFrameWndEx::OnDestroy();

}

 

cf.) 프로그램이 실행되는 순서? 이벤트 발생-메시지발생-메시지에 대응하는 이벤트 처리 함수

 

4. 리소스뷰-Menu-리소스뷰 추가

 

EXIT, ABOUT버튼은 따로 코드 구현필요없이 속성-ID수정으로 가능(ID_APP_EXIT, ID_APP_ABOUT로)

 

5. 이벤트처리기(사용자 정의 함수) 생성

 

선언(사용자 정의 함수의 선언은 메시지 맵 함수 안에.)-MainFrm.h

사용자 정의 함수의 선언은 메시지 맵 함수 안에.

// 생성된 메시지 맵 함수
protected:
 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
 afx_msg void OnApplicationLook(UINT id);
 afx_msg void OnUpdateApplicationLook(CCmdUI* pCmdUI);
 afx_msg void OnFilePrint();
 afx_msg void OnFilePrintPreview();
 afx_msg void OnUpdateFilePrintPreview(CCmdUI* pCmdUI);
 DECLARE_MESSAGE_MAP()

 void InitializeRibbon();
public:
 afx_msg LRESULT OnTray(WPARAM wParam, LPARAM lParam);
 afx_msg void OnClose();
 afx_msg void OnDestroy();
};

메시지 맵 등록

 

 

구현-MainFrm.cpp

LRESULT CMainFrame::OnTray(WPARAM wParam, LPARAM lParam)
{
 if (lParam == WM_RBUTTONDOWN)
 {
  SetCapture();
 }
 else if(lParam == WM_RBUTTONDOWN)
 {
  CMenu menu, *pMenu = NULL;
  CPoint pt;

  menu.LoadMenu(IDR_MENU1);
  pMenu = menu.GetSubMenu(0);

  GetCursorPos(&pt);
  SetForegroundWindow();
  pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, this);

  ::PostMessage(m_hWnd, WM_NULL, 0, 0);
  ReleaseCapture();
 }
 else if (lParam == WM_LBUTTONDBLCLK)
 {
  ShowWindow(SW_SHOW);
  SetForegroundWindow();
 }
 return 0;
}

6. 리소스뷰-MainFrame 메뉴 추가

.....


'PROGRAMMING > MFC(C++)' 카테고리의 다른 글

Dialog에서 Enter키 종료 방지  (0) 2012.02.23
소켓 프로그래밍 주요함수  (0) 2011.04.27
[0425수업] dll파일 만들기  (0) 2011.04.25
[0425수업] ClipBoard  (0) 2011.04.25
[0422수업] IPC통신  (0) 2011.04.22
Posted by 마마필로 :

1. MFC DLL기반으로 새 프로젝트를 생성한다.(DllApp)

DLL형식=MFC 확장 DLL

 

2.DllMain함수 확인

DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)

 

// dll이 올라갈 때

if (dwReason == DLL_PROCESS_ATTACH) 사용

 

// dll이 내려갈 때

else if (dwReason == DLL_PROCESS_DETACH) 사용

 

3. 함수만들기

BOOL WINAPI GetModulePath(CString& strPath)

{

}

 



__declspec(dllexport)
BOOL WINAPI GetModulePath(CString& strPath) // 전체 파일의 물리적 경로를 읽어오는 함수
{
 TCHAR szBuffer[MAX_PATH];
 ::ZeroMemory(szBuffer, sizeof(szBuffer));
 ::GetModuleFileName(NULL, szBuffer, MAX_PATH);

 for (int i=lstrlen(szBuffer)-1; 1>=0; --i)
 {
  if (szBuffer[i] == '\\')
  {
   int j = lstrlen(szBuffer)-1;
   for (;j>=i;--i)
   {
    szBuffer[j]=NULL;
   }
   if (szBuffer[j] == ':')
   {
    szBuffer[j+1] = '\\';
   }
   strPath = szBuffer;
   return TRUE;
  }
 }
 return FALSE;
}

 

4. 프로젝트 새로 만들기. MFC응용프로그램. 솔루션에 추가. 대화상자 기반(TestApp)

 

5. 버튼추가하여 캡션 수정(Button1->Call GetModule())

:버튼을 클릭하면 GetModule()함수를 호출하도록.

 

6. stdafx.h파일에 선언

__declspec(dllimport!) BOOL WINAPI GetModulePath(CString& strPath);
#pragma comment(lib, "DllApp")

 

7. 버튼의 이벤트 핸들러 추가

void CTestAppDlg::OnBnClickedButton1()
{
 CString strPath = _T("");
 ::GetModulePath(strPath);

}

 

8."TestApp"를 시작프로젝트로 설정해주고, 빌드시 Appdll.lib파일을 찾을 수 없다는 오류가 나면,

DllApp프로젝트의 디버그 폴더에서 해당파일을 찾아 TestApp프로젝트 폴더로 복사하여 다시 빌드한다.

 

 

암시적링크/명시적링크


'PROGRAMMING > MFC(C++)' 카테고리의 다른 글

소켓 프로그래밍 주요함수  (0) 2011.04.27
[0426수업] Windows Service Process  (0) 2011.04.26
[0425수업] ClipBoard  (0) 2011.04.25
[0422수업] IPC통신  (0) 2011.04.22
[0422수업] 중복실행 방지  (0) 2011.04.22
Posted by 마마필로 :

기능생각...기능에 따른 구현함수나 흐름

외부에서 데이터가 들어올때 그 데이터를 담아놓을 공간을 만드는것이 제일먼저.

 

1. 대화상자 기반의 새 프로젝트 생성(Clipboard)

2. 버튼 두 개, 에디트 컨트롤 한 개 추가

   캡션 수정 Button1(Copy to clipboard), Button2(Read from clipboard)

3. 두 개의 버튼 이벤트 핸들러 추가

3.1 에디트컨트롤에 변수 추가(형식: value, 변수이름: m_strText)

3.2 Button1, Button2 코드 작성

 

**

데이터와 핸들을 받아올때는 무조건 예외처리 코드가 있어야 함!!!

 

void CClipboardDlg::OnBnClickedButton2()
{
 if(!::OpenClipboard(m_hWnd))   return;

 HGLOBAL hGlobalMemory;
 hGlobalMemory = ::GetClipboardData(CF_UNICODETEXT);
 ::EmptyClipboard(); // 다시 초기화 시키고 싶을 때 가장 적당한 위치. 바로 해주는게 좋음
 
 if (hGlobalMemory == NULL)
 {
  // ...
  return;
 }
 
 TCHAR* pszBuffer = (TCHAR*)::GlobalLock(hGlobalMemory);
 if(pszBuffer == NULL)
 {
   // ...
  return;
 }

 m_strText.Format(_T("%s"), pszBuffer);
 ::GlobalUnlock(hGlobalMemory);

 // ::EmptyClipboard();
 ::CloseClipboard();
 UpdateData(FALSE);

}

void CClipboardDlg::OnBnClickedButton1()
{
 UpdateData(TRUE);
 
 if (m_strText.GetLength() <= 0)
  return; // 에디트 컨트롤에 글자 있는지 검사. 없으면 굳이 열어서 쓸필요 없음

 HGLOBAL hGlobalMemory;
 TCHAR* pszBuffer=NULL;
 
 if (::OpenClipboard(m_hWnd) == FALSE)
 {
  AfxMessageBox(_T("ERROR : Failed to open clipboard"));
  return;
 }
 


 int nLength = m_strText.GetLength()*sizeof(TCHAR)+1;
 //문자열대신 이미지를 사용할때는 비트맵사이즈를 이용할 수 있다.
 
 // 가상메모리 공간에 nLength 길이만큼 공간을 할당받는다.
 hGlobalMemory = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, nLength);
 if (hGlobalMemory == NULL)
 {
  AfxMessageBox(_T("ERROR : Failed to allocate global memory"));
  return;
 }

 pszBuffer = (TCHAR*)::GlobalLock(hGlobalMemory);
 wsprintf(pszBuffer, _T("%s"), m_strText);
 ::GlobalUnlock(hGlobalMemory);


 ::EmptyClipboard(); //클립보드를 비워준다
 ::SetClipboardData(CF_UNICODETEXT, hGlobalMemory);
 ::CloseClipboard();

 
}


'PROGRAMMING > MFC(C++)' 카테고리의 다른 글

[0426수업] Windows Service Process  (0) 2011.04.26
[0425수업] dll파일 만들기  (0) 2011.04.25
[0422수업] IPC통신  (0) 2011.04.22
[0422수업] 중복실행 방지  (0) 2011.04.22
[0421수업] 소프트웨어 업데이트  (0) 2011.04.21
Posted by 마마필로 :

IPC통신 프로그램 작성 순서

 

1.공유메모리(반드시 Virtual Memory공간) 할당, 초기화

2. 문자열을 수신하는 이벤트 생성

3. 이벤트(신호)를 감시하는 스레드 생성

4. 수신한 문자열을 화면에 출력(사용자 정의 메시지 이용)

5. 문자열을 전송하는 부분을 뮤텍스 객체로 동기화

전송하는 쪽에서 스레드를 동기화 시켜야 하는 이유(뮤텍스를 이용하는 이유)

-> 전송하는 응용프로그램이 공유메모리를 쓰는 동안은 다른 프로세스가 접근하지 못하도록 막아두는 것

(동기화 되는 타이밍이 맞지 않을 수 있어 크리티컬 섹션보다는 커널 객체인 뮤택스를 사용한다)

 

 

1. 대화상자 기반의 새 프로젝트(SharedMem) 생성

 

2. 화면구성

버튼1, 리스트박스1, 에디트컨트롤1 추가

 

3. SharedMem.h파일에 객체 선언

class CSharedMemApp : public CWinApp
{
public:
 CSharedMemApp();

 HANDLE m_hMap;
 TCHAR* m_pSharedMemory;
 CMutex m_Mutex;

 CEvent m_ExitEvent;
 CEvent m_ReadEvent;

 

4. SharedMem.cpp에 객체 초기화

4.1 세 개의 커널 객체 초기화

CSharedMemApp::CSharedMemApp()
:m_Mutex(FALSE, TEXT("IPC_TEST_MUTEX")),        //뮤텍스객체 초기화, 이름지어줌
m_ExitEvent(FALSE, TRUE),

m_ReadEvent(FALSE, TRUE, _T("IPC_READ_SHAREDMEMORY"))    //이벤트객체 초기화, 이름지어줌

{

.

.

.

 

4.2 두개의 핸들과 TCHAR* 초기화

CSharedMemApp::CSharedMemApp()
:m_Mutex(FALSE, TEXT("IPC_TEST_MUTEX")),
m_ExitEvent(FALSE, TRUE),
m_ReadEvent(FALSE, TRUE, _T("IPC_READ_SHAREDMEMORY"))
{
     m_hMap                          =NULL;
     m_pSharedMemory           =NULL;

     // TODO: 여기에 생성 코드를 추가합니다.
     // InitInstance에 모든 중요한 초기화 작업을 배치합니다.
}

 

5. <공유메모리를 할당하고 초기화 하는 프로그램 작성> ->자체를 함수로 만들어서 불러오도록 할것임 (SharedMem.cpp에)

함수 만들기

BOOL CSharedMemApp::InitSharedMemory(void)

{

}

 

함수 만들고 바로 헤더파일에서 원형 선언해줌. 잊어버리지 않도록.

BOOL InitSharedMemory(void);

 

코드 작성

BOOL CSharedMemApp::InitSharedMemory(void)
{
     m_hMap   =::CreateFileMapping( INVALID_HANDLE_VALUE,
                                                    NULL,
                                                    PAGE_READWRITE,
                                                    0,
                                                    sizeof(TCHAR)*128,
                                                    _T("IPC_TEST_SHARED_MEMORY"));
     if (::GetLastError() != ERROR_ALREADY_EXISTS)
     {
          m_hMap = ::OpenFileMapping( FILE_MAP_ALL_ACCESS, FALSE,
             _T("IPC_TEST_SHARED_MEMORY")); //공유메모리에 파일공간을 만들어 줌
     }

 

     if (m_hMap == NULL)
     {
          AfxMessageBox(_T("ERROR : Failed to create(open) file mapping object!")); // 예외처리:공유메모리가 할당되지 않았을경우 에러메시지 출력
          return FALSE;
     }
 
     m_pSharedMemory = (TCHAR*)::MapViewOfFile( m_hMap,
                                                                          FILE_MAP_ALL_ACCESS,
                                                                          0,
                                                                          0,
                                                                          sizeof(TCHAR)*128);
     if (m_pSharedMemory == NULL)
     {
          AfxMessageBox(_T("ERROR : Failed to get shared memory!")); // 예외처리:공유메모리에 문자열이 없다면 파일생성 하지 않음
          return FALSE;
     }

     return TRUE;
}

 

함수 설명

CreateFileMapping()     // 가상메모리 커널모드 공간에 실제 값을 넣을 수 있는 공간을 만들어줌

pSharedMemory           // 문자를 가리킬 수있는 포인터

MapViewOfFile()          // 전체공간(sizeof(TCHAR)*128)안의 값을 전부다 가지고 오도록 함(통째로). 이 때 실제 값을 가져오는것이 아니라 포인터로 가리키도록 함. (들어가 있는 값만을 가지고 오고자 할 때는 for/while문을 사용하여 null문자를 만날때까지 반복해서 문자를 가져오도록 해야한다.)

 

6.

int CSharedMemApp::ExitInstance()
{
     if(m_pSharedMemory != NULL)  ::UnmapViewOfFile(m_pSharedMemory);
     if(m_hMap != NULL)                 ::CloseHandle(m_hMap);

 

     return CWinApp::ExitInstance();
}

 

7.

BOOL CSharedMemApp::InitInstance()
{
     if (!InitSharedMemory()) return FALSE;

.

.

.

 

<문자열을 수신할 수 있도록 이벤트 객체 생성하고 스레드 만들기>

1. App클래스에 스레드 함수 생성(스레드 함수는 되도록 마법사로...)

 

// 문자열 수신을 위한 이벤트 객체의 생성
UINT CSharedMemApp::ThreadReadSharedMemory(LPVOID pParam)
{
     return 0;
}     // 이벤트가 들어오는 것을 기다리는 함수를 작성(리스너)

 

// 문자열 수신을 위한 이벤트 객체의 생성
UINT CSharedMemApp::ThreadReadSharedMemory(LPVOID pParam)
{
     DWORD dwResult = WAIT_OBJECT_0+1;
     HANDLE arhList[2];
     arhList[0] = theApp.m_ExitEvent;
     arhList[1] = theApp.m_ReadEvent;

 

     while(dwResult == WAIT_OBJECT_0+1)
     {
          dwResult = ::WaitForMultipleObjects(2, arhList, FALSE, INFINITE);
          if(dwResult == WAIT_OBJECT_0)
          {break;}
          else if(dwResult == WAIT_OBJECT_0+1)
          {
               theApp.m_pMainWnd->PostMessage(UM_RECV_EVENT);
               ::Sleep(10);
          }
     }
     return 0;
}//ExitEvent가 나오면 빠져나오도록 조건을 준다

 

 

2. User Message작성(UM_RECV_EVENT), stdafx.h파일에

#define UM_RECV_EVENT WM_USER + 100

 

3.

BOOL CSharedMemApp::InitInstance()
{
     if (!InitSharedMemory()) return FALSE;
     AfxBeginThread(CSharedMemApp::ThreadReadSharedMemory, NULL);

.

.

.


 

<수신 받았을 때 화면에 출력. Dlg클래스>

1. Dlg클래스의 헤더파일에  선언

 

public:
 CListBox m_List;
 CString m_csMessage;

 

2. 특정 메시지에 대응하는 이벤트 처리기 직접 만듬(SharedMemDlg.cpp)

RESULT CSharedMemDlg::OnRecvEvent(WPARAM wParam, LPARAM lParam)
{
     m_List.InsertString(0, theApp.m_pSharedMemory);

     return 0;
}

 

3. 메시지 맵에 작성

BEGIN_MESSAGE_MAP(CSharedMemDlg, CDialog)
     ON_WM_SYSCOMMAND()
     ON_WM_PAINT()
     ON_WM_QUERYDRAGICON()
     //}}AFX_MSG_MAP
     ON_BN_CLICKED(IDC_BUTTON1, &CSharedMemDlg::OnBnClickedButton1)
     ON_MESSAGE(UM_RECV_EVENT, &CSharedMemDlg::OnRecvEvent)
END_MESSAGE_MAP()

 

 

헤더파일(SharedMemDlg.h)의 메시지 맵 함수에도 선언

 

     // 생성된 메시지 맵 함수
     virtual BOOL OnInitDialog();
     afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
     afx_msg void OnPaint();
     afx_msg HCURSOR OnQueryDragIcon();
     DECLARE_MESSAGE_MAP()
public:
     afx_msg LRESULT OnRecvEvent(WPARAM wParam, LPARAM lParam);
     afx_msg void OnBnClickedButton1(); 
};

 

<버튼 클릭 이벤트 처리기>

void CSharedMemDlg::OnBnClickedButton1()
{
     if(!UpdateData(TRUE) || m_csMessage.GetLength() <= 0)
      return;

 

     if(theApp.m_Mutex.Lock(1000))
     {
          CEvent EventSend(FALSE, TRUE, _T("IPC_READ_SHAREDMEMORY"));
          wsprintf(theApp.m_pSharedMemory, _T("%s"), m_csMessage);

          

         EventSend.SetEvent();
          ::Sleep(1);
  

         EventSend.ResetEvent();
          theApp.m_Mutex.Unlock();
     }
     else
     {
          AfxMessageBox(_T("ERROR : Lock() Function is return FALSE!!"));
     }
     m_csMessage = _T("");
     UpdateData(FALSE);
}

 

<아래코드 추가>

 

void CSharedMemDlg::DoDataExchange(CDataExchange* pDX)
{
     CDialog::DoDataExchange(pDX);
     DDX_Control(pDX, IDC_LIST1, m_List);
     DDX_Text(pDX, IDC_EDIT1, m_csMessage);
     DDV_MaxChars(pDX, m_csMessage, 64);
}


'PROGRAMMING > MFC(C++)' 카테고리의 다른 글

[0425수업] dll파일 만들기  (0) 2011.04.25
[0425수업] ClipBoard  (0) 2011.04.25
[0422수업] 중복실행 방지  (0) 2011.04.22
[0421수업] 소프트웨어 업데이트  (0) 2011.04.21
[0421수업] 세마포어  (0) 2011.04.21
Posted by 마마필로 :