반응형
반응형
반응형

 화면 깜박거림을 제거합시다.  | VC++ 일반 2005-05-09 오후 11:34:20
최병훈 (angleafro)  최병훈님께 메시지 보내기최병훈님을 내 주소록에 추가합니다.최병훈님의 개인게시판 가기 번호: 7084   / 평점:  (6.3)  / 읽음:3,712

화면 깜박거림을 제거합시다.

 

화면 떨림이란 모니터의 주사선과 그래픽카드의 잘못된 설정으로 생길수 있습니다.

그래픽 카드를 바꾸고, 모니터를 좋은 것으로 바꾸서 쓰면 되죠..

맞나요.. ㅋㅋ

 

그럼 프로그램할 때 화면 조정 말고 , 사이즈 조정이나 크기조정 또는 드래그, 및 콘트롤 사용시 나오는 깜박거림은 도대체 모죠?

 

해답은 있나요?

네 당근 입니다, (오이 부모왈~~)

 

그럼 시작하는 과정이므로 간단하게 가장 쉬운 폼뷰부터 시작하죠..

 

그럼 왜 깜박거림이 생기는가?

 

한가지 동일한 원인에 의하여 프로그램중 일부분이 그리기 작업을 하게 되고, 다른 부분이 그 곳에 다시 그리기 작업을 하게 됩니다.

그리고, 순식간에 두 가지 페인팅 작업이 발생이 되는데, 두번째 작업을 하기 전에 사용자가 첫번째 그리기 작업을 보기 되늣것입니다.

 

그럼 해결 방법은 있군요.

. 두가지를 그리기를 하는데, 첫번째 작업이 끝나고 아주 빨리 두번째 그리기 작업을 하는것입니다.

그러나 , 이 방법은 사용자 피씨가 슈퍼콤이 아니면 아주 힘들겠죠..

어차피 두가지가 순식간의 터울로 드려지기 때문에 그리기 작업을 나눠서 하지 말고 동시에 해보자는 것입니다.

 

그럼 깜박꺼림(Flicker)를 제거한는 간단한 프로그램을 짜 보죠.

초보자들이 비절씨를 할 때 아장 쉽게 할수 있는 것 중 하나가, CFormView 클레스를 뷰로 하여 작성한는 것입니다.

폼뷰로 프로그램을 작성하였을 때 쉽게 볼수 있는데요..

OwerDraw 속성을 적용하지 않는 기본 콘트롤이 폼뷰의 사이즈를 조정하면 깜박거리죠..

 

그럼 깜박거림을 없게하려면.

가장 쉬운게 부모 윈도우, 즉 폼뷰가 변경될 때 콘트롤들이 다시 그려지지 않게 하면 됩니다.

WS_CLIPCHILDREN

이 속성을 이용하면 되죠.

대신 이속성을 폼이 키기가 변경될 때 적용하면 더욱 효과적이죠.

void CFlicker_freeView::OnSize(UINT nType, int cx, int cy)

{

             ModifyStyle(0, WS_CLIPCHILDREN); // turn on WS_CLIPCHILDREN

             CFormView::OnSize(nType, cx, cy);           // default

             UpdateWindow();

             //this->Invalidate(false);

             ModifyStyle(WS_CLIPCHILDREN, 0); // turn off WS_CLIPCHILDREN

             GetClientRect(&m_rectDraw);

}

이렇게요..

한가지 중요한게 남았는데.

구릅박스죠..

그릅박스 경우는 문제가 있어요.

 

 

자신의 차일드를 포함할수 있는데,

구름박스의 테두리 안쪽이 폼에도 속하지 않고, WS_CLIPCHILDREN 속성에 의하여 그룹박스에도

속하지 않게되어서  화면이 요상해 집니다.

이걸 해결 할려면. Transparent속성을 활성화 시켜 주면 됩니다.

 

다음에는 페인팅 작업이 너무 많아서 깜박거리는 것을 예기해 보죠..

수고 하세요.

이 글에 평점 주기: 

질문&답변
 formview를 이용한 다디얼로그의 크기를 변경시킬때 깜박이는 현상해결.. 방법 없나요..ㅜㅜ  | VC++ 일반 2003-12-20 오전 1:23:29
