Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)
(시리즈 글이 21개 있습니다.)
Windows: 226. Win32 C/C++ - Dialog에서 값을 반환하는 방법
; https://www.sysnet.pe.kr/2/0/13284

Windows: 227. Win32 C/C++ - Dialog Procedure를 재정의하는 방법
; https://www.sysnet.pe.kr/2/0/13285

Windows: 228. Win32 - 리소스에 포함된 대화창 Template의 2진 코드 해석 방법
; https://www.sysnet.pe.kr/2/0/13286

Windows: 229. Win32 - 대화창 템플릿의 2진 리소스를 읽어들여 윈도우를 직접 띄우는 방법
; https://www.sysnet.pe.kr/2/0/13287

Windows: 230. Win32 - 대화창의 DLU 단위를 pixel로 변경하는 방법
; https://www.sysnet.pe.kr/2/0/13288

Windows: 231. Win32 - 대화창 템플릿의 2진 리소스를 읽어들여 자식 윈도우를 생성하는 방법
; https://www.sysnet.pe.kr/2/0/13289

Windows: 232. C/C++ - 일반 창에도 사용 가능한 IsDialogMessage
; https://www.sysnet.pe.kr/2/0/13292

Windows: 233.  Win32 - modeless 대화창을 modal처럼 동작하게 만드는 방법
; https://www.sysnet.pe.kr/2/0/13295

Windows: 234. IsDialogMessage와 협업하는 WM_GETDLGCODE Win32 메시지
; https://www.sysnet.pe.kr/2/0/13296

Windows: 235. Win32 - Code Modal과 UI Modal
; https://www.sysnet.pe.kr/2/0/13297

Windows: 237. Win32 - 모든 메시지 루프를 탈출하는 WM_QUIT 메시지
; https://www.sysnet.pe.kr/2/0/13299

Windows: 238. Win32 - Modal UI 창에 올바른 Owner(HWND)를 설정해야 하는 이유
; https://www.sysnet.pe.kr/2/0/13300

Windows: 242. Win32 - 시간 만료를 갖는 MessageBox 대화창 구현 (쉬운 버전)
; https://www.sysnet.pe.kr/2/0/13305

Windows: 243. Win32 - 윈도우(cbWndExtra) 및 윈도우 클래스(cbClsExtra) 저장소 사용 방법
; https://www.sysnet.pe.kr/2/0/13306

Windows: 244. Win32 - 시간 만료를 갖는 MessageBox 대화창 구현 (개선된 버전)
; https://www.sysnet.pe.kr/2/0/13312

Windows: 245. Win32 - 시간 만료를 갖는 컨텍스트 메뉴와 윈도우 메시지의 영역별 정의
; https://www.sysnet.pe.kr/2/0/13315

Windows: 246. Win32 C/C++ - 직접 띄운 대화창 템플릿을 위한 Modal 메시지 루프 생성
; https://www.sysnet.pe.kr/2/0/13329

Windows: 247. Win32 C/C++ - CS_GLOBALCLASS 설명
; https://www.sysnet.pe.kr/2/0/13330

Windows: 248. Win32 C/C++ - 대화창을 위한 메시지 루프 사용자 정의
; https://www.sysnet.pe.kr/2/0/13332

Windows: 249. Win32 C/C++ - 대화창 템플릿을 런타임에 코딩해서 사용
; https://www.sysnet.pe.kr/2/0/13333

Windows: 250. Win32 C/C++ - Modal 메시지 루프 내에서 SetWindowsHookEx를 이용한 Thread 메시지 처리 방법
; https://www.sysnet.pe.kr/2/0/13334




Win32 - 대화창 템플릿의 2진 리소스를 읽어들여 윈도우를 직접 띄우는 방법

지난 글에 다이얼로그 리소스 형식을 알아봤으니,

Win32 - 리소스에 포함된 대화창 Template의 2진 코드 해석 방법
; https://www.sysnet.pe.kr/2/0/13286

이번에는 그 리소스를 읽어 처리하는 "Dialog manager" 관점에서의 이야기를 풀어보겠습니다. 이것 역시 Raymond Chen의 oldnewthing 블로그에 실려 있는 글을 제 맘대로 정리한 것입니다. ^^

The dialog manager, part 1: Warm-ups
; https://devblogs.microsoft.com/oldnewthing/20050329-00/?p=36043




