programing

wpf를 사용하여 dll을 단일 .exe로 병합

nicescript 2021. 1. 17. 10:33
반응형

wpf를 사용하여 dll을 단일 .exe로 병합


저는 현재 많은 의존성이있는 프로젝트를 진행하고 있습니다. 포함 된 리소스를 사용하는 것처럼 참조 된 모든 dll을 .exe로 컴파일하고 싶습니다. ILMerge를 시도 했지만 .xaml 리소스를 처리 할 수 ​​없습니다.

그래서 내 질문은 : 여러 종속성을 가진 WPF 프로젝트를 단일 .exe로 병합하는 방법이 있습니까?


.NET 리액터 에는 어셈블리를 병합하는 기능이 있으며 그다지 비싸지 않습니다.


http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application

이것은 나를 위해 매력처럼 작동했습니다 :) 그리고 완전히 무료입니다.

블로그가 사라지는 경우 코드 추가.

1) 다음을 .csproj파일에 추가 하십시오.

<Target Name="AfterResolveReferences">
  <ItemGroup>
    <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
      <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
    </EmbeddedResource>
  </ItemGroup>
</Target>

2) Main을 Program.cs다음과 같이 만드십시오 .

[STAThreadAttribute]
public static void Main()
{
    AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
    App.Main();
}

3) OnResolveAssembly방법 추가 :

private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
    Assembly executingAssembly = Assembly.GetExecutingAssembly();
    AssemblyName assemblyName = new AssemblyName(args.Name);

    var path = assemblyName.Name + ".dll";
    if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false) path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);

    using (Stream stream = executingAssembly.GetManifestResourceStream(path))
    {
        if (stream == null) return null;

        var assemblyRawBytes = new byte[stream.Length];
        stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
        return Assembly.Load(assemblyRawBytes);
    }
}

Costura Fody는 wpf 어셈블리 병합을 처리하도록 설계된 오픈 소스 도구입니다.

https://github.com/Fody/Costura#how-it-works


{smartassembly}는 그러한 제품 중 하나입니다. dll을 난독 화하거나 포함시킬 수 있습니다.

이것을 시도하십시오 : http://www.smartassembly.com/

또한 애플리케이션이 더 빠르게 실행되도록 많은 개선을 수행 할 수 있습니다.

그리고 네. WPF에 사용할 수 있습니다.

2015 년 8 월 6 일 업데이트 : ILRepack 2.0.0 (ILMerge의 오픈 소스 대안)은 이제 대부분의 WPF 케이스 병합을 지원합니다 : https://twitter.com/Gluckies/status/607680149157462016


ILMerge 웹 사이트 에 게시 된 대로 Jeffrey Richter의 dll을 리소스로 취급 하십시오 .

많은 응용 프로그램은 많은 DLL 파일에 의존하는 EXE 파일로 구성됩니다. 이 애플리케이션을 배포 할 때 모든 파일을 배포해야합니다. 그러나 단일 EXE 파일 만 배포하는 데 사용할 수있는 기술이 있습니다. 먼저 Microsoft .NET Framework 자체의 일부로 제공되지 않는 EXE 파일이 의존하는 모든 DLL 파일을 식별합니다. 그런 다음 이러한 DLL을 Visual Studio 프로젝트에 추가합니다. 추가하는 각 DLL 파일에 대해 해당 속성을 표시하고 "Build Action"을 "Embedded Resource"로 변경합니다. 이렇게하면 C # 컴파일러가 DLL 파일을 EXE 파일에 포함하고이 하나의 EXE 파일을 배포 할 수 있습니다. 런타임시 CLR은 종속 DLL 어셈블리를 찾을 수 없으며 이는 문제입니다. 이 문제를 해결하려면 애플리케이션이 초기화 될 때 AppDomain의 ResolveAssembly 이벤트에 콜백 메서드를 등록합니다.

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {

   String resourceName = "AssemblyLoadingAndReflection." +

      new AssemblyName(args.Name).Name + ".dll";

   using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) {

      Byte[] assemblyData = new Byte[stream.Length];

      stream.Read(assemblyData, 0, assemblyData.Length);

      return Assembly.Load(assemblyData);

   }

}; 

이제 스레드가 종속 DLL 파일의 형식을 참조하는 메서드를 처음 호출하면 AssemblyResolve 이벤트가 발생하고 위에 표시된 콜백 코드가 원하는 포함 된 DLL 리소스를 찾고 Assembly의 Load 메서드 오버로드를 호출하여로드합니다. Byte []를 인수로받습니다.


Costura.Fody 사용 -어셈블리에 리소스를 포함하는 가장 쉽고 쉬운 방법을 위해 Nuget Pkg로 사용할 수 있습니다.

Install-Package Costura.Fody

프로젝트에 추가하면 추가 된 모든 참조가 주 어셈블리에 자동으로 포함됩니다.


