Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

(시리즈 글이 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" 크기가 그대로 반환됩니다. (마지막에 언급하겠지만, 위와 같이 구한 값은 정상적으로 대화창의 크기를 정하지 못했습니다.)




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으로 나오긴 했지만, 암튼 정확한 공식을 찾을 수는 없었습니다. 혹시 아시는 분은 덧글 부탁드립니다. ^^





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







[최초 등록일: ]
[최종 수정일: 11/2/2023]

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)
13565정성태2/23/2024110닷넷: 2219. .NET CLR2 보안 모델에서의 개별 System.Security.Permissions 제어
13564정성태2/22/2024413Windows: 259. Hyper-V Generation 1 유형의 VM을 Generation 2 유형으로 바꾸는 방법
13563정성태2/21/2024454디버깅 기술: 196. windbg - async/await 비동기인 경우 메모리 덤프 분석의 어려움
13562정성태2/21/2024431오류 유형: 896. ASP.NET - .NET Framework 기본 예제에서 System.Web에 대한 System.IO.FileNotFoundException 예외 발생
13561정성태2/20/2024819닷넷: 2218. C# - (예를 들어, Socket) 비동기 I/O에 대한 await 호출 시 CancellationToken을 이용한 취소파일 다운로드1
13560정성태2/19/20241041디버깅 기술: 195. windbg 분석 사례 - Semaphore 잠금으로 인한 Hang 현상 (닷넷)
13559정성태2/19/20241259오류 유형: 895. ASP.NET - System.Security.SecurityException: 'Requested registry access is not allowed.'
13558정성태2/18/20241149닷넷: 2217. C# - 최댓값이 1인 SemaphoreSlim 보다 Mutex 또는 lock(obj)를 선택하는 것이 나은 이유
13557정성태2/18/20241024Windows: 258. Task Scheduler의 Author 속성 값을 변경하는 방법
13556정성태2/17/20241016Windows: 257. Windows - Symbolic (hard/soft) Link 및 Junction 차이점
13555정성태2/15/20241146닷넷: 2216. C# - SemaphoreSlim 사용 시 주의점
13554정성태2/15/2024950VS.NET IDE: 189. Visual Studio - 닷넷 소스코드 디컴파일 찾기가 안 될 때
13553정성태2/14/20241081닷넷: 2215. windbg - thin/fat lock 없이 동작하는 Monitor.Wait + Pulse
13552정성태2/13/20241004닷넷: 2214. windbg - Monitor.Enter의 thin lock과 fat lock
13551정성태2/12/20241124닷넷: 2213. ASP.NET/Core 웹 응용 프로그램 - 2차 스레드의 예외로 인한 비정상 종료
13550정성태2/11/20241169Windows: 256. C# - Server socket이 닫히면 Accept 시켰던 자식 소켓이 닫힐까요?
13549정성태2/3/20241465개발 환경 구성: 706. C# - 컨테이너에서 실행하기 위한 (소켓) 콘솔 프로젝트 구성
13548정성태2/1/20241280개발 환경 구성: 705. "Docker Desktop for Windows" - ASP.NET Core 응용 프로그램의 소켓 주소 바인딩(IPv4/IPv6 loopback, Any)
13547정성태1/31/20241113개발 환경 구성: 704. Visual Studio - .NET 8 프로젝트부터 dockerfile에 추가된 "USER app" 설정
13546정성태1/30/20241053Windows: 255. (디버거의 영향 등으로) 대상 프로세스가 멈추면 Socket KeepAlive로 연결이 끊길까요?
13545정성태1/30/2024982닷넷: 2212. ASP.NET Core - 우선순위에 따른 HTTP/HTTPS 호스트:포트 바인딩 방법
13544정성태1/30/20241016오류 유형: 894. Microsoft.Data.SqlClient - Could not load file or assembly 'System.Security.Permissions, ...'
13543정성태1/30/2024988Windows: 254. Windows - 기본 사용 중인 5357 포트 비활성화는 방법
13542정성태1/30/2024954오류 유형: 893. Visual Studio - Web Application을 실행하지 못하는 IISExpress - 두 번째 이야기
13541정성태1/29/20241000VS.NET IDE: 188. launchSettings.json의 useSSL 옵션
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...