달력

52025  이전 다음

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

'프로그래밍/API 프로그래밍'에 해당되는 글 24건

  1. 2008.10.17 두 프로그램간 통신!!
  2. 2008.10.17 api 가장 기본적인 소스 입니다.
  3. 2008.10.16 모달리스형 대화 상자
  4. 2008.10.16 대화상자


// A 프로그램..
#include <windows.h>
#include <stdio.h>

// 두 프로그램간 통신하기 위한 사용자 정의 메세지
#define WM_ADD WM_APP + 100
void main()
{
 HWND hwnd = FindWindow( 0, "B");

 if ( hwnd == 0 )
 {
  printf("B 를 먼저 실행해 주세요\n");
  return ;
 }
 //----------------------------------
 int sum = 0;

 // 블럭킹 함수 , 동기화 함수
// sum = SendMessage(hwnd, WM_ADD, 10, 20 );

 // 비동기 전달.
 BOOL b = PostMessage( hwnd, WM_ADD, 10, 20);

 printf("전송 완료 : %d\n", sum );
}

//////////////////////////////////////////////////////////////////////

//B프로그램

#pragma comment(linker, "/subsystem:windows")

#include <windows.h>

#define WM_ADD  WM_APP + 100

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
 switch( msg )
 { 
 // A 가 보낸 메세지
 case WM_ADD:
  {
   char s[256];
   wsprintf( s, "WM_ADD 도착 : %d, %d", wParam, lParam);

   MessageBox( 0, s, "", MB_OK);

   return wParam + lParam; // SendMessage()의 리턴값이 된다.
  }
      // B를 먼저 실행하고 A를 실행해 보세요.

 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 }
 return DefWindowProc( hwnd, msg, wParam, lParam);
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
     LPSTR   lpCmdLine, int nShowCmd )
{
 ATOM atom;
 WNDCLASS wc;
 HWND hwnd;
 MSG msg;
 
 wc.cbClsExtra = 0;
 wc.cbWndExtra = 0;
 wc.hbrBackground= (HBRUSH)GetStockObject( WHITE_BRUSH );
 wc.hCursor  = LoadCursor( 0, IDC_ARROW );
 wc.hIcon  = LoadIcon( 0, IDI_APPLICATION);
 wc.hInstance = hInstance;
 wc.lpfnWndProc  = WndProc;
 wc.lpszClassName= "First";
 wc.lpszMenuName = 0;
 wc.style  = 0;

 atom = RegisterClass( &wc);
 
 if ( atom == 0 )
 {
  MessageBox( 0, "Fail To RegisterClass", "Error", MB_OK);
  return 0;
 }

 hwnd = CreateWindowEx( 0, "first", "B", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT,0, 0, 0,
        hInstance, 0);
 ShowWindow( hwnd, nShowCmd);
 UpdateWindow( hwnd );

 while ( GetMessage( &msg, 0, 0, 0) )
 {       
  TranslateMessage(&msg);
  DispatchMessage( &msg);
 }

 return 0;
}



Posted by CokeBell
|


#include <windows.h>


LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
 switch( msg )
 {
 case WM_LBUTTONDOWN:
  return 0;
 case WM_RBUTTONDOWN:
  return 0;
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 }
 return DefWindowProc(hwnd, msg, wParam, lParam);
}


int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
       LPSTR lpCmdLine, int nShowCmd)
{
 // 1. 윈도우 클래스 만들기
 WNDCLASS wc;
 wc.cbWndExtra   = 0;
 wc.cbClsExtra   = 0;
 wc.hbrBackground  = (HBRUSH)GetStockObject(WHITE_BRUSH);
 wc.hCursor    = LoadCursor(0, IDC_ARROW);
 wc.hIcon    = LoadIcon(0, IDI_APPLICATION);
 wc.hInstance   = hInstance;
 wc.lpfnWndProc   = WndProc;   // DefWindowProc;
 wc.lpszClassName  = "First";
 wc.lpszMenuName   = 0;
 wc.style    = 0;

 // 2. 등록(레지스트리에)
 RegisterClass(&wc);

 // 3. 윈도우 창 만들기
 HWND hwnd = CreateWindowEx( 0,     // WS_EX_TOPMOST
     "first",    // 클래스 명
     "Hello",    // 캡션바 내용
     WS_OVERLAPPEDWINDOW,
     CW_USEDEFAULT , 0, CW_USEDEFAULT, 0, // 초기 위치
     0, 0,   // 부모 윈도우 핸들, 메뉴 핸들
     hInstance,  // WinMain의 1번째 파라미터 (exe 주소)
     0);   // 생성 인자

 

 // 4. 윈도우 보여주기
 ShowWindow(hwnd, SW_SHOW);
 UpdateWindow(hwnd);
 

 // 5. Message
 MSG msg;
 while( GetMessage( &msg, 0, 0, 0 ) )
 {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }
 return 0;
}

