성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[양승조] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
[공진영] 안녕하세요 좋은글 감사합니다. 현재 제가 wpf로 관제 모...
[정성태] The Windows Registry Adventure #1: ...
[정성태] systemd for Developers I ; https:/...
글쓰기
제목
이름
암호
전자우편
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'>DEVPATH 환경 변수의 사용 예 - .NET Reflector의 (PDB 연결이 없는) DLL의 소스 코드 디버깅</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;' > 개발자를 위한 닷넷 어셈블리 바인딩 - DEVPATH 환경 변수 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12276'>https://www.sysnet.pe.kr/2/0/12276</a> </pre> <br /> DEVPATH를 멋들어지게 활용한 사례가 바로 ".NET Reflector"입니다. 일반적으로 "소스 코드가 없는" 어셈블리를 디버깅하는 방법이,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .NET Reflector를 이용한 "소스 코드가 없는" 어셈블리 디버깅 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/1201'>https://www.sysnet.pe.kr/2/0/1201</a> </pre> <br /> ".NET Reflector" 뿐만 아니라, <a target='tab' href='https://github.com/0xd4d/dnSpy'>dnSpy</a>나 JetBrains의 "<a target='tab' href='https://www.jetbrains.com/help/decompiler/Generating_PDB_Files.html'>dotPeek</a>"와 같은 도구에서도 제공하긴 합니다. 그런 와중에 .NET Reflector가 특별한 것은, 바로 PDB 정보가 없는 DLL에 대해서도 소스 코드 디버깅을 지원한다는 점입니다.<br /> <br /> 예전에도 이런 문제에 대해 설명한 적이 있는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 서드파티 dll 디버깅에 대해 질문드립니다. ; <a target='tab' href='https://www.sysnet.pe.kr/3/0/4852'>https://www.sysnet.pe.kr/3/0/4852</a> C# - PDB 파일 경로를 PE 파일로부터 얻는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/11237'>https://www.sysnet.pe.kr/2/0/11237</a> </pre> <br /> "IMAGE_DEBUG_DIRECTORY"를 갖지 않는 어셈블리의 경우 dnSpy는 다음과 같이 "Save PDB File" 옵션이 아예 비활성화되어 있습니다.<br /> <br /> <img alt='save_pdb_debug_1.png' src='/SysWebRes/bbs/save_pdb_debug_1.png' /><br /> <br /> 또한 dotPeek 도구에서는 해당 메뉴는 제공하지만 (오류 없이) 실행은 돼도 아무런 출력물이 없습니다. (사실 기능을 제공한다고 해도 이런 도구들은 DEVPATH와 연동을 하지 않기 때문에 정상적인 동작을 하지 않게 됩니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그렇다면 IMAGE_DEBUG_DIRECTORY의 유무에 따라 어떻게 기능이 달라지는 걸까요?<br /> <br /> 우선, IMAGE_DEBUG_DIRECTORY가 있는 어셈블리는 디버거가 IMAGE_DEBUG_DIRECTORY를 읽음으로써 PDB 등의 심벌 파일을 로드해야 한다는 것을 알 수 있으므로 dnSpy, dotPeek, .NET Reflector 등의 도구에서는 해당 DLL을 기반으로 PDB 파일만을 생성해 비주얼 스튜디오가 관리하는 심벌 파일 위치에,<br /> <br /> <img alt='save_pdb_debug_2.jpg' src='/SysWebRes/bbs/save_pdb_debug_2.jpg' /><br /> <br /> 넣어 놓으면 됩니다. 그럼, 비주얼 스튜디오는 디버깅 시 DLL에 대한 심벌 파일을 정해진 위치에서 찾아 로드하는 식으로 동작하게 됩니다.<br /> <br /> 문제는, IMAGE_DEBUG_DIRECTORY가 없는 경우입니다. 당연히 디버거 입장에서는 어떤 종류의 심벌 파일이 있는지, 그 심벌 파일의 이름이 뭔지조차 알 수 없으므로 아예 무시를 하게 됩니다. 즉, 심벌 파일을 로드하도록 만들려면 원본 DLL에 IMAGE_DEBUG_DIRECTORY를 심어 넣어야 하는 것입니다.<br /> <br /> 예상할 수 있듯이 dnSpy와 dotPeek 등의 도구는 원본 DLL을 수정하지 않기 때문에 IMAGE_DEBUG_DIRECTORY가 없는 어셈블리에 대해서는 역어셈블된 소스 코드에 대한 디버깅을 지원하지 못합니다. 반면 .NET Reflector는 마찬가지로 원본 DLL을 수정하지는 않지만, IMAGE_DEBUG_DIRECTORY를 가진 새로운 DLL을 생성해서 DEVPATH에 놓기 때문에 비주얼 스튜디오는 디버깅 시 IMAGE_DEBUG_DIRECTORY를 가진 DLL을 로드하게 되고, 이어서 PDB 파일도 로드하게 되므로 소스 코드 디버깅이 가능해지는 것입니다.<br /> <br /> (물론, IMAGE_DEBUG_DIRECTORY가 있는 DLL에 대해서는 DEVPATH에 변경된 어셈블리를 만들진 않습니다.)<br /> <br /> 보다 구체적으로 언급해 보면. ^^<br /> <br /> Visual Studio 2019의 경우 "<a target='tab' href='https://marketplace.visualstudio.com/items?itemName=vs-publisher-306627.NETReflectorVisualStudioExtension'>.NET Reflector Visual Studio Extension</a>" 확장은 "Extensions" / ".NET Reflector" / "Generate PDBs..." 메뉴로 (PDB가 연결되지 않은) DLL 파일을 선택하면 시스템에 다음과 같은 작업을 해 둡니다.<br /> <br /> <ol> <li>"%LOCALAPPDATA%\Red Gate\.NET Reflector\Cache\0" 폴더에 해당 어셈블리의 PDB 파일 및 역어셈블한 소스 코드 파일 생성</li> <li>위의 1번 과정에서 생성한 PDB를 연결한 새로운 DLL을 %DEVPATH% 경로에 생성</li> </ol> <br /> 위의 과정 중 2번 단계에서 재미있는 것은, 원본 DLL이 서명된 어셈블리라면 당연히 IMAGE_DEBUG_DIRECTORY를 포함한 새로운 DLL은 검증에 실패한다는 점입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 서명된 원본 DLL D:\temp> <span style='color: blue; font-weight: bold'>sn -v InterSystems.Data.IRISClient.dll</span> Microsoft (R) .NET Framework Strong Name Utility Version 4.0.30319.0 Copyright (c) Microsoft Corporation. All rights reserved. <span style='color: blue; font-weight: bold'>Assembly 'InterSystems.Data.IRISClient.dll' is valid</span> </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // DEVPATH에 새롭게 생성된 DLL C:\ProgramData\Red Gate\.NET Reflector\DevPath> <span style='color: blue; font-weight: bold'>sn -v InterSystems.Data.IRISClient.dll</span> Microsoft (R) .NET Framework Strong Name Utility Version 4.0.30319.0 Copyright (c) Microsoft Corporation. All rights reserved. <span style='color: blue; font-weight: bold'>Failed to verify assembly -- Strong name validation failed.</span> </pre> <br /> 하지만, CLR은 DEVPATH로부터 로드하는 어셈블리에 대해 검증 절차를 생략해 줍니다. 정리해 보면 DEVPATH에 들어 있는 DLL은 CLR로부터 다음의 특별한 혜택을 누리는데,<br /> <br /> <ol> <li>로컬 경로에 있는 DLL보다 더 높은 로딩 우선순위</li> <li>서명된 어셈블리여도 검증 생략</li> </ol> <br /> 이것들은 마치... 뭐랄까 애당초 ".NET Reflector"에 의해 소스 코드 디버깅 용도로 활용하라는 배려였다는 느낌마저 듭니다. ^^ (아마도 DEVPATH에 저런 규칙을 만들어 둔 마이크로소프트의 개발자들조차도 저런 활용 사례를 예상치 못했을 것입니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이런 특성으로 인해, 오히려 주의해야 할 사항이 있는데 저렇게 DEVPATH에 넣어진 DLL이 가장 우선순위가 높게 로딩이 이뤄지므로 개발 시 필요가 없어지면 반드시 해당 파일을 삭제하는 습관을 들여야 합니다. 그렇지 않고, 혹시라도 잊어버린다면 이후 개발할 때마다 분명히 비주얼 스튜디오에서 빌드하고 있는데도 불구하고 예전 기능을 수행하는 희한한 현상을 겪을 수 있습니다.<br /> <br /> 그리고, 아마도 아래의 오류들도 그런 식으로 캐시가 되어 발생한 문제들이지 않았나... 하는 예상을 해봅니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > regsvcs 등록 시 0x80040153 오류 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/1656'>https://www.sysnet.pe.kr/2/0/1656</a> vcpkg 빌드 오류 - Starting the CLR failed with HRESULT 80040153 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/11427'>https://www.sysnet.pe.kr/2/0/11427</a> </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1706
(왼쪽의 숫자를 입력해야 합니다.)