기본 콘텐츠로 건너뛰기

MFC ActiveX 컨트롤: Windows 컨트롤 서브클래싱

MFC ActiveX 컨트롤: Windows 컨트롤 서브클래싱

이 문서에서는 공통되는 Windows 컨트롤을 서브클래싱하여 ActiveX 컨트롤을 만드는 과정을 보여 줍니다. 기존 Windows 컨트롤을 서브클래싱하면 ActiveX 컨트롤을 손쉽게 개발할 수 있습니다. 새 컨트롤은 서브클래싱된 Windows 컨트롤의 그리기 및 마우스 클릭에 대한 응답과 같은 기능을 갖게 됩니다. MFC ActiveX 컨트롤 BUTTON 샘플은 Windows 컨트롤의 서브클래싱 예제입니다.
Windows 컨트롤을 서브클래싱하려면 다음 작업을 수행합니다.
컨트롤 서브클래싱에 대한 자세한 내용은 Knowledge Base article Q243454를 참조하십시오.

IsSubclassedControl 및 PreCreateWindow 재정의

PreCreateWindowIsSubclassedControl을 재정의하려면 해당 컨트롤 클래스 선언의 protected 섹션에 다음 코드 줄을 추가합니다.
BOOL PreCreateWindow( CREATESTRUCT& cs );
BOOL IsSubclassedControl( );
컨트롤 구현 파일(.CPP)에는 다음의 코드 줄을 추가하여 두 개의 재정의된 함수를 구현합니다.
BOOL CSampleCtrl::PreCreateWindow( CREATESTRUCT& cs )
{
    cs.lpszClass = _T("BUTTON");
    return COleControl::PreCreateWindow(cs);
}
BOOL CSampleCtrl::IsSubclassedControl( )
{
    return TRUE;
}
이 예제의 PreCreateWindow에는 Windows 단추 컨트롤이 지정되어 있지만 모든 표준 Windows 컨트롤을 서브클래싱할 수 있습니다.
표준 Windows 컨트롤에 대한 자세한 내용은 컨트롤을 참조하십시오.
Windows 컨트롤을 서브클래싱할 경우에는 컨트롤의 창을 만들 때 특정 창 스타일(WS_) 또는 확장 창 스타일(WS_EX_) 플래그가 사용되도록 지정할 수 있습니다.
PreCreateWindow 멤버 함수에서 cs.stylecs.dwExStyle 구조체 필드를 수정하면 이러한 매개 변수의 값을 설정할 수 있습니다.
이 필드를 수정할 때 COleControl 클래스에 의해 설정된 기본 플래그를 유지하려면 OR 연산을 사용해야 합니다.
예를 들어, 컨트롤이 BUTTON 컨트롤을 서브클래싱하는 경우 해당 컨트롤이 확인란으로 나타나도록 하려면 CSampleCtrl::PreCreateWindow 구현의 return 문 앞에 다음 코드 줄을 삽입합니다.
cs.style |= BS_CHECKBOX;
이 연산을 사용하면 COleControl 클래스의 기본 스타일 플래그(WS_CHILD)는 그대로 유지되면서 BS_CHECKBOX 스타일 플래그가 추가됩니다.

OnDraw 멤버 함수 수정

서브클래싱된 컨트롤이 해당 Windows 컨트롤과 동일한 모양을 유지하도록 하려면 해당 컨트롤의 OnDraw 멤버 함수에는 다음과 같이 DoSuperclassPaint 멤버 함수에 대한 호출만 들어 있어야 합니다.
void CSampleCtrl::OnDraw( CDC* pdc, const CRect& rcBounds,
    const CRect& rcInvalid )
{
    DoSuperclassPaint( pdc, rcBounds );
}
COleControl에 의해 구현되는 DoSuperclassPaint 멤버 함수는 Windows 컨트롤의 창 프로시저를 사용하여 특정 디바이스 컨텍스트의 사각형 내에 컨트롤을 그립니다.
이렇게 하면 컨트롤이 활성화되어 있지 않을 때에도 해당 컨트롤을 볼 수 있습니다.
참고   DoSuperclassPaint 멤버 함수는 디바이스 컨텍스트를 WM_PAINT 메시지의 wParam으로 전달할 수 있는 컨트롤 형식에 대해서만 작동합니다. 이러한 컨트롤 형식에는 SCROLLBARBUTTON 같은 일부 표준 Windows 컨트롤과 모든 공용 컨트롤이 포함됩니다. 이 동작이 지원되지 않는 컨트롤의 경우에는 비활성화된 컨트롤이 올바르게 표시될 수 있도록 하는 코드를 사용자가 직접 작성해야 합니다.

리플렉션된 창 메시지 처리

일반적으로 Windows 컨트롤은 특정한 창 메시지를 해당 부모 창에 보냅니다.
WM_COMMAND와 같은 이러한 메시지 중 일부는 사용자가 수행한 동작을 알립니다. 또한 WM_CTLCOLOR 같은 다른 메시지들은 부모 창에서 정보를 가져오는 데 사용됩니다.
그러나 대부분의 경우 ActiveX 컨트롤은 이와는 다른 방법으로 부모 창과 통신합니다.
즉, 알림은 이벤트를 발생시킴으로써 전달되고(이벤트 알림 보내기) 컨트롤 컨테이너에 대한 정보는 해당 컨테이너의 앰비언트 속성에 액세스하여 가져올 수 있습니다.
이러한 통신 방식을 사용할 수 있기 때문에 ActiveX 컨트롤 컨테이너는 대개 컨트롤에 의해 전달된 창 메시지를 처리하지 않습니다.
컨테이너가 서브클래싱된 Windows 컨트롤이 보낸 창 메시지를 받지 않도록 하기 위해 COleControl은 해당 컨트롤의 부모 역할을 하는 추가 창을 만듭니다.
"리플렉터"라고 하는 이 추가 창은 Windows 컨트롤을 서브클래싱하는 ActiveX 컨트롤에 대해서만 만들어지며 해당 컨트롤 창과 크기 및 위치가 같습니다.
리플렉터 창은 특정 창 메시지를 가로챈 다음 해당 컨트롤에 다시 보냅니다.
이렇게 하면 해당 컨트롤이 자체 창 프로시저에서 이벤트 발생과 같은 ActiveX 컨트롤에 대해 적합한 동작을 수행하여 리플렉션된 메시지를 처리할 수 있게 됩니다.
차단되는 창 메시지 및 이 메시지에 대응하는 리플렉팅된 메시지의 목록은 리플렉션된 창 메시지 ID를 참조하십시오.
ActiveX 컨트롤 컨테이너가 스스로 메시지 리플렉션을 수행하도록 디자인되는 경우도 있습니다.
이 경우에는 COleControl에서 리플렉터 창을 만들지 않아도 되므로 서브클래싱된 Windows 컨트롤의 런타임 오버헤드를 줄일 수 있습니다.
COleControl은 MessageReflect 값이 TRUE인지 여부를 검사하여 해당 컨테이너가 앰비언트 MessageReflect 속성 기능을 지원하는지 확인합니다.
리플렉션된 창 메시지를 처리하려면 해당 컨트롤 메시지 맵에 엔트리를 추가하고 처리기 함수를 구현해야 합니다.
리플렉션된 메시지는 Windows에서 정의하는 표준 메시지 집합에 포함되지 않기 때문에 클래스 뷰에서는 이러한 메시지 처리기를 추가할 수 없습니다.
하지만 처리기를 직접 추가하는 것은 어렵지 않습니다.
리플렉션된 창 메시지에 대한 메시지 처리기를 직접 추가하려면 다음 작업을 수행합니다.
  • 해당 컨트롤 클래스의 .H 파일에서 처리기 함수를 선언합니다.
    이 함수의 반환 형식은 LRESULT여야 하며 WPARAM 형식 및 LPARAM 형식의 두 매개 변수가 있어야 합니다.
    다음 코드에서는 이러한 예를 보여 줍니다.
    class CSampleCtrl : public COleControl
    {
    protected:
       LRESULT OnOcmCommand( WPARAM wParam, LPARAM lParam );
    ...
    }
  • 해당 컨트롤 클래스 .CPP 파일의 메시지 맵에 ON_MESSAGE 엔트리를 추가합니다.
    이 엔트리의 매개 변수로 해당 처리기 함수의 메시지 식별자와 이름을 사용해야 합니다.
    다음 코드에서는 이러한 예를 보여 줍니다.
    BEGIN_MESSAGE_MAP(CSampleCtrl, COleControl)
       ...
       ON_MESSAGE(OCM_COMMAND, OnOcmCommand)
       ...
    END_MESSAGE_MAP()
  • 또한 .CPP 파일에 OnOcmCommand 멤버 함수를 구현하여 리플렉션된 메시지를 처리하도록 합니다. wParamlParam 매개 변수는 원본 창 메시지의 매개 변수와 같아야 합니다.
리플렉팅된 메시지 처리 방법의 예제는 MFC ActiveX 컨트롤 BUTTON 샘플을 참조하십시오. 이 샘플에서는 BN_CLICKED 알림 코드를 감지하면 Click 이벤트를 발생시켜 이를 보내 응답하는 OnOcmCommand 처리기 함수를 예를 들어 설명합니다.