Modeless 대화창을 생성하는 CreateDialogXxx 유의 함수들을 볼까요?

// Modeless 대화창 생성하는 함수
#define CreateDialogA(hInstance, lpName, hWndParent, lpDialogFunc) CreateDialogParamA(hInstance, lpName, hWndParent, lpDialogFunc, 0L)
#define CreateDialogW(hInstance, lpName, hWndParent, lpDialogFunc) CreateDialogParamW(hInstance, lpName, hWndParent, lpDialogFunc, 0L)

#define CreateDialogIndirectA(hInstance, lpTemplate, hWndParent, lpDialogFunc) CreateDialogIndirectParamA(hInstance, lpTemplate, hWndParent, lpDialogFunc, 0L)
#define CreateDialogIndirectW(hInstance, lpTemplate, hWndParent, lpDialogFunc) CreateDialogIndirectParamW(hInstance, lpTemplate, hWndParent, lpDialogFunc, 0L)

위에서 CreateDialog 매크로가 연결된 CreateDialogParam의 경우 내부 코드에서 CreateDialogIndirectParam을 호출하게 됩니다.

HWND WINAPI CreateDialogParam(HINSTANCE hinst,
    LPCTSTR pszTemplate, HWND hwndParent,
    DLGPROC lpDlgProc, LPARAM dwInitParam)
{
  HWND hdlg = NULL;
  HRSRC hrsrc = FindResource(hinst, pszTemplate, RT_DIALOG);
  if (hrsrc) {
    HGLOBAL hglob = LoadResource(hinst, hrsrc);
    if (hglob) {
      LPVOID pTemplate = LockResource(hglob); // 여기서 읽힌 데이터는 대화창 Template을 나타내는 이진 코드
      if (pTemplate) {
        hdlg = CreateDialogIndirectParam(hinst, pTemplate, hwndParent, lpDlgProc, dwInitParam);
      }
      FreeResource(hglob);
    }
  }
  return hdlg;
}

보는 바와 같이, 단지 대화창 리소스를 찾아 해당 바이너리 스트림을 메모리에 로드하는 기능을 살짝 래핑해서 이후의 실질적인 해석 절차는 CreateDialogIndirectParam 함수 내에서 하게 되는 것입니다.

그러니까, 최종적으로는 모든 CreateDialogXxx 유의 함수들이 결국 CreateDialogIndirectParam 호출로 연결됩니다.




두 번째 글도 마저 살펴보겠습니다. ^^

The dialog manager, part 2: Creating the frame window
; https://devblogs.microsoft.com/oldnewthing/20050330-00/?p=36023

이전 글에서 설명했듯이 근래에는 (Windows 2000부터 지원하기 시작한) "32-bit Extended Templates"을 사용하고 있는데요, 이에 대한 구조체는 표준 Win32 헤더 파일에 포함되지는 않았지만 공식 문서로는 나와 있습니다. (아마도, 중간의 필드가 가변 길이를 갖기 때문에 C/C++ 헤더 파일에 실을 수는 없었을 것입니다.)

DLGTEMPLATEEX structure
; https://learn.microsoft.com/en-us/windows/win32/dlgbox/dlgtemplateex

DLGITEMTEMPLATEEX structure
; https://learn.microsoft.com/en-us/windows/win32/dlgbox/dlgitemtemplateex

따라서 첫 번째 할 일은, LockResource로 구한 포인터로부터 아래의 구조체를 채워 넣으면 됩니다.

typedef struct {
  WORD      dlgVer;
  WORD      signature;
  DWORD     helpID;
  DWORD     exStyle;
  DWORD     style;
  WORD      cDlgItems;
  short     x;
  short     y;
  short     cx;
  short     cy;
  sz_Or_Ord menu;
  sz_Or_Ord windowClass;
  WCHAR     title[titleLen];
  WORD      pointsize;
  WORD      weight;
  BYTE      italic;
  BYTE      charset;
  WCHAR     typeface[stringLen];
} DLGTEMPLATEEX;

중간에 문자열 처리가 가변적으로 돼 코드가 길어지므로 구체적인 코드는 첨부 파일에 실었으니 참고하시고 간단하게 다음의 메서드로 처리한다고 가정하겠습니다.

{
    // ...[생략]...
    LPVOID lpv = ::LockResource(hglobal);
    DWORD resSize = SizeofResource(hInst, hrsrc);

    DLGTEMPLATEEX* item = DLGTEMPLATEEX::ReadDialog(hInst, hWndMainWindow, AboutModeless, lpv);

    // 처리

    delete item;
}

INT_PTR CALLBACK AboutModeless(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        {
            WORD cmdId = LOWORD(wParam);
            if (cmdId == IDOK || cmdId == IDCANCEL)
            {
                ::DestroyWindow(hDlg);
                return (INT_PTR)TRUE;
            }
        }
        break;
    }

    return (INT_PTR)FALSE;
}

이후 가장 먼저 할 일은 DS_* 상수 스타일을 WS_* 또는 WS_EX_* 상수 스타일로 바꾸는 건데, 이에 대해 다음과 같이 정리하고 있습니다.

DS_MODALFRAME
    ==> Extended windw style: WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE

DS_CONTEXTHELP
    ==> Extended windw style: WS_EX_CONTEXTHELP

DS_CONTROL
    ==> window style에서 제거: WS_CAPTION, WS_SYSMENU    
    ==> Extended windw style: WS_EX_CONTROLPARENT
    (* DS_CONTROL 옵션을 WS_POPUP과는 배타적으로, WS_CHILD와는 함께 사용)

이전 글에서 만든 About 대화상자는 다음과 같은 스타일을 가지고 있기 때문에,

dwStyle == DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU

대충 이런 식으로 처리해 주면 됩니다.

if ((style & DS_MODALFRAME) == DS_MODALFRAME)
{
    style &= ~DS_MODALFRAME;
    exStyle |= WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE;
}

// ...[생략: DS_CONTEXTHELP, DS_CONTROL]...

그다음, menu가 지정되었다면 로드하는 것도 추가하고,

if (menu != nullptr && menu->HasValue())
{
    if (menu->ordinal != 0)
    {
        _hMenu = ::LoadMenu(NULL, MAKEINTRESOURCE(menu->ordinal));
    }
    else
    {
        _hMenu = ::LoadMenu(NULL, menu->name);
    }
}

폰트 정보도 DS_SETFONT의 유무에 따라 다음과 같이 읽어들여 준비해 둡니다.

/*
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "About Project1"
FONT 8, "MS Shell Dlg"
*/

if ((style & DS_SETFONT) == DS_SETFONT)
{
    HDC dc = GetDC(nullptr);
    int pixels = MulDiv(this->pointsize, GetDeviceCaps(dc, LOGPIXELSY), 72); // MM_TEXT 모드인 경우
    ReleaseDC(0, dc);

    _hFont = ::CreateFont(
        -pixels,
        0,
        0,
        0,
        weight,
        italic,
        0,
        0,
        DEFAULT_CHARSET,
        OUT_DEFAULT_PRECIS,
        CLIP_DEFAULT_PRECIS,
        DEFAULT_QUALITY,
        DEFAULT_PITCH,
        typeface);
}
else if ((style & DS_FIXEDSYS) == DS_FIXEDSYS) // DS_SETFONT보다 우선순위가 낮음(NT 4 운영체제 이하에서 실행되는 것을 고려)
{
    _hFont = (HFONT)GetStockObject(SYSTEM_FIXED_FONT);
}
else 
{
    _hFont = (HFONT)GetStockObject(SYSTEM_FONT);
}

자, 그다음은 대화창이 보일 위치를 정하는 건데요,

IDD_ABOUTBOX DIALOGEX 0, 0, 170, 62

이게 좀 재미있습니다. WPF가 pixel에 독립적인 단위를 썼던 것처럼, Dialog도 0, 0, 170, 62라고 기록한 값이 픽셀이 아닌 DLU라는 단위를 사용하고 있습니다.

따라서 DLU 단위를 픽셀로 변환해야 하는데요, 아래는 그 방법을 보여줍니다.

long baseUinits = GetDialogBaseUnits();
DWORD baseUinitsX = LOWORD(baseUinits); // 필자의 시스템에서, 8
DWORD baseUinitsY = HIWORD(baseUinits); // 필자의 시스템에서, 16

_dlgPosition.left = MulDiv(x, baseUinitsX, 4);
_dlgPosition.right = _dlgPosition.left + MulDiv(cx, baseUinitsX, 4); // 4 x-DLU

_dlgPosition.top = MulDiv(y, baseUinitsY, 8);
_dlgPosition.bottom = _dlgPosition.top + MulDiv(cy, baseUinitsY, 8); // 8 y-DLU

