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

(시리즈 글이 2개 있습니다.)
.NET Framework: 786. ASP.NET - HttpCookieCollection을 다중 스레드에서 사용할 경우 무한 루프 현상
; https://www.sysnet.pe.kr/2/0/11587

디버깅 기술: 221. WinDbg 분석 사례 - ASP.NET HttpCookieCollection을 다중 스레드에서 사용할 경우 무한 루프 현상 - 두 번째 이야기
; https://www.sysnet.pe.kr/2/0/13916




WinDbg 분석 사례 - ASP.NET HttpCookieCollection을 다중 스레드에서 사용할 경우 무한 루프 현상 - 두 번째 이야기

아래와 같이 덤프 파일을 분석해,

windbg 분석 사례 - x86 ASP.NET 웹 응용 프로그램의 CPU 100% 현상 (4)
; https://www.sysnet.pe.kr/2/0/13914

requestValidationMode 값을 4.0으로 설정하면 문제가 발생하지 않을 거라고 했는데, 그래도 여전히 해당 현상이 재현된다는 소식과 함께 다시 덤프 파일을 받았습니다.

이럴 땐 자연스럽게, ^^; 제 눈으로 본 것이 아니기 때문에 정말 4.0으로 설정했는지 확인해야 합니다. 어떻게 할 수 있을까요? ^^

우선 HttpRuntimeSection.RequestValidationMode 속성의 역어셈블 코드를 보면,

[ConfigurationProperty("requestValidationMode", DefaultValue = "4.0")]
[TypeConverter(typeof(VersionConverter))]
public Version RequestValidationMode
{
    get
    {
        if (this._requestValidationMode == null)
        {
            this._requestValidationMode = (Version)base[HttpRuntimeSection._propRequestValidationMode];
        }
        return this._requestValidationMode;
    }
    set
    {
        this._requestValidationMode = value;
        base[HttpRuntimeSection._propRequestValidationMode] = value;
    }
}

보는 바와 같이 _requestValidationMode 필드에 값이 보관됩니다. 다행히 HttpRuntimeSection은 Web Application에 1개의 인스턴스, 또는 다른 이유로 인해 1~2개 정도 더 생성될 것이라고 짐작할 수 있으므로 GC Heap에 존재할 그 인스턴스를 검색해 보면 됩니다.

// 우선 해당 타입의 MethodTable을 알아내고,
0:000> !name2ee System.Web.dll!System.Web.Configuration.HttpRuntimeSection
Module:      19c1a4c4
Assembly:    System.Web.dll
Token:       020006fe
MethodTable: 1a7c4248
EEClass:     1b5966e0
Name:        System.Web.Configuration.HttpRuntimeSection

// GC Heap에서 HttpRuntimeSection 인스턴스를 검색
0:000> !dumpheap -mt 1a7c4248
 Address       MT     Size
0b6522c8 1a7c4248      112     
0b652438 1a7c4248      112     

Statistics:
      MT    Count    TotalSize Class Name
1a7c4248        2          224 System.Web.Configuration.HttpRuntimeSection
Total 2 objects
Fragmented blocks larger than 0.5 MB:
    Addr     Size      Followed by
0a862394    0.6MB         0a8f6234 System.Byte[]

보는 바와 같이 2개의 인스턴스가 있는데요, 각각을 덤프해 보면,

// 인스턴스 1개는 _requestValidationMode 필드가 비어 있고,

0:000> !do 0b6522c8
Name:        System.Web.Configuration.HttpRuntimeSection
MethodTable: 1a7c4248
EEClass:     1b5966e0
Size:        112(0x70) bytes
File:        C:\Windows\Microsoft.Net\assembly\GAC_32\System.Web\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Web.dll
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
0663aa48  4000198       30       System.Boolean  1 instance        0 _bDataToWrite
...[생략]...
0663c9b0  4002e15       50         System.Int32  1 instance       -1 _RequestLengthDiskThresholdBytes
1a62e23c  4002e17       3c       System.Version  0 instance 00000000 _requestValidationMode
0663c9b0  4002e18       54         System.Int32  1 instance        0 _MaxUrlLength
...[생략]...

// 다른 인스턴스에는 4.0.-1.-1 값이 설정됨