참고 항목

MFC ActiveX 컨트롤

댓글

이 블로그의 인기 게시물

80040154 오류로 인해 CLSID가 {xxxx-...}인 구성 요소의 COM 클래스 팩터리를 검색하지 못했습니다.

원문보기 .NET 으로 만든 응용프로그램에서 com 객체를 호출한 경우 Windows7 64bit 에서 제목과 같은 에러가 발생했다. Win32 COM 과 .NET 프로그램간의 호환성 때문에 생긴 문제였다. 원인은 .NET 실행시 JIT 컴파일러에 의해 최적화된 기계어로 변환되기 때문.. Win32 COM은 컴파일시.. Win32 COM에 맞춰 빌드 속성에서 하위버전으로 맞춰 컴파일을 다시하는 방법도 있지만 메인 프로젝트가 .NET이라면 참조되는 모든 프로젝트를 다 바꿔야할 노릇.. 또 다른 방법은 COM+를 이용하여 독립적으로 만드는 것이다. 분리시키는 방법은 아래 주소해서 확인할 수 있다. http://support.microsoft.com/kb/281335 나의 경우는 Win32 COM DLL을 64비트 .NET 프로그램에서 참조하니 COM 객체를 제대로 호출하지 못하였습니다. 그래서 .NET 프로그램의 Target Machine을 x86으로 설정하니 제대로 COM 객체를 호출하였습니다.

[Pyinstaller] 실행 파일 관리자 권한 획득하기

고객사에서 일부 사용자에게서 프로그램 오류가 발생한다며 아래와 같이 에러 캡처를 보내왔습니다. 프로그램에서 로그를 남기기 위해 로그 파일을 생성하는데 권한의 문제로 로그 파일을 생성하지 못해 프로그램 오류가 발생한 것 같습니다. 처음에는 Python 코드에서 관리자 권한을 요청하는 코드를 넣으려고 했는데, 실제로 Stackoverflow를 찾아보면 이런 내용이 나옵니다. 프로그램이 관리자 권한으로 실행되지 않았다면 관리자 권한으로 다시 프로그램을 실행시키는 코드입니다. import os import sys import win32com.shell.shell as shell ASADMIN = 'asadmin' if sys.argv[-1] != ASADMIN: script = os.path.abspath(sys.argv[0]) params = ' '.join([script] + sys.argv[1:] + [ASADMIN]) shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params) sys.exit(0) 하지만 개인적으로 이런 방식은 마음에 들지 않았고 조금 더 찾아보니 Pyinstaller로 exe 파일을 만들 때 옵션을 설정하여 관리자 권한을 요청하도록 할 수 있다고 합니다. --uac-admin을 옵션에 추가하면 프로그램 실행 시 관리자 권한을 요청할 수 있습니다. pyinstaller.exe --uac-admin sample.py 하지만 안타깝게도 이 방식은 원하는 대로 동작하지 않았습니다. 마지막으로 manifest 파일을 이용하여 시도해보았습니다. spec 파일을 이용하여 pyinstaller로 빌드하면 <실행 파일 이름>.manifest 라는 파일이 생성됩니다. 파일에서 아랫부분을 찾아볼 수 있습니다. <security> <re

초간단 프로그램 락 걸기

프로그램에 락을 걸 일이 생겨났다. 하드웨어 락을 걸면 쉬울텐데 그 정도는 아니고 프로그램의 실행 날짜를 제한 해 달라고 한다. 그래서 파일(license.lic)을 가지고 락을 걸리고 결정을 했다. 요구 사항은 아래와 같다. 1. license.lic 파일이 없으면 프로그램을 실행 할수 없게 한다. 2. 지정한 날짜를 넘어서는 프로그램을 실행 할수 없게 한다. 3. 사용자가 시스템 날짜를 되돌렸을때 인식하여 프로그램을 실행 할수 없게 한다. 음.... 1.번 문제는 사용자가 프로그램을 실행하기 위해서 license.lic 파일을 받아야만 한다. license.lic 파일에는 최근 실행 날짜/종료날짜 이런식으로 적도록 한다.(물론 내용은 암호화 한다.) 최근 실행날짜는 프로그램이 실행때마다 업데이트 하도록 하고 시스템 날짜와 비교하여 시스템 날짜가 최근 실행 날짜보다 이전의 날짜면 시스템 날짜를 되돌렸다고 인식하도록 한다.(3.번 문제 해결) 시스템 날짜와 종료 날짜를 비교하여 시스템 날짜가 종료 날짜를 넘으면 프로그램을 실행 할수 없도록 한다.(2.번 문제 해결)