성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>Win32 - 대화창의 DLU 단위를 pixel로 변경하는 방법</h1> <p> 지난 글을 쓰면서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Win32 - 대화창 템플릿의 2진 리소스를 읽어들여 윈도우를 직접 띄우는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13287'>https://www.sysnet.pe.kr/2/0/13287</a> </pre> <br /> 대화창 리소스의 DLU 단위를 픽셀로 변경하는 것에 실패했었는데요, 이와 관련해 검색해 보면 마이크로소프트의 공식 코드를 이제는 없어진 <a target='tab' href='https://www.betaarchive.com/wiki/index.php?title=Microsoft_KB_Archive/145994'>예전의 Microsoft KB 145994 자료</a>에서 찾을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > void CAboutDlg::OnPaint() { CPaintDC dc(this); // device context for painting CFont* pFont = GetFont(); CFont* oldFont = dc.SelectObject(pFont); TEXTMETRIC tm; dc.GetTextMetrics( &tm ); int baseUnitY = tm.tmHeight; CSize size; size = dc.GetTextExtent( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52); int baseUnitX = (size.cx / 26 + 1) / 2; TRACE("baseUnitX = %d\n", baseUnitX); TRACE("baseUnitY = %d\n", baseUnitY); dc.SelectObject(oldFont); } </pre> <br /> 그러니까, <a target='tab' href='https://stackoverflow.com/questions/6870485/how-to-convert-dlu-into-pixels'>How to convert DLU into pixels?</a>에서의 답변이 맞았던 것입니다.<br /> <br /> 그런데 왜 안 되는 걸까요? ^^ 혹시나 싶어 찾아본 ReactOS의 코드에서 답이 나왔습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > DIALOG_CreateIndirect() ; <a target='tab' href='https://doxygen.reactos.org/d0/dfb/win32ss_2user_2user32_2windows_2dialog_8c.html#a96baa0b632423329434041a27e810755'>https://doxygen.reactos.org/d0/dfb/win32ss_2user_2user32_2windows_2dialog_8c.html#a96baa0b632423329434041a27e810755</a> </pre> <a name='pt2pixel'></a> <br /> 즉, 대화창 리소스에 있던 폰트 크기가,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About Project1" FONT <span style='color: blue; font-weight: bold'>8</span>, "MS Shell Dlg" </pre> <br /> pixel이 아니라 pt 단위였던 것입니다. 따라서 이것을 Pixel로 변환해야 하는데요, ReactOS 소스코드에 실린 것처럼 이 작업을 다음과 같이 할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int pixels = MulDiv(template.pointSize, GetDeviceCaps(dc , LOGPIXELSY), 72); hUserFont = CreateFontW( -pixels, 0, 0, 0, template.weight, template.italic, FALSE, FALSE, DEFAULT_CHARSET, 0, 0, PROOF_QUALITY, FF_DONTCARE, template.faceName ); </pre> <br /> 아울러, 뒤늦게 공식 문서를 보니까 다음과 같은 내용을 담고 있습니다. ^^;<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > CreateFontW function (wingdi.h) ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createfontw#parameters'>https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createfontw#parameters</a> For the MM_TEXT mapping mode, you can use the following formula to specify a height for a font with a specified point size: nHeight = -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72); </pre> <br /> <a target='tab' href='https://www-user.tu-chemnitz.de/~heha/petzold/ch05f.htm'>MM_TEXT가 기본 매핑 모드</a>이기 때문에,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int mappingMode = GetMapMode(dc); // 1 == MM_TEXT </pre> <br /> 저 공식대로 변환하는 것이 맞습니다. 이렇게 폰트를 생성하게 되면 그것을 적용한 DC(Device Context)를 통해 다음과 같이 정확하게 baseUnits 관련 값을 구할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > HDC dc = GetDC(nullptr); int pixels = MulDiv(pointsize, GetDeviceCaps(dc, LOGPIXELSY), 72); _hFont = ::CreateFont(-pixels, 0, 0, 0, weight, italic, 0, 0, charset, OUT_DEFAULT_PRECIS, // 0 CLIP_DEFAULT_PRECIS, // 0 PROOF_QUALITY, // 0 == DEFAULT_QUALITY, 2 == PROOF_QUALITY FF_DONTCARE, // 0 typeface); HFONT hOldFont = (HFONT)::SelectObject(dc, _hFont); { TEXTMETRIC tm = { 0 }; GetTextMetrics(dc, &tm); <span style='color: blue; font-weight: bold'>_baseUnitsY = tm.tmHeight;</span> SIZE size = { 0 }; GetTextExtentPoint32(dc, L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &size); <span style='color: blue; font-weight: bold'>_baseUnitsX = (size.cx / 26 + 1) / 2;</span> } </pre> <br /> 재미있는 건, gdi32.dll에 위와 같이 baseUnitsX, baseUnitsY 값을 구하는 함수가 제공된다는 점입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > gdi32.dll ; <a target='tab' href='https://www.nirsoft.net/dll_information/windows8/user32_dll.html'>https://www.nirsoft.net/dll_information/windows8/user32_dll.html</a> </pre> <br /> 하지만 공식 문서 및 win32 헤더 파일에는 없어 직접 구해야 하는 번거로움이 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > typedef LONG (WINAPI* GdiGetCharDimensionsProc)(HDC hdc, LPTEXTMETRICW lptm, LONG* height); ...[생략]... { HMODULE hGdi32 = ::LoadLibrary(L"gdi32.dll"); if (hGdi32 != nullptr) { GdiGetCharDimensionsProc farProc = (GdiGetCharDimensionsProc)::GetProcAddress(hGdi32, "GdiGetCharDimensions"); TEXTMETRIC tm = { 0 }; <span style='color: blue; font-weight: bold'>LONG tmHeight = 0; LONG tmWidth = farProc(dc, &tm, &tmHeight);</span> } } </pre> <br /> 게다가 위의 GdiGetCharDimensions 소스 코드를 ReactOS에서 찾아보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > GdiGetCharDimensions ; <a target='tab' href='https://doxygen.reactos.org/de/da2/wingdi_8h.html#a339712ef552530086058f3ceab852a24'>https://doxygen.reactos.org/de/da2/wingdi_8h.html#a339712ef552530086058f3ceab852a24</a> </pre> <br /> 마이크로소프트의 KB 문서에 있던 것과 유사한 방법으로 코딩된 것을 볼 수 있습니다. <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > { SIZE sz; TEXTMETRICW tm; static const WCHAR alphabet[] = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q', 'r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H', 'I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',0 }; if(!GetTextMetricsW(hdc, &tm)) return 0; if(!GetTextExtentPointW(hdc, alphabet, 52, &sz)) return 0; if (lptm) *lptm = tm; if (height) *height = tm.tmHeight; return (sz.cx / 26 + 1) / 2; } </pre> <br /> 어쨌든, ^^ 이렇게 해서 지난 글의 소스 코드를 완전하게 동작하도록 마무리 지을 수 있었습니다.<br /> <br /> (첨부 파일은 <a target='tab' href='https://www.sysnet.pe.kr/2/0/13287'>지난 글의 예제 코드</a>를 <a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=2033&boardid=331301885'>이번 글의 변경 사항을 적용해 수정한 버전</a>입니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 참고로, <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapdialogrect'>MapDialogRect</a> Win32 함수도 DLU 단위를 픽셀로 변환은 해줍니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > void CAboutDlg::OnPaint() { CPaintDC dc(this); // device context for painting CRect rc( 0, 0, 4, 8 ); <span style='color: blue; font-weight: bold'>MapDialogRect( &rc );</span> int baseUnitY = rc.bottom; int baseUnitX = rc.right; TRACE("baseUnitX = %d\n", baseUnitX); TRACE("baseUnitY = %d\n", baseUnitY); } </pre> <br /> 하지만 이 함수는 우리가 직접 만든 대화창의 소스코드에서는 사용할 수 없습니다. 왜냐하면 MapDialogRect의 내부에서 HWND로부터 <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw'>GetWindowLongPtr</a>을 이용해 부가 정보를 요구하는데, 이것은 마이크로소프트가 만든 DialogBox Win32 API 등에서 설정을 해주기 때문입니다. (물론 우리도 그것이 요구하는 포맷에 맞게 맞춰주면 됩니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 마지막으로, pt를 pixel로 변환하는 것을 검색해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Font dialog font size in pixel ; <a target='tab' href='https://www.syncfusion.com/forums/164411/font-dialog-font-size-in-pixel'>https://www.syncfusion.com/forums/164411/font-dialog-font-size-in-pixel</a> </pre> <br /> 아예 다음과 같이 고정 공식을 소개하기도 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 72 points = 96 pixels 1 point = 96/72 points </pre> <br /> 위의 변환은 96dpi 시스템이라면 정상 동작하겠지만 dpi가 다른 경우라면 틀릴 수밖에 없습니다. 반드시 GetDeviceCaps 함수로 DPI를 구한 후 적용하는 것이 권장됩니다.<br /> </p><br /> <br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1686
(왼쪽의 숫자를 입력해야 합니다.)