0:000> !do 0b652438
Name:        System.Web.Configuration.HttpRuntimeSection
MethodTable: 1a7c4248
EEClass:     1b5966e0
Size:        112(0x70) bytes
File:        C:\Windows\Microsoft.Net\assembly\GAC_32\System.Web\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Web.dll
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
0663aa48  4000198       30       System.Boolean  1 instance        0 _bDataToWrite
...[생략]...
0663c9b0  4002e15       50         System.Int32  1 instance    81920 _RequestLengthDiskThresholdBytes
1a62e23c  4002e17       3c       System.Version  0 instance 0b652664 _requestValidationMode
0663c9b0  4002e18       54         System.Int32  1 instance      260 _MaxUrlLength
...[생략]...

0:000> !do 0b652664
Name:        System.Version
MethodTable: 1a62e23c
EEClass:     1a63b0c4
Size:        24(0x18) bytes
File:        C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
0663c9b0  400073a        4         System.Int32  1 instance        4 _Major
0663c9b0  400073b        8         System.Int32  1 instance        0 _Minor
0663c9b0  400073c        c         System.Int32  1 instance       -1 _Build
0663c9b0  400073d       10         System.Int32  1 instance       -1 _Revision
19c14e2c  400073e      1d0        System.Char[]  0   shared   static SeparatorsArray

그러니까, requestValidationMode 값을 4.0으로 설정한 것이 맞고, 그럼에도 불구하고 문제가 발생하고 있는 것입니다.




그렇다면, 분명히 어떤 식으로든 EnableGranularValidation 코드가 수행되고 있다는 건데요, 이건 어떻게 확인할 수 있을까요? ^^

이를 위해 WinDbg를 동원할 필요는 없고, 위의 분석 대상이었던 응용 프로그램의 유형이 ASP.NET MVC 프로젝트였으므로 이에 대한 간단한 예제 프로젝트를 Visual Studio에서 생성한 후 쿠키를 사용하는 코드를 넣어,

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var oldCookie = this.Request.Cookies["test"];
        this.Response.Cookies.Add(new HttpCookie("test", "testValue") { Expires = DateTime.Now.AddDays(1) });

        return View();
    }

    // ...[생략]...
}

디버깅을 시작해 솔루션 탐색기의 "External Sources"로부터 HttpCookieCollection 소스 코드를 열어 BP를 걸어보면 됩니다.

cookie_validation_on_mvc_1.png

실제로, web.config의 requestValidationMode를 4.0으로 설정해도,

<httpRuntime targetFramework="4.8" requestValidationMode="4.0" />

동일하게 BP가 걸리는데요, 이때의 callstack을 따라가 보면,

>    System.Web.dll!System.Web.HttpCookieCollection.EnableGranularValidation(System.Web.ValidateStringCallback validationCallback = {Method = Inspecting the state of an object in the debuggee of type System.Delegate is not supported in this context.}) Line 127 C#  Symbols loaded.
    System.Web.dll!System.Web.HttpRequest.Cookies.get() Line 1225   C#  Symbols loaded.
    net48_mvc.dll!net48_mvc.Controllers.HomeController.Index() Line 14  C#  Symbols loaded.
    [Lightweight Function]      Annotated Frame
    System.Web.Mvc.dll!System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(System.Web.Mvc.ControllerContext controllerContext = {System.Web.Mvc.ControllerContext}, System.Web.Mvc.ActionDescriptor actionDescriptor = {System.Web.Mvc.ReflectedActionDescriptor}, System.Collections.Generic.IDictionary<string, object> parameters) Line 253  C#  Symbols loaded.
    System.Web.Mvc.dll!System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeSynchronousActionMethod.AnonymousMethod__9_0(System.IAsyncResult asyncResult, System.Web.Mvc.Async.AsyncControllerActionInvoker.ActionInvocation innerInvokeState) Line 304 C#  Symbols loaded.
    System.Web.Mvc.dll!System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult<System.__Canon, System.Web.Mvc.Async.AsyncControllerActionInvoker.ActionInvocation>.CallEndDelegate(System.IAsyncResult asyncResult) Line 224   C#  Symbols loaded.
    // ...[생략]...
    System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags) Line 386  C#  Symbols loaded.
    [AppDomain Transition]      Annotated Frame

관련 코드의 역어셈블 결과에서,

// HttpRequest.cs

public HttpCookieCollection Cookies
{
    get
    {
        EnsureCookies();
        if (_flags[4])
        {
            _flags.Clear(4);
            ValidateCookieCollection(_cookies);
        }
        return _cookies;
    }
}

private void ValidateCookieCollection(HttpCookieCollection cc)
{
	if (GranularValidationEnabled)
	{
		cc.EnableGranularValidation(delegate(string key, string value)
        {
            ValidateString(value, key, RequestValidationSource.Cookies);
        });
		return;
	}
	int count = cc.Count;
	for (int i = 0; i < count; i++)
	{
		string key2 = cc.GetKey(i);
		string value2 = cc.Get(i).Value;
		if (!string.IsNullOrEmpty(value2))
		{
			ValidateString(value2, key2, RequestValidationSource.Cookies);
		}
	}
}

Cookies get 메서드로부터 결국 _flags[4] 값으로 인해 EnableGranularValidation이 호출되고 있음을 알 수 있습니다. 그리고 다시, 그것은 GranularValidationEnabled get 속성이 true를 반환하기 때문인데,

// HttpRequest.cs

private const int granularValidationEnabled = 1073741824; // 0x40000000

private bool GranularValidationEnabled => _flags[0x40000000]; // 0x40000000

internal void EnableGranularRequestValidation()
{
	_flags[0x40000000] = true;
}

따라서 EnableGranularRequestValidation 메서드가 호출된 적이 있다는 것이므로 이것도 BP를 걸어 확인하면, 호출 스택이 이렇게 나오고,

>    System.Web.dll!System.Web.HttpRequest.EnableGranularRequestValidation() Line 2672   C#  Symbols loaded.
    System.Web.Mvc.dll!System.Web.Mvc.MvcHandler.ProcessRequestInit(System.Web.HttpContextBase httpContext = {System.Web.HttpContextWrapper}, out System.Web.Mvc.IController controller = null, out System.Web.Mvc.IControllerFactory factory = null) Line 168  C#  Symbols loaded.
    System.Web.Mvc.dll!System.Web.Mvc.MvcHandler.BeginProcessRequest(System.Web.HttpContextBase httpContext, System.AsyncCallback callback = {Method = {System.Reflection.RuntimeMethodInfo}}, object state = null) Line 86 C#  Symbols loaded.
    System.Web.dll!System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() Line 648 C#  Symbols loaded.
    System.Web.dll!System.Web.HttpApplication.ExecuteStepImpl(System.Web.HttpApplication.IExecutionStep step) Line 3596 C#  Symbols loaded.
    System.Web.dll!System.Web.HttpApplication.ExecuteStep(System.Web.HttpApplication.IExecutionStep step = {System.Web.HttpApplication.CallHandlerExecutionStep}, ref bool completedSynchronously = false) Line 3622    C#  Symbols loaded.
    System.Web.dll!System.Web.HttpApplication.PipelineStepManager.ResumeSteps(System.Exception error) Line 1161 C#  Symbols loaded.
    System.Web.dll!System.Web.HttpApplication.BeginProcessRequestNotification(System.Web.HttpContext context, System.AsyncCallback cb) Line 4054    C#  Symbols loaded.
    System.Web.dll!System.Web.HttpRuntime.ProcessRequestNotificationPrivate(System.Web.Hosting.IIS7WorkerRequest wr = {System.Web.Hosting.IIS7WorkerRequest}, System.Web.HttpContext context = {System.Web.HttpContext}) Line 1407  C#  Symbols loaded.
    System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext = 0x0000026e78687998, System.IntPtr moduleData, int flags) Line 461   C#  Symbols loaded.
    System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags) Line 386  C#  Symbols loaded.
    [Native to Managed Transition]      Annotated Frame
    [Managed to Native Transition]      Annotated Frame
    System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags) Line 499    C#  Symbols loaded.
    System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags) Line 386  C#  Symbols loaded.
    [AppDomain Transition]      Annotated Frame

Mvc 프레임워크에 놓인 ProcessRequestInit 메서드에서 ValidationUtility.EnableDynamicValidation 메서드를 호출하며, 그 원인은 다시 ValidationUtility.IsValidationEnabled가 true를 반환하기 때문입니다.


// MvcHandler.cs (System.Web.Mvc.dll)
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
    HttpContext current = HttpContext.Current;
    if (current != null && ValidationUtility.IsValidationEnabled(current) == true)
    {
        ValidationUtility.EnableDynamicValidation(current);
    }
    AddVersionHeader(httpContext);
    RemoveOptionalRoutingParameters();
    string requiredString = RequestContext.RouteData.GetRequiredString("controller");
    factory = ControllerBuilder.GetControllerFactory();
    controller = factory.CreateController(RequestContext, requiredString);
    if (controller == null)
    {
        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[2]
        {
            factory.GetType(),
            requiredString
        }));
    }
}

