반응형
BOOL InvalidateRect(HWND hWnd, CONST RECT *lpRect, BOOL bErase); 
▶hWnd:무효 영역을 설정할 윈도우의 핸들. NULL일 경우 모든 윈도우를 무효화한다.
▶lpRect:무효화할 영역. NULL이면 작업 영역 전체가 무효화된다.
▶bErase:무효 영역의 배경을 먼저 지울 것인가를 지정한다. TRUE이면 BeginPaint 함수가 배경을 먼저 지운 후 작업 영역을 그린다.

설명:WM_PAINT를 직접 호출하지는 않지만 특정 영역을 무효화하여 시스템이 WM_PAINT를 호출
시스템은 무효화 영역을 캐취하면 WM_PAINT를 호출한다. 직접 호출하지 않으니
시스템이 message queue에 있는 다른 메세지를 처리 후 WM_PAINT를 호출하므로 딜레이 발생.
참고: http://www.winapi.co.kr/reference/Function/InvalidateRect.htm

BOOL UpdateWindow(HWND hWnd);
▶ hWnd : 윈도우 핸들
윈도우 프로시저로 WM_PAINT 메시지를 보내 작업영역을 강제로 그리도록 한다.
다른 어떤 메시지보다 WM_PAINT를 먼저 처리해야 할 경우 이 함수를 호출하여 즉시 처리.
(메시지 큐를 통하지 않고 호출하므로 적용이 빠르다.)
작업영역을 완전히 다시 즉시 그리려면 InvalidateRect함수로 작업영역 무효화 후 이 함수 호출
참고: http://www.winapi.co.kr/reference/Function/UpdateWindow.htm
반응형

__declspec(extended-decl-modifier-seq) : 데이터나 모듈의 저장형태를 지정.

 

