【发布时间】:2014-07-15 18:30:37
【问题描述】:
我按照本教程将一些 DLL 合并到我的 EXE 中。
我理解这个工作的方式是: - 它首先告诉编译器嵌入(作为嵌入资源)每个将本地副本设置为 True 的 DLL。
那部分工作正常。它显然没有将它们作为资源“添加”到我的项目中(教程中的图 1 另有说明),但我可以看出我的 EXE 的大小是正确的。
仅供参考,我的程序使用 WPFtoolkit,就我而言,这是 2 个 DLL: system.windows.controls.datavisualization.toolkit.dll WPFToolkit.dll
然后,我将 App.xaml 的 Build Action 设置为 Page,并制作了一个 program.cs 文件,并将其添加到我的项目中。
这是我的 project.cs:
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Media.TextFormatting;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Shell;
using System.IO;
using System.Reflection;
using System.Globalization;
namespace Swf_perium {
public class Program {
//[System.Diagnostics.DebuggerNonUserCodeAttribute()]
//[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
[STAThreadAttribute]
public static void Main() {
Swft_perium.App app = new Swf_perium.App();
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
app.InitializeComponent();
app.Run();
}
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
AssemblyName assemblyName = new AssemblyName(args.Name);
string path = assemblyName.Name + ".dll";
Console.WriteLine(path);
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);
}
}
}
}
构建项目后,我在 VS2013 上运行它,没问题,因为两个 DLL 的本地副本都设置为 true。如果我进入调试文件夹,取出两个 DLL 并在 Windows 资源管理器中运行 EXE,然后程序会立即崩溃,因为它可以找到 DLL。
本教程应该允许我做的是能够在没有 DLL 的情况下自行运行该 EXE,所以是的,它不起作用。
我添加了我的 program.cs 的 OnResolveAssembly 方法正在读取的路径的控制台写入行。这就是我得到的: 4次相同的路径: "swf_perium.resources.dll"
显然,当它到达 Stream 时,它是 null,然后该方法返回 null。
我想了解这些路径的来源?不明白为什么是4?为什么要走这条路?
有没有人尝试过这种技术?博客上的评论显示成功率相当高。 有人有想法吗?
为了达到这个阶段我犯了几个错误,但在这一点上我看不出我做错了什么。
谢谢 史蒂夫
编辑:按照 HB 的指导,这就是我所做的:
我取出了 MSBuild 目标“mod”。 将两个引用的本地副本设置为 FALSE。 手动将两个 DLL 添加为嵌入式资源。它们都位于项目根目录的“资源”目录中。 我将 App.xaml 构建操作设置回“ApplicationDefinition”。 我将我的 program.cs 排除在项目之外。
并将此代码添加到 App.xaml.cs:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Reflection;
namespace Swf_perium
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var execAssembly = Assembly.GetExecutingAssembly();
string resourceName = execAssembly.FullName.Split(',').First() + ".Resources." + new AssemblyName(args.Name).Name + ".dll";
Console.WriteLine(resourceName);
using (var stream = execAssembly.GetManifestResourceStream(resourceName))
{
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
}
}
现在,控制台打印出 2 个 DLL 的文件名,但不是另一个。我猜这就是它仍然无法工作的原因。
这就是我所在的地方。
编辑:
我的代码没有直接调用不显示的 DLL。它依赖于第一个 DLL。我从引用和资源中取出了第二个 DLL。如果我将第一个 DLL(我的程序实际使用的)的本地复制设置为 true,则构建项目会在根目录生成两个 DLL - 在这种情况下,两个 dll 都会生成程序有效,有趣的是,如果我删除第二个 DLL,程序仍然有效。所以问题不在于第二个 DLL,而是第一个。
我遇到的错误(无论我使用什么技术,我一直都有这个错误)是我的 XAML 正在调用该命名空间并且找不到它!
编辑:
好吧,还是不行。我已将我的 program.cs 带回解决方案,将其设置为入口点。并将HB建议的代码添加到其中。 我确保在 main 的第一行完成了 assemblyresolve,因此它是在任何 wpf 完成之前完成的。我什至添加了 5s sleep 只是为了确保在任何 wpf 发生之前加载了 dll。还是不行。
要么是对第二个 DLL 的依赖导致了问题(?),要么我在 XAML 中导入命名空间的方式不正确。我需要指定这个命名空间是嵌入的吗?以及它的位置 - 即它的路径?。
谢谢
【问题讨论】: