성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용...
[이종효] 오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능...
[정성태] @Keystroke IEEE의 문서를 소개해 주시다니... +_...
[손민수 (Keystroke)] 괜히 듀얼채널 구성할 때 한번에 같은 제품 사라고 하는 것이 아...
[정성태] 전각(Full-width)/반각(Half-width) 기능을 토...
[정성태] Vector에 대한 내용은 없습니다. Vector가 닷넷 BCL...
[orion] 글 읽고 찾아보니 디자인 타임에는 InitializeCompon...
[orion] 연휴 전에 재현 프로젝트 올리자 생각해 놓고 여의치 않아서 못 ...
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
글쓰기
제목
이름
암호
전자우편
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'>C# - Win32 API에 대한 P/Invoke를 대신하는 Microsoft.Windows.CsWin32 패키지</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;' > Making Win32 APIs More Accessible to More Languages ; <a target='tab' href='https://blogs.windows.com/windowsdeveloper/2021/01/21/making-win32-apis-more-accessible-to-more-languages/'>https://blogs.windows.com/windowsdeveloper/2021/01/21/making-win32-apis-more-accessible-to-more-languages/</a> microsoft/win32metadata - Tooling to generate metadata for Win32 APIs in the Windows SDK. ; <a target='tab' href='https://github.com/microsoft/win32metadata'>https://github.com/microsoft/win32metadata</a> C#/Win32 ; <a target='tab' href='https://github.com/microsoft/CsWin32'>https://github.com/microsoft/CsWin32</a> </pre> <br /> 설명 대신 곧바로 ^^ 코드로 들어가 볼까요?<br /> <br /> 우선, C# 프로젝트를 생성한 다음 Nuget으로부터 Microsoft.Windows.CsWin32 패키지를 참조 추가합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Install-Package <a target='tab' href='https://www.nuget.org/packages/Microsoft.Windows.CsWin32/'>Microsoft.Windows.CsWin32</a> -Version 0.1.422-beta </pre> <br /> 그다음, NativeMethods.txt 파일을 프로젝트에 추가하고 호출을 원하는 Win32 API 함수를 적어놓습니다. 가령 지난 글의 실습으로 RTT(Round-Trip Time) 값을 구할 수 있는 방법으로 "GetRTTAndHopCount" API를 소개만 하고 지나갔는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > BDP(Bandwidth-delay product)와 TCP Receive Window ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12536'>https://www.sysnet.pe.kr/2/0/12536</a> </pre> <br /> 이 함수를 사용하기 위해 NativeMethods.txt 파일에 다음과 같이 함수 이름을 (라인 하나당) 하나 기입합니다. (asterisk(*)를 이용한 와일드카드 사용도 가능합니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > GetRTTAndHopCount </pre> <br /> 이후 다음과 같이 using 문과 (정의한 적도 없는) PInvoke 타입의 정적 멤버로 GetRTTAndHopCount를 호출할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Net; <span style='color: blue; font-weight: bold'>using Microsoft.Windows.Sdk;</span> class Program { static void Main(string[] args) { foreach (IPAddress addr in Dns.GetHostAddresses("www.microsoft.com")) { if (addr.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) { continue; } if (<span style='color: blue; font-weight: bold'>PInvoke.GetRTTAndHopCount((uint)addr.Address, out uint hopCount, 30, out uint roundTripTime)</span> == true) { Console.WriteLine($"{addr} {roundTripTime}(ms), {hopCount}"); } } } } /* 출력 결과 23.201.37.168 2(ms), 9 */ </pre> <br /> <hr style='width: 50%' /><br /> <br /> 그냥 보면 마법 같아 보이지만, 사실 이에는 "Source Generator" 기술이 숨겨져 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - Source Generator 소개 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12223'>https://www.sysnet.pe.kr/2/0/12223</a> C# - Source Generator를 적용한 XmlCodeGenerator ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12228'>https://www.sysnet.pe.kr/2/0/12228</a> </pre> <br /> 따라서, 참조 추가했던 Microsoft.Windows.CsWin32 패키지는 "Analyzers"에도 추가되어 "NativeMethods.txt" 파일이 프로젝트와 같은 폴더에 존재하면 그 파일 내에 지정된 Win32 API를 포함하는 PInvoke 타입을 다음과 같이 만들어 빌드에 포함시킵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // %LOCALAPPDATA%\Temp\VisualStudioSourceGeneratedDocuments\dd7594ed-db9c-471a-82b2-2e0635bc62ba\Microsoft.Windows.CsWin32\Microsoft.Windows.CsWin32.SourceGenerator\PInvoke.IPHlpApi.cs using System; using System.Runtime.InteropServices; namespace Microsoft.Windows.Sdk { internal static class PInvoke { internal unsafe static BOOL GetRTTAndHopCount(uint DestIpAddress, out uint HopCount, uint MaxHops, out uint RTT) { fixed (uint* ptr = &RTT) { uint* RTTLocal = ptr; fixed (uint* ptr2 = &HopCount) { uint* HopCountLocal = ptr2; return PInvoke.GetRTTAndHopCount(DestIpAddress, HopCountLocal, MaxHops, RTTLocal); } } } [DllImport("IPHlpApi", ExactSpelling = true, SetLastError = true)] internal unsafe static extern BOOL GetRTTAndHopCount(uint DestIpAddress, [Out] uint* HopCount, uint MaxHops, [Out] uint* RTT); } } </pre> <br /> 따라서 비록 베타 버전이긴 해도 그 자체의 기능을 이용하는 것이 아닌, 별도로 생성한 (그리고 눈으로 확인할 수 있는) 코드 파일을 사용하는 것이므로 정식 버전을 기다릴 필요 없이 지금 당장 사용해도 무방합니다.<br /> <br /> 그 외에 약간 <a target='tab' href='https://github.com/microsoft/CsWin32#customizing-generated-code'>사용자 정의할 수 있는 부분들이 제공</a>되는데 이에 대해서는 다음의 json 파일을 참고하세요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > settings.schema.json ; <a target='tab' href='https://raw.githubusercontent.com/microsoft/CsWin32/main/src/Microsoft.Windows.CsWin32/settings.schema.json'>https://raw.githubusercontent.com/microsoft/CsWin32/main/src/Microsoft.Windows.CsWin32/settings.schema.json</a> </pre> <br /> 마지막으로, <a target='tab' href='https://github.com/microsoft/CsWin32'>문서</a>상으로는 "C# 9 + .NET 5 SDK" 또는 "비주얼 스튜디오 2019 (16.8) 버전"을 요구합니다. 하지만 프로젝트의 닷넷 버전도 문제가 될 수 있는데요, 가령 제가 테스트해 본 바에 의하면 .NET Core 2.0 미만 프로젝트와 .NET Framework 4.6.1 미만에서는 동작하지 않았습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 아직 Prerelease 상태이므로 다음과 같이 추가하는 경우 오류가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PM> <span style='color: blue; font-weight: bold'>Install-Package Microsoft.Windows.CsWin32</span> Install-Package : Unable to find package 'Microsoft.Windows.CsWin32' At line:1 char:1 + Install-Package Microsoft.Windows.CsWin32 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [Install-Package], Exception + FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PackageManagement.PowerShellCmdlets.InstallPackageCommand Time Elapsed: 00:00:00.0433365 </pre> <br /> 현재(2021-02-17) 기준으로 버전을 반드시 명시해야 합니다.<br /> <br /> 또한, NativeMethods.txt 파일은 프로젝트에 명시적으로 등록하지 않아도 됩니다. 즉, csproj 파일과 같은 폴더에 있기만 하면 됩니다. 이 때문에 서로 다른 프로젝트에서 기존 NativeMethods.txt 파일을 재사용할 수 없습니다. 즉, "Add as Link"로 추가하는 식으로 사용할 수 없습니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
5632
(왼쪽의 숫자를 입력해야 합니다.)