성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
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'>msbuild - .NET Core/5+ 프로젝트에서 resgen을 이용한 리소스 파일 생성 방법</h1> <p> 대개의 경우, 닷넷 리소스 파일은 단순히 resx 파일을 포함해 관리할 것입니다. 그런 경우, 프로젝트 파일에는 EmbeddedResource 유형으로 항목이 생기는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <Compile Update="Resource1.Designer.cs"> <DesignTime>True</DesignTime> <AutoGen>True</AutoGen> <DependentUpon>Resource1.resx</DependentUpon> </Compile> </ItemGroup> <ItemGroup> <span style='color: blue; font-weight: bold'><EmbeddedResource Update="Resource1.resx"> <Generator>ResXFileCodeGenerator</Generator> <LastGenOutput>Resource1.Designer.cs</LastGenOutput> </EmbeddedResource></span> </ItemGroup> </Project> </pre> <br /> 사실 저 설정은 Resource1.resx에 표시된 리소스를 어셈블리 파일 내에 임베딩하는 것과는 무관합니다. 단지, 보는 바와 같이 해당 파일이 편집되면 저장 시 ResXFile<a target='tab' href='https://www.sysnet.pe.kr/2/0/1518'>CodeGenerator</a>가 동작하게 되고 그 출력으로 Resource1.Designer.cs 파일이 생길 뿐입니다. (resx 파일을 임베딩하지 않도록 설정하는 것은 나중에 언급할 것입니다.)<br /> <br /> 그나저나, 위의 기본 동작 말고 혹시 리소스를 외부로 분리할 수는 없을까요?<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그런 동작을 원한다면, 간단하게는 프로젝트에서 Resource1.resx와 그것이 포함하는 리소스를 전혀 별개의 디렉터리에 복사한 다음 "resgen.exe"를 이용해 resources 파일을 생성하는 방법이 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 아래와 같이 빌드하면 Resource1.resources 파일이 생성됨 c:\temp> <span style='color: blue; font-weight: bold'>resgen Resource1.resx</span> Read in 1 resources from "Resource1.resx" Writing resource file... Done. </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;' > <Target Name="PreBuild" BeforeTargets="PreBuildEvent"> <Exec Command="<span style='color: blue; font-weight: bold'>resgen &quot;$(ProjectDir)Resource1.resx&quot;</span>" /> </Target> </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;' > error MSB3073: The command "resgen "c:\temp\ConsoleApp1\ConsoleApp1\Resource1.resx"" exited with code 9009. </pre> <br /> 왜냐하면, 현재 msbuild에서는 resgen.exe의 위치를 찾지 못하기 때문인데요, 반면 "Developer Command Prompt for VS 2022" 창에서는 Windows SDK가 설치된 경우 다음과 같이 경로 풀이가 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> <span style='color: blue; font-weight: bold'>which resgen</span> /c/Program Files (x86)/Microsoft SDKs/Windows/v10.0A/bin/NETFX 4.8 Tools/resgen </pre> <a name='run_resgen'></a> <br /> 그러니까, .NET Framework용 resgen이 활용되고 있던 것입니다. 따라서, 위와 같은 환경에서 실행되기를 원하면 이런 식으로 맞춰줘야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <!-- <a target='tab' href='https://learn.microsoft.com/en-us/archive/blogs/lifenglu/how-to-make-generated-resource-class-public'>https://learn.microsoft.com/en-us/archive/blogs/lifenglu/how-to-make-generated-resource-class-public</a> --> <Target Name="PreBuild" BeforeTargets="PreBuildEvent"> <GetFrameworkSdkPath> <Output TaskParameter="Path" PropertyName="SdkPath" /> </GetFrameworkSdkPath> <Exec Command="&quot;$(SdkPath)bin\NETFX 4.8 Tools\resgen&quot; &quot;$(ProjectDir)Resource1.resx&quot;" /> </Target> </pre> <br /> 또는,<br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Target Name="PreBuild" BeforeTargets="PreBuildEvent"> <Exec Command="&quot;<span style='color: blue; font-weight: bold'>$(TargetFrameworkSDKToolsDirectory)</span>resgen&quot; &quot;$(ProjectDir)Resource1.resx&quot;" /> </Target> </pre> <br /> 이와 마찬가지로 <a target='tab' href='https://learn.microsoft.com/en-us/visualstudio/msbuild/generateresource-task'>GenerateResource task</a>를 이용하는 경우에도,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Target Name="PreBuild" BeforeTargets="PreBuildEvent"> <GenerateResource Sources="Resource1.resx" OutputResources="Resource1.resources" /> </Target> </pre> <br /> 이런 오류가 발생할 것입니다. (메시지에 나오는 환경이 갖춰졌다면 오류가 발생하지 않습니다.)<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> error MSB3091: Task failed because "resgen.exe" was not found, or the correct Microsoft Windows SDK is not installed. The task is looking for "resgen.exe" in the "bin" subdirectory beneath the location specified in the InstallationFolder value of the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0A\WinSDK-NetFx35Tools-x86. You may be able to solve the problem by doing one of the following: 1) Install the Microsoft Windows SDK. 2) Install Visual Studio 2010. 3) Manually set the above registry key to the correct location. 4) Pass the correct location into the "ToolPath" parameter of the task.<br /> </div><br /> <br /> 저대로 해주면 되겠지만, 메시지에서 볼 수 있듯이 Visual Studio 2010 시절에나 사용했을 Task라는 의미이므로 이제는 Legacy가 되어 버렸습니다.<br /> <br /> 어쩔 수 없군요, 만약 resx 파일이 자주 변경되지 않는다면 수작업으로 resgen을 실행해 직접 생성하거나, 아니면 적절한 하드 코딩 또는, 레지스트리에 적절한 경로를 입력해 GenerateResource task가 동작하도록 맞춰주는 것이 좋겠습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 한 가지 주의해야 할 부분이 있다면, resx 파일의 경우는 .NET Core/5+ 프로젝트 디렉터리에 함께 있다는 것만으로도 자동으로 출력 어셈블리에 임베딩됩니다. 실제로 다음과 같이 구성한 프로젝트에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project> </pre> <br /> Resource1.resx 파일이 디렉터리에 있다면 빌드 시 DLL/EXE에 임베딩되는 것을 (용량이 늘어나는 것으로) 확인할 수 있습니다. 만약 이것을 배제하고 싶다면 다음과 같은 식으로 EmbeddedResource 노드를 명시해야 합니다.<br /> <a name='ext_res'></a> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <span style='color: blue; font-weight: bold'><ItemGroup> <EmbeddedResource Remove="Resource1.resx"> </EmbeddedResource> </ItemGroup></span> </Project> </pre> <br /> 대신, 이런 경우 프로젝트 관리에서도 Resource1.resx 파일이 삭제되므로, 즉 솔루션 탐색기에서 없어지므로 이를 위해 None 노드를 하나 추가하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <ItemGroup> <EmbeddedResource Remove="Resource1.resx"> </EmbeddedResource> <span style='color: blue; font-weight: bold'><None Include="Resource1.resx"> </None></span> </ItemGroup> </pre> <br /> 마찬가지로, resgen으로 직접 실행해 Resource1.resources 파일을 생성했다면 프로젝트의 관리를 위해 다음과 같이 추가해 주시면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <ItemGroup> <EmbeddedResource Remove="Resource1.resx"> </EmbeddedResource> <None Include="Resource1.resx"> </None> <span style='color: blue; font-weight: bold'><None Include="Resource1.resources"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None></span> </ItemGroup> </pre> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=2123&boardid=331301885'>첨부 파일은 이 글의 예제 파일을 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그나저나, 언제 한번 기회가 되면 위와 같은 역할만 하는 ResGen 확장을 하나 만들어야겠군요. ^^ 방법은, 이미 아래에 모두 나와 있으니 어렵지는 않습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Resources in .resources files ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/core/extensions/create-resource-files#resources-in-resources-files'>https://learn.microsoft.com/en-us/dotnet/core/extensions/create-resource-files#resources-in-resources-files</a> Tutorial: Create a custom task for code generation ; <a target='tab' href='https://learn.microsoft.com/en-us/visualstudio/msbuild/tutorial-custom-task-code-generation'>https://learn.microsoft.com/en-us/visualstudio/msbuild/tutorial-custom-task-code-generation</a> </pre> <br /> 그냥 좀 귀찮을 뿐!<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1472
(왼쪽의 숫자를 입력해야 합니다.)