win32api 서브클래싱, 전역클래싱, 슈퍼클래싱

프로그래밍 언어/WinApi 2020. 7. 27. 13:23

Tab(탭) 키로 각 차일드 윈도우간 포커스 이동을 하려고 했더니 클래싱을 사용해야할 것 같다.

 

# 서브클래싱

서브클래싱이란 도스의 인터럽트 가로채기와 개념적으로 동일하며 윈도우 프로시저로 전달되는 메시지를 중간에
가로채는 기법이다.

운영체제 -> 서브클래스 프로시저 -> 윈도우 프로시저

서브클래스 프로시저는 메시지를 먼저 받아서 다음의 세가지 방법으로 이 메시지를 처리할 수 있다.
① 통과 : 관심 없는 메시지는 바로 윈도우 프로시저로 넘겨버린다.
② 직접 처리 : 원하는 메시지가 왔을때 중간에 메시지를 처리하고 리턴. 윈도우 프로시저로는 전달되지않는다.
③ 변형 : 메시지를 받아서  원하는 변경 후 윈도우 프로시저로 보낸다. (키입력의 변경등)


새로운 윈도우 프로시저를 생성해서 아래 함수를 통해 변경할 대상 윈도우의 새로운 메시지 처리 함수를 지정한다.
원래번지=SetWindowLongPtr(변경할 대상윈도우,GWLP_WNDPROC,서브클래스 프로시저)서브 클래스 프로시저에서는 원하는 메시지 이외에는 다시 윈도우 프로시저로 넘겨줘야 하기때문에 관심없는
메시지에 대해서는 프로시저 마지막에 아래 함수를 사용하여 메시지를 다시 윈도우 프로시저로 넘겨주면된다.
LRESULT CallWindowProc(WNDPRCO lpPrevWndFunc,HWND hWnd,UINT Msg,WPARAM wParam,
LPARAM lParam);
메시지를 마음대로 변경할 수 있는 서브클래싱은 아주 유용하고도 강력한 기법임에 틀림없지만 그 이면에는 항상
잠재적인 위험성이 도사리고 있어 아주 조심스럽게 사용해야 한다. 꼭 필요하지 않는 한 서브클래싱은 하지 않는
것이 좋지만 불가피할 경우는 다음의 주의사항을 잘 숙지하고 안전하게 사용하자.

1. 윈도우의 원래 기능을 보존할 것.
2. 필요한 메시지를 제외한 메시지는 온전하게 윈도우 프로시저로 보낼 것.
3. 여분 메모리(cbWndExtra,cbClsExtra)를 절대로 건드리지 말것.
4. 여분 메모리가 필요한 경우 윈도우 프로퍼티를 사용할 것.

 

 

예제)

 

HWND hSub;        // 서브클래싱할 윈도우 핸들
WNDPROC wp_OldSubProc;        // 서브클래싱하기 전의 윈도우 프로시져 주소

 

 

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam) 
{ 
    ... 
    WNDCLASS WndClass;


    switch (iMessage)

    { 
    case WM_CREATE: 

 

    ...

 

        // 프로시저 변경

        wp_OldSubProc = (WNDPROC)SetWindowLong( hSub, GWL_WNDPROC, (LONG)Test1Proc );

 

    ...

 

        // 프로그램 종료시 다시 원래의 프로시져 주소로 교체 해준다.
        SetWindowLong( hSub, GWL_WNDPROC, (LONG)wp_OldSubProc );

    ...

}

 

 

LRESULT CALLBACK Test1Proc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    switch( iMessage )
    {
    case WM_KEYDOWN:
        break;
    }

    return CallWindowProc( wp_OldSubProc, hWnd, iMessage, wParam, lParam);
}




# 전역 서브클래싱

서브 클래싱도 시점과 방법, 그리고 효과가 미치는 범위에 따라 다음 두 가지로 분류된다.

■ 인스턴스 서브클래싱 : 특정한 하나의 윈도우 만을 대상으로 서브클래싱, SetWindowLongPtr 사용된다.

■ 전역 서브클래싱 : 윈도우 클래스에 대해 서브 클래싱, 이미 만들어진 윈도우에 대해서는 효과가 없으며 앞으로
  만들어질 윈도우만 영향을 받는다. SetClassLongPtr이 사용된다.


에디트를 생성하고 에디트를 대상으로 전역 서브 클래싱을 할 경우 에디트가 속한 "edit"윈도우 클래스가
서브 클래싱의 대상
이 되므로 서브 클래싱한 이 후 생겨나는 모든 edit는 서브 클래싱의 영향을 받게된다.

SetClassLongPtr(hEdit1,GCLP_WNDPROC,(LONG_PTR)OldEditProc);전역 클래싱은 윈도우 클래스 자체를 바꾸는 것이지만 다른 프로세스에는 영향을 주지 못한다.
시스템 전역 클래스에 정보는 시스템이 가지고 있지만 각 프로세스가 참조하는 클래스 정보는 응용프로그램이
시작될 때 시스템으로 전달받는 복사본일 뿐이다. 즉 서브클래싱은 같은 프로세스 내에서만 효력을 발휘한다.


# 슈퍼클래싱

슈퍼클래싱은 기존클래스(=베이스 클래스)의 정보를 바탕으로 하여 완전히 새로운 클래스를 만드는 것이다.
슈퍼클래싱에 의해 새로 만들어진 윈도우 클래스는 베이스 클래스와 동일하지만 여기에 변화를 주면 원하는대로
윈도우 클래스를 수정할 수 있다.

슈퍼클래싱의 핵심 함수는 GetClassInfo(Ex)함수이다.