.Netz ( http://madebits.com/netz/ )를 사용 해보세요. 무료 (맥주에서와 같이)이며 타겟이 exe 인 경우 몇 가지 좋은 일을합니다.


다음은 코드를 추출하기 위해 네임 스페이스를 알 필요가없는 Matthieu에서 인용 된 코드의 수정 된 버전입니다. WPF의 경우이를 애플리케이션 시작 이벤트 코드에 넣으십시오.

AppDomain.CurrentDomain.AssemblyResolve += (s, args) =>
{
    // Note: Requires a using statement for System.Reflection and System.Diagnostics.
    Assembly assembly = Assembly.GetExecutingAssembly();
    List<string> embeddedResources = new List<string>(assembly.GetManifestResourceNames());
    string assemblyName = new AssemblyName(args.Name).Name;
    string fileName = string.Format("{0}.dll", assemblyName);
    string resourceName = embeddedResources.Where(ern => ern.EndsWith(fileName)).FirstOrDefault();
    if (!string.IsNullOrWhiteSpace(resourceName))
    {
        using (var stream = assembly.GetManifestResourceStream(resourceName))
        {
            Byte[] assemblyData = new Byte[stream.Length];
            stream.Read(assemblyData, 0, assemblyData.Length);
            var test = Assembly.Load(assemblyData);
            string namespace_ = test.GetTypes().Where(t => t.Name == assemblyName).Select(t => t.Namespace).FirstOrDefault();
#if DEBUG
            Debug.WriteLine(string.Format("\tNamespace for '{0}' is '{1}'", fileName, namespace_));
#endif
            return Assembly.Load(assemblyData);
        }
    }

    return null;
}; 

컴파일 타임에 사용할 수 있도록 ExternalDLLs라는 폴더를 만들고 거기에 dll을 복사 한 다음 위에서 언급 한대로 EmbeddedResource로 설정합니다. 코드에서 사용하려면 여전히 참조를 설정해야하지만 로컬 복사를 False로 설정해야합니다. 코드가 오류없이 깔끔하게 컴파일되도록하려면 코드에서 통계를 사용하여 dll의 네임 스페이스로 설정해야합니다.

다음은 포함 된 리소스 이름을 살펴보고 출력 창에 네임 스페이스를 표시하는 작은 유틸리티입니다.

private void getEmbeddedResourceNamespaces()
{
    // Note: Requires a using statement for System.Reflection and System.Diagnostics.
    Assembly assembly = Assembly.GetExecutingAssembly();
    List<string> embeddedResourceNames = new List<string>(assembly.GetManifestResourceNames());
    foreach (string resourceName in embeddedResourceNames)
    {
        using (var stream = assembly.GetManifestResourceStream(resourceName))
        {
            Byte[] assemblyData = new Byte[stream.Length];
            stream.Read(assemblyData, 0, assemblyData.Length);
            try
            {
                var test = Assembly.Load(assemblyData);
                foreach (Type type in test.GetTypes())
                {
                    Debug.WriteLine(string.Format("\tNamespace for '{0}' is '{1}'", type.Name, type.Namespace));
                }
            }
            catch 
            {
            }
        }
    }
}

  1. 다음을 .csprofj 파일에 추가하십시오.

>

<Target Name="AfterResolveReferences">
  <ItemGroup>
    <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
      <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
    </EmbeddedResource>
  </ItemGroup>
</Target>
  1. 프로젝트 / 속성 / 응용 프로그램 / 스타 업 개체를 마우스 오른쪽 버튼으로 클릭하고 Sinhro.Program을 선택합니다.

  2. program.cs 파일에 다음을 추가하십시오.

    using System.Reflection; using System.IO; using System.Globalization;

    [STAThreadAttribute]
    static void Main()
    {
        AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
        ...
    
    
    private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
    {
        Assembly executingAssembly = Assembly.GetExecutingAssembly();
        AssemblyName assemblyName = new AssemblyName(args.Name);
        string path = assemblyName.Name + ".dll";
        if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
        {
            path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
        }
        using (Stream stream = executingAssembly.GetManifestResourceStream(path))
        {
            if (stream == null)
                return null;
            byte[] assemblyRawBytes = new byte[stream.Length];
            stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
            return Assembly.Load(assemblyRawBytes);
        }
    }   
    

source: http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application


Since all the other solutions are in C#, and I needed this for VB.NET, this includes clarification about where to insert the configuration change, the necessary imports, and the way to add a handler, instead of C#'s += syntax.

For any WPF application, not each project, the following needs to be added to make the code compile to a single EXE. It will still include the DLL’s in the output folder, but the EXE will contain all of them.

  1. Unload the WPF project (usually the view)
  2. Right-click the project and edit it
  3. In the document, paste the following code after this line
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />

Code to paste

<Target Name="AfterResolveReferences">
   <ItemGroup>
      <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
         <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)
         </LogicalName>
      </EmbeddedResource>
   </ItemGroup>
</Target>
  1. Close it, save it, and then reload the project
  2. In the Application.xaml.vb file add the following code, or if something already exists in the file, add this to it:
Imports System.Reflection
Imports System.Globalization
Imports System.IO

Class Application

    Public Sub New()
        AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf OnResolveAssembly
    End Sub

    Private Shared Function OnResolveAssembly(ByVal sender As Object, ByVal args As ResolveEventArgs) As Assembly

        Dim executingAssembly As Assembly = Assembly.GetExecutingAssembly()
        Dim assemblyName As AssemblyName = New AssemblyName(args.Name)
        Dim path = assemblyName.Name & ".dll"
        If assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) = False Then path = String.Format("{0}\{1}", assemblyName.CultureInfo, path)

        Using stream As Stream = executingAssembly.GetManifestResourceStream(path)
            If stream Is Nothing Then Return Nothing
            Dim assemblyRawBytes = New Byte(stream.Length - 1) {}
            stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length)
            Return Assembly.Load(assemblyRawBytes)
        End Using

    End Function

End Class

ReferenceURL : https://stackoverflow.com/questions/1025843/merging-dlls-into-a-single-exe-with-wpf

반응형