using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Security.Cryptography; namespace TfsExcelImporter { /// /// 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 目录、绿色解压版)时不做任何处理。 /// 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); } } }