윈도우가 출력하는 사이즈 및 위치를 정의 할 수 있다.

 프로그램은 사각형을 만드는 방법을 총 2가지 방법이 있다.


 1. 두점을 알고 있을 경우.

 2. 한 점을 알고, 크기를 알고 있을 경우.


 1.은 사각형의 점으로 왼측 상단의 점과 우측 하단의 점 두개의 점을 알고 있다면, 쉽게 사각형을 만들 수 있다. (사각형을 그리는 프로그래밍을 할때, 사용한다.)


 2.는 기준이 되는 사각형의 점 1개를 만들고, 나머지는 넓이와 높이를 가지는 값을 가진다.


 1이든 2이든 모든 값은 4개씩 필요하다.(평면의 독립변수는 {x, y}로 2개이다.)


 윈도우 출력은 2.을 기준으로 작업한다.


 

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <stdlib.h>
 
#include <tchar.h>
#include <windows.h>
 
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
 
int WINAPI WinMain(HINSTANCE hInstance,
            HINSTANCE hPrevInstance,
            LPSTR lpszCmdParam,
            int nCmdShow)
{
    HWND hWnd;
    MSG Message;
    WNDCLASS WndClass;
    g_hInst = hInstance;
    
    //윈도우 클래스 초기화
    WndClass.cbClsExtra=0;
    WndClass.cbWndExtra=0;
    WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
    WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
    WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
    WndClass.hInstance=hInstance;
    WndClass.lpfnWndProc=(WNDPROC)WndProc;
    WndClass.lpszClassName="ApiBase";
    WndClass.lpszMenuName=NULL;
    WndClass.style=CS_HREDRAW|CS_VREDRAW;
 
    //윈도우 클래스 생성.
    RegisterClass(&WndClass);
    
    //윈도우 객체 생성.
    hWnd = CreateWindow("ApiBase",
                        "ApiBase",
                        WS_OVERLAPPEDWINDOW,
                        10,// X
                        100,// Y
                        400,// Width
                        400,// Height
                        NULL,
                        (HMENU)NULL,
                        hInstance,
                        NULL);
    
    //윈도우 창 띄우기.
    ShowWindow(hWnd,nCmdShow);
    
    while(GetMessage(&Message,0,0,0))
    {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    
    return Message.wParam;
}
 
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    long dwStyle;
    
    switch(iMessage)
    {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        case WM_LBUTTONDOWN:
            dwStyle = GetWindowLong(hWnd, GWL_STYLE);//현재 윈도우 스타일 받아들이기.
            dwStyle = dwStyle | WS_HSCROLL;
            SetWindowLong(hWnd, GWL_STYLE, dwStyle);
            break;
    }
    return DefWindowProc(hWnd,iMessage,wParam, lParam);
}
 
cs



 전과 다른 점은 기존 값을 가지고 처리하는 것이 아니라, 값을 정의해서 처리하는 것이다.


 X,Y, Width, Height로 나뉘고, 위치와 크기를 조절 할 수 있다.


 유동적으로 크기를 조절하는 경우는 다음과 같은 소스로 고치면 된다.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <stdlib.h>
 
#include <tchar.h>
#include <windows.h>
 
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
 
int WINAPI WinMain(HINSTANCE hInstance,
            HINSTANCE hPrevInstance,
            LPSTR lpszCmdParam,
            int nCmdShow)
{
    HWND hWnd;
    MSG Message;
    WNDCLASS WndClass;
    g_hInst = hInstance;
    
    //윈도우 클래스 초기화
    WndClass.cbClsExtra=0;
    WndClass.cbWndExtra=0;
    WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
    WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
    WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
    WndClass.hInstance=hInstance;
    WndClass.lpfnWndProc=(WNDPROC)WndProc;
    WndClass.lpszClassName="ApiBase";
    WndClass.lpszMenuName=NULL;
    WndClass.style=CS_HREDRAW|CS_VREDRAW;
 
    //윈도우 클래스 생성.
    RegisterClass(&WndClass);
    
    //윈도우 객체 생성.
    hWnd = CreateWindow("ApiBase",
                        "ApiBase",
                        WS_OVERLAPPEDWINDOW,
                        10,// X
                        100,// Y
                        400,// Width
                        400,// Height
                        NULL,
                        (HMENU)NULL,
                        hInstance,
                        NULL);
    
    //윈도우 창 띄우기.
    ShowWindow(hWnd,nCmdShow);
    
    while(GetMessage(&Message,0,0,0))
    {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    
    return Message.wParam;
}
 
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    long dwStyle;
    
    switch(iMessage)
    {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        case WM_LBUTTONDOWN:
            
            MoveWindow(hWnd, LOWORD(lParam),HIWORD(lParam), 5005001);
            
            break;
    }
    return DefWindowProc(hWnd,iMessage,wParam, lParam);
}
 