align(#)

    자주 사용되는 데이터를 특정 프로세서의 캐시라인 크기로 정렬해 캐시성능을 높인다. #값은

    1부터 8192 (바이트) 까지 2의 거듭제곱 수 즉 2, 4, 8, 16, 32, 64.

    Visual C++은 기본적으로 데이터 크기에 따라 경계정렬을 한다. 가령 4바이트 정수는 4바이트

    경계, 8바이트 더블은 8바이트 경계에 정렬되며, 클래스나 구조체의 데이터들은 현재 패킹 설정

    (#pragma pack(n) 또는 /Zp 옵션)에 따라 클래스나 구조체 내에 최소크기로 정렬된다.

    구조체, 공용체, 클래스, 변수에 대해 사용할 수 있지만, 스택변수에는 사용불가.

 

    #define CACHE_LINE      32

    #define CACHE_ALIGN    __declspec(align(CACHE_LINE))

    struct CACHE_ALIGN S1

    {

        int a, b, c, d;                // 이 타입의 모든 인스턴스는 32바이트 경계에서 시작해야하며,

    };                                    // sizeof(struct S1)는 32. 뒤따르는 16바이트는 채우기용

   

    __declspec(align(8)) struct S2

    {

        int a, b, c, d;               // sizeof(struct S2)는 16

    };

 

    struct S3

    {

        int a;                          // 4바이트 뒤를 잇는 28 바이트가 채우기용으로 쓰이며,

        struct S1 s1;              // s1은 오프셋 32에서 시작. sizeof(struct S3)는 64

    };

 

    // 배열의 시작주소는 32바이트로 정렬되지만, 각 배열멤버는 그렇지 않다.

    CACHE_ALIGN int array[128];

    // 배열의 각 멤버를 정렬하는 예

    typedef CACHE_ALIGN struct { int a; } S4;

    S4 array[10];

 

    TLS(Thread Local Storage : 정적변수가 스레드마다 다른 값을 갖도록 윈도우에서 제공

    하는 메모리 영역)에서 데이터 정렬

    __declspec(thread) 속성으로 만들어지고, 이미지에서 .tls 섹션에 놓인 TLS는 일반적인 정적

    데이터와 똑같이 정렬된다. 운영체제는 .tls 섹션 크기만큼 데이터를 할당하고 .tls 섹션 정렬

    속성을 고려해 TLS 데이터를 만든다.

 

    아래는 TLS에 정렬된 데이터를 놓는 다양한 방법들이다.

    __declspec(thread) __declspec(align(32)) int a;    // TLS에 정렬된 정수 놓기

 

    // 정렬된 구조체를 정의하고 구조체 타입의 변수를 TLS에 놓기

    __declspec(thread) __declspec(align(32)) struct F1 { int a; int b; } a;

 

    이터 패킹과 함께 정렬하는 방식

    /Zp 옵션과 #pragma pack(n)은 구조체와 공용체 멤버 데이터를 패킹하는 효과가 있다.

    /Zp 옵션과 __declspec(align(#))을 같이 사용한 예와 각 멤버의 오프셋

 

    struct S{                                // 변수        /Zp1    /Zp2    /Zp4    /Zp8

        char a;                               //   a            0          0         0         0

        short b;                              //   b           1          2         2         2

        double c;                            //   c            3          4         4         4

        CACHE_ALIGN double d;    //   d           32        32       32       32

        char e;                               //   e           40        40       40        40

        double f;                            //    f           41        42       44        48

    };                                           //sizeof(S)  64        64       64        64

 

    따라서 객체의 오프셋은 객체가 __declspec(align(#)) 속성을 갖지 않으면, 이전 객체의

    오프셋과 현재 패킹 설정에 따라 결정되며, 그렇지 않으면 이전 객체의 오프셋과 객체의

    __declspec(align(#)) 값에 따라 결정된다.



allocate(segname)

    데이터가 할당될 데이터 세그먼트 지정. 가능한 segname pragma는

    code_seg, const_seg, data_seg, init_seg, section

 

    ㄱ. #pragma code_seg( ["섹션이름"[, "섹션클래스"]] )

    함수가 할당될 기본 코드섹션 지정. 클래스와 섹션이름은 선택적이며, 섹션이름이 없는 경우

    컴파일 시작시 값으로 재설정된다

 

    ㄴ. #pragma const_seg( ["섹션이름"[, "섹션클래스"]] )

    상수데이터의 기본 섹션 지정. 모든 상수데이터를 하나의 읽기전용 섹션에 넣을 때 사용

    #pragma const_seg("MY_DATA")    // 이후의 모든 상수데이터는 MY_DATA에 놓여짐

    const_seg pragma로 할당된 데이터는 어떤 위치정보도 유지하지 않는다.

    "섹션클래스"는 Visual C+ 2.0 이전 버전과의 호환을 위해 포함되며, 이제는 무시된다

 

    ㄷ. #pragma data_seg( ["섹션이름"[, "섹션클래스"]] )

    초기화된 데이터의 기본 섹션 지정

    #pragma data_seg("MY_DATA")    // 이후의 모든 데이터는 MY_DATA에 놓여짐

    data_seg pragma로 할당된 데이터는 어떤 위치정보도 유지하지 않는다.

    "섹션클래스"는 Visual C++ 2.0 이전 버전과의 호환을 위해 포함되며, 이제는 무시된다

 

    ㄹ. #pragma init_seg( {compiler | lib | user | "섹션이름"[, 함수이름]} )

    시작코드가 실행되는 순서에 영향을 주는 키워드나 코드섹션 지정. 전역 정적 객체의 초기화는

    실행코드를 포함할 수 있으므로, 언제 객체가 생성되는지를 정의하는 키워드를 지정해야한다.

    특히 이는 초기화가 필요한 라이브러리 또는 DLL에서 init_seg pragma 사용시 중요하다.

    compiler

             Microsoft C 런타임 라이브러리 초기화를 위해 예약됨. 이 그룹의 객체가 먼저 생성됨

    lib

             타사의 클래스 라이브러리 초기화. 이 그룹의 객체는 compiler 그룹 바로 뒤에 생성됨

    user

             그 외. 이 그룹의 객체는 마지막에 생성됨

    섹션이름

             초기화 섹션을 명시적으로 지정. 사용자지정된 섹션이름의 객체는 암묵적으로 생성되지

             않지만 그 주소는 명명된 섹션에 놓여진다.

             섹션이름은 pragma 이후의 모듈에 선언된 전역 객체를 생성할 도우미 함수에 대한

             포인터를 포함한다.

    함수이름

             프로그램 종료시 atexit 대신에 호출될 함수 지정. 또한 이 도우미 함수는 전역객체용

             파괴자에 대한 포인터를 갖고 atexit 또한 호출한다. 다음처럼 함수 식별자를 지정하면,

             int myexit( void (__cdecl *pf) (void) )

             C 런타임 라이브러리인 atexit 대신 사용자 함수가 호출된다. 이로써 객체를 파괴할 준비

             가 됐을 때 호출되어야 할 파괴자 목록을 만들 수 있다.

 

    초기화를 연기해야 한다면(DLL의 경우에), 섹션이름을 명시적으로 지정할 수 있다.

    atexit 대체 식별자는 인용부호가 없으며, 여전히 사용자 객체는 XXX_seg pragma로 정의된

    섹션에 놓일 수 있다.

    모듈에 선언된 객체는 C 런타임에 의해 자동으로 초기화되지 않으므로, 직접 해주어야 한다.

    #include <stdio.h>

    // init_seg pragma는 허용되지 않은 섹션이름을 사용하므로 C4075 경고(초기자가 허용되지

    // 않은 영역에 놓인다)를 발생시키는데 이를 무시

    #pragma warning(disable : 4075)

 

    typedef void (__cdecl *PF)(void);

    int    cxpf = 0;     // 호출해야 할 파괴자 수

    PF    pfx[200];    // 파괴자에 대한 포인터. 오버플로우에 주의!

 

    int myexit(PF pf){

        pfx[cxpf++] = pf;

        return 0;

    }

 

    struct A{

        A(){ puts("A()"); }

        ~A(){ puts("~A()"; }

    }

 

    // pragma init_seg 이전이므로, 생성자와 파괴자는 CRT 시작코드에서 호출

    A aaaa;

 

    // 여기서 순서가 중요하며, 섹션이름은 8자 이하다.

    // $ 이전에 동일한 이름의 섹션은 하나의 섹션으로 합쳐진다. 합쳐지는 순서는 $ 이후 문자를

    // 정렬함으로써 결정된다.

    // InitSegStart와 InitSegEnd는 경계를 설정하는데 사용되며, 이로써 초기화를 위해 호출할

    // 실제 함수를 찾을 수 있다.

 

    #pragma data_seg(".mine$a")

    PF InitSegStart = (PF)1;

    #pragma data_seg(".mine$z")

    PF InitSegEnd = (PF)1;

    #pragma data_seg()

 

    // 0값 비교는 중요하다.

    // 지금부터, 각 섹션은 256바이트다. 각 섹션이 통합될 때, 각각은 0으로 채워진다.

    // 섹션이 256바이트인것은 보장할 수 없지만, 0으로 채워지는 건 보장할 수 있다.

 

    void InitializeObjects(){

        PF *x = &InitSegStart;

        for(++x; x<&InitSegEnd; ++x){

            if(*x) (*x)();

        }

    }

 

    void DestroyObjects(){

        while(cxpf>0){

            --cxpf;

            (pfx[cxpf])();

        }

    }

 

    #pragma init_seg(".mine$m", myexit)

 

    // 지금부터 생성자와 파괴자를 호출한다

    A bbbb;

    A cccc;

 

    int main(){

        InitializeObjects();

 

        // 여기서 잡다한 작업을 한다

 

        DestroyObjects();

        return 0;

    }

dllimport, dllexport

    DLL에서(로) 함수, 데이터, 객체를 가져오는(내보내는) 속성. 함수를 dllexport로 선언하면,

    적어도 노출된 함수와 관련된 .DEF (module-definition) 파일이 필요없어진다.

    또한 dllexport__export 키워드를 대체한다.

 

    __declspec( dllimport ) int i;

    __declspec( dllexport ) void func();

 

    좀 더 보기좋게 다음과 같이 매크로를 쓸 수도 있겠다.

 

    #define DllImport __declspec( dllimport )

    #define DllExport __declspec( dllexport )

 

    DLL 인터페이스에 포함된 모든 선언은 선언을 내포하고 있는 dllimport 또는 정의를 내포하고

    있는 dllexport이며, externdllexport를 같이 사용해 선언을 강제화할 수도 있다.

 

    DllImport int func(){ return 1; }    // 에러 : dllimport는 정의할 수 없다

    DllImport int i = 10;                       // 요것도 정의이므로 에러

    DllExport int i = 10;                       // 요건 OK

 

    extern DllImport int k;                   // 둘다 OK

    DllImport int j;

 

    static __declspec( dllimport ) int l;          // 에러. extern으로 선언되지 않음

    void func()

    {

        static __declspec( dllimport ) int s;      // 에러. extern으로 선언되지 않음

        __declspec( dllimport ) int m;              // OK. 이것은 선언

        __declspec( dllexport ) int n;               // 에러. 지역범위에서 외부정의를 포함

        extern __declspec( dllimport ) int i;     // OK. 이것은 선언

        extern __declspec( dllexport ) int k;    // OK. extern은 선언을 포함

        __declspec( dllexport ) int x = 5;         // 에러. 지역범위에서 외부정의를 포함

    }

 

    라인함수로 선언하기

 

    dllexport 함수를 인라인으로 선언하면, 모듈이 그 함수를 참조하던말던 함수는 항상 초기화

    되고 익스포트된다. 함수는 다른 프로그램에 의해 임포트된다고 가정한다.

    dllimport 함수를 인라인으로 선언하면, 그 함수는 확장될 수 있지만 (/Ob가 지정돼 있을 때)

    절대 초기화되지는 않는다. 특히 인라인 임포트 함수의 주소를 가지면, DLL 내의 함수 주소가

    리턴된다. 이는 인라인이 아닌 임포트 함수의 주소를 가져오는 것과 같은 방식이다.

    이 규칙은 클래스 안에서 정의된 인라인 함수에 적용된다. 게다가, 인라인 함수 내 정적 지역

    데이터와 문자열은 마치 같은 프로그램 (즉, DLL 인터페이스가 없는 실행 파일) 안에 있는 것

    처럼 DLL과 클라이언트 사이에 동일성을 유지한다.

    임포트된 인라인 함수는 주의깊게 제공해야한다. 가령 DLL을 업데이트할 때, 클라이언트가

    바뀐 버전의 DLL을 사용할 거라고 가정하지 않는다. 적절한 버전의 DLL을 로딩하려면, DLL

    클라이언트 또한 재빌드해야한다.

 

    반 규칙과 제한사항

 

    ■ 함수나 객체를 dllimportdllexport 없이 선언하면 이는 DLL 인터페이스 일부로 간주되

    지 않으므로, 그들의 정의는 선언한 모듈이나 같은 프로그램 안의 다른 모듈에 있어야한다. 함수

    나 객체를 DLL 인터페이스로 만들려면 다른 모듈에서 그 정의를 dllexport로 선언해야하며, 그

    렇지 않을 경우 링커에러가 발생한다.

    함수나 객체를 dllexport로 선언하면, 그 정의는 같은 프로그램 모듈에 있어야하며, 그렇지 않

    을 경우 링커에러가 발생한다.

 

    ■ 프로그램 내 단일 모듈이 같은 함수나 객체에 대해 dllimport와 dllexport 2개의 선언을 가지

    면, dllexport 속성이 dllimport 속성보다 우선한다. 하지만, 컴파일러 경고는 발생한다.

 

    __declspec( dllimport ) int i;

    __declspec( dllexport ) int i;    // 경고; 불일치; dllexport가 우선한다

 

    ■ C++에서는 전역 포인터 데이터 또는 정적 지역 포인터 데이터를 dllimport 데이터 객체의

    주소로 초기화할 수 있다. 하지만 C에서는 에러다. 또한, 정적 지역 포인터 함수를

    dllimport 함수 주소로 초기화할 수 있다. C에서는 함수 주소가 아닌 DLL 임포트 청크 (함수에

    대한 제어를 전송하는 코드 조각) 의 주소로 설정한다.

 

    __declspec( dllimport ) void func1( void );

    __declspec( dllimport ) int i;

 

    int *pi = &i;                                       // C에서는 에러

    static void ( *pf )( void ) = &func1;    // C에서는 청크 주소, C++에선 함수 주소

 

    void func2()

    {

        static int *pi = &i;                              // C에서는 에러

        static void ( *pf )( void ) = &func1;    // C에서는 청크 주소, C++에선 함수 주소

    }

 

    하지만 dllexport 객체 선언을 가진 프로그램은 정의도 포함해야하므로, 전역 또는 지역 정적

    포인터 함수를 dllexport 함수의 주소로 초기화할 수 있다. 비슷하게, 전역 또는 지역 정적 포인

    터 데이터를 dllexport 데이터 객체의 주소로 초기화할 수도 있다. 예를 들어 다음 코드는 C와

    C++에서 모두 에러가 발생하지 않는다:

 

    __declspec( dllexport ) void func1( void );

    __declspec( dllexport ) int i;

 

    int *pi = &i;

    static void ( *pf )( void ) = &func1;

 

    void func2()

    {

        static int *pi = &i;

        static void ( *pf )( void ) = &func1;

    }

 

    래스에서 사용하기

 

    dllimportdllexport로 클래스를 선언하면, 하나의 클래스 전체에 적용된다. 즉 모든 멤버함

    수와 정적 데이터가 익스포트된다. 이렇게 익스포트 된 클래스를 익스포터블 클래스라 한다.

 

    #define  DllExport  __declspec( dllexport )

    class DllExport C

    {

        int i;

        virtual int func( void ){ return 1; }

    };

 

    참고 익스포터블 클래스의 멤버는 dllimportdllexport를 명시적으로 쓸 수 없다.

 

    dllexport 클래스는 모든 멤버함수와 정적 데이터멤버가 익스포트되므로, 프로그램 내에 정의

    를 포함해야한다. 그렇지않으면 링커에러가 발생한다. 단 순수가상함수의 경우 예외지만, 가상

    클래스 파괴자는 기본클래스 파괴자에 의해 항상 호출되므로, 순수가상파괴자는 항상 정의해야

    한다.

    이런 규칙들은 익스포터블 클래스가 아니여도 적용되므로, 클래스타입의 데이터나 클래스를 리

    턴하는 함수를 익스포트하려면, 클래스를 익스포트해야한다.

 

    dllimport 클래스는 모든 멤버함수와 정적 데이터멤버가 임포트된다. 비클래스 타입의

    dllimport/dllexport와는 달리, 정적 데이터멤버는 dllimport 클래스가 정의된 프로그램 내에

    정의할 수 없다.

 

    익스포터블 클래스의 모든 기본 클래스는 익스포트될 수 있어야한다. 아니면, 컴파일러 경고가

    발생한다.

이로써 dllexport 클래스는 dllimport 클래스에서 상속받을 수 있고, dllimport 클래스는 dllexport 클래스에서 상속받을 수 있다

+ Recent posts