// Microsoft.Web.Infrastructure.dll

[EditorBrowsable(EditorBrowsableState.Never)]
public static class ValidationUtility
{
    // ...[생략]...

    [SecuritySafeCritical]
    public static bool? IsValidationEnabled(HttpContext context)
    {
        if (DynamicValidationShimReflectionUtil.Instance != null)
        {
            return DynamicValidationShimReflectionUtil.Instance.IsValidationEnabled(context);
        }
        return CollectionReplacer.IsValidationEnabled(context);
    }
}

// System.Web.dll

internal static class DynamicValidationShim
{
    internal static bool IsValidationEnabled(HttpContext context)
    {
        return context.Request.ValidateInputWasCalled;
    }

    // ...[생략]...
}

// HttpRequest.cs (System.Web.dll)

internal bool ValidateInputWasCalled => _flags[32768]; // 0x8000

public void ValidateInput()
{
    if (!ValidateInputWasCalled && !RequestValidationSuppressed)
    {
        _flags.Set(32768);
        _flags.Set(1);
        _flags.Set(2);
        _flags.Set(4);
        _flags.Set(64);
        _flags.Set(128);
        _flags.Set(256);
        _flags.Set(512);
        _flags.Set(8);
    }
}

끝이 보이는군요. ^^ 이제 ValidateInput이 호출된 위치를 다시 BP를 걸어 확인해 보면,

internal void ValidateInputIfRequiredByConfig()
{
    RuntimeConfig config = RuntimeConfig.GetConfig(Context);
    HttpRuntimeSection httpRuntime = config.HttpRuntime;
    if (CanValidateRequest())
    {
        string path = Path;
        if (path.Length > httpRuntime.MaxUrlLength)
        {
            throw new HttpException(400, SR.GetString("Url_too_long"));
        }
        if (QueryStringText.Length > httpRuntime.MaxQueryStringLength)
        {
            throw new HttpException(400, SR.GetString("QueryString_too_long"));
        }
        char[] requestPathInvalidCharactersArray = httpRuntime.RequestPathInvalidCharactersArray;
        if (requestPathInvalidCharactersArray != null && requestPathInvalidCharactersArray.Length != 0)
        {
            int num = path.IndexOfAny(requestPathInvalidCharactersArray);
            if (num >= 0)
            {
                string text = new string(path[num], 1);
                throw new HttpException(400, SR.GetString("Dangerous_input_detected", "Request.Path", text));
            }
            _flags.Set(65536);
        }
    }
    Version requestValidationMode = httpRuntime.RequestValidationMode;
    if (requestValidationMode == VersionUtil.Framework00)
    {
        _flags[int.MinValue] = true;
    }
    else if (requestValidationMode >= VersionUtil.Framework40)
    {
        ValidateInput();
        if (requestValidationMode >= VersionUtil.Framework45)
        {
            EnableGranularRequestValidation();
        }
    }
}

이렇게 requestValidationMode >= VersionUtil.Framework40인 상황에서 호출된 것이 MVC Framework에서는 (마이크로소프트가 의도한 것인지, 논리적으로 버그인 것인지 알 수 없으나) 기본적으로 Cookie의 EnableGranularValidation 기능을 활성화시켜버리는 것입니다.

결국, MVC 프로젝트라면 web.config에 requestValidationMode 값을 오히려 4.0 미만으로 설정해야 하는데, 가능한 값이 정해져 있으므로 2.0 정도로 내려야 합니다.




부가적으로 하나 더 정리하자면, ValidationUtility의 경우 중간에 DynamicValidationShimReflectionUtil.Instance를 접근하는데,

// ValidationUtility.cs (Microsoft.Web.Infrastructure.dll)

[SecuritySafeCritical]
public static bool? IsValidationEnabled(HttpContext context)
{
    if (DynamicValidationShimReflectionUtil.Instance != null)
    {
        return DynamicValidationShimReflectionUtil.Instance.IsValidationEnabled(context);
    }
    return CollectionReplacer.IsValidationEnabled(context);
}

이것은 Reflection을 이용해 System.Web.dll에 있는 Microsoft.Web.Infrastructure.DynamicValidationHelper.DynamicValidationShim 타입 정보를 로드하는 역할을 합니다.