cs



 다음과 같이 하면, 클릭시 크기가 변하면서 이동할 것이다.


 lParam에서 마우스 클릭한 위치를 저장하고 있기 때문에, X와 Y가 들어가 있다.

 X, Y에 맞춰서 이동되기 때문에 클릭시 이동하는 현상이 있다.


 이 이동이 없길 원한다면, GetWindowRect()이라는 걸 이용해서 X, Y을 찾아 줘야한다.


 참조 : https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms633519.aspx


 Client(내부 윈도우)는 GetClientRect()을 이용해서 사용한다.

Posted by JunkMam
,

 Windows에서 Frame이 화면에 띄워지는 형태를 다양하게 정할 수 있다.


 이것을 Window Style이라고 하고, Style의 종류는 다음을 참조하면 알 수 있다.


 참조 : https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms632600(v=vs.85).aspx


 Windows의 스타일은 2가지로 개발 할 수 있는데, 첫번째로 Window 객체를 만들때 정의를 내릴 수 있다.


 객체를 만들때는 CreateWindow라는 함수를 사용하는데. 여기서 스타일을 적용할때, 저잘하는 방식을 bitdata형태로 저장한다.


 쉽게 말해서 이진법으로 되어있는 데이터들로 처리한다는 것이다.


 예을 들어서 이진수 4자리는 0000이 된다.

 여기서 첫번째 자리는 세로 스크롤바. 두번째 자리는 가로 스크롤바. 세번째 자리는 최소화 버튼 활성화. 네번째 자리는 타이틀바 존재 유무.


 이런식으로 각 자리에 대한 뜻이 존재하고, 0은 off/ 1은 on 이라는 식으로 관리하게 된다.


 이렇게 할 경우에 or 연산을 붙이게 되면 원하는 속성을 정할 수 있게 되기 때문에 개발의 편의성이 존재한다.


 맨 처음 글에서 사용되느 소스를 보면 알 수 가 있다.


1
2
3
4
5
6
7
8
9
10
11
    hWnd = CreateWindow("ApiBase",
                        "ApiBase",
                        WS_OVERLAPPEDWINDOW | WS_HSCROLL,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        NULL,
                        (HMENU)NULL,
                        hInstance,
                        NULL);
cs


 여기서 WS_OVERLAPPDWINDOW 것과 WS_HSCROLL이라는 스타일을 정할 수 있다.

 이렇게 해서 HSCROLL(수평 스크롤)을 정할 수 있게 되는 것으며, 결과로 스크롤 바가 나오는 것을 알 수 있다.


 하지만, 이 방법은 윈도우 객체가 만들어질때 사용하는 방법이다.


 그러면, 중간에 윈도우 스타일을 바꾸고 싶어지면, 어떻게 해야될까?


 Windows에서는 다음과 같은 함수를 지원해 준다.


 GetWindowLong(), SetWindowLong()


 이 두 함수는 WIndow에 대한 정보를 받아들이고/수정하는 용도로 사용하는 함수이다. 즉, 스타일만 수정하는 함수가 아니다.


 참조 :

 GetWindowLong -> https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms633584(v=vs.85).aspx


 SetWindowLong -> https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms633591(v=vs.85).aspx



 여기서 GWL_EXSTYLE라는 것을 SetWindowLong에 사용하게 되면, 스타일에 관련된 정보를 수정할 수 있게 된다.


 예제

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <stdlib.h>
 
#include <tchar.h>
#include <windows.h>
 
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
 
int WINAPI WinMain(HINSTANCE hInstance,
            HINSTANCE hPrevInstance,
            LPSTR lpszCmdParam,
            int nCmdShow)
{
    HWND hWnd;
    MSG Message;
    WNDCLASS WndClass;
    g_hInst = hInstance;
    
    //윈도우 클래스 초기화
    WndClass.cbClsExtra=0;
    WndClass.cbWndExtra=0;
    WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
    WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
    WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
    WndClass.hInstance=hInstance;
    WndClass.lpfnWndProc=(WNDPROC)WndProc;
    WndClass.lpszClassName="ApiBase";
    WndClass.lpszMenuName=NULL;
    WndClass.style=CS_HREDRAW|CS_VREDRAW;
 
    //윈도우 클래스 생성.
    RegisterClass(&WndClass);
    
    //윈도우 객체 생성.
    hWnd = CreateWindow("ApiBase",
                        "ApiBase",
                        WS_OVERLAPPEDWINDOW,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        NULL,
                        (HMENU)NULL,
                        hInstance,
                        NULL);
    
    //윈도우 창 띄우기.
    ShowWindow(hWnd,nCmdShow);
    
    while(GetMessage(&Message,0,0,0))
    {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    
    return Message.wParam;
}
 
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    long dwStyle;
    
    switch(iMessage)
    {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        case WM_LBUTTONDOWN:
            dwStyle = GetWindowLong(hWnd, GWL_STYLE);//현재 윈도우 스타일 받아들이기.
            dwStyle = dwStyle | WS_HSCROLL;
            SetWindowLong(hWnd, GWL_STYLE, dwStyle);
            break;
    }
    return DefWindowProc(hWnd,iMessage,wParam, lParam);
}
 
