Files
aiData/WordAddIn/WpsInstaller/Program.cs

345 lines
14 KiB
C#
Raw Normal View History

2026-01-28 16:54:11 +08:00
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Diagnostics;
using System.Reflection;
namespace WpsInstaller
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("========================================");
Console.WriteLine(" WPS Add-in Installer Tool");
Console.WriteLine("========================================");
Console.WriteLine();
string mode = "wps"; // default
if (args.Length > 0)
{
mode = args[0].ToLower();
}
try
{
// 1. Locate WordAddIn.dll and WordAddIn.vsto
string currentDir = AppDomain.CurrentDomain.BaseDirectory;
string dllPath = Path.Combine(currentDir, "WordAddIn.dll");
string vstoPath = Path.Combine(currentDir, "WordAddIn.vsto");
if (!File.Exists(dllPath))
{
// Fallback for Dev scenario
string devPathDll = Path.GetFullPath(Path.Combine(currentDir, @"..\..\..\WordAddIn\bin\Debug\WordAddIn.dll"));
if (!File.Exists(devPathDll))
{
devPathDll = Path.GetFullPath(Path.Combine(currentDir, @"..\..\..\WordAddIn\bin\Release\WordAddIn.dll"));
}
if (File.Exists(devPathDll))
{
dllPath = devPathDll;
vstoPath = Path.ChangeExtension(dllPath, ".vsto");
}
else
{
Console.WriteLine("Error: Could not find WordAddIn.dll.");
Pause();
return;
}
}
Console.WriteLine("Found DLL: " + dllPath);
if (mode == "word")
{
Console.WriteLine("Mode: Switch to MS Word (VSTO)");
// 1. Clean WPS Keys
CleanupWpsKeys();
// 1.5 Clear ClickOnce cache to avoid upgrade conflicts
Console.WriteLine("Clearing ClickOnce cache to avoid upgrade conflicts...");
ClearClickOnceCache();
// 2. Register VSTO
if (RegisterVsto(vstoPath))
{
Console.WriteLine(" [OK] VSTO registered successfully.");
}
else
{
Console.WriteLine(" [FAIL] Failed to register VSTO.");
}
}
else
{
Console.WriteLine("Mode: Switch to WPS Office");
// 1. Register Assembly (COM Registration)
Console.WriteLine("Registering Assembly for COM Interop...");
if (RegisterAssembly(dllPath))
{
Console.WriteLine(" [OK] Assembly registered successfully.");
}
else
{
Console.WriteLine(" [FAIL] Failed to register assembly.");
Pause();
return;
}
// 2. Register for WPS (Registry Keys)
Console.WriteLine("Configuring WPS Registry Keys...");
if (RegisterForWPS())
{
Console.WriteLine(" [OK] WPS Registry keys configured successfully.");
}
else
{
Console.WriteLine(" [FAIL] Failed to configure WPS registry keys.");
Pause();
return;
}
// 3. Cleanup conflicting VSTO registration
Console.WriteLine("Checking for conflicting VSTO registrations...");
CleanupVstoKeys();
}
Console.WriteLine();
Console.WriteLine("========================================");
Console.WriteLine(" Operation Completed Successfully!");
Console.WriteLine("========================================");
}
catch (Exception ex)
{
Console.WriteLine("An unexpected error occurred: " + ex.Message);
Console.WriteLine(ex.StackTrace);
}
Pause();
}
static void Pause()
{
Console.WriteLine("\nPress any key to exit...");
Console.ReadKey();
}
static void ClearClickOnceCache()
{
try
{
string localApp = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string cachePath = System.IO.Path.Combine(localApp, "Apps", "2.0");
if (Directory.Exists(cachePath))
{
Directory.Delete(cachePath, true);
Console.WriteLine(" [OK] Cleared ClickOnce cache at " + cachePath);
}
else
{
Console.WriteLine(" ClickOnce cache not found, skipping.");
}
}
catch (Exception ex)
{
Console.WriteLine(" Warning: Failed to clear ClickOnce cache: " + ex.Message);
}
}
static bool RegisterVsto(string vstoPath)
{
if (!File.Exists(vstoPath))
{
Console.WriteLine(" Error: .vsto file not found at " + vstoPath);
return false;
}
try
{
// Register via Registry for Development/Local use
// HKCU\Software\Microsoft\Office\Word\Addins\WordAddIn
string keyPath = @"Software\Microsoft\Office\Word\Addins\WordAddIn";
using (RegistryKey key = Registry.CurrentUser.CreateSubKey(keyPath))
{
if (key == null) return false;
key.SetValue("Description", "WordAddIn", RegistryValueKind.String);
key.SetValue("FriendlyName", "WordAddIn", RegistryValueKind.String);
key.SetValue("LoadBehavior", 3, RegistryValueKind.DWord);
// Point to vsto file with |vstolocal to indicate local file
key.SetValue("Manifest", vstoPath + "|vstolocal", RegistryValueKind.String);
Console.WriteLine(" Created Key: HKCU\\" + keyPath);
Console.WriteLine(" Manifest: " + vstoPath + "|vstolocal");
}
return true;
}
catch (Exception ex)
{
Console.WriteLine(" Error registering VSTO: " + ex.Message);
return false;
}
}
static void CleanupWpsKeys()
{
try
{
string keyPath = @"Software\Kingsoft\Office\Word\Addins\WordAddIn.WpsAddIn";
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(keyPath, true))
{
if (key != null)
{
Console.WriteLine(" Found WPS key. Deleting...");
Registry.CurrentUser.DeleteSubKeyTree(keyPath);
Console.WriteLine(" [OK] WPS key deleted.");
}
}
}
catch (Exception ex)
{
Console.WriteLine(" Warning: Failed to cleanup WPS keys: " + ex.Message);
}
}
static bool RegisterAssembly(string assemblyPath)
{
try
{
// Use RegistrationServices to register the assembly
// This is equivalent to regasm /codebase
// Note: This requires the assembly to be strong-named if registered in GAC,
// but for simple codebase registration, it might work if run as Admin.
// However, standard RegistrationServices doesn't easily support /codebase option programmatically
// without some caveats.
// The most robust way for a utility is often to shell out to RegAsm.exe if available,
// OR use RegistrationServices.RegisterAssembly.
// Let's try RegistrationServices first.
RegistrationServices regService = new RegistrationServices();
Assembly asm = Assembly.LoadFrom(assemblyPath);
// RegisterAssembly adds entries to HKCR\CLSID\{GUID}
// We need to set the CodeBase registry key manually if not in GAC,
// or ensure RegistrationServices does it.
// Actually, RegistrationServices.RegisterAssembly does NOT set the CodeBase by default.
// It works like RegAsm without /codebase.
// So calling RegAsm.exe via process is safer to ensure /codebase behavior.
string regAsmPath = GetRegAsmPath();
if (string.IsNullOrEmpty(regAsmPath))
{
Console.WriteLine(" Warning: Could not find RegAsm.exe. Attempting manual registration...");
return ManualRegister(asm, assemblyPath);
}
string args = string.Format("\"{0}\" /codebase /tlb", assemblyPath);
ProcessStartInfo psi = new ProcessStartInfo(regAsmPath, args);
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.CreateNoWindow = true;
Console.WriteLine(string.Format(" Executing: {0} {1}", psi.FileName, psi.Arguments));
using (Process p = Process.Start(psi))
{
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine(output);
return p.ExitCode == 0;
}
}
catch (Exception ex)
{
Console.WriteLine(" Error registering assembly: " + ex.Message);
return false;
}
}
static bool ManualRegister(Assembly asm, string assemblyPath)
{
// Fallback: This is complex to do fully manually for all COM types.
// If RegAsm is missing, we might be in trouble.
// But usually on a machine with .NET Framework (needed to run this app), RegAsm is present.
return false;
}
static string GetRegAsmPath()
{
// Usually in C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe
// or Framework64
string windir = Environment.GetEnvironmentVariable("windir");
string fxDir = Path.Combine(windir, @"Microsoft.NET\Framework\v4.0.30319");
// Check for 64 bit OS and prefer Framework64 if this process is 64 bit,
// BUT WordAddIn is likely AnyCPU.
// If Word/WPS is 32-bit, we need 32-bit RegAsm. If 64-bit, we need 64-bit RegAsm.
// Ideally register both.
// For safety, let's try to find the one matching the current process architecture,
// or just use the 32-bit one as it is most common for Office.
string regAsm = Path.Combine(fxDir, "RegAsm.exe");
if (File.Exists(regAsm)) return regAsm;
return null;
}
static bool RegisterForWPS()
{
try
{
string keyPath = @"Software\Kingsoft\Office\Word\Addins\WordAddIn.WpsAddIn";
using (RegistryKey key = Registry.CurrentUser.CreateSubKey(keyPath))
{
if (key == null)
{
Console.WriteLine(" Error: Could not create registry key HKCU\\" + keyPath);
return false;
}
key.SetValue("LoadBehavior", 3, RegistryValueKind.DWord);
key.SetValue("FriendlyName", "AI Word Assistant for WPS", RegistryValueKind.String);
key.SetValue("Description", "AI Assistant", RegistryValueKind.String);
key.SetValue("CommandLineSafe", 0, RegistryValueKind.DWord);
Console.WriteLine(" Created Key: HKCU\\" + keyPath);
Console.WriteLine(" Set LoadBehavior = 3");
}
return true;
}
catch (Exception ex)
{
Console.WriteLine(" Error writing WPS registry keys: " + ex.Message);
return false;
}
}
static void CleanupVstoKeys()
{
try
{
// VSTO adds keys in HKCU\Software\Microsoft\Office\Word\Addins\WordAddIn
string vstoKeyPath = @"Software\Microsoft\Office\Word\Addins\WordAddIn";
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(vstoKeyPath, true))
{
if (key != null)
{
Console.WriteLine(" Found VSTO key in HKCU\\" + vstoKeyPath + ". Deleting to avoid conflicts...");
Registry.CurrentUser.DeleteSubKeyTree(vstoKeyPath);
Console.WriteLine(" [OK] VSTO key deleted.");
}
}
}
catch (Exception ex)
{
Console.WriteLine(" Warning: Failed to cleanup VSTO keys: " + ex.Message);
}
}
}
}