// DynamicValidationShimReflectionUtil.cs (Microsoft.Web.Infrastructure.dll)

namespace Microsoft.Web.Infrastructure.DynamicValidationHelper;

[SecurityCritical]
internal sealed class DynamicValidationShimReflectionUtil
{
    // ...[생략]...

    [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We catch and ignore all errors because we do not want to bring down the application.")]
    private static DynamicValidationShimReflectionUtil GetInstance()
    {
        try
        {
            DynamicValidationShimReflectionUtil dynamicValidationShimReflectionUtil = new DynamicValidationShimReflectionUtil();
            Type type = CommonAssemblies.SystemWeb.GetType("Microsoft.Web.Infrastructure.DynamicValidationHelper.DynamicValidationShim", throwOnError: false);
            if (type == null)
            {
                return null;
            }
            MethodInfo method = CommonReflectionUtil.FindMethod(type, "EnableDynamicValidation", isStatic: true, new Type[1] { typeof(HttpContext) }, typeof(void));
            dynamicValidationShimReflectionUtil.EnableDynamicValidation = CommonReflectionUtil.MakeDelegate<Action<HttpContext>>(method);
            CommonReflectionUtil.Assert(dynamicValidationShimReflectionUtil.EnableDynamicValidation != null);
            MethodInfo method2 = CommonReflectionUtil.FindMethod(type, "IsValidationEnabled", isStatic: true, new Type[1] { typeof(HttpContext) }, typeof(bool));
            dynamicValidationShimReflectionUtil.IsValidationEnabled = CommonReflectionUtil.MakeDelegate<Func<HttpContext, bool>>(method2);
            CommonReflectionUtil.Assert(dynamicValidationShimReflectionUtil.IsValidationEnabled != null);
            MethodInfo method3 = CommonReflectionUtil.FindMethod(type, "GetUnvalidatedCollections", isStatic: true, new Type[3]
            {
                typeof(HttpContext),
                typeof(Func<NameValueCollection>).MakeByRefType(),
                typeof(Func<NameValueCollection>).MakeByRefType()
            }, typeof(void));
            dynamicValidationShimReflectionUtil.GetUnvalidatedCollections = CommonReflectionUtil.MakeDelegate<GetUnvalidatedCollectionsCallback>(method3);
            CommonReflectionUtil.Assert(dynamicValidationShimReflectionUtil.GetUnvalidatedCollections != null);
            return dynamicValidationShimReflectionUtil;
        }
        catch
        {
            return null;
        }
    }
}

그리고 System.Web.dll에 있는 DynamicValidationShim은 마치 "Microsoft.Web.Infrastructure.dll"에서 접근할 수 있는 통로를 열어두는 것처럼 구현돼 있습니다.

// DynamicValidationShim.cs (System.Web.dll)
using System;
using System.Collections.Specialized;
using System.Web;

namespace Microsoft.Web.Infrastructure.DynamicValidationHelper;

internal static class DynamicValidationShim
{
    internal static void EnableDynamicValidation(HttpContext context)
    {
        context.Request.EnableGranularRequestValidation();
    }

    internal static bool IsValidationEnabled(HttpContext context)
    {
        return context.Request.ValidateInputWasCalled;
    }