cs


이렇게 하면, 좌 클릭시 수평 스크롤이 완성되는걸 알 수 있다.

Posted by JunkMam
,

 윈도우 프로그래밍을 하는데 기본적으로 윈도우와 C언어의 차이점은 main 함수와 WinMain 함수를 사용하는것에서 차이가 있다. 라고 했었다.


 하지만, 차이점은 이것 뿐만 아니다.

 일반적으로 C언어에서는 while을 이용해서 프로그램의 키 입력을 항상 기다리고 있는 것이다.


 main.c 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    char data = 0;
    
    while(1)
    {
        data = getc(stdin);
        if(data == 'q')
        {
            break;
        }
        printf("%c", data);
    }
    
    return 0;
}
cs


 이렇게 하면 q을 받게 되면, 탈출하는 형태로 처리가 된다.


 이와 유사하게 윈도우에서도 메세지라는 장치를 이용해서 윈도우를 컨트롤한다.


전 소스를 보면, 다음과 같은 부분이 보일 것이다.


1
2
3
4
5
    while(GetMessage(&Message,0,0,0))
    {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
cs


 이 부분이 main.c에 적혀있는

 while(1) 부분과 동일하다고 볼 수 있다.

 GetMessage에는 Message에 대한 정보를 가지고 있고, 이 정보를 클래스에서 적용시킨

     WndClass.lpfnWndProc=(WNDPROC)WndProc;

 여기에 있는 WndProc라는 함수에 연결을 하도록 만들어 져 있다.


 WndProc은 운영체제에서 보내주는 이벤트를 받아서 해당 값을 처리하게 되어있다.


 여기서 이벤트란, 사용자가 컴퓨터에게 주는 행위들을 전부 다 이벤트라고 보면 된다.

 예을 들어서 키보드에 있는 키를 입력하는 것이나 마우스를 이동시키는 것 또한, 이벤트이다. 버튼을 누르는 것도 이벤트로 취급이 된다.

 이 이벤트를 개발자는 보고 원하는 형태로 작동하도록 만드는 것이고, 이것을 주고 받는 매개가 Message이다.


 이벤트에 관련된 정보를 WndProc에게 준 후에 WndProc에서 전부 다 컨트롤 하도록 되어 있다.


 그래서 이전 소스의 WndProc을 본다면,

 

1
2
3
4
5
6
7
8
9
10
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    switch(iMessage)
    {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
    }
    return DefWindowProc(hWnd,iMessage,wParam, lParam);
}
cs


 여기서 iMessage로 해당 이벤트의 정보를 받아 들이고, 해당 이벤트에 맞춰서 프로그램이 행동하도록 하게 된다.

 WM_DESTORY는 윈도우가 파괴 되었을 경우. 즉, 윈도우 창을 닫게 했을 경우를 뜻한다. PostQuitMessage()을 사용하여, 프로그램을 종료하는 메세지를 보낸다.

 종료하는 메세지를 받은 프로그램은 결국 전체적인 프로그램을 종료하는 작업을 하게 된다.

Posted by JunkMam
,

 Win32을 이용해서 프로그래밍을 하는 이유는 기본적으로 C언어를 어느 정도 할 줄안다는 조건이 필요하다.

 여기서 Windows에 나오는 일반 프로그램 처럼 콘솔 이상의 프로그램을 사용하기 위해서 Win32 API을 이용한 프로그램을 거친다.

 Win32 API을 이용하는 이유는 일단 MS쪽에서 제공하는 Window OS에 맞게 프로그램을 작성 할 수 있기 때문이다.

 일단, Win32 API 없이도 작성을 할 수 있다.

 문제는 Win32 API 이전에 나온 것들은 Windows을 분석하여 OS에서 동작하는 방법을 어셈블리와 C언어 등을 이용해서 작성을 했을 것이다.

 이런것은 만들기 매우 어려울 뿐만 아니라. 생산성이 떨어진다. 그러므로 MS 측에서 제공하는 API을 이용해서 프로그램을 작성하는 것이 이득이다.


 Win32 API 말고도 MFC라는게 있다.

 MFC는 Win32의 객체형식으로 만든 API라고 인식하면 쉽게 이해 될 것 같다.


 필자는 Win32 API을 이용한 프로그래밍 책을 보면서 이 글을 작성할 것이다.


 필자가 Win32 API의 공부를 여기에 기록하는 것은, 차후에 나 자신이 읽고 보기 위해서이다.


 Windows 프로그래밍과 일단 C언어(콘솔)의 프로그래밍의 차이가 있다.


 Windows 프로그래밍은 Window라는 우리가 일반적으로 보는 창이라는 프레임이 나오게되어 있고, 콘솔 프로그래밍은 프로그래밍을 처음 접하면 나오는 창.이 나오게 되어 있다.


 과거에는 콘솔창으로 돌아가는 형식이였다.(Windows 98 이전 사용자들은 대부분 알 것이다. MDir[일명; M]을 알면 대부분 알것이다.)


 하지만, 최근에는 GUI의 발달로 사용자들이 일반적으로 보는 프레임이 띄워지는 프로그램.이 만들어 졌다.


 Win32 API는 이 프레임이 띄워지는 프로그램을 쉽게 만들기 위해서 도움을 주는 라이브러리들이 모여있는 것들이며, 그게 없는 경우는 콘솔로 동작하는 것이다.


 윈도우에서 코딩이 차이가 생기게 되는데, 일반적으로 C언어로 프로그래밍을 한다면 다음과 같은 형식이 된다.


 일반적으로 C언어를 이용해서 작성할 경우. 