홍성호 (dybbls)  홍성호님께 메시지 보내기홍성호님을 내 주소록에 추가합니다.홍성호님의 개인게시판 가기 번호: 406068   / 평점:  (-)  / 읽음:1,194

 formview를 이용해서 MDI 프로그램을 만들었습니다.

formview안에는 대부분 이미지로 차 있습니다.

 

근데 창 크기를 변경시킬때 심하게 깜박이는 현상이 나타납니다...

물론 이미지는 변하지 않습니다. 단지 크기만 창 크기메 맞게 변합니다.

 

정말 답답합니다. 왜 이러는건지.. 힘드네요.. 프로그램..

 

이 글에 평점 주기: 
 [답변][MAX] Window 깜빡임의 실체 - WM_ERASEBKGND와 WM_PAINT 2003-12-20 오전 2:42:08
박민재 (MAXIST)  박민재님께 메시지 보내기박민재님을 내 주소록에 추가합니다.박민재님의 개인게시판 가기 번호: 406075   / 평점:  (8.3)  
Window는 뭔가 새로 그려야할 필요성 있을 때마다 WM_PAINT Message를 받습니다. OnPaint라는 이름으로 WM_PAINT에 대한 Handler Function이 보통 만들어지죠. System으로부터 WM_PAINT가 날아오는 상황은 다음과 같습니다.

    - 윈도우가 처음 생성되었을 때
    - 윈도우의 위치가 이동되었을 때 
    - 윈도우의 크기가 변경되었을 때(최소 및 최대화 포함)
    - 윈도우의 전체 또는 일부가 다른 윈도우에 가려져 있다가 나타날 때
    - 윈도우가 스크롤 될 때
    - UpdateWindow나 RedrawWindow 함수가 불렸을 때
    - InvalidateRect나 InvalidateRgn 함수가 불려서 다시 그려져야할 영역이 발생 한 후,
      Message Queue에 다른 처리할 Windows Message가 없을 때

그런데 WM_PAINT가 날아오기 '전'에 대부분 같이 따라오는 Message가 있습니다 WM_ERASEBKGND라는 것으로, Erase Background 라는 의미죠. 즉, 배경을 지워라.

Window의 기본 Message Procedure(DefWindowProc)는 사용자가 WM_ERASEBKGND를 받고도 아무 처리를 하지 않으면, WNDCLASS의 hbrBackground 맴버에 정의된 색상으로 배경을 지워버립니다. 깜빡이는 것은 아래 처럼 되기 때문에 발생하는 것이지요.

    1. WM_ERASEBKGND 받음
    2. 배경 지움
    3. WM_PAINT 받음
    4. Image 다시 그림

2까지가 '깜'이고 4까지 가면 '빡'이 됩니다. 깜빡 깜빡의 실체는 이것입니다.
손쉬운 처리는 WM_ERASEBKGND를 받았을 때, 배경을 안지우도록 하는 방법입니다. 보통 OnEraseBkgnd 정도의 이름을 갖게 되는 WM_ERASEBKGND Message Handler에서 0을 return 하면 됩니다.

    BOOL CAboutDlg::OnEraseBkgnd(CDC* pDC)
    {
        return 0;
    }

깜빡 거리는 것이 WM_PAINT Message Handler에서 그려주는 어떤 것이 아니라, 또 다른 Window인 '자식' Control이라면, Window Styles 중에 WS_CLIPCHILDREN Style을 '부모' Window에 먹여서 자식 Control에 의해 가려지는 영역은 그리기 대상에서 아예 제외시켜버리는 것이 일반적인 해결책입니다.

 질문&답변
 RedrawWindow와 Invalidate와의 차이?  | VC++ 일반 2003-12-21 오후 8:33:13
김성용 (mage44)  김성용님께 메시지 보내기김성용님을 내 주소록에 추가합니다.김성용님의 개인게시판 가기 번호: 406316   / 평점:  (-)  / 읽음:1,009

 RedrawWindow와 Invalidate와의 차이가 무엇인지 알고 싶습니다.