하지만 해당 API 문서 및 Raymond Chen도 언급하듯이,

GetDialogBaseUnits is a crock
; https://devblogs.microsoft.com/oldnewthing/20040217-00/?p=40573

GetDialogBaseUnits 함수는 대화창이 시스템 폰트를 사용하는 경우를 위한 것이라고 합니다. 따라서 폰트가 지정되었다면 GetTextMetrics와 GetTextExtentPoint32를 이용해 baseUinitsX, baseUinitsY을 구해야 하는데 아쉽게도 2개 모두 HDC 타입의 인자를 필요로 합니다.

윈도우가 아직 생성 전인데, HDC 타입으로 구했다는 것은, 아마도 어차피 Font에 대한 평균 width, height를 구하면 되기 때문에 빈 HDC를 하나 생성해서 구한 것이 아닌가... 예상해 봅니다. ^^ 따라서 다음과 같은 식으로 구하는 것도 가능합니다.

HDC hDC = GetDC(nullptr);
::SelectObject(hDC, _hFont);

TEXTMETRIC tm = { 0 };
GetTextMetrics(hDC, &tm);

int baseUnitsY = tm.tmHeight;
SIZE size = { 0 };

GetTextExtentPoint32(hDC, L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &size);
int baseUnitsX = (size.cx / 26 + 1) / 2;

int cxDlg = MulDiv(cx, baseUnitsX, 4);
int cyDlg = MulDiv(cy, baseUnitsY, 8);

::DeleteDC(hDC);

참고로, About 상자의 기본 폰트 정보로는,

FONT 8, "MS Shell Dlg"

baseUinitsX = 4, baseUinitsY = 8이 나오기 때문에 대화상자에 지정한 "0, 0, 170, 62" 크기가 그대로 반환됩니다. (마지막에 언급하겠지만, 위와 같이 구한 값은 정상적으로 대화창의 크기를 정하지 못했습니다.) (업데이트: Win32 - 대화창의 DLU 단위를 pixel로 변경하는 방법)




DLU에서 pixel로 변환한 크기는 윈도우의 Title Bar, Frame 등의 크기를 제외한 순수 client area에 해당합니다. 따라서, Window style에 따라 이 영역에 대한 크기 조정까지 해야 하는데요, AdjustWindowRectEx를 이용해 이 작업을 다음과 같이 처리할 수 있습니다.

RECT rcAdjust = { 0, 0, cxDlg, cyDlg };
AdjustWindowRectEx(&rcAdjust, dwStyle, hmenu != NULL, dwExStyle);
cxDlg = rcAdjust.right - rcAdjust.left;
cyDlg = rcAdjust.bottom - rcAdjust.top;

마지막으로, 윈도우의 생성 위치는 부모 윈도우를 기준으로 하므로, 만약 DS_ABSALIGN 설정이 없다면 x, y의 위치도 함께 조정해 줍니다.

if ((style & DS_ABSALIGN) != DS_ABSALIGN)
{
    POINT pt = { MulDiv(x, baseUnitsX, 4), MulDiv(y, baseUnitsY, 8) };
    ClientToScreen(_hwndParent, &pt);

    _dlgPosition.left = pt.x;
    _dlgPosition.top = pt.y;
}

_dlgPosition.right = _dlgPosition.left + cxDlg;
_dlgPosition.bottom = _dlgPosition.top + cyDlg;

위치를 잡았으니 이제 화면에 보여줘야 하는데요, 대화창의 경우 생성과 함께 보이도록 만들지는 않았다고 합니다. 따라서 윈도우 style에 VISIBLE 설정이 있으면 이를 제거하는 것으로 최종적으로는 다음과 같은 코드로 대화창 윈도우를 생성하게 됩니다.

BOOL fWasVisible = style & WS_VISIBLE;
style &= ~WS_VISIBLE;

LPCWSTR className = nullptr;
    
if (windowClass != nullptr && windowClass->HasValue())
{
    if (windowClass->ordinal != 0)
    {
        className = MAKEINTRESOURCE(windowClass->ordinal);
    }
    else
    {
        className = windowClass->name;
    }
}

if (className == nullptr)
{
    // 기본 대화창의 window classname == #32770
    className = MAKEINTRESOURCE(32770);
}
    