1
2
3
4
5
6
7
#include <stdio.h>
 
int main()
{
    printf("Hello World!\n");
    return 0;
}
cs


 이렇게 사용할 경우엔 stdio.h라는 콘솔 입출력 관련된 헤더 파일을 참조하여 링커에서 콘솔 입출력 관련된 라이브러리를 연결하여 콘솔 창에서 Hello World!\n가 출력되는 역할을 한다.


 하지만, Win32에서는 콘솔을 컨트롤하는 것이 아니기 때문에 GUI에 맞춰서 프레임 출력등을 설정을 해줘야 한다.


 Win32에서 프레임이 출력되기 위해서는 먼저 윈도우 클래스(윈도우 프레임의 상태를 결정하는 구조체)를 작성하고, 해당 클래스를 이용해서 객체를 생성.

 그것을 이용해서 프레임을 윈도우 창에 띄우는 형태를 취한다.


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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <stdlib.h>
 
#include <tchar.h>
#include <windows.h>
 
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
 
int WINAPI WinMain(HINSTANCE hInstance,
            HINSTANCE hPrevInstance,
            LPSTR lpszCmdParam,
            int nCmdShow)
{
    HWND hWnd;
    MSG Message;
    WNDCLASS WndClass;
    g_hInst = hInstance;
    
    //윈도우 클래스 초기화
    WndClass.cbClsExtra=0;
    WndClass.cbWndExtra=0;
    WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
    WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
    WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
    WndClass.hInstance=hInstance;
    WndClass.lpfnWndProc=(WNDPROC)WndProc;
    WndClass.lpszClassName="ApiBase";
    WndClass.lpszMenuName=NULL;
    WndClass.style=CS_HREDRAW|CS_VREDRAW;
 
    //윈도우 클래스 생성.
    RegisterClass(&WndClass);
    
    //윈도우 객체 생성.
    hWnd = CreateWindow("ApiBase",
                        "ApiBase",
                        WS_OVERLAPPEDWINDOW,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        NULL,
                        (HMENU)NULL,
                        hInstance,
                        NULL);
    
    //윈도우 창 띄우기.
    ShowWindow(hWnd,nCmdShow);
    
    while(GetMessage(&Message,0,0,0))
    {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    
    return Message.wParam;
}
 
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    switch(iMessage)
    {
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
    }
    return DefWindowProc(hWnd,iMessage,wParam, lParam);
}
cs


 이렇게 하면, 윈도우 창만 띄워진다.


 참고로 gcc로 컴파일을 할려면, 다음과 같은 라이브러리가 필요해진다.

 GDI32.lib


 이유는 GetStockObject라는 함수가 정의 되어 있는 곳이 GDI32에 정의가 되어 있기 때문이다.

 그리고 Windowgdi.h을 사용하는데, 이 windowgdi는 Window.h에 포함되어 있기 때문에 사용을 할 수 있다.[각주:1]


 

1
2
gcc window_ex00.c -lgdi32
 
cs


 이렇게 하면, 결과적으로 gdi을 참조해서 Window창이 띄워진다.

  1. https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd144925(v=vs.85).aspx(2016-06-12) [본문으로]