RedrawWindow한부분에 Invalidate로 대체하면 제대로 안되는 이유를 모르겠습니다.

 

코드

 

    CClientDC dc(this);

    CRect rect;

    CString status;

 

    GetClientRect(rect);

    status.Format( "%-20s Line %d, Col %d", msg, point.x, point.y);

 

    RedrawWindow();

    //Invalidate();

    dc.MoveTo(0,rect.bottom-30);

    dc.LineTo(rect.right, rect.bottom-30);

    dc.TextOut(0, rect.bottom-20, status);

 

RedrawWindow();로하면 문자열이 잘 출력되는데

Invalidate();로 하면 문자열이 출력되었다가 이내 지워 집니다.

 

이 글에 평점 주기: 
 [답변][MAX] Win32 API RedrawWindow와 Invalidate 함수의 차이(내용 추가) 2003-12-21 오후 9:17:08
박민재 (MAXIST)  박민재님께 메시지 보내기박민재님을 내 주소록에 추가합니다.박민재님의 개인게시판 가기 번호: 406322   / 평점:  (9.0)  
    BOOL RedrawWindow(
       LPCRECT lpRectUpdate = NULL,
       CRgn* prgnUpdate = NULL,
       UINT flags = RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE 
    ); 

보시다시피, MFC CWnd::RedrawWindow 함수는 Win32 API RedrawWindow 함수를 호출 할 때 아래와 같은 옵션을 '기본값'으로 씁니다.


    RDW_INVALIDATE
        - 대상 Window의 특정 영역 또는 Client 영역 전체를 Invalidate 함
    RDW_ERASE
        - 대상 Window가 Repaint될 때, WM_ERASEBKGND Message도 같이 받게 함
    RDW_UPDATENOW
        - 함수가 반환되기 전에 대상 Window들이
          WM_NCPAINT, WM_ERASEBKGND, WM_PAINT Message를 받음

Win32 API Invalidate 함수의 역할은, OS가 Window에게 '어떤 영역이 무효화 되었으니 다음 번 WM_PAINT를 받을 때 다시 그렸으면 좋겠다'는 표시를 하는 것입니다. 보통 Invalidate가 호출되고 나면 OS는 대상 Window의 Message Queue에 WM_ERASEBKGND 그리고 WM_PAINT 순으로 Message를 보냅니다.

Window가 실제로 Repaint를 하는 시점은, Invalidate가 호출된 시점이 아니라, Invalidate의 호출 이후에 OS가 대상 Window에 다시 WM_PAINT Message를 던져준 시점입니다.

Invalidate를 썼을 때, TextOut으로 뿌려준 부분이 '낼름' 지워지는 것은 아마도, TextOut으루 뿌려준 이후에, WM_PAINT Message Handler 함수 안에서 TextOut으로 출력한 내용을 덮어써버리는 것 같네요.

RedrawWindow를 쓰더라도 RDW_UPDATENOW 옵션을 빼면 그냥 Invalidate를 불러준 것과 같은 효과입니다. RedrawWindow를 쓸 때 RDW_UPDATENOW을 쓰면, 함수가 반환 되기 전에 WM_PAINT Message가 Message Queue 들어옵니다. 그렇게 되면 그 시점에서 바로 다시 그리기가 일어나고 뒤에 TextOut으로 뭔가 그려진 집니다.

따라서, 화면에 보이고 있는 것은, RedrawWindow가 제 역할을 했다기 보다는 그냥 불필요한 Paint 동작이 다행스럽게도 TextOut 보다 먼저 발생했기 때문입니다. 차라리 Invalidate나 RedrawWindow를 모두 호출하지 않았다면 문자열들이 그냥 보였을 것입니다.

지워진다는 것을 보니, 문제의 Code WM_PAINT Message Handler 함수 안에 있는 것이 아닐 것 같은데 맞나요? 그럼 잘 출력된 것 처럼 보여도, 출력된 문자를 다른 Window로 한 번 덮었다가 (Invalidate) 빼면 글자가 지워집니다. 확인해보세요. 화면에 보이는 것을 계속 유지하고 싶다면, WM_PAINT Message Handler 안에서 계속 그려지게 만들어야 합니다.

