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); } } } }