_hDlg = CreateWindowExW(
    exStyle, 
    className,
    title,
    style & 0xFFFF0000, // DS_* 스타일을 제거
    _dlgPosition.left,
    _dlgPosition.top,
    _dlgPosition.right - _dlgPosition.left,
    _dlgPosition.bottom - _dlgPosition.top,
    _hwndParent,
    _hMenu,
    _hInstance,
    nullptr);

::SetWindowLongPtr(_hDlg, DWLP_DLGPROC, (LPARAM)_dlgProc); // 사용자가 구현한 Dialog Procedure 호출을 위해
    
::SendMessageW(_hDlg, WM_SETFONT, (WPARAM)_hFont, FALSE); // 폰트 설정

::ShowWindow(_hDlg, SW_SHOW); // 원래는 이 단계에서 보여주진 않지만, 테스트를 위해 추가

위의 단계를 보면, 대화창과 관련된 몇 가지 의문이 풀립니다. 1) 사용자의 Dialog Procedure에서 WM_CREATE 이벤트를 받지 못하는 것은 윈도우를 생성 후 DWLP_DLGPROC 값을 설정하기 때문이고, 2) 마찬가지로 가장 먼저 받게 되는 이벤트가 WM_SETFONT인 것은 DWLP_DLGPROC 설정 후 폰트 설정이 뒤따르기 때문입니다.

실행 결과를 보면,

dialog_manager_part1_1.png

아직 내부 컨트롤들에 대한 처리가 없기 때문에 저렇게 빈 윈도우만 뜨게 됩니다. ^^

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




뒷이야기를 들여다볼까요? ^^

아래의 글을 보면,

What's so special about the desktop window?
; https://devblogs.microsoft.com/oldnewthing/20040224-00/?p=40493

Desktop 윈도우를 부모로 한 Modal 대화창을 띄우는 경우 바탕 화면 및 기타 다른 윈도우들이 먹통이 될 수 있다고 합니다.

  1. A modal dialog disables its owner.
  2. Every window is a descendant of the desktop.
  3. When a window is disabled, all its descendants are also disabled.

하지만 실제로 테스트를 해보면,

HWND hDesktopWnd = ::GetDesktopWindow();
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hDesktopWnd, About);

먹통이 되지 않았는데요, 아마도 2004년 저 당시의 윈도우에서 그랬을 것이고 이후로는 저 부분이 개선이 된 듯합니다.

또 하나는, 왜 대화창이 CreateWindowEx 호출 순간에는 보이지 않도록 했는지 설명하는 글이 있습니다.

Why are dialog boxes initially created hidden?
; https://devblogs.microsoft.com/oldnewthing/20040311-00/?p=40303

한 마디로, 저 당시에는 시스템 성능이 너무 좋지 않아서 그랬다고 합니다. 그런 탓에, 대화창이 뜨기도 전에 이미 대화창이 요구하는 입력을 타이핑한 후 Enter 키를 치면 대화창을 곧바로 종료시키게 만드는 묘기같은 사용법도 가능했다고 합니다.

시스템 성능이 나아진 지금도, 위의 처리가 유지된 것은 그것 나름대로 유용한 면이 있었다고 합니다. 일례로 CreateWindowEx 시점에 대화창이 보인 후, 사용자가 WM_INITDIALOG에서 넣어 두었던 컨트롤에 대한 조정 코드들이 수행되는 것이 보기에 좋지가 않았고, 또한 대화창이 보였을 때 체크박스 상자가 있어 체크를 했더니 이후에 실행된 WM_INITDIALOG 코드에서 체크 박스를 해제하는 동작이 뒤늦게 발생할 수 있다고... ^^;

마지막으로, 위에서 제가 작성한 코드의 대화창 크기가 올바르게 나오지 않았는데요, 수작업으로 맞춰 보니 각각 6과 13에서 정상적으로 대화창의 크기가 나왔습니다.

baseUnitsX = 6;
baseUnitsY = 13;

int cxDlg = MulDiv(this->cx, baseUnitsX, 4); // baseUnitsX:4 비율 == 1.5
int cyDlg = MulDiv(this->cy, baseUnitsY, 8); // baseUnitsY:8 비율 == 1.625

음... 저 값이 왜 저렇게 나왔는지 끼워 맞출 수가 없었습니다. ^^; 그나마 baseUnitsX의 경우 한글로 테스트를 했더니,