이 글에 평점 주기: 
         [답변]한가지만 더요.. 2003-12-21 오후 10:22:22
김성용 (mage44)  김성용님께 메시지 보내기김성용님을 내 주소록에 추가합니다.김성용님의 개인게시판 가기 번호: 406334   / 평점:  (-)  

 그렇다면 RedrawWindow()는 WM_NCPAINT, WM_ERASEBGD, WM_PAINT메세지가

처리된 다음에 리턴이 된다는 것인가요?

아니면 그냥 위 메세지들을 메세지 큐에만 던져두고 리턴을 한다는 것인가요?

 

 

이 글에 평점 주기: 
                 [답변]RedrawWindow에 RDW_UPDATENOW 옵션이 있으면 WM_PAINT가 즉시 전달됩니다. 2003-12-21 오후 10:31:18
박민재 (MAXIST)  박민재님께 메시지 보내기박민재님을 내 주소록에 추가합니다.박민재님의 개인게시판 가기 번호: 406335   / 평점:  (9.0)  

RedrawWindow 함수에 현재 쓰인 옵션(RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE)만 보면, WM_ERASEBGD Message와 WM_PAINT Message가 먼저 처리됩니다.

Break Point를 걸어서 확인해도 되죠. RedrawWindow 함수에 RDW_UPDATENOW가 있으면, RedrawWindow 호출 시, 바로 WM_PAINT Message Handler가 처리되는 것을 볼 수 있습니다. 그 이후에 RedrawWindow 함수 이후 부분의 Code가 실행됩니다.

Invalidate는 WM_PAINT Message를 대상 Window로 보내는 것이 아니라 System이 대상 Window에 WM_PAINT Message 보내는 작용을 유발시키는 것입니다. 정확히 언제 WM_PAINT Message가 대상 Window에 도착하는지는 알 수 없죠.
이 글에 평점 주기: 
                         [답변]감사합니다...그런데.. 2003-12-21 오후 10:54:19
김성용 (mage44)  김성용님께 메시지 보내기김성용님을 내 주소록에 추가합니다.김성용님의 개인게시판 가기 번호: 406337   / 평점:  (-)  

 저도 이런저런 코드를 넣고 spy++로 확인해 보니

ReDrawWindow의 경우는 WM_PAINT를 Send시키고 바로 처리를 한후 리턴하는데

Invalidate의 경우는 WM_PAINT를 Post시키는 군요.

 

아무튼 알려주셔서 감사합니다. 도움이 많이 되었습니다.

이 글에 평점 주기: 
                                 [답변]... 2003-12-21 오후 11:55:13
박민재 (MAXIST)  박민재님께 메시지 보내기박민재님을 내 주소록에 추가합니다.박민재님의 개인게시판 가기 번호: 406347   / 평점:  (-)  
옙 내부적으론 그렇게 처리됩니다.

첨언하자면, WM_PAINT는 우선 순위가 낮아서, Invalidate에서 Post가 되더라도, 항상 다 도착하지는 않습니다. Queue 안에 아직 처리되지 못한 WM_PAINT가 하나라도 존재하면 그냥 무시 당합니다. (따라서 Message Queue에는 WM_PAINT는 하나가 있거나 없거나 그렇죠.)
반응형
반응형
출처 : 

CreateThread는 생성된 쓰레드 내에서 CRL 계열의 함수를 사용할 때 TLS를 사용하지 않기 때문에 Thread-Safe하지 않은 문제가 있다는 것은 널리 알려져 있다. 대신 _beginthreadex 나 AfxBeginThread를 사용하라고들 얘길 하는데....

AfxBeginThread 함수의 문제는 아니지만... AfxBeginThread를 다른 쓰레드 생성 함수들처럼 사용해버리면 문제가 되곤 한다.

먼저 _beginthreadex로 쓰레드를 생성하는 샘플코드이다.

  1. CWinThread* pWinThread = NULL;  
  2. pWinThread  = AfxBeginThread(pfnThreadEntryFunc, this);  
  3. if (pWinThread == NULL)  
  4.     return FALSE;  
  5.   
  6. CloseHandle (pWinThread->m_hThread);  
  7. pWinThread->m_hThread = NULL;  

