我有依赖于我使用 PInvoke 访问的本机 dll 的 AC# Visual Studio 2012 解决方案。当我部署应用程序时,我必须确保此 Dll 在应用程序文件夹中。
无论如何我可以将此Dll合并到可执行文件中吗?
也许作为一种资源?
我听说过 ILMerge,但有人告诉我它无法处理本机代码。
任何帮助,将不胜感激。
我有依赖于我使用 PInvoke 访问的本机 dll 的 AC# Visual Studio 2012 解决方案。当我部署应用程序时,我必须确保此 Dll 在应用程序文件夹中。
无论如何我可以将此Dll合并到可执行文件中吗?
也许作为一种资源?
我听说过 ILMerge,但有人告诉我它无法处理本机代码。
任何帮助,将不胜感激。
您可以使用 Visual Studio 创建安装程序包项目,将所有文件部署到正确的位置或使用其他第三方打包软件(如完整的 InstallShield 或替代品)
但是,您的问题让我想起了Open Hardware Monitor项目,其中它们将驱动程序作为嵌入式资源包含在内,并在用户启动应用程序时提取它们。它的工作原理是这样的:他们已经将WinRing0.sys
and添加WinRing0x64.sys
到项目中并将其Build Action设置为Embedded Resource,然后他们有一个从资源中提取驱动程序的方法:
private static bool ExtractDriver(string fileName) {
string resourceName = "OpenHardwareMonitor.Hardware." +
(OperatingSystem.Is64BitOperatingSystem() ? "WinRing0x64.sys" :
"WinRing0.sys");
string[] names =
Assembly.GetExecutingAssembly().GetManifestResourceNames();
byte[] buffer = null;
for (int i = 0; i < names.Length; i++) {
if (names[i].Replace('\\', '.') == resourceName) {
using (Stream stream = Assembly.GetExecutingAssembly().
GetManifestResourceStream(names[i]))
{
buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
}
}
}
if (buffer == null)
return false;
try {
using (FileStream target = new FileStream(fileName, FileMode.Create)) {
target.Write(buffer, 0, buffer.Length);
target.Flush();
}
} catch (IOException) {
// for example there is not enough space on the disk
return false;
}
// make sure the file is actually writen to the file system
for (int i = 0; i < 20; i++) {
try {
if (File.Exists(fileName) &&
new FileInfo(fileName).Length == buffer.Length)
{
return true;
}
Thread.Sleep(100);
} catch (IOException) {
Thread.Sleep(10);
}
}
// file still has not the right size, something is wrong
return false;
}
他们将资源读入缓冲区,将该缓冲区写入磁盘并等待文件刷新到磁盘。
我的解决方案在概念上类似于Wouter提出的解决方案。
这是我们在自己的应用程序中使用的,我们可以使用原生/混合模式和 c# dll,它们都嵌入在同一个 .exe 中。
每次运行应用程序时,它都会将 dll 提取到临时目录中。显然你可能不想在生产版本中这样做,因为那里的 dll 是稳定的;你可能会在那里选择一个不同的目录(可能在 %AppData% 的某个地方)。但是,它将使用具有相同版本号的现有 dll(例如,它仅在启动计算机之间多次打开应用程序时第一次完成)。
既然我们在做
AppDomain.CurrentDomain.AssemblyResolve += (sender, args)
只要系统尝试解析 dll,就会调用此函数。而且由于它是在静态 Program 类中初始化的,所以它都是自动运行的。
程序.cs:
namespace MyApp
{
internal class Program
{
static Program()
{
LoadAssemblyResource.Initialize("MyApp");
}
//....
}
}
LoadAssemblyResource.cs
namespace MyAppStartup
{
public static class LoadAssemblyResource
{
private readonly static String _version_string =
Assembly.GetExecutingAssembly().GetName().Version.ToString();
private readonly static String _dll_path = Path.GetTempPath()
+ "\\MyApp\\" + _version_string;
static public String last_error_msg = null;
public static bool WriteBytesToFile(string filename, byte[] bytes)
{
try
{
var fs = new FileStream(filename, FileMode.Create, FileAccess.Write);
fs.Write(bytes, 0, bytes.Length);
fs.Close();
return true;
}
catch (Exception e)
{
Console.WriteLine("Writing file failed. Exception: {0}", e.ToString());
}
return false;
}
public static Assembly LoadUnsafe(String assembly_name, Byte[] assembly)
{
if (!Directory.Exists(_dll_path))
{
Directory.CreateDirectory(_dll_path);
Console.WriteLine("Created tmp path '" + _dll_path + "'.");
}
String fullpath = _dll_path + "\\" + assembly_name;
if (!File.Exists(fullpath))
{
Console.WriteLine("Assembly location: " + fullpath + ".");
if (!WriteBytesToFile(fullpath, assembly))
return null;
}
return Assembly.UnsafeLoadFrom(fullpath);
}
public static void Initialize(String exe_name)
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
String assembly_name = new AssemblyName(args.Name).Name + ".dll";
String resource_name = exe_name + "." + assembly_name;
using (var stream =
Assembly.GetExecutingAssembly().GetManifestResourceStream(resource_name))
{
if (stream == null)
return null;
Byte[] assembly_data = new Byte[stream.Length];
stream.Read(assembly_data, 0, assembly_data.Length);
try
{
Assembly il_assembly = Assembly.Load(assembly_data);
return il_assembly;
}
catch (System.IO.FileLoadException ex)
{
// might have failed because it's an mixed-mode dll.
last_error_msg = ex.Message;
}
Assembly mixed_mode_assembly = LoadUnsafe(assembly_name, assembly_data);
return mixed_mode_assembly;
}
};
}
}
}