Posted by JunkMam
,

 동적 라이브러리의 특징은 해당 라이브러리를 파일로 따로 가지고 있게 만들어져 있다. 해당 라이브러리를 사용하는 프로그램이 실행하는 도중에 해당 파일을 열어서 참조하는 형태로 동적 라이브러리는 작동이 된다.


 쉽게 말해서 프로그램이 특정 기능을 하는 파일을 불러와서 사용하는 것이 동적 라이브러리가 돌아가는 방식이다.


 정적 라이브러리는 해당 프로그램에 링크 과정에서 추가되는 형식이라면, 동적 라이브러리는 해당 프로그램이 실행하는 도중에 호출하는 형식의 차이점을 갖는다.


 정적 라이브러리는 링크 과정에서 해당 프로그램에 기입되는 특징 때문에, 컴파일을 매번 해줘야 된다.

 결국 기능을 수정하게 되면 새로 작성해야된다는 점이. 유동성의 문제점을 가지게 된다.

 반면, 동적 라이브러리는 프로그램이 실행하는 도중에 호출해서 기능을 사용하기 때문에, 호출하는 파일이 변경이 되면 문제가 없다.

 호출 처리를 하는 것으로 파일이 알아서 처리가 되고, 이것은 곧 유동성의 증가가 생기게 된다.


 정적 라이브러리의 단점은 해당 파일에 삽입되는 과정이 있어야되지만, 동적 라이브러리는 해당 파일의 위치를 알게 한다면, 문제가 없어지게 된다.

 이것은 링크과정이 줄게 되는 것이며, 실행하는 도중에 메모리를 먹거나 해제등 복잡해져서 속도가 상대적으로 줄어드는 특징을 가지진다. 그것만 제외하면 제작 시간/개발자의 관리 등에 편의성을 제공한다.


 이전에 작성한 정적 라이브러리의 소스를 그대로 사용할 것이다.


 math.h

1
2
int sum(intint);
int diff(intint);
cs



 math.c

1
2
3
4
5
6
7
8
9
10
11
#include "math.h"
 
int sum(int a, int b)
{
    return a + b;
}
 
int diff(int a, int b)
{
    return a - b;
}
cs


 다음과 같은 명령어를 준다면, 동적 라이브러리가 완성이 된다.


1
gcc -shared -o libmath.so math.c
cs


 이렇게 하면, 동적 라이브러리가 완성이 된다.


 main.c

1
2
3
4
5
6
7
8
9
10
11
12
#include "math.h"
 
int main()
{
    int a = 10;
    int b = 40;
    
    printf("sum : %d\n",sum(a,b));
    printf("diff : %d\n",diff(a,b));
    
    return 0;
}
cs


기존에 있던 방식으로 이렇게 하면, 묵시적 방법으로 라이브러리가 연결이 되어야 된다.


1
gcc -o b main.c .\libmath.so
cs


 이 방법을 사용해서 동적 라이브러리를 연결할 수 있게 되어있다.[각주:1]


 이 외에도 동적 라이브러리는 파일 입출력으로 읽어들어서 기능을 사용할는 방법이 있으며(명시적 호출), 이 방법은 WIndows와 Linux에 따라서 바뀐다.


 Windows에서는 Win32 API을 사용해서 LoadLibrary라는 함수를 사용하게 된다.

 Linux에서는 dlopen이라는 함수가 있다.


 Windows 기준으로 작성을 한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
 
#include <windows.h>
 
int main()
{
    int a = 10;
    int b = 40;
    
    //명시적 연결을 위한 변수.
    HINSTANCE hInstDll;
    int(*sum)(intint);
    int(*diff)(int,int);
    
    hInstDll = LoadLibrary("libmath.so");
    
    sum = (int(*)(int,int))GetProcAddress(hInstDll,"sum");
    diff = (int (*)(intint))GetProcAddress(hInstDll,"diff");
    
    printf("sum : %d\n",(*sum)(a,b));
    printf("diff : %d\n",(*diff)(a,b));
    
    return 0;
}
cs


 여기서, HINSTANCE라는 것은 DLL의 정보를 받기 위해서 설정된 변수이다.

 이 후에 관련 함수의 정보를 가지고 와야하는데, 이 정보는 GetProcAddress을 이요해서 포인트 함수를 받아들여 처리한다.

 여기서, hInstDll가 없을 경우에 없다. 라는 형식으로 작동을 하게 만들 수 있게 된다.


 

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
#include <stdio.h>
 
#include <windows.h>
 
int main()
{
    int a = 10;
    int b = 40;
    
    //명시적 연결을 위한 변수.
    HINSTANCE hInstDll = NULL;
    int(*sum)(intint);
    int(*diff)(int,int);
    printf("%d\n", hInstDll);
    hInstDll = LoadLibrary("libmath.so");
    printf("%d\n", hInstDll);
    if(hInstDll!=NULL)
    {
        sum = (int(*)(int,int))GetProcAddress(hInstDll,"sum");
        diff = (int (*)(intint))GetProcAddress(hInstDll,"diff");
        
        printf("sum : %d\n",(*sum)(a,b));
        printf("diff : %d\n",(*diff)(a,b));
    }
    
    return 0;
}
cs


이렇게 해서 DLL로 문제 없이 작업이 가능해진다.

  1. 암시적으로 호출 한다. libmath.so가 없을 경우엔 제대로 작동이 되지 않는다. [본문으로]