GetTextExtentPoint32(hDC, L"가나다", 3, &size); // size.cx == 18
int baseUnitsX = size.cx / 3.0;

6으로 나오긴 했지만, 암튼 정확한 공식을 찾을 수는 없었습니다. 혹시 아시는 분은 덧글 부탁드립니다. ^^
(업데이트: Win32 - 대화창의 DLU 단위를 pixel로 변경하는 방법)





[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/21/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




1  2  3  4  5  6  7  [8]  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13667정성태7/7/20243603닷넷: 2273. C# - 리눅스 환경에서의 Hyper-V Socket 연동 (AF_VSOCK)파일 다운로드1
13666정성태7/7/20244272Linux: 74. C++ - Vsock 예제 (Hyper-V Socket 연동)파일 다운로드1
13665정성태7/6/20244295Linux: 73. Linux 측의 socat을 이용한 Hyper-V 호스트와의 vsock 테스트파일 다운로드1
13663정성태7/5/20244184닷넷: 2272. C# - Hyper-V Socket 통신(AF_HYPERV, AF_VSOCK)의 VMID Wildcards 유형파일 다운로드1
13662정성태7/4/20244706닷넷: 2271. C# - WSL 2 VM의 VM ID를 알아내는 방법 - Host Compute System API파일 다운로드1
13661정성태7/3/20244402Linux: 72. g++ - 다른 버전의 GLIBC로 소스코드 빌드
13660정성태7/3/20244106오류 유형: 912. Visual C++ - Linux 프로젝트 빌드 오류
13659정성태7/1/20244357개발 환경 구성: 715. Windows - WSL 2 환경의 Docker Desktop 네트워크
13658정성태6/28/20244323개발 환경 구성: 714. WSL 2 인스턴스와 호스트 측의 Hyper-V에 운영 중인 VM과 네트워크 연결을 하는 방법 - 두 번째 이야기
13657정성태6/27/20244686닷넷: 2270. C# - Hyper-V Socket 통신(AF_HYPERV, AF_VSOCK)을 위한 EndPoint 사용자 정의
13656정성태6/27/20244195Windows: 264. WSL 2 VM의 swap 파일 위치
13655정성태6/24/20244272닷넷: 2269. C# - Win32 Resource 포맷 해석파일 다운로드1
13654정성태6/24/20244201오류 유형: 911. shutdown - The entered computer name is not valid or remote shutdown is not supported on the target computer.
13653정성태6/22/20244314닷넷: 2268. C# 코드에서 MAKEINTREOURCE 매크로 처리
13652정성태6/21/20244912닷넷: 2267. C# - Linux 환경에서 (Reflection 없이) DLL AssemblyFileVersion 구하는 방법파일 다운로드2
13651정성태6/19/20244539닷넷: 2266. C# - (Reflection 없이) DLL AssemblyFileVersion 구하는 방법파일 다운로드1
13650정성태6/18/20244559개발 환경 구성: 713. "WSL --debug-shell"로 살펴보는 WSL 2 VM의 리눅스 환경
13649정성태6/18/20244305오류 유형: 910. windbg - !py 확장 명령어 실행 시 "failed to find python interpreter" (2)
13648정성태6/17/20244145오류 유형: 909. C# - DynamicMethod 사용 시 System.TypeAccessException
13647정성태6/16/20244689개발 환경 구성: 712. Windows - WSL 2의 네트워크 통신 방법 - 세 번째 이야기 (같은 IP를 공유하는 WSL 2 인스턴스) [1]
13646정성태6/14/20243815오류 유형: 908. Process Explorer - "Error configuring dump resources: The system cannot find the file specified."
13645정성태6/13/20244482개발 환경 구성: 711. Visual Studio로 개발 시 기본 등록하는 dev tag 이미지로 Docker Desktop k8s에서 실행하는 방법
13644정성태6/12/20244935닷넷: 2265. C# - System.Text.Json의 기본적인 (한글 등에서의) escape 처리 [1]
13643정성태6/12/20244660오류 유형: 907. MySqlConnector 사용 시 System.IO.FileLoadException 오류
13642정성태6/11/20244817스크립트: 65. 파이썬 - asgi 버전(2, 3)에 따라 달라지는 uvicorn 호스팅
13641정성태6/11/20244655Linux: 71. Ubuntu 20.04를 22.04로 업데이트
1  2  3  4  5  6  7  [8]  9  10  11  12  13  14  15  ...