• 카테고리

    질문 & 답변
  • 세부 분야

    데스크톱 앱 개발

  • 해결 여부

    해결됨

pOldPen = dc.SelectObject(); 관련 질문

24.03.23 22:45 작성 24.03.23 22:47 수정 조회수 81

0

안녕하세요 선생님

강의에서 옛날 윈도우(XP)에서는 pOldPen = dc.SelectObject()를 안해주면 문제가 생길수 있고, 요즘 윈도우즈에서는 괜찮을거라고 하셨는데요.

이부분에 대해서 조금 더 자세하게 알고 싶습니다.

테스트용으로 코드를 작성했습니다.

 

void CPenBrushDemoView::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	CBrush NewBrush;
	//비트맵 클래스 객체를 선언하여 비트맵 리소스 로딩.
	CBitmap Bmp;
	Bmp.LoadBitmap(IDB_BITMAP1);
	//로딩된 비트맵 리소스로 패턴 브러시 생성.
	NewBrush.CreatePatternBrush(&Bmp);
	CBrush* pOldBrush = dc.SelectObject(&NewBrush);

	//클라이언트 뷰 클래스의 클라이언트 영역 크기를 알아내고
	//그 크기에 해당하는 네모를 그림.
	CRect Rect;
	GetClientRect(&Rect);
	dc.Rectangle(&Rect);
	dc.SelectObject(pOldBrush);

	DWORD style[] = {6, 3};
	LOGBRUSH lb;
	lb.lbStyle = BS_SOLID;
	lb.lbColor = RGB(255, 0, 0);



	// 여기서부터...

	CPen* pOldPen;

	CPen FirstPen;
	{
		FirstPen.CreatePen(PS_GEOMETRIC | PS_USERSTYLE | PS_ENDCAP_FLAT,
			20, &lb, 2, style);
		pOldPen = dc.SelectObject(&FirstPen);
		dc.MoveTo(40, 40);
		dc.LineTo(240, 40);
	}

	CPen SecondPen;
	{
		SecondPen.CreatePen(PS_GEOMETRIC | PS_USERSTYLE | PS_ENDCAP_FLAT,
			40, &lb, 2, style);
		dc.SelectObject(&SecondPen);
		dc.MoveTo(40, 240);
		dc.LineTo(240, 240);
	}

	{
		CPen ThirdPen;
		ThirdPen.CreatePen(PS_GEOMETRIC | PS_USERSTYLE | PS_ENDCAP_FLAT,
			60, &lb, 2, style);
		dc.SelectObject(&ThirdPen);

		dc.MoveTo(40, 440);
		dc.LineTo(240, 440);
	}

	dc.MoveTo(40, 640);
	dc.LineTo(240, 640);

	dc.SelectObject(pOldPen);
}

 

위 코드에서는 총 4개의 직선을 그리고 있습니다.

여기서, CPen ThirdPen은 스코프 안에서 소멸자가 불러질 때 DeleteObject()가 실행되면서 gdi는 없어집니다. 하지만 디바이스컨텍스트(dc)는 계속 해당 gdi를 가리키고 있습니다. 그런데 그 이후에 dc는 지워진 gdi의 방식대로 직선을 그리고 있습니다.(dc.MoveTo(40, 640); dc.LineTo(240, 640);) dc가 가리키고 있던 gdi가 지워졌는데도 불구하고 어떻게 기존의 방식대로 그릴수 있는지 이유를 모르겠습니다.

 

그리고 위의 코드에서,

dc.MoveTo(40, 440);

dc.LineTo(240, 440);

를 지우게 되면 ...

	{
		CPen ThirdPen;
		ThirdPen.CreatePen(PS_GEOMETRIC | PS_USERSTYLE | PS_ENDCAP_FLAT,
			60, &lb, 2, style);
		dc.SelectObject(&ThirdPen);

		// dc.MoveTo(40, 440);
		// dc.LineTo(240, 440);
	}

ThirdPen이 스코프를 벗어난 이후 마지막 직선은 ThirdPen의 방식이 아닌 SecondPen의 방식으로 그려지고 있습니다. 좀 전의 위에서 동작한 방식과 일관성이 맞지 않는것같아요.

 

질문글이 길어서 요약을 해보자면..

  1. 2개의 소스코드가 사소한 차이가 있는데 왜 DC가 동작하는 방식이 다를까요?

  2. DC와 GDI에 대한 저의 생각에서 혹시 오류가 있을까요?

     

답변 1

답변을 작성해보세요.

1

저도 정확히는 알 수 없습니다. 그러려면 윈도우 GDI 관련 요소를 디버깅하고 분석해야 하기 때문입니다. 그러나 추정은 해볼 수 있을 것 같습니다. 조금 이상한(?) 실험용 코드입니다만...추정으로 말씀드리자면 아마도 지역변수로 선언된 CPen 클래스 인스턴스들이 아직 메모리에 남아있어서 발생한 문제로 보입니다.

모든 지역변수는 Stack 메모리를 사용합니다. 그리고 스택은 스코프를 벗어날 때 크기가 줄어듭니다. 하지만 그렇다고 기존 값을 모두 0으로 Clear하지도 않습니다. 그리고 우연히 위치가 맞는 지점에서 값을 읽는다면 기존의 값이 그대로 출력되기도 합니다. 이와 같은 문제는 자동변수인 지역변수의 주소나 참조를 반환했을 때 발생하는 문제와 매우 흡사합니다.

참고하시기 바랍니다. 😄