web-dev-qa-db-ja.com

wpfを使用してDLLを単一の.exeにマージする

現在、多くの依存関係があるプロジェクトに取り組んでいます。埋め込まれたリソースで行うのと同じように、参照されているすべてのdllを.exeにコンパイルしたいと思います。 ILMerge を試しましたが、.xamlリソースを処理できません。

だから私の質問は:複数の依存関係を持つWPFプロジェクトを単一の.exeにマージする方法はありますか?

52
Farawin

。NETリアクタ には、アセンブリをマージする機能があり、それほど高価ではありません。

8
Hemant

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)メイン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);
    }
}
60
Wegged

Costura Fodyは、wpfアセンブリのマージを処理するように設計されたオープンソースツールです。

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

44
Simon

{smartassembly}はそのような製品の1つです。 DLLを難読化または埋め込むことができます。

これを試してください: http://www.smartassembly.com/

また、アプリケーションを大幅に改善して、より高速に実行することもできます。

はい。 WPFに使用できます。

アップデート2015年6月6日:ILRepack 2.0.0(ILMergeのオープンソースの代替)は、マージするほとんどのWPFケースをサポートするようになりました: https://Twitter.com/Gluckies/status/607680149157462016

13
Timotei

ILMerge Webサイト に投稿されているように、Jeffrey Richterによるこれらのdllをリソースとして扱います here

多くのアプリケーションは、多くのDLLファイルに依存するEXEファイルで構成されています。このアプリケーションをデプロイする場合、すべてのファイルをデプロイする必要があります。ただし、単一のデプロイに使用できるテクニックがありますEXEファイル:まず、EXEファイルが依存するすべてのDLLファイルを特定し、Microsoft .NET Framework自体の一部として出荷されません。次に、これらのDLLをVisual Studioプロジェクトに追加します。各DLLファイルを追加し、そのプロパティを表示し、「ビルドアクション」を「埋め込みリソース」に変更します。これにより、C#コンパイラはDLL=実行時に、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リソースが必要です。Byte[]を引数として取るAssemblyのLoadメソッドのオーバーロードを呼び出して、リソースをロードします。

9
Matthieu

使用Costura.Fody-アセンブリにリソースを埋め込むための最良かつ最も簡単な方法としてNuget Pkgとして利用可能です。

Install-Package Costura.Fody

プロジェクトに追加すると、追加されたすべての参照がメインアセンブリに自動的に埋め込まれます。

8
Dark Knight

.Netzを試してください( http://madebits.com/netz/ )-(ビールのように)無料で、ターゲットがexeの場合はいくつかの素晴らしいことを行います。

2
Nils

以下は、コードを抽出するために名前空間を知る必要のない、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 
            {
            }
        }
    }
}
2
j2associates
  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. project/properties/application/starupオブジェクトを右クリックし、Sinhro.Programを選択します

  2. これをprogram.csファイルに追加します。

    system.Reflectionを使用します。 System.IOを使用します。 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);
        }
    }   
    

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

1
Tone Škoda

他のすべてのソリューションはC#であり、VB.NETでこれが必要だったため、これには、C#の+ =構文の代わりに、構成変更を挿入する場所、必要なインポート、ハンドラーを追加する方法についての明確化が含まれます。

各プロジェクトではなく、WPFアプリケーションの場合、コードを1つのEXEにコンパイルするには、次を追加する必要があります。出力フォルダーにはDLLが含まれますが、EXEにはすべてが含まれます。

  1. WPFプロジェクト(通常はビュー)をアンロードします
  2. プロジェクトを右クリックして編集します
  3. ドキュメントで、この行の後に次のコードを貼り付けます
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />

貼り付けるコード

<Target Name="AfterResolveReferences">
   <ItemGroup>
      <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
         <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)
         </LogicalName>
      </EmbeddedResource>
   </ItemGroup>
</Target>
  1. 閉じて保存し、プロジェクトをリロードします
  2. Application.xaml.vbファイルに次のコードを追加するか、ファイルに既に存在するものがある場合は、これを追加します。
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
0
James Igoe