성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그런 부분은 클라우드 업체 쪽에 문의를 하는 것이 더 좋지 않을...
[정성태] 정적 분석과 함께, 이제는 실행 시 성능 분석까지 (비록 Azu...
[정성태] .NET Source Browser를 이용해 Roslyn 소스 ...
[정성태] Experimental C# Interceptors: AOT &...
[정성태] .NET Conf 2023 (Day 2) - Tiny, fast...
[정성태] The end of the Tye Experiment #1622...
[정성태] This is a simple app that converts ...
[정성태] Wrathmark: An Interesting Compute W...
[정성태] FFmpeg Filters Every Youtuber Needs...
[정성태] 일단, PInvokeStackImbalance 오류가 발생했다는...
글쓰기
제목
이름
암호
전자우편
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/C++ - Windows 10 Version 1607부터 지원하는 /DEPENDENTLOADFLAG 옵션</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;' > How can I try to escape the disease-ridden hot-tubs known as the TEMP and Downloads directories? ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20230328-00/?p=107978'>https://devblogs.microsoft.com/oldnewthing/20230328-00/?p=107978</a> </pre> <br /> 새로운 옵션이 소개됐군요. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > /DEPENDENTLOADFLAG (Set default dependent load flags) ; <a target='tab' href='https://learn.microsoft.com/en-us/cpp/build/reference/dependentloadflag'>https://learn.microsoft.com/en-us/cpp/build/reference/dependentloadflag</a> </pre> <br /> 이 옵션은 <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw'>LoadLibraryEx</a>의 dwFlags 인자와 동일한 값을 취하고, 그 역할도 같습니다. 단지 차이점이라면, LoadLibraryEx는 사용자가 직접 호출하는 코드에 flags를 지정하게 되는 반면, /DEPENDENTLOADFLAG는 정적으로 링크된 DLL들에 대해 자동으로 적용된다는 점입니다.<br /> <br /> 일반적으로는 DLL은 다음의 문서에 따라,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Dynamic-link library search order ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order'>https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order</a> </pre> <br /> 제법 복잡한 규칙으로 찾게 됩니다. 자세한 것은 저 문서를 읽어보시고 여기서는 ^^ 저 옵션을 간단한 예제로 테스트해 보겠습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> Visual Studio로 C/C++ Console Application과 Dynamic-Link Library 유형의 프로젝트를 각각 생성한 다음, DLL 프로젝트에는 다음과 같이 테스트용 함수를 하나 추가하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Dll1.h #pragma once #ifdef DLL1LIB_EXPORTS #define DLL1LIBRARY_API __declspec(dllexport) #else #define DLL1LIBRARY_API __declspec(dllimport) #endif extern "C" DLL1LIBRARY_API void test_func(); </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // pch.cpp #include "pch.h" #include "Dll1.h" #include <stdio.h> void DLL1LIBRARY_API test_func() { printf("test_func"); } </pre> <br /> Console Application에서는 위의 함수를 사용하는 코드를 다음과 같이 넣어둡니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > #include <iostream> #include "..\\Dll1\\Dll1.h" #pragma comment(lib, "..\\x64\\debug\\Dll1.lib") int main() { std::cout << "Hello World!\n"; <span style='color: blue; font-weight: bold'>test_func();</span> } </pre> <br /> 빌드 하면 ./x64/Debug/ 디렉터리에 각각 다음과 같은 파일들이 생성되고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ConsoleApplication1.exe Dll1.dll ...[필요 없는 파일 생략]... </pre> <br /> 당연히 저 디렉터리에서 실행하면 화면에는 Hello World와 test_func가 출력됩니다. 자, 그럼 여기서 Dll1.dll 파일을 그 부모 디렉터리로 이동한 다음, 그 x64 디렉터리에서 ConsoleApplication1.exe를 실행해 보면 어떻게 될까요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\ConsoleApplication1\x64> <span style='color: blue; font-weight: bold'>dir /b /s</span> C:\ConsoleApplication1\<span style='color: blue; font-weight: bold'>x64\Dll1.dll</span> C:\ConsoleApplication1\<span style='color: blue; font-weight: bold'>x64\Debug\ConsoleApplication1.exe</span> ...[필요 없는 파일 생략]... C:\ConsoleApplication1\x64> <span style='color: blue; font-weight: bold'>.\Debug\ConsoleApplication1.exe</span> </pre> <br /> ConsoleApplication1.exe가 위치한 디렉터리에 Dll1.dll 파일이 없는데도 잘 실행이 될 것입니다. 왜냐하면, "<a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order'>Dynamic-link library search order</a>" 문서에 나오듯이 "Current Directory"에서도, 즉 해당 응용 프로그램을 실행한 경로에서도 dll을 찾기 때문입니다.<br /> <br /> 이제 약간씩 변형을 줘볼까요? ^^ 우선, DEPENDENTLOADFLAG 옵션으로 0xa00 값을 줄 텐데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Linker / Command Line의 "<a target='tab' href='https://www.sysnet.pe.kr/2/0/13040#linker_opt'>Additional Options</a>"에 명시 /DEPENDENTLOADFLAG:0xa00 </pre> <br /> <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters'>문서</a>에 0xa00은 다음의 2개 플래그를 OR 연산한 것입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x00000200 LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 </pre> <br /> 그러니까, DLL을 c:\windows\system32 디렉터리와 exe 파일이 있는 디렉터리에서만 찾으라는 의미입니다. 이렇게 빌드하고 다시 위와 같은 상황으로 실행하면 이번에는 다음과 같은 오류가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ConsoleApplication1.exe - System Error The code execution cannot proceed because Dll1.dll was not found. Reinstalling the program may fix this problem. </pre> <br /> 또한 이벤트 로그에는 다음과 같은 "정보" 항목이 남습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Log Name: System Source: Application Popup ...[생략]... Event ID: 26 Task Category: None Level: Information User: SYSTEM Description: Application popup: ConsoleApplication1.exe - System Error : The code execution cannot proceed because Dll1.dll was not found. Reinstalling the program may fix this problem. </pre> <br /> 왜냐하면, Dll1.dll 파일이 exe와 같은 디렉터리이거나, system32에만 있어야 하기 때문입니다. 대충 느낌이 오시죠? ^^ 그렇다면, 다음의 옵션(LOAD_LIBRARY_SEARCH_SYSTEM32)으로 주고 빌드하면 어떻게 될까요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > /DEPENDENTLOADFLAG:0x800 </pre> <br /> 그럼 dll 파일을 오직 system32 디렉터리에서만 찾기 때문에 exe와 dll 파일이 같은 디렉터리에 있어도 실행되지 않습니다. 따라서 위의 옵션을 주었다면 의존하는 dll을 모두 system32로 복사해야만 합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 해보는 김에, 혹시 LOAD_LIBRARY_SEARCH_APPLICATION_DIR만 주면 어떻게 될까요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > /DEPENDENTLOADFLAG:0x200 </pre> <br /> 현재 예제에서는 Visual C++ Debug 빌드를 했기 때문에 다음의 DLL들에 대한 의존성이 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > msvcp140d.dll msvcp140_1d.dll ucrtbased.dll vcruntime140d.dll vcruntime140_1d.dll </pre> <br /> Visual Studio를 설치한 경우 위의 DLL들은 모두 c:\windows\system32 디렉터리에 있기 때문에 LOAD_LIBRARY_SEARCH_APPLICATION_DIR 옵션만으로 빌드하게 되면 위의 DLL들을 찾을 수 없어 System Error가 발생합니다. 따라서 해당 DLL들을 exe와 같은 디렉터리에 복사해,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\ConsoleApplication1\x64\Debug> <span style='color: blue; font-weight: bold'>dir /b</span> ConsoleApplication1.exe Dll1.dll msvcp140d.dll msvcp140_1d.dll ucrtbased.dll vcruntime140d.dll vcruntime140_1d.dll ...[필요 없는 파일 생략]... </pre> <br /> 실행하면 정상적으로 구동됩니다. 여기서 재미있는 것은, windows의 시스템 DLL(예를 들어, combase.dll, gdi32.dll, imm32.dll 등)은 이전처럼 system32 디렉터리에서 로드한다는 것입니다. 다시 말해 LOAD_LIBRARY_SEARCH_APPLICATION_DIR 옵션을 줘도 시스템 DLL들은 예외인 것입니다. (사실, 편의상 예외로 할 수밖에 없었을 것입니다. ^^)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 비록 이 옵션의 지원은 Windows 10 Version 1607부터지만 개발자에게 있어 지금도 충분한 의미가 있습니다. 위의 설명을 이해하셨다면 눈치채셨을 텐데요, ^^ 보통, 개발된 응용 프로그램을 다른 PC에서 실행할 때 의존성 문제로 고생하는 경우가 있습니다. 바로 그럴 때, 저 옵션을 주고 실행해 보면 되는 것입니다. 최종적으로 실행이 잘 되면, 그 상태의 출력 디렉터리를 xcopy로 복사해 배포하면 끝입니다.<br /> <br /> 마지막으로 유의해야 할 점은, 저 옵션은 정적 링크된 DLL에 대해서만 통용된다는 점입니다. 코드상에서 <a target='tab' href='LoadLibrary'>LoadLibrary</a>로 DLL 로딩을 시도하면, 그에 대해서는 "<a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order'>Dynamic-link library search order</a>" 문서의 규칙을 적용받습니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
8524
(왼쪽의 숫자를 입력해야 합니다.)