Posted by JunkMam
,

 GCC을 이용한 정적 라이브러리(Static Library)을 만드는 방법을 사용한다.


 정적 라이브러리는 과거 개발자의 편의성을 위해서 작성되었다.

 먼저, 소스를 한 소스에 몰아서 정리하는 것에서 개발자는 차후에 유지 보수하기가 어려워진다는 문제점을 가지고 있게 되었다.

 그래서 필요한 기능만 가지고 있는 장치만 만들고, 이 장치를 호출하는 형태로 관리하는 것이다.

 다시 그 기능을 수정할려면, 그 부분을 수정한 후에 컴파일을 하면 되는 것이다.


 정적 라이브러리가 사용되는 위치는 링크 작업에서 하기 때문에, 소스를 받아서 컴파일하는 것 보다는 상대적으로 처리속도가 빨라진다.


 정적 라이브러리(Static Library)의 단점은 해당 라이브러리의 기능이 약간이라도 변경이 되면, 다시 작업을 해야되는 단점을 가진다.

 이러한 문제는 차후에 개발자의 유지/보수에서 큰 문제를 가지게 되었다.

 하나의 기능을 고치기 위해선 모든 소스를 컴파일 해야되는 문제점을 가지게 되었고, 이것은 곧 하나의 문제를 위해서 프로그램을 모두 다시 제작하는 과정이 있어야된다는 점이다.[각주:1][각주:2]


 정적 라이브러리를 작성할 예제이다.


 math.h

1
2
int sum(intint);
int diff(intint);
cs



 math.c

1
2
3
4
5
6
7
8
9
10
11
#include "math.h"
 
int sum(int a, int b)
{
    return a + b;
}
 
int diff(int a, int b)
{
    return a - b;
}
cs


 이렇게 한 후에 math.a(*.lib)을 작성하는 방법은 다음과 같다.

1
2
gcc -c math.c -o math.o
ar rc libmath.a math.o
cs


 이렇게 하면 libmath.lib이라는 정적 라이브러리 파일이 완성되게 된다.


 libmath.lib을 이용한다.


 사용하기 위한 예제.


 main.c

1
2
3
4
5
6
7
8
9
10
11
12
#include "math.h"
 
int main()
{
    int a = 10;
    int b = 40;
    
    printf("sum : %d\n",sum(a,b));
    printf("diff : %d\n",diff(a,b));
    
    return 0;
}
cs


 이렇게해서 사용을 할 수 있다.


 참고로 math.h에 있는 sum과 diff가 위에는 정의가 되어있지 않기 때문에 원래는 사용을 할 수 없을 것이다.(math.c가 없다고 가정한다.)

 하지만, libmath.a라는 파일이 있다면, 다음과 같은 방법으로 사용할 수 있게 된다.


1
gcc .\main.c -L./ -lmath
cs


 이렇게 하면, 정적 라이브러리를 사용해서 프로그램을 만들 수 있게 된다.'

 여기서 -L은 참조할 Library의 위치를 지정하는 것이고, -l은 lib과 .a을 제외한 라이브러리의 명칭을 뜻한다.

 만약 .lib이라면(libmath.lib), -llibmath 라는 형식을 취해야된다.


 정적 라이브러리를 이용해서 작성한 기능을 불러와서 사용하는 형태를 취하면 분할 컴파일을 할때 조금 더 효율적으로 작성을 할 수 있게 된다.


 참조 : http://www.joinc.co.kr/w/Site/C/Documents/Make_Library

  1. 이런 유연성 부족 문제에 의해서 정적라이브러리를 지양(피하는)한다. [본문으로]
  2. 유연성을 올리기 위해서 동적 라이브러리를 이용한다. [본문으로]
Posted by JunkMam
,

Windows MouseEvent 처리

연습 2016. 6. 13. 00:00

 마우스를 이동하는 것은 완료되었지만, 마우스를 클릭하는게 불가능하다.


 이걸 처리하기 위해서 검색한 것에서 mouse_event라는 함수등 다양한 방법으로 처리 방법을 설명한 사이트가 있다.


 http://www.dreamincode.net/forums/topic/8138-mouse-event/

 http://www.tipssoft.com/bulletin/board.php?bo_table=old_bbs&wr_id=186


 소스

 

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
#include <windows.h>
 
void ClickMouse(int parm_x, int parm_y, char parm_left_flag) 
  int x_pos = parm_x*65535/GetSystemMetrics(SM_CXSCREEN); 
  int y_pos = parm_y*65535/GetSystemMetrics(SM_CYSCREEN); 
 
  mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, x_pos, y_pos, 
                                            0, GetMessageExtraInfo()); 
  if(parm_left_flag){ 
      mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE, 
                              x_pos, y_pos, 0, GetMessageExtraInfo()); 
      mouse_event(MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE, 
                              x_pos, y_pos, 0, GetMessageExtraInfo()); 
  } else { 
      mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_ABSOLUTE, 
                              x_pos, y_pos, 0, GetMessageExtraInfo()); 
      mouse_event(MOUSEEVENTF_RIGHTUP | MOUSEEVENTF_ABSOLUTE, 
                              x_pos, y_pos, 0, GetMessageExtraInfo()); 
  } 
 
 