Posted by CokeBell
|

모델리스형은 대화상자를 열어놓은 채로 메인 윈도우를 조작할 수 있기 때문에 모달형 대화상자보다는 더 복잡하며 사용하기도 어렵다. 골치 아픈 문제가 발생할 수도 있으며 게다가 대화상자를 닫지 않은 채로 값을 변경하고 메인 윈도우가 변경된 값을 즉각 인지할 수 있도록 해 주어야 하는 부담까지 지니고 있다. 다음 단계를 따라 InfoDlg 프로젝트를 모델리스형으로 변경해 보자.


1일단 Mless라는 이름으로 프로젝트를 만들고 이 프로젝트를 InfoDlg 프로젝트와 완전히 동일한 기능을 가지도록 만들어 보자. Mless 프로젝트를 만든 후 Mless 디렉토리로 InfoDlg.cpp 소스 파일과 InfoDlg.rc, resource.h 파일을 복사한다. 그리고 InfoDlg.*를 Mless.*로 이름을 변경한 후 프로젝트에 포함시킨다. Mless.cpp의 선두에 있는 다음 한줄만 변경하고 나머지는 그대로 둔다.

LPSTR lpszClass="Mless";

이 프로젝트를 컴파일하여 실행시키면 InfoDlg.exe와 완전히 동일한(타이틀 바만 다르다) 예제가 만들어졌을 것이다. 이 예제를 변형하여 모델리스형 대화상자를 만들어 볼 것이다.


2모델리스형 대화상자를 만들기 위해서는 우선 대화상자를 호출하는 DialogBox 함수를 없애야 한다. 이 함수는 모달형의 대화상자를 만들고 실행시키는 모든 일을 다 하며 대화상자를 종료하기 전에는 리턴하지 않는 특성을 가지고 있으므로 이 함수를 사용해서는 모델리스형 대화상자를 만들 수 없다. 대화상자를 호출하는 WM_LBUTTONDOWN 메시지를 다음과 같이 수정한다.


case WM_LBUTTONDOWN:
	if (!IsWindow(hDlg)) {
	hDlg=CreateDialog(g_hInst,MAKEINTRESOURCE(IDD_DIALOG1),
			hWnd,MlessDlgProc);
		ShowWindow(hDlg,SW_SHOW);
	}
	return 0;

DialogBox 함수 대신 CreateDialog 함수를 사용하여 대화상자를 만들기만 하였다. CreateDialog 함수는 DialogBox 함수와 동일한 인수를 사용하되 대화상자를 만들기만 하며 만든 즉시 대화상자의 핸들값을 리턴한다. 이에 비해 DialogBox 함수는 대화상자를 만드는 것은 물론 대화상자를 운용하는 모든 일을 주관하며 대화상자가 닫힐 때까지 리턴하지 않는다. CreateDialog 함수가 대화상자를 만든 직후 바로 종료하기 때문에 모델리스형 대화상자가 되는 것이다. 대화상자 프로시저의 이름은 MlessDlgProc으로 변경하였다.


대화상자를 만든 후 곧바로 ShowWindow 함수를 호출하여 대화상자가 화면으로 나타나도록 하였다. 대화상자에 WS_VISIBLE 스타일을 주었다면 ShowWindow 함수는 호출해주지 않아도 상관없지만 디폴트로 대화상자는 WS_VISIBLE 스타일을 가지지 않으므로 ShowWindow 함수를 호출해 주어야 한다. 이 함수 호출을 생략해 버리면 대화상자가 만들어지기만 할 뿐 화면으로는 보이지 않게 된다.


모델리스형 대화상자를 만들 때 특히 주의해야 할 점은 같은 대화상자가 두번 만들어지지 않도록 해야 한다는 것이다. 모달형은 대화상자를 닫기 전에는 메인 윈도우를 조작할 수 없으므로 그런 문제가 발생하지 않지만 모델리스형은 대화상자가 열린채로 메인 윈도우에 명령을 내릴 수 있다. 대화상자가 열려있는 상태에서 또 마우스 왼쪽 버튼을 누르면 대화상자가 두 개 만들어지게 되는 기현상이 발생하게 되며 이는 골치 아픈 문제의 원인이 된다. 그래서 WM_LBUTTONDOWN 메시지에서는 대화상자를 만들기 전에 hDlg, 즉 대화상자의 윈도우 핸들을 조사해 보고 이 핸들이 유효한 윈도우 핸들인지를 먼저 조사해 본다.


