- 更新 .gitignore 文件,添加便携版分发目录和 Fody 生成的配置文件。 - 修改 README.md,详细说明便携版的构建与分发流程,强调单文件分发的优势。 - 在 Config.cs 中实现便携配置管理,优先使用 exe 同目录的 config.json,提升用户体验。 - 更新 ExcelService.cs,优化默认模板的导出逻辑,支持现场替换模板文件。 - 在 MainForm.cs 中加载应用图标,提升界面一致性。 - 调整 Program.cs,确保在单文件分发时正确加载 native DLL。
84 lines
3.4 KiB
C#
84 lines
3.4 KiB
C#
using System;
|
||
using System.Diagnostics;
|
||
using System.IO;
|
||
using System.Runtime.InteropServices;
|
||
using System.Security.Cryptography;
|
||
|
||
namespace TfsExcelImporter
|
||
{
|
||
/// <summary>
|
||
/// TFS Client OM 除托管程序集外还依赖两个纯 win32 DLL
|
||
/// (Microsoft.WITDataStore32/64.dll)。托管程序集由 Costura 内嵌并从内存加载,
|
||
/// native DLL 只能落盘后 LoadLibrary,所以单文件分发时:
|
||
/// 启动阶段把内嵌的 native DLL 释放到 %TEMP%\TfsExcelImporter\native\{内容hash},
|
||
/// 再 LoadLibrary 预加载 —— 之后 TFS 内部的 DllImport 会直接命中已加载模块。
|
||
/// 目录名带 DLL 内容 hash,升级后不会残留旧版本。
|
||
/// exe 同目录存在同名 DLL(开发机 bin 目录、绿色解压版)时不做任何处理。
|
||
/// </summary>
|
||
internal static class NativeLibraryLoader
|
||
{
|
||
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "LoadLibraryW")]
|
||
private static extern IntPtr LoadLibrary(string path);
|
||
|
||
public static void EnsureLoaded()
|
||
{
|
||
var fileName = Environment.Is64BitProcess
|
||
? "Microsoft.WITDataStore64.dll"
|
||
: "Microsoft.WITDataStore32.dll";
|
||
|
||
if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName)))
|
||
return;
|
||
|
||
try
|
||
{
|
||
var asm = typeof(NativeLibraryLoader).Assembly;
|
||
var resourceName = "native." + fileName;
|
||
using (var stream = asm.GetManifestResourceStream(resourceName))
|
||
{
|
||
if (stream == null)
|
||
{
|
||
Debug.WriteLine("未找到内嵌 native 资源: " + resourceName);
|
||
return;
|
||
}
|
||
|
||
byte[] bytes;
|
||
using (var buffer = new MemoryStream())
|
||
{
|
||
stream.CopyTo(buffer);
|
||
bytes = buffer.ToArray();
|
||
}
|
||
|
||
var target = Path.Combine(NativeDir(bytes), fileName);
|
||
Directory.CreateDirectory(Path.GetDirectoryName(target));
|
||
if (!File.Exists(target))
|
||
{
|
||
try { File.WriteAllBytes(target, bytes); }
|
||
catch (IOException)
|
||
{
|
||
// 同 hash 目录已被另一实例写住,内容等价,直接用
|
||
if (!File.Exists(target)) throw;
|
||
}
|
||
}
|
||
|
||
if (LoadLibrary(target) == IntPtr.Zero)
|
||
Debug.WriteLine("LoadLibrary 失败(" + Marshal.GetLastWin32Error() + "): " + target);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 释放失败不阻断启动:TFS 连接时会自行报 DllNotFound,提示仍是可读的
|
||
Debug.WriteLine("释放 native DLL 失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
private static string NativeDir(byte[] bytes)
|
||
{
|
||
byte[] hash;
|
||
using (var sha = SHA256.Create())
|
||
hash = sha.ComputeHash(bytes);
|
||
var stamp = BitConverter.ToString(hash, 0, 4).Replace("-", "");
|
||
return Path.Combine(Path.GetTempPath(), "TfsExcelImporter", "native", stamp);
|
||
}
|
||
}
|
||
}
|