int main(int argc, char** argv) {
    
    ClickMouse(1000,1000,1);
    
    return (EXIT_SUCCESS);
}
 
cs


 이렇게 되어 있는 정보에서 mouse_event라는 것이 처리 되는 것을 알 수 있다.


 특히, mouse_event 다음에 매개변수가 정의가 되어 있다.


 void mouse_event( DWORD event, DWORD posX, DWORD posY, DWORD data, ULONG_PTR extra);


 이렇게 되어 있다.


 전에 설명한, 마우스 커서 조절하는 것과 지금과 차이는 다음과 같다.


 SetCursorPos는 절대 위치값만을 처리한다.

 쉽게 말해서 100, 100이라면, 화면의 기준으로 이동하게 된다.


 반면, mouse_event는 MOUSEEVENTF_MOVE만을 사용하면 상대적 위치로 이동시키는 등. 설정을 할 수 있다.(그리고 동시에 적용 할 수 있다.)


 그 외에도 절대위치를 처리하기 위해선, MOUSEEVENTF_ABSOLUTE을 같이 사용해야된다.

 그리고 같이 사용하는 대신에 화면 크기를 short형으로 잡고 작업한다.

 화면 최대치가, short 최대치가 되는 것이다.


 GetSystemMetrics는 모니터의 해상도를 받아들이는 함수이다.


 즉, 절대값을 사용할려면, 비율을 알아야된다는 뜻이된다.


 예을 들어서 화면의 해상도가 x축이 1900이라고 했을때, x값은 65535가 되는 것이다.


 더 자세한 정보는 MSND을 참조하면 될 것 같다.


 참조 : https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms646260(v=vs.85).aspx

'연습' 카테고리의 다른 글

GCC로 Dynamic Library 만들기.  (0) 2016.06.15
GCC로 Static Library 만들기.  (0) 2016.06.14
Windows Local Host IPv4 주소 찾기.  (0) 2016.06.12
마우스 커서 위치 조절하기.  (0) 2016.06.11
GAS로 플로피 BootLoader 만들기.  (0) 2016.06.10
Posted by JunkMam
,

 IPv4의 주소를 알아보는 방법을 얻기 위해서, popen을 사용하는 방법을 찾았었다.


 하지만, 이것이 생각보다 불편하다.(각 열을 분석해서 출력해야되는 문제점이 있다.


 그래서 구글링을 해보니, gethostbyname[각주:1]라는 함수가 있다는 걸 알게 되었다.


 예시 사이트들이 많이 있는데, 그 중에 하나의 소스를 사용하고자 한다.


 예시 사이트 : 

 http://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancednsrnr8e.html


 http://www.tenouk.com/Winsock/Winsock2example7.html


 http://stackoverflow.com/questions/26888853/ip-address-order-in-gethostbyname-function

 

 gethostbyname이라는 함수는 ipv4에 맞춰서 데이터를 가지는 구조체를 가지게 해주고, 해당 구조체에서 inet_ntoa 함수를 사용해서 십진수 3자리 4블럭으로 된 문자열을 출력한다.


 이걸 이용하면, 간단하게 현재 설정되어 있는 IPv4 값을 얻을 수 있게 되고, 이걸 이용해서 자동으로 ip을 생성하는 소스를 짤 수 있게 된다.


 다음은 예시 사이트에서 있는 답변 중 하나를 가지고 왔다.


 소스

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
#include <iostream>
#include <winsock.h>
 
#pragma comment(lib, "Ws2_32.lib")
 
int main()
{
    char hostname[255];
    struct hostent *he;
    struct in_addr **addr_list;
 
    WSAData data;
    WSAStartup(MAKEWORD(22), &data);
 
    gethostname(hostname, 255);
    std::cout << "Host name: " << hostname << std::endl;
 
    if ((he = gethostbyname(hostname)) == NULL) {
        std::cout << "gethostbyname error" << std::endl;
    } else {
        std::cout << "IP addresses: "  << std::endl;
        addr_list = (struct in_addr **)he->h_addr_list;
        for(int i = 0; addr_list[i] != NULL; i++) {
            std::cout << inet_ntoa(*addr_list[i]) << std::endl;
        }
    }
    
    return 0;
}
cs


 예시를 보면, 알겠지만, ws2_32.lib을 사용해줘야 한다.

 만약, #pragma comment(lib, "Ws2_32.lib")을 넣지 않으면, 사용자가 일일히 lib을 연결시켜 줘야 한다.

 예제

 gcc -o winsock winsock.c -lws2_32


 이렇게 사용하는 것이다.

  1. https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms738524(v=vs.85).aspx(2016-06-06) [본문으로]
Posted by JunkMam
,

 윈도우에서 마우스 커서 이동시키는 작업이 필요할 것 같아서 구글링을 해보았다.[각주:1][각주:2][각주:3][각주:4]


 먼저 알아야 되는 구조체는 다음과 같다.


 typedef struct tagPOINT {

   LONG x;

   LONG y;

 } POINT, *LPPOINT;


 이것은 position의 점을 정의 내리는 것으로 2차원 평면에서의 독립 변수 x, y를 가지는 구조체이다.

 마우스 커서 위치 또한 평면 위에 존재하는 것이기 때문에, POINT라는 값을 가진다.


 원본 함수.

 BOOL WINAPI GetCursorPos(

   _Out_ LPPOINT lpPoint

 );


 _Out_LPPPOINT라는 것은 POINT를 변수로 가지는 point 구조체 매개변수이다.

 여기에 값을 LPPOINT라는 걸로 가지기 때문에, POINT로 값을 가지고 올 수 있다.


 마우스 커서의 위치를 이동을 하도록 만드는 방법이 있다.


 마우스의 위치를 자동으로 할 수 있다.


 원본 함수.

 BOOL WINAPI SetCursorPos(

   _In_ int X,

   _In_ int Y

 );


 이것들은 전부다 정의한 것으로

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
#include <stdio.h>
 
#include <conio.h>
 
#include <windows.h>
 
int main()
{
    POINT p;
    
    printf("마우스 위치를 100, 100으로 이동시킵니다.\n");
    
    SetCursorPos(100,100);
    
    do{
        GetCursorPos(&p);
        printf("마우스 위치 (x, y) : (%d, %d)\n",p.x, p.y);
        if(kbhit())
        {
            break;
        }
    }while(1);
    
    return 0;
}
cs


 이런 소스를 만들면, 100, 100으로 이동한 후에 현재 마우스 위치를 파악할 수 있게 된다.

  1. http://breadlab.net/165 (2016-06-06) [본문으로]
  2. https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms648394(v=vs.85).aspx (2016-06-06) [본문으로]
  3. https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms648390(v=vs.85).aspx (2016-06-06) [본문으로]
  4. https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms648383(v=vs.85).aspx (2016-06-06) [본문으로]
Posted by JunkMam
,

 GAS란, GNU ASsembler 라는 약자로 GNU 측에서 만든 어셈블러이다.


 OS을 만들어 보자. 라는 책들이나 인터넷 강의들을 보면, 보통 설명해주는 곳에서는 대부분 NASM을 이용하는 방법을 설명이 되어있다.


 필자는 GAS로도 가능한 것으로 기억하고 있기 때문에(NASM 만들어지기 전에도 대부분 만들어 냈으니.) 이걸 알아보기 위해서 구글링을 하니. 답을 찾아서 기록을 위해서 글을 작성한다.


 찾아낸 사이트는 외국에서 질문을 답변해준 사이트에서 적혀 있다.

 http://stackoverflow.com/questions/12650291/how-to-locate-a-variable-correctly-in-att-assembly[각주:1]


 찾아낸 사이트에는 GAS만 사용할 수 있는게 아니다.

 해당 사이트에서 설명되어있는 소스 사이트를 보면, 다음과 같이 설명이 되어 있다.


 GAS로 object 파일을 만든다.

 objcopy로 object 파일을 binaray 파일로 출력하도록 만든다.


 이렇게 사용하면, NASM을 사용하지 않아도 된다.


 GAS로만으로 만들 수 없는 이유는 다음과 같다.


 GAS는 Object 파일을 만드는 것이다. 여기서 binary 형태로 설정하는디 PE 파일로 설정하는지 ELF 형태로 하고자하는지 설정하는게 따로 있지 않다.

 그러므로 Object 파일을 Copy하는 도중에 binary 파일 형태로 변환시켜서 올리게 만드는 형태를 취한다.


 

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
.text
.global     _start
.code16
 
_start:
movw    %cs, %ax
movw    %ax, %ds
movw    %ax, %es
call    hello
jmp     .
 
.org    0x10
 
hello:
movw    $message, %ax
movw    %ax, %bp
movw    $13, %cx
movw    $0x1301, %ax
movw    $0x000c, %bx
movb    $0, %dl
int     $0x10
ret
 
message:    
.ascii  "Hello, World!"
 
.org    0x01fe
.byte   0x55
.byte   0xaa
cs



 이렇게 해서 나오는 값이 이상이 없는데, .fill이라는 방법과 .org라는 방법 2가지가 있는걸 확인 되었다.

 .fill을 사용해서 만들었었는데, fill은 유동적인 값을 사용해선 안된다. 라는 오류가 뜨면서, 제대로 작동이 안된다.

 그렇기 때문에, 유사한 명령어를 찾고 있었다. 구글링을 하다가. 드디어 .org라는 명령어를 찾게되었다.

 다음부터는 .org을 사용하는 것을 해야겠다.


boot.img

boot.o

boot.s


  1. 검색 일자 : (2016-06-04) [본문으로]
Posted by JunkMam
,