BOOL IsWindow( HWND hWnd );

IsWindow 함수는 hWnd가 유효한 윈도우 핸들인지 조사해 보고 윈도우가 존재하면 TRUE를 리턴하고 그렇지 않으면 FALSE를 리턴한다. 그래서 hDlg가 존재하지 않은 경우에 한해서만 대화상자를 만들도록 하여 대화상자를 두번 만들지 않도록 한다. 대화상자의 존재 여부를 핸들의 유효성으로 판별하므로 hDlg 변수는 전역으로 선언해야 한다.

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass="Mless";
int x;
int y;
char str[128];
HWND hDlg;

3모달형의 대화상자는 OK, Cancel 버튼 두 개가 주로 사용된다. OK는 입력한 값들을 받아들인다는 뜻이며 Cancel은 입력값을 버리라는 명령이되 두 버튼 모두 대화상자를 종료시키는 것은 동일하다. 그런데 모델리스형 대화상자에는 항상 열려있기 때문에 OK, Cancel 버튼이 논리적으로 잘 어울리지 않는다. 이 버튼보다는 대화상자를 열어놓은 채로 값을 실제로 변경시키는 Change 버튼과 대화상자를 닫는 Close 버튼이 모델리스형에 더 적합하다. 대화상자 편집기를 열어 두 버튼의 ID와 캡션을 다음과 같이 변경한다.


버튼 ID Caption
OK ID_CHANGE Change
Cancel ID_CLOSE Close
 

4CreateDialog나 DialogBox 등의 함수들처럼 첫번째 인수로 인스턴스 핸들을 요구하는 함수들이 많이 있다. 그런데 프로그램의 인스턴스 핸들은 WinMain의 인수로 전달되는 지역변수이기 때문에 WinMain 외부의 다른 함수에서는 사용할 수 없다. 그래서 보통 g_hInst 나 glohInstance 등의 전역 변수를 선언하고 WinMain에서 이 전역변수에 인스턴스 핸들값을 대입해 주는 방법을 많이 사용한다. InfoDlg 예제에서도 이 방법을 사용하였다.


이와 마찬가지로 메인 윈도우의 핸들도 여러 곳에서 사용되는데 WinMain이나 WndProc 이외의 함수에서는 이 핸들값을 알 수 없다. 그래서 메인 윈도우의 핸들도 인스턴스 핸들과 마찬가지로 별도의 전역 변수에 대입해 두는 방법을 많이 사용한다. hMainWnd라는 이름의 전역 변수를 만들고 WinMain에서 윈도우를 만든 직후에 이 변수에 메인 윈도우의 핸들을 대입해 두도록 하자.

HWND hMainWnd;

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
		  ,LPSTR lpszCmdParam,int nCmdShow)
{
	HWND hWnd;
	MSG Message;
	WNDCLASS WndClass;
	g_hInst=hInstance;
	................
	
	hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
		  CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
		  NULL,(HMENU)NULL,hInstance,NULL);
	ShowWindow(hWnd,nCmdShow);
	hMainWnd=hWnd;

이 핸들값은 잠시 후 MlessDlgProc에서 사용된다.


5마지막으로 InfoDlgProc 함수를 MlessDlgProc으로 이름을 바꾼 후 코드를 약간 수정한다. 변경한 후의 코드는 다음과 같다. 어디가 어떻게 변경되었는지 InfoDlgProc의 경우와 비교해 보도록 하자.


BOOL CALLBACK MlessDlgProc(HWND hDlg,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	switch(iMessage) {
	case WM_INITDIALOG:
		SetDlgItemText(hDlg,IDC_STR,str);
		SetDlgItemInt(hDlg,IDC_X,x,FALSE);
		SetDlgItemInt(hDlg,IDC_Y,y,FALSE);
		return TRUE;
	case WM_COMMAND:
		switch (wParam) {
		case ID_CHANGE:
			GetDlgItemText(hDlg,IDC_STR,str,128);
			x=GetDlgItemInt(hDlg,IDC_X,NULL,FALSE);
			y=GetDlgItemInt(hDlg,IDC_Y,NULL,FALSE);
			InvalidateRect(hMainWnd,NULL,TRUE);
			return TRUE;
		case ID_CLOSE:
			DestroyWindow(hDlg);
			hDlg=NULL;
			return TRUE;
		}
		break;
	}
return FALSE;
}