    internal static void GetUnvalidatedCollections(HttpContext context, out Func<NameValueCollection> formGetter, out Func<NameValueCollection> queryStringGetter)
    {
        UnvalidatedRequestValues unvalidated = context.Request.Unvalidated;
        formGetter = () => unvalidated.Form;
        queryStringGetter = () => unvalidated.QueryString;
    }
}

사실 Microsoft.Web.Infrastructure.dll은 System.Web.dll을 직접 참조하고 있으므로 저런 식으로 접근할 필요가 없습니다. 그런데도, 이렇게 구현된 것은...? 아마도 닷넷 프레임워크의 BCL에 기본 포함된 System.Web.dll과는 달리 Microsoft.Web.Infrastructure.dll의 경우 .NET Framework 버전에 의존적이지 않게 만들 목적이었지 않았을까 싶습니다. 일례로, .NET 3.5 버전의 System.Web.dll에는 DynamicValidationShim 타입이 없다는 식의 고려를 한 것이 아닐까...라고 추측해 봅니다. ^^




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







[최초 등록일: ]
[최종 수정일: 4/23/2025]

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

비밀번호

댓글 작성자
 




... 46  47  48  49  50  51  [52]  53  54  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12637정성태5/10/202117776사물인터넷: 62. NodeMCU v1 ESP8266 보드의 A0 핀에 다중 아날로그 센서 연결 [1]
12636정성태5/10/202117990사물인터넷: 61. NodeMCU v1 ESP8266 보드의 A0 핀 사용법 - FSR-402 아날로그 압력 센서 연동파일 다운로드1
12635정성태5/9/202116328기타: 81. OpenTabletDriver를 (관리자 권한으로 실행하지 않고도) 관리자 권한의 프로그램에서 동작하게 만드는 방법
12634정성태5/9/202114798개발 환경 구성: 572. .NET에서의 필수 무결성 제어 - 외부 Manifest 파일을 두는 방법파일 다운로드1
12633정성태5/7/202117743개발 환경 구성: 571. UAC - 관리자 권한 없이 UIPI 제약을 없애는 방법
12632정성태5/7/202118973기타: 80. (WACOM도 지원하는) Tablet 공통 디바이스 드라이버 - OpenTabletDriver
12631정성태5/5/202117784사물인터넷: 60. ThingSpeak 사물인터넷 플랫폼에 ESP8266 NodeMCU v1 + 조도 센서 장비 연동파일 다운로드1
12630정성태5/5/202118537사물인터넷: 59. NodeMCU v1 ESP8266 보드의 A0 핀 사용법 - CdS Cell(GL3526) 조도 센서 연동파일 다운로드1
12629정성태5/5/202120298.NET Framework: 1057. C# - CoAP 서버 및 클라이언트 제작 (UDP 소켓 통신) [1]파일 다운로드1
12628정성태5/4/202118211Linux: 39. Eclipse 원격 디버깅 - Cannot run program "gdb": Launching failed
12627정성태5/4/202118300Linux: 38. 라즈베리 파이 제로 용 프로그램 개발을 위한 Eclipse C/C++ 윈도우 환경 설정
12626정성태5/3/202118404.NET Framework: 1056. C# - Thread.Suspend 호출 시 응용 프로그램 hang 현상 (2)파일 다운로드1
12625정성태5/3/202116886오류 유형: 714. error CS5001: Program does not contain a static 'Main' method suitable for an entry point
12624정성태5/2/202121356.NET Framework: 1055. C# - struct/class가 스택/힙에 할당되는 사례 정리 [10]파일 다운로드1
12623정성태5/2/202117675.NET Framework: 1054. C# 9 최상위 문에 STAThread 사용 [1]파일 다운로드1
12622정성태5/2/202113532오류 유형: 713. XSD 파일을 포함한 프로젝트 - The type or namespace name 'TypedTableBase<>' does not exist in the namespace 'System.Data'
12621정성태5/1/202118419.NET Framework: 1053. C# - 특정 레지스트리 변경 시 알림을 받는 방법 [1]파일 다운로드1
12620정성태4/29/202121556.NET Framework: 1052. C# - 왜 구조체는 16 바이트의 크기가 적합한가? [1]파일 다운로드1
12619정성태4/28/202121545.NET Framework: 1051. C# - 구조체의 크기가 16바이트가 넘어가면 힙에 할당된다? [2]파일 다운로드1
12618정성태4/27/202119806사물인터넷: 58. NodeMCU v1 ESP8266 CP2102 Module을 이용한 WiFi UDP 통신 [1]파일 다운로드1
12617정성태4/26/202117079.NET Framework: 1050. C# - ETW EventListener의 Keywords별 EventId에 따른 필터링 방법파일 다운로드1
12616정성태4/26/202116777.NET Framework: 1049. C# - ETW EventListener를 상속받았을 때 초기화 순서파일 다운로드1
12615정성태4/26/202114021오류 유형: 712. Microsoft Live 로그인 - 계정을 선택하는(Pick an account) 화면에서 진행이 안 되는 문제
12614정성태4/24/202118352개발 환경 구성: 570. C# - Azure AD 인증을 지원하는 ASP.NET Core/5+ 웹 애플리케이션 예제 구성 [4]파일 다운로드1
12613정성태4/23/202116695.NET Framework: 1048. C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (2) 관리 코드파일 다운로드1
12612정성태4/23/202116569.NET Framework: 1047. C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (1) PInvoke파일 다운로드1
... 46  47  48  49  50  51  [52]  53  54  55  56  57  58  59  60  ...