스레드 생성시에는 스레드 내부적으로 별도의 핸들을 가지고 있기 때문에 _beginthreadex가 리턴하는 핸들까지 하면 두개의 핸들이 생성된다. 따라서, 생성한 스레드를 부모 스레드에서 계속 관리할 것이 아니라면 바로 CloseHandle 해버리는 것이 자원을 관리하는 FM이다. (그러지 않는다면 나중에 스레드가 종료된 후에도 스레드 Object가 파괴되지 않을 것이다. 왜냐구? 핸들카운트가 0이 되지 않았기 때문이지!)

그런데... MFC계열의 AfxBeginThread는 사용법이 약간 다르다.
MSDN의 함수 Prototype은 다음과 같다.
CWinThread* AfxBeginThread( 
                    AFX_THREADPROC
 pfnThreadProc, 
                         LPVOID pParam, 
                         int nPriority = THREAD_PRIORITY_NORMAL,
                         UINT nStackSize = 0, 
                         DWORD dwCreateFlags = 0, 
                         LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL 
                    );

일단... 리턴값이 다르다. 스레드의 HANDLE이 아니라 CWinThread 클래스의 포인터이다.
CWinThread? 이건 또 모야... MFC코드에는 다음과 같이 선언되어 있다.
class CWinThread : public CCmdTarget
{
    DECLARE_DYNAMIC(CWinThread)

public:
// Constructors
    CWinThread();
   // ... 중간생략

    // only valid while running
    HANDLE m_hThread;       // this thread's HANDLE
    operator HANDLE() const;
    DWORD m_nThreadID;      // this thread's ID

아항... CWinThread의 내부에 스레드 핸들을 가지고 있군.
흠. 그렇다면 이렇게 코드를 써주면 되겠구만

  1. CWinThread* pWinThread = NULL;  
  2. pWinThread  = AfxBeginThread(pfnThreadEntryFunc, this);  
  3. if (pWinThread == NULL)  
  4.     return FALSE;  
  5.   
  6. CloseHandle (pWinThread->m_hThread);  

바로 이게 오바질이 되것다.
저렇게 하면... 스레드 종료할 때(정확히 말하면 CWinThread 개체 파괴시) 원인불명의 메모리 폴트가 뜨게 된다. 왜냐구?
AfxBeginThread에 의해 생성된 스레드가 종료될 때 CWinThread의 소멸자가 호출되는데, 이 소멸자 내에서 CWinThread 개체의 m_hHandle을 CloseHandle하도록 되어 있다. 이때 이미 Close된 핸들을 다시 Close하려고 시도하니까 문제가 발생하는 것이다.

AfxBeginThread로 스레드를 생성했을 경우에는 굳이 CloseHandle을 해줄 필요가 없다.
꼭 해주려면 다음과 같이 해주면 에러가 발생하지 않는다.

  1. CWinThread* pWinThread = NULL;  
  2. pWinThread  = AfxBeginThread(pfnThreadEntryFunc, this);  
  3. if (pWinThread == NULL)  
  4.     return FALSE;  
  5.   
  6. CloseHandle (pWinThread->m_hThread);  
  7. pWinThread->m_hThread = NULL;  



※ 만약 부모 스레드에서 자식스레드의 상태를 계속 관리해주어야 하는 경우에는
다음과 같이 스레드 종료 후 CWinThread가 자동 파괴되지 않도록 해주어야 한다.

  1. CWinThread* pWinThread = NULL;  
  2. pWinThread  = AfxBeginThread(pfnThreadEntryFunc, this);  
  3. if (pWinThread == NULL)  
  4.     return FALSE;  
  5.   
  6. pWinThread->m_bAutoDelete = FALSE; // 자동파괴되지 않도록 설정  
  7.   
  8. // 여기서 WaitFor.. 나 GetExitCodeThread 같은 관리 코드를...  
  9. // ...  
반응형
반응형

Visual Studio에서 "First Chance Exception"이 발생할 때가 있다.
이건 또 뭔소리여... 첫번째 기회 예외??

대략 인터넷을 뒤져 보니 이런 뜻이더군... 맞게 이해했나 모르겠다.