WM_INITDIALOG 메시지 처리 부분은 전혀 변경되지 않았으며 WM_COMMAND 메시지 처리 부분이 많이 변경되었다. 우선 ID_CHANGE 버튼이 눌러진 경우를 보면 대화상자 컨트롤에서 값을 읽어 전역 변수에 대입해주는 부분은 동일하되 대화상자를 종료하는 EndDialog 함수가 사라졌다. 모델리스형이므로 값만 변경할 뿐 대화상자를 종료할 필요는 없기 때문이다. 대신 변경된 값을 메인 윈도우에 즉각 반영하기 위해 InvalidateRect 함수를 호출하여 메인 윈도우 전체를 무효화시켰다.


ID_CLOSE가 눌러진 경우에는 대화상자를 파괴한다. 대화상자를 만들 때 CreateDialog 함수를 사용했으므로 대화상자를 닫을 때는 DestroyWindow 함수를 사용해야 한다. 대화상자를 파괴한 후 대화상자의 윈도우 핸들인 hDlg에 NULL을 대입하여 다시 대화상자를 만들수 있도록 해 주어야 한다.

그럼 이제 프로그램을 컴파일하여 실행시켜 보자. 실행 중의 모습은 다음과 같다.



대화상자를 열어놓고 메인 윈도우의 크기를 변경하거나 위치를 옮길 수 있다. 만약 메인 윈도우에 메뉴나 툴바 등이 있다면 선택할 수도 있을 것이다.

Posted by CokeBell
|

대화상자는 사용자에게 값을 보여주거나 또는 값을 입력받는 장치이다. 그러므로 대화상자와 부모 윈도우간에 정보를 교환할 수 있는 방법이 필요하다. 그래야 대화상자가 열릴 때 보여주고자 하는 정보를 컨트롤에 출력할 수 있으며 또한 대화상자가 닫힐 때 사용자가 입력한 값을 다시 돌려 받을 수가 있다. 대화상자와 부모 윈도우간에 교환할 수 있는 정보의 종류는 크게 문자열과 정수형 두가지가 있다. 우선 문자열 값을 교환하는 함수에는 컨트롤로부터 문자열을 읽는 함수와 컨트롤로 문자열을 출력하는 함수가 있다.


UINT GetDlgItemText( HWND hDlg, int nIDDlgItem, LPTSTR lpString, int nMaxCount );
BOOL SetDlgItemText( HWND hDlg, int nIDDlgItem, LPCTSTR lpString );


 

첫번째 인수는 대화상자의 윈도우 핸들이며 두번째 인수 nIDDlgItem은 값을 읽거나 쓸 컨트롤의 ID이다. lpString은 대입하고자 하는 문자열, 또는 문자열을 읽을 버퍼이며 문자열을 읽을 때는 버퍼의 길이를 nMaxCount로 명시해 주어야 한다. 정수를 교환하는 함수도 이와 유사하다.


UINT GetDlgItemInt( HWND hDlg, int nIDDlgItem, BOOL *lpTranslated, BOOL bSigned );
BOOL SetDlgItemInt( HWND hDlg, int nIDDlgItem, UINT uValue, BOOL bSigned );


 

첫번째, 두번째 인수는 문자열의 경우와 동일하다. GetDlgItemInt는 해당 컨트롤에 입력된 정수값을 읽어 리턴값으로 둘려주되 네번째 인수 bSigned가 TRUE일 경우는 부호있는 정수값을 읽어주며 FALSE일 경우는 부호를 무시하고 무조건 양수로 읽어준다. 그런데 컨트롤로부터 정수형값을 읽어들일 때는 항상 에러가 발생할 소지가 있다. 예를 들어 에디트에 입력된 정수를 읽어들일 경우 에디트에 숫자 이외의 문자가 있거나 숫자가 너무 클 경우 등이다. 이럴 경우 GetDlgItemInt는 세번째 인수로 지정된 BOOL형 포인터에 에러가 있었는지 없었는지를 대입해준다. 에러 검사를 할 필요가 없을 때는 세번째 인수로 NULL값을 주면 된다.


정수값을 대입하는 SetDlgItemInt는 지정한 컨트롤에 nValue 정수값을 대입해 준다. 잠시 후에 실습을 통해 이 함수들을 사용해 보도록 하자.

Posted by CokeBell
|