BOOL GetClassInfo(HINSTANCE hInstance,LPCTSTR lpClassName,LPWNDCLASS lpWndClass);이 함수는 lpClassName 윈도우 클래스의 정보를 조사하여 lpWndClass가 가리키는 WNDCLASS 구조체에
채운다. 첫번 째 인수는 이 윈도우 클래스를 만든 응용프로그램의 인스턴스 핸들이되 버튼, 에디트 등의 시스템에
의해 만들어진 윈도우 클래스일 경우는 NULL을 주면 된다. 이 함수 호출에 의해 윈도우 클래스의 정보를 조사할
수 있는데 예를 들자면 GetClassInfo(NULL,"edit",&WndClass); 함수를 호출하면 "edit" 윈도우 클래스의 정보를
WndClass 구조체에 복사한다. 이 함수는 등록된 윈도우 클래스의 정보를 다시 돌려받으므로 RegisterClass함수의
반대 함수라고 생각하면 쉽다.

WndClass 구조체에 윈도우 클래스의 정보가 조사되었으므로 이 구조체의 정보를 바탕으로 적절히 수정한 후 다시
RegisterClass로 새로운 윈도우 클래스를 등록하는 것이 바로 슈퍼클래싱이다.

원본(GetClassInfo) ->  사본(수정) -> 새로등록(RegisterClass)

WndClass 구조체의 정보중 몇가지는 반드시 수정되어야 한다. 일단 클래스 이름이 베이스 클래스와 같아서는
안되므로 lpszClassName 멤버가 달라져야 한다. 또한 hInstance 멤버에는 이 윈도우 클래스를 등록하는 응용
프로그램의 인스턴스 핸들을 대입해야 하는데 그래야 이 인스턴스가 종료될 때 등록해제 된다. 그리고 가장 중요한
lpfnWndProc멤버를 수정하여 응용 프로그램이 정의한 윈도우 프로시저로 메시지를 보내도록 해야 할 것이다.
단 이때 기존의 윈도우 프로시저 번지는 복구를 위해 잘 보관해 두어야 한다.


[슈퍼 클래싱 예제]
LRESULT CALLBACK SuperProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    switch (iMessage)
    {
    ...
    }
    return CallWindowProc(OldEditProc,hWnd,iMessage,wParam,lParam);
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    ...
    WNDCLASS WndClass;


    switch (iMessage)

    {
    case WM_CREATE:
        GetClassInfo(NULL,TEXT("edit"),&WndClass); // "edit"클래스에 대한 정보 조사
        WndClass.hInstance=g_hInst;
        WndClass.lpszClassName=TEXT("SuperEdit"); // 새로운 이름 등록
        WndClass.hCursor=LoadCursor(NULL,IDC_WAIT); // 커서 변경
        OldEditProc=WndClass.lpfnWndProc; // 기존의 윈도우 프로시저 저장
        WndClass.lpfnWndProc=SuperProc; // 새로운 프로시저로 변경
        RegisterClass(&WndClass); // 수정한 윈도우 등록
        hEdit1=CreateWindow(TEXT("SuperEdit"),NULL,WS_CHILD | WS_VISIBLE |
            WS_BORDER | ES_AUTOHSCROLL,
            10,10,200,25,hWnd,(HMENU)ID_EDIT1,g_hInst,NULL);
        SetFocus(hEdit1);
        return 0;

    ...
}

 

서브 클래싱과 슈퍼클래싱은 둘다 사용목적이 비슷하며 상호 대체성이 있다. 어떤 방법을 쓸 것인가는 상황에
따라 다르겠지만 대체로 슈퍼클래싱이 더 안전하고 부릴 수 있는 기교도 더 많다.
특정 윈도우 하나만 수정할 때는 인스턴스 서브클래싱을 사용하고 다량의 윈도우를 한꺼번에 바꿀 때는 전역 또는
슈퍼클래싱을 사용하는 것이 편리하다.

 

 

 

 

참고 사이트 : http://egloos.zum.com/itbaby/v/4513218

참고 사이트 : http://blog.daum.net/creazier/15309359

설정

트랙백

댓글

Visual Studio 2019 win32api 아이콘 변경이 안된다

프로그래밍 언어/WinApi 2020. 7. 22. 13:32

리소스에 아이콘 추가 후,

 

메인 클래스에

 

wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_ICON1));

 

를 수정해도 탐색기에 표시되는 아이콘이 변경되지 않았다.

 

 

자료를 찾아도 MFC, C#은 나와있지만 win32api는 없었다.

아마 내부적으로 캐시된게 잘 처리가 안되지 않았을까... 라고 누가 그러더라..

아래와 같이 다시 불러와 주니 바뀌었다.

 

 

프로젝트에서 기존항목을 누르고 변경할 아이콘을 선택한 후에 컴파일을 하니 바뀌었다.

 

다시 해보니 안된다....

 

껐다 키고 뭔짓을 해야된다.....

설정

트랙백

댓글

Visual Studio 2019 sprintf 에러 끄기

프로그래밍 언어/WinApi 2020. 7. 22. 12:05

Visual Studio 2019에서 컴파일시, sprintf 를 사용할 수 없음으로 에러가 뜰 경우

 

심각도 코드 설명 프로젝트 파일 줄 비표시 오류(Suppression) 상태
오류 C4996 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. MP_works C:\Users\root\Documents\Visual Studio 2019\Projects\MP_works\MP_works\MP_works.cpp

 

 

 

 

아래와 같이 대처

---> 메뉴에 프로젝트 -> 속성 -> 왼쪽 메뉴에 구성속성 -> C/C++ -> 일반 -> SDL검사를 아니오로 변경

 

 

 

설정

트랙백

댓글