  1. 어플리케이션이 디버그 모드로 실행될 때, 디버거는 모든 Exception 발생을 알아차릴 수 있다. (이 Exception이 Handle되든 아니든..) 이 단계를 "First Chance"라 한다.
  2. 만약 이 Exception이 적절하게 Handling되었다면 실제로 응용프로그램은 문제를 일으키거나 종료(Crash)되지 않을 것이다. 하지만, 디버거의 설정에 따라서 적절히 Handling된 Exception에 대해서 조차도 반응 (실행중지 및 오류메시지 발생)하도록 설정할 수 있는데, 이 것을 First-Chance Exception이라 한다.
  3. First-Chance Exception이 발생했으나, 실제 Release 실행시에는 별 문제가 생기지 않는다면 이것은 Exception이 발생했으나 내부적으로 Handling되었음을 의미한다. 따라서 이러한 경우 Debug모드에서 Exception이 발생했다 하더라도 큰 문제가 되지 않는다. 대부분 WinAPI 내부에서 처리되는 경우가 많다.
  4. 단, 이 Exception이 적절하게 Handle되지 않은 경우, Release 실행시에 실제로 오류가 발생하고 프로그램이 종료되게 된다. (Second Chance Exception) 이 단계라면 당연히 적절한 조치를 해주어야 한다.
    http://blogs.msdn.com/davidklinems/archive/2005/07/12/438061.aspx

    Visual Studio에서도 First-Chance Exception을 Handling하도록 (혹은 하지 않도록) 설정할 수 있다. 간혹 디버그 모드에서 디버그 실행을 시키자마자 First-Chance란 놈이 발생해서 아예 디버그 시작을 못하는 경우가 있는데 이럴 땐 First-Chance Exception을 Handle하지 않도록 설정해 주어야 한다.
    http://blog.naver.com/li0129?Redirect=Log&logNo=60018574262
반응형
반응형
프로그램이 실행되는 모습
CLR(The Common Language Runtime)는 MSIL(Intermediate Language)를 실행함
Code는 MSIL로 compile됩

자바의 JRE와 class를 생각하면 될 것 같음

* assembly : 어떤 기능을 하는 하나의 모듈을 의미
* manifest : assembly가 동작하는 데 어떤 파일이 필요한 지 가지는 목록
* program : 하나 혹은 여러개의 assembly로 구성

* CLR은 assembly로부터 그것이 어떤 보안/권한 범위를 가지는지, 사용버전은 어디서부터 어디까지인지를 보고 실행하게 됨

* DLL지옥의 관점에서 Private Assembly, Shared Assembly
   http://hoons.kr/Board.aspx?Name=cshaptip&Mode=2&BoardIdx=406&Key=&Value=

어셈블리는 DLL 지옥을 해결하였다는데…
앞에서 언급했듯이 직접 작성한 대부분의 어셈블리는 private이다. 그러므로 각 어플리케이션은 자체적으로 설치된 폴더에 있는 어셈블리를 참조한다. Private형식에서는 같은 이름의 여러버전이 있다하더라도 충돌을 피할 수 있다.

이해를 돕기위해 한가지 예를 들어본다.
1. Assembly1이라는 이름의 어셈블리를 제작한다.
2. Assembly1을 이용하는 Client1이라는 어플리케이션을 만든다.
3. 클라이언트 프로그램을 c:\MyApp1이라는 폴더에 설치하고, Assembly1도 이 폴더에 설치한다.
4. 며칠이 지난 후에 Assembly1이 수정이 되었다고 가정한다.
5. 수정된 Assembly1을 이용한 클라이언트 어플리케이션(Client2)를 제작한다.
6. 이 클라이언트 프로그램을 c:\MyApp2라는 폴더에 설치하고 Assembly1도 이 폴더에 설치한다.
7. 각각의 프로그램은 같은 이름의 Assembly1이라는 어셈블리를 사용하고 있지만, 버전은 서로 다른 것

* 데브피아에서 퍼옴 왕성현 님의 강좌입니다.


*private assembly를 shared assembly로 만드는 과정
반응형

+ Recent posts