├── .gitattributes
├── .gitignore
├── Build
└── BuildInstaller.cmd
├── LICENSE
├── README.md
└── Source
├── AddInManager.sln
├── ExcelDna.AddInManager.Common
├── AddInFile.cs
├── Bitness.cs
├── ExcelDna.AddInManager.Common.csproj
├── Utils.cs
└── XmlSerializer.cs
├── ExcelDna.AddInManager.IndexGenerator
├── ExcelDna.AddInManager.IndexGenerator.csproj
└── Program.cs
├── ExcelDna.AddInManager
├── AddInVersionInfo.cs
├── AddInsSource.cs
├── Controller.cs
├── Dialogs
│ ├── AddInsListView.xaml
│ ├── AddInsListView.xaml.cs
│ ├── InstallDialog.xaml
│ ├── InstallDialog.xaml.cs
│ ├── ManageDialog.xaml
│ ├── ManageDialog.xaml.cs
│ ├── OptionsDialog.xaml
│ └── OptionsDialog.xaml.cs
├── ExcelDna.AddInManager.csproj
├── ExceptionHandler.cs
├── GeneralOptions.cs
├── MainAddIn.cs
├── Ribbon.cs
├── RibbonResources.Designer.cs
├── RibbonResources.resx
├── RibbonResources
│ ├── Image1.png
│ └── Ribbon.xml
└── Storage.cs
└── Installer
├── ExcelAddInDeploy
├── CustomMessages.wxl
├── EnglishLoc.wxl
├── ExcelAddInDeploy.wixproj
├── Product.wxs
└── Resources
│ ├── Banner.jpg
│ ├── Dialog.jpg
│ ├── EULA.rtf
│ └── Icon.ico
├── Installer.sln
├── InstallerCA
├── ClosePromptForm.cs
├── ClosePromptForm.designer.cs
├── CustomAction.config
├── CustomAction.cs
├── InstallerCA.csproj
├── InstallerClass.Designer.cs
├── InstallerClass.cs
├── PromptCloseApplication.cs
├── Properties
│ └── AssemblyInfo.cs
└── WindowWrapper.cs
└── WiRunSQL.vbs
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 | *.VC.VC.opendb
85 |
86 | # Visual Studio profiler
87 | *.psess
88 | *.vsp
89 | *.vspx
90 | *.sap
91 |
92 | # TFS 2012 Local Workspace
93 | $tf/
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 | *.DotSettings.user
102 |
103 | # JustCode is a .NET coding add-in
104 | .JustCode
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | _NCrunch_*
114 | .*crunch*.local.xml
115 | nCrunchTemp_*
116 |
117 | # MightyMoose
118 | *.mm.*
119 | AutoTest.Net/
120 |
121 | # Web workbench (sass)
122 | .sass-cache/
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.[Pp]ublish.xml
142 | *.azurePubxml
143 | # TODO: Comment the next line if you want to checkin your web deploy settings
144 | # but database connection strings (with potential passwords) will be unencrypted
145 | *.pubxml
146 | *.publishproj
147 |
148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
149 | # checkin your Azure Web App publish settings, but sensitive information contained
150 | # in these scripts will be unencrypted
151 | PublishScripts/
152 |
153 | # NuGet Packages
154 | *.nupkg
155 | # The packages folder can be ignored because of Package Restore
156 | **/packages/*
157 | # except build/, which is used as an MSBuild target.
158 | !**/packages/build/
159 | # Uncomment if necessary however generally it will be regenerated when needed
160 | #!**/packages/repositories.config
161 | # NuGet v3's project.json files produces more ignoreable files
162 | *.nuget.props
163 | *.nuget.targets
164 |
165 | # Microsoft Azure Build Output
166 | csx/
167 | *.build.csdef
168 |
169 | # Microsoft Azure Emulator
170 | ecf/
171 | rcf/
172 |
173 | # Windows Store app package directories and files
174 | AppPackages/
175 | BundleArtifacts/
176 | Package.StoreAssociation.xml
177 | _pkginfo.txt
178 |
179 | # Visual Studio cache files
180 | # files ending in .cache can be ignored
181 | *.[Cc]ache
182 | # but keep track of directories ending in .cache
183 | !*.[Cc]ache/
184 |
185 | # Others
186 | ClientBin/
187 | ~$*
188 | *~
189 | *.dbmdl
190 | *.dbproj.schemaview
191 | *.pfx
192 | *.publishsettings
193 | node_modules/
194 | orleans.codegen.cs
195 |
196 | # Since there are multiple workflows, uncomment next line to ignore bower_components
197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
198 | #bower_components/
199 |
200 | # RIA/Silverlight projects
201 | Generated_Code/
202 |
203 | # Backup & report files from converting an old project file
204 | # to a newer Visual Studio version. Backup files are not needed,
205 | # because we have git ;-)
206 | _UpgradeReport_Files/
207 | Backup*/
208 | UpgradeLog*.XML
209 | UpgradeLog*.htm
210 |
211 | # SQL Server files
212 | *.mdf
213 | *.ldf
214 |
215 | # Business Intelligence projects
216 | *.rdl.data
217 | *.bim.layout
218 | *.bim_*.settings
219 |
220 | # Microsoft Fakes
221 | FakesAssemblies/
222 |
223 | # GhostDoc plugin setting file
224 | *.GhostDoc.xml
225 |
226 | # Node.js Tools for Visual Studio
227 | .ntvs_analysis.dat
228 |
229 | # Visual Studio 6 build log
230 | *.plg
231 |
232 | # Visual Studio 6 workspace options file
233 | *.opt
234 |
235 | # Visual Studio LightSwitch build output
236 | **/*.HTMLClient/GeneratedArtifacts
237 | **/*.DesktopClient/GeneratedArtifacts
238 | **/*.DesktopClient/ModelManifest.xml
239 | **/*.Server/GeneratedArtifacts
240 | **/*.Server/ModelManifest.xml
241 | _Pvt_Extensions
242 |
243 | # Paket dependency manager
244 | .paket/paket.exe
245 | paket-files/
246 |
247 | # FAKE - F# Make
248 | .fake/
249 |
250 | # JetBrains Rider
251 | .idea/
252 | *.sln.iml
253 |
254 | # Task Canvas
255 | *.TaskCanvasSL
256 |
257 | # Tabs Studio
258 | *.marker.tss
259 |
260 | /Source/ExcelDna.AddInManager/Properties/launchSettings.json
261 | /Source/ExcelDna.AddInManager.IndexGenerator/Properties/launchSettings.json
262 | /Source/Installer/ExcelAddInDeploy/SourceFiles
263 |
--------------------------------------------------------------------------------
/Build/BuildInstaller.cmd:
--------------------------------------------------------------------------------
1 | setlocal
2 |
3 | set MSBuildPath="c:\Program Files\Microsoft Visual Studio\2022\Preview\Msbuild\Current\Bin\amd64\MSBuild.exe"
4 | set OutFileName=ExcelDna.AddInManager.msi
5 |
6 | del /q %OutFileName%
7 |
8 | %MSBuildPath% ..\Source\AddInManager.sln /t:restore,build /p:Configuration=Release
9 | @if errorlevel 1 goto end
10 |
11 | xcopy ..\Source\ExcelDna.AddInManager\bin\Release\net6.0-windows\publish ..\Source\Installer\ExcelAddInDeploy\SourceFiles /I /Y
12 |
13 | "c:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" ..\Source\Installer\Installer.sln /t:Rebuild /p:Configuration=Release
14 | @if errorlevel 1 goto end
15 |
16 | copy "..\Source\Installer\ExcelAddInDeploy\bin\Release\en-us\ExcelAddInDeploy.msi" %OutFileName%
17 |
18 | :end
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Excel-DNA
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AddInManager
2 | The _Excel-DNA Add-In Manager_ assists in distributing and managing Excel add-ins.
3 |
4 | 
5 |
6 |
--------------------------------------------------------------------------------
/Source/AddInManager.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.33006.217
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExcelDna.AddInManager", "ExcelDna.AddInManager\ExcelDna.AddInManager.csproj", "{CE907517-6F55-49EC-A6CE-BB614C292CF1}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExcelDna.AddInManager.IndexGenerator", "ExcelDna.AddInManager.IndexGenerator\ExcelDna.AddInManager.IndexGenerator.csproj", "{598D30ED-1525-45C5-81D6-4A26AFAC1CAA}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExcelDna.AddInManager.Common", "ExcelDna.AddInManager.Common\ExcelDna.AddInManager.Common.csproj", "{AF852BE1-3522-4EB5-B546-50F412A17EE9}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {CE907517-6F55-49EC-A6CE-BB614C292CF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {CE907517-6F55-49EC-A6CE-BB614C292CF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {CE907517-6F55-49EC-A6CE-BB614C292CF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {CE907517-6F55-49EC-A6CE-BB614C292CF1}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {598D30ED-1525-45C5-81D6-4A26AFAC1CAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {598D30ED-1525-45C5-81D6-4A26AFAC1CAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {598D30ED-1525-45C5-81D6-4A26AFAC1CAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {598D30ED-1525-45C5-81D6-4A26AFAC1CAA}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {AF852BE1-3522-4EB5-B546-50F412A17EE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {AF852BE1-3522-4EB5-B546-50F412A17EE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {AF852BE1-3522-4EB5-B546-50F412A17EE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {AF852BE1-3522-4EB5-B546-50F412A17EE9}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {6F72F8CC-8B73-4E05-90AD-A99783DCDBF2}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager.Common/AddInFile.cs:
--------------------------------------------------------------------------------
1 | namespace ExcelDna.AddInManager.Common
2 | {
3 | public class AddInFile
4 | {
5 | public string? FileName;
6 | public string? CompanyName;
7 | public string? ProductName;
8 | public string? Version;
9 | public Bitness Bitness;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager.Common/Bitness.cs:
--------------------------------------------------------------------------------
1 | namespace ExcelDna.AddInManager.Common
2 | {
3 | public enum Bitness
4 | {
5 | Unknown = 0,
6 | Bit32 = 32,
7 | Bit64 = 64,
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager.Common/ExcelDna.AddInManager.Common.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0-windows
5 | enable
6 | enable
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager.Common/Utils.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 |
3 | namespace ExcelDna.AddInManager.Common
4 | {
5 | public class Utils
6 | {
7 | public const string IndexFileName = "index.xml";
8 |
9 | public static List GetSourceAddins(string sourcePath)
10 | {
11 | List addins = new();
12 | if (Directory.Exists(sourcePath))
13 | addins.AddRange(Directory.GetFiles(sourcePath, "*.xll").Select(i => GetAddInInfo(i)));
14 |
15 | return addins;
16 | }
17 |
18 | public static AddInFile GetAddInInfo(string path)
19 | {
20 | AddInFile result = new();
21 | result.FileName = Path.GetFileName(path);
22 |
23 | FileVersionInfo version = FileVersionInfo.GetVersionInfo(path);
24 | result.CompanyName = version.CompanyName;
25 | result.ProductName = version.ProductName;
26 | result.Version = version.FileVersion;
27 |
28 | if (result.ProductName == "Excel-DNA Add-In Framework for Microsoft Excel" && result.CompanyName == "Govert van Drimmelen")
29 | {
30 | result.CompanyName = null;
31 | result.ProductName = null;
32 | result.Version = null;
33 | }
34 |
35 | if (TryFindBitness(path, out Bitness bitness))
36 | result.Bitness = bitness;
37 |
38 | return result;
39 | }
40 |
41 | private static bool TryFindBitness(string exePath, out Bitness bitness)
42 | {
43 | bitness = Bitness.Unknown;
44 |
45 | try
46 | {
47 | using (var fileStream = File.OpenRead(exePath))
48 | {
49 | using (var reader = new BinaryReader(fileStream))
50 | {
51 | // See http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
52 | // Offset to PE header is always at 0x3C.
53 | // The PE header starts with "PE\0\0" = 0x50 0x45 0x00 0x00,
54 | // followed by a 2-byte machine type field (see the document above for the enum).
55 |
56 | fileStream.Seek(0x3c, SeekOrigin.Begin);
57 | var peOffset = reader.ReadInt32();
58 |
59 | fileStream.Seek(peOffset, SeekOrigin.Begin);
60 | var peHead = reader.ReadUInt32();
61 |
62 | if (peHead != 0x00004550) // "PE\0\0", little-endian
63 | {
64 | return false;
65 | }
66 |
67 | var machineType = (MachineType)reader.ReadUInt16();
68 |
69 | switch (machineType)
70 | {
71 | case MachineType.ImageFileMachineI386:
72 | {
73 | bitness = Bitness.Bit32;
74 | return true;
75 | }
76 | case MachineType.ImageFileMachineAmd64:
77 | case MachineType.ImageFileMachineIa64:
78 | {
79 | bitness = Bitness.Bit64;
80 | return true;
81 | }
82 | default:
83 | {
84 | bitness = Bitness.Unknown;
85 | return false;
86 | }
87 | }
88 | }
89 | }
90 | }
91 | catch
92 | {
93 | }
94 |
95 | return false;
96 | }
97 |
98 | private enum MachineType : ushort
99 | {
100 | ImageFileMachineAmd64 = 0x8664,
101 | ImageFileMachineI386 = 0x14c,
102 | ImageFileMachineIa64 = 0x200,
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager.Common/XmlSerializer.cs:
--------------------------------------------------------------------------------
1 | namespace ExcelDna.AddInManager.Common
2 | {
3 | public class XmlSerializer
4 | {
5 | ///
6 | public static void XmlSerialize(string file, T o)
7 | {
8 | try
9 | {
10 | System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings();
11 | settings.Indent = true;
12 | using (System.Xml.XmlWriter stream = System.Xml.XmlWriter.Create(file, settings))
13 | {
14 | CreateSerializer().Serialize(stream, o);
15 | }
16 | }
17 | catch (Exception e)
18 | {
19 | throw new ApplicationException(e.Message);
20 | }
21 | }
22 |
23 | ///
24 | public static T XmlDeserialize(string file)
25 | {
26 | try
27 | {
28 | using (System.Xml.XmlReader stream = System.Xml.XmlReader.Create(file))
29 | {
30 | return (T)CreateSerializer().Deserialize(stream)!;
31 | }
32 | }
33 | catch (Exception e)
34 | {
35 | throw new ApplicationException(e.Message);
36 | }
37 | }
38 |
39 | private static System.Xml.Serialization.XmlSerializer CreateSerializer()
40 | {
41 | return new System.Xml.Serialization.XmlSerializer(typeof(T));
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager.IndexGenerator/ExcelDna.AddInManager.IndexGenerator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0-windows
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager.IndexGenerator/Program.cs:
--------------------------------------------------------------------------------
1 | using ExcelDna.AddInManager.Common;
2 |
3 | namespace ExcelDna.AddInManager.IndexGenerator
4 | {
5 | internal class Program
6 | {
7 | static void Main(string[] args)
8 | {
9 | if (args.Length != 1 || !Directory.Exists(args[0]))
10 | {
11 | Console.WriteLine("Usage: ExcelDna.AddInManager.IndexGenerator.exe [Add-Ins Source Path]");
12 | return;
13 | }
14 |
15 | string sourcePath = args[0];
16 | List addins = Utils.GetSourceAddins(sourcePath);
17 |
18 | string indexFile = Path.Combine(sourcePath, Utils.IndexFileName);
19 | XmlSerializer.XmlSerialize(indexFile, addins);
20 |
21 | Console.WriteLine("Generated " + indexFile);
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/AddInVersionInfo.cs:
--------------------------------------------------------------------------------
1 | using ExcelDna.AddInManager.Common;
2 |
3 | namespace ExcelDna.AddInManager
4 | {
5 | internal class AddInVersionInfo
6 | {
7 | public AddInVersionInfo(string path) : this(Utils.GetAddInInfo(path), path, null)
8 | {
9 | }
10 |
11 | public AddInVersionInfo(string sourceDirectory, AddInFile addInFile) : this(addInFile, System.IO.Path.Combine(sourceDirectory, addInFile.FileName ?? string.Empty), null)
12 | {
13 | }
14 |
15 | public AddInVersionInfo(Uri sourceDirectory, AddInFile addInFile) : this(addInFile, null, new Uri(sourceDirectory, addInFile.FileName))
16 | {
17 | }
18 |
19 | private AddInVersionInfo(AddInFile addInFile, string? path, Uri? uri)
20 | {
21 | Path = path;
22 | Uri = uri;
23 | CompanyName = addInFile.CompanyName;
24 | ProductName = addInFile.ProductName;
25 | Bitness = addInFile.Bitness;
26 | if (Version.TryParse(addInFile.Version, out Version? version))
27 | Version = version;
28 |
29 | IsVersioned = !string.IsNullOrWhiteSpace(CompanyName) && !string.IsNullOrWhiteSpace(ProductName) && Version != null;
30 | }
31 |
32 | public string? Path { get; }
33 | public Uri? Uri { get; }
34 | public bool IsVersioned { get; }
35 | public string? CompanyName { get; }
36 | public string? ProductName { get; }
37 | public Version? Version { get; }
38 | public Bitness Bitness { get; }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/AddInsSource.cs:
--------------------------------------------------------------------------------
1 | namespace ExcelDna.AddInManager
2 | {
3 | public class AddInsSource
4 | {
5 | public string? source;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/Controller.cs:
--------------------------------------------------------------------------------
1 | using ExcelDna.AddInManager.Common;
2 | using ExcelDna.Integration;
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.IO;
5 |
6 | namespace ExcelDna.AddInManager
7 | {
8 | internal class Controller
9 | {
10 | public Controller()
11 | {
12 | LoadGeneralOptions();
13 |
14 | if (!Directory.Exists(installedDir))
15 | return;
16 |
17 | if (generalOptions.autoUpdateAddIns)
18 | AutoUpdateAddIns();
19 |
20 | foreach (string xll in Directory.GetFiles(installedDir, "*.del"))
21 | {
22 | try
23 | {
24 | File.Delete(xll);
25 | }
26 | catch
27 | {
28 | }
29 | }
30 |
31 | foreach (var i in GetInstalledAddins())
32 | {
33 | Register(i.Path!);
34 | }
35 | }
36 |
37 | public void OnInstall()
38 | {
39 | InstallDialog dialog = new InstallDialog(GetSourceAddins());
40 | if (dialog.ShowDialog().GetValueOrDefault())
41 | {
42 | var installedAddIns = GetInstalledAddins();
43 | foreach (var i in dialog.GetSelectedAddins()!)
44 | {
45 | Install(i, installedAddIns, true);
46 | }
47 | }
48 | }
49 |
50 | public void OnManage()
51 | {
52 | ManageDialog dialog = new ManageDialog(GetInstalledAddins());
53 | if (dialog.ShowDialog().GetValueOrDefault())
54 | {
55 | foreach (var i in dialog.GetAddinsForUninstall()!)
56 | {
57 | Uninstall(i.Path!);
58 | }
59 | }
60 | }
61 |
62 | public void OnOptions()
63 | {
64 | OptionsDialog dialog = new OptionsDialog(generalOptions);
65 | if (dialog.ShowDialog().GetValueOrDefault())
66 | {
67 | Storage.SaveGeneralOptions(generalOptions);
68 | }
69 | }
70 |
71 | private void AutoUpdateAddIns()
72 | {
73 | var installedVersionedAddins = GetInstalledAddins().Where(i => i.IsVersioned).ToList();
74 | if (installedVersionedAddins.Count() == 0)
75 | return;
76 |
77 | var sourceAddIns = GetSourceAddins().Where(i => i.IsVersioned);
78 | foreach (var installedAddIn in installedVersionedAddins)
79 | {
80 | var latestSourceAddin = sourceAddIns.Where(i => SameProduct(i, installedAddIn)).OrderByDescending(i => i.Version).FirstOrDefault();
81 | if (latestSourceAddin != null && latestSourceAddin.Version > installedAddIn.Version)
82 | Install(latestSourceAddin, installedVersionedAddins, false);
83 | }
84 | }
85 |
86 | private static void Install(AddInVersionInfo addin, List installedAddins, bool register)
87 | {
88 | if (addin.IsVersioned)
89 | {
90 | foreach (var i in installedAddins.Where(i => SameProduct(i, addin)))
91 | {
92 | Uninstall(i.Path!);
93 | }
94 | }
95 |
96 | string installedXllPath = Path.Combine(installedDir, Path.GetFileName(addin.Path ?? addin.Uri!.LocalPath));
97 | if (File.Exists(installedXllPath))
98 | {
99 | Uninstall(installedXllPath);
100 | }
101 | Storage.CreateDirectoryForFile(installedXllPath);
102 | if (addin.Path != null)
103 | {
104 | File.Copy(addin.Path, installedXllPath, true);
105 | }
106 | else
107 | {
108 | #pragma warning disable SYSLIB0014
109 | using (System.Net.WebClient wc = new())
110 | wc.DownloadFile(addin.Uri!, installedXllPath);
111 | #pragma warning restore SYSLIB0014
112 | }
113 |
114 | if (register)
115 | Register(installedXllPath);
116 | }
117 |
118 | private static void Uninstall(string xllFileName)
119 | {
120 | string installedXllPath = Path.Combine(installedDir, xllFileName);
121 | Unregister(installedXllPath);
122 |
123 | string delXllPath = installedXllPath + ".del";
124 | File.Move(installedXllPath, delXllPath, true);
125 | }
126 |
127 | private static List GetInstalledAddins()
128 | {
129 | List addins = new();
130 | if (Directory.Exists(installedDir))
131 | {
132 | addins = Directory.GetFiles(installedDir, "*.xll").Select(i => new AddInVersionInfo(i)).Where(i => SameProcessBitness(i.Bitness)).ToList();
133 | }
134 |
135 | return addins;
136 | }
137 |
138 | private List GetSourceAddins()
139 | {
140 | List addins = new();
141 | if (generalOptions.sources != null)
142 | {
143 | foreach (var addinSource in generalOptions.sources)
144 | addins.AddRange(GetSourceAddins(addinSource.source).Where(i => SameProcessBitness(i.Bitness)));
145 | }
146 |
147 | return addins;
148 | }
149 |
150 | private static IEnumerable GetSourceAddins(string? source)
151 | {
152 | IEnumerable sourceAddins = new AddInVersionInfo[0];
153 | if (Uri.IsWellFormedUriString(source, UriKind.Absolute))
154 | {
155 | if (Uri.TryCreate(source, UriKind.Absolute, out Uri? sourceUri) && Uri.TryCreate(sourceUri, Utils.IndexFileName, out Uri? indexFileUri))
156 | {
157 | List addinFiles = new();
158 | try
159 | {
160 | addinFiles = XmlSerializer.XmlDeserialize>(indexFileUri.AbsoluteUri);
161 | }
162 | catch (ApplicationException e)
163 | {
164 | ExceptionHandler.ShowException(e);
165 | }
166 | sourceAddins = addinFiles.Select(i => new AddInVersionInfo(sourceUri, i));
167 | }
168 | }
169 | else if (Directory.Exists(source))
170 | {
171 | string indexFile = Path.Combine(source, Utils.IndexFileName);
172 | if (File.Exists(indexFile))
173 | {
174 | List addinFiles = new();
175 | try
176 | {
177 | addinFiles = XmlSerializer.XmlDeserialize>(indexFile);
178 | }
179 | catch (ApplicationException e)
180 | {
181 | ExceptionHandler.ShowException(e);
182 | }
183 | sourceAddins = addinFiles.Select(i => new AddInVersionInfo(source, i));
184 | }
185 | else
186 | {
187 | sourceAddins = Directory.GetFiles(source, "*.xll").Select(i => new AddInVersionInfo(i));
188 | }
189 | }
190 |
191 | return sourceAddins;
192 | }
193 |
194 | private static void Register(string xllPath)
195 | {
196 | ExcelAsyncUtil.QueueAsMacro(() =>
197 | {
198 | ExcelIntegration.RegisterXLL(xllPath);
199 | });
200 | }
201 |
202 | private static void Unregister(string xllPath)
203 | {
204 | ExcelAsyncUtil.QueueAsMacro(() =>
205 | {
206 | ExcelIntegration.UnregisterXLL(xllPath);
207 | });
208 | }
209 |
210 | private static bool SameProduct(AddInVersionInfo a1, AddInVersionInfo a2)
211 | {
212 | return a1.IsVersioned && a2.IsVersioned && a1.CompanyName == a2.CompanyName && a1.ProductName == a2.ProductName;
213 | }
214 |
215 | private static bool SameProcessBitness(Bitness bitness)
216 | {
217 | if (Environment.Is64BitProcess)
218 | return bitness == Bitness.Bit64;
219 | else
220 | return bitness == Bitness.Bit32;
221 | }
222 |
223 | [MemberNotNull(nameof(generalOptions))]
224 | private void LoadGeneralOptions()
225 | {
226 | try
227 | {
228 | generalOptions = Storage.LoadGeneralOptions() ?? null!;
229 | }
230 | catch (System.ApplicationException e)
231 | {
232 | System.Windows.MessageBox.Show(e.ToString(), "ExcelDna.AddInManager Load general options", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
233 | }
234 | if (generalOptions == null)
235 | generalOptions = new GeneralOptions();
236 | }
237 |
238 | private static string installedDir = Storage.GetInstalledAddinsDirectory();
239 | private GeneralOptions generalOptions;
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/Dialogs/AddInsListView.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/Dialogs/AddInsListView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace ExcelDna.AddInManager
4 | {
5 | internal partial class AddInsListView : System.Windows.Controls.UserControl
6 | {
7 | private class ListItem
8 | {
9 | public ListItem(AddInVersionInfo addin)
10 | {
11 | this.Addin = addin;
12 | }
13 |
14 | public string? CompanyName => Addin.CompanyName;
15 | public string? ProductName => Addin.IsVersioned ? Addin.ProductName : Path.GetFileNameWithoutExtension(Addin.Path ?? Addin.Uri?.LocalPath);
16 | public string? Version => Addin.Version?.ToString();
17 |
18 | public AddInVersionInfo Addin { get; }
19 | }
20 |
21 | public AddInsListView()
22 | {
23 | InitializeComponent();
24 | }
25 |
26 | public void Add(List addins)
27 | {
28 | foreach (var i in addins.Select(i => new ListItem(i)).OrderBy(i => i.ProductName))
29 | addinsListView.Items.Add(i);
30 | }
31 |
32 | public List GetSelectedAddins()
33 | {
34 | return addinsListView.SelectedItems.Cast().Select(i => i.Addin).ToList();
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/Dialogs/InstallDialog.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/Dialogs/InstallDialog.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace ExcelDna.AddInManager
4 | {
5 | internal partial class InstallDialog : Window
6 | {
7 | public InstallDialog(List addins)
8 | {
9 | InitializeComponent();
10 |
11 | addinsListView.Add(addins);
12 | }
13 |
14 | public List? GetSelectedAddins()
15 | {
16 | return selectedAddins;
17 | }
18 |
19 | private void OnInstall(object sender, RoutedEventArgs args)
20 | {
21 | try
22 | {
23 | selectedAddins = addinsListView.GetSelectedAddins();
24 |
25 | DialogResult = true;
26 | }
27 | catch (Exception e)
28 | {
29 | ExceptionHandler.ShowException(e);
30 | }
31 | }
32 |
33 | private List? selectedAddins;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/Dialogs/ManageDialog.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/Dialogs/ManageDialog.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace ExcelDna.AddInManager
4 | {
5 | internal partial class ManageDialog : Window
6 | {
7 | public ManageDialog(List addins)
8 | {
9 | InitializeComponent();
10 |
11 | addinsListView.Add(addins);
12 | }
13 |
14 | public List? GetAddinsForUninstall()
15 | {
16 | return addinsForUninstall;
17 | }
18 |
19 | private void OnUninstall(object sender, RoutedEventArgs args)
20 | {
21 | try
22 | {
23 | addinsForUninstall = addinsListView.GetSelectedAddins();
24 |
25 | DialogResult = true;
26 | }
27 | catch (Exception e)
28 | {
29 | ExceptionHandler.ShowException(e);
30 | }
31 | }
32 |
33 | private List? addinsForUninstall;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/Dialogs/OptionsDialog.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/Dialogs/OptionsDialog.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace ExcelDna.AddInManager
4 | {
5 | internal partial class OptionsDialog : Window
6 | {
7 | public OptionsDialog(GeneralOptions options)
8 | {
9 | InitializeComponent();
10 |
11 | this.options = options;
12 |
13 | autoUpdateAddInsCheckBox.IsChecked = options.autoUpdateAddIns;
14 | if (options.sources != null)
15 | {
16 | string text = "";
17 | foreach (var i in options.sources)
18 | {
19 | if (text.Length > 0)
20 | text += Environment.NewLine;
21 | text += i.source;
22 | }
23 | sourcesTextBox.Text = text;
24 | }
25 | }
26 |
27 | private void OnSave(object sender, RoutedEventArgs args)
28 | {
29 | try
30 | {
31 | options.autoUpdateAddIns = autoUpdateAddInsCheckBox.IsChecked.GetValueOrDefault();
32 | List sources = new();
33 | for (int i = 0; i < sourcesTextBox.LineCount; ++i)
34 | {
35 | AddInsSource source = new AddInsSource();
36 | source.source = sourcesTextBox.GetLineText(i).Trim();
37 | if (source.source.Length > 0)
38 | sources.Add(source);
39 | }
40 | options.sources = sources;
41 |
42 | DialogResult = true;
43 | }
44 | catch (System.Exception e)
45 | {
46 | ExceptionHandler.ShowException(e);
47 | }
48 | }
49 |
50 | private GeneralOptions options;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/ExcelDna.AddInManager.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0-windows
5 | enable
6 | enable
7 | true
8 | true
9 |
10 |
11 |
12 | true
13 | ExcelDna.AddInManager.Common.dll
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | True
28 | True
29 | RibbonResources.resx
30 |
31 |
32 |
33 |
34 |
35 | ResXFileCodeGenerator
36 | RibbonResources.Designer.cs
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/ExceptionHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace ExcelDna.AddInManager
8 | {
9 | internal class ExceptionHandler
10 | {
11 | public static void ShowException(System.Exception e)
12 | {
13 | System.Windows.MessageBox.Show(e.ToString(), "ExcelDna.AddInManager exception", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/GeneralOptions.cs:
--------------------------------------------------------------------------------
1 | namespace ExcelDna.AddInManager
2 | {
3 | public class GeneralOptions
4 | {
5 | public bool autoUpdateAddIns;
6 | public List? sources;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/MainAddIn.cs:
--------------------------------------------------------------------------------
1 | using ExcelDna.Integration;
2 |
3 | namespace ExcelDna.AddInManager;
4 |
5 | public class MainAddIn : IExcelAddIn
6 | {
7 | public void AutoOpen()
8 | {
9 | controller = new Controller();
10 | }
11 |
12 | public void AutoClose()
13 | {
14 | }
15 |
16 | internal static Controller? GetController()
17 | {
18 | return controller;
19 | }
20 |
21 | private static Controller? controller;
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/Ribbon.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 | using ExcelDna.Integration.CustomUI;
3 |
4 | namespace ExcelDna.AddInManager;
5 |
6 | [ComVisible(true)]
7 | public class Ribbon : ExcelRibbon
8 | {
9 | public override string GetCustomUI(string RibbonID)
10 | {
11 | return RibbonResources.Ribbon;
12 | }
13 |
14 | public override object? LoadImage(string imageId)
15 | {
16 | // This will return the image resource with the name specified in the image='xxxx' tag
17 | return RibbonResources.ResourceManager.GetObject(imageId);
18 | }
19 |
20 | public void OnButtonInstallPressed(IRibbonControl control)
21 | {
22 | try
23 | {
24 | MainAddIn.GetController()?.OnInstall();
25 | }
26 | catch (Exception e)
27 | {
28 | ExceptionHandler.ShowException(e);
29 | }
30 | }
31 |
32 | public void OnButtonManagePressed(IRibbonControl control)
33 | {
34 | try
35 | {
36 | MainAddIn.GetController()?.OnManage();
37 | }
38 | catch (Exception e)
39 | {
40 | ExceptionHandler.ShowException(e);
41 | }
42 | }
43 |
44 | public void OnButtonOptionsPressed(IRibbonControl control)
45 | {
46 | try
47 | {
48 | MainAddIn.GetController()?.OnOptions();
49 | }
50 | catch (Exception e)
51 | {
52 | ExceptionHandler.ShowException(e);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/RibbonResources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace ExcelDna.AddInManager
12 | {
13 | using System;
14 |
15 |
16 | ///
17 | /// A strongly-typed resource class, for looking up localized strings, etc.
18 | ///
19 | // This class was auto-generated by the StronglyTypedResourceBuilder
20 | // class via a tool like ResGen or Visual Studio.
21 | // To add or remove a member, edit your .ResX file then rerun ResGen
22 | // with the /str option, or rebuild your VS project.
23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
26 | internal class RibbonResources
27 | {
28 |
29 | private static global::System.Resources.ResourceManager resourceMan;
30 |
31 | private static global::System.Globalization.CultureInfo resourceCulture;
32 |
33 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
34 | internal RibbonResources()
35 | {
36 | }
37 |
38 | ///
39 | /// Returns the cached ResourceManager instance used by this class.
40 | ///
41 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
42 | internal static global::System.Resources.ResourceManager ResourceManager
43 | {
44 | get
45 | {
46 | if (object.ReferenceEquals(resourceMan, null))
47 | {
48 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ExcelDna.AddInManager.RibbonResources", typeof(RibbonResources).Assembly);
49 | resourceMan = temp;
50 | }
51 | return resourceMan;
52 | }
53 | }
54 |
55 | ///
56 | /// Overrides the current thread's CurrentUICulture property for all
57 | /// resource lookups using this strongly typed resource class.
58 | ///
59 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
60 | internal static global::System.Globalization.CultureInfo Culture
61 | {
62 | get
63 | {
64 | return resourceCulture;
65 | }
66 | set
67 | {
68 | resourceCulture = value;
69 | }
70 | }
71 |
72 | ///
73 | /// Looks up a localized resource of type System.Drawing.Bitmap.
74 | ///
75 | internal static System.Drawing.Bitmap Image1
76 | {
77 | get
78 | {
79 | object obj = ResourceManager.GetObject("Image1", resourceCulture);
80 | return ((System.Drawing.Bitmap)(obj));
81 | }
82 | }
83 |
84 | ///
85 | /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?>
86 | ///<customUI xmlns='http://schemas.microsoft.com/office/2009/07/customui' loadImage='LoadImage'>
87 | /// <ribbon>
88 | /// <tabs>
89 | /// <tab id='tab1' label='CSfull'>
90 | /// <group id='group1' label='My Group'>
91 | /// <button id='button1' label='My Button' onAction='OnButtonPressed' image='Image1'/>
92 | /// </group>
93 | /// </tab>
94 | /// </tabs>
95 | /// </ribbon>
96 | ///</customUI>.
97 | ///
98 | internal static string Ribbon
99 | {
100 | get
101 | {
102 | return ResourceManager.GetString("Ribbon", resourceCulture);
103 | }
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/RibbonResources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | RibbonResources\Ribbon.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
123 |
124 |
125 | RibbonResources\Image1.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
126 |
127 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/RibbonResources/Image1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Excel-DNA/AddInManager/0798fe7ef31dedb59bfeec072dc6827e2f99b67e/Source/ExcelDna.AddInManager/RibbonResources/Image1.png
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/RibbonResources/Ribbon.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Source/ExcelDna.AddInManager/Storage.cs:
--------------------------------------------------------------------------------
1 | using ExcelDna.AddInManager.Common;
2 | using System.IO;
3 |
4 | namespace ExcelDna.AddInManager
5 | {
6 | internal class Storage
7 | {
8 | ///
9 | public static void SaveGeneralOptions(GeneralOptions options)
10 | {
11 | string file = GetGeneralOptionsFile();
12 | CreateDirectoryForFile(file);
13 | XmlSerializer.XmlSerialize(file, options);
14 | }
15 |
16 | ///
17 | public static GeneralOptions? LoadGeneralOptions()
18 | {
19 | string file = GetGeneralOptionsFile();
20 | if (!File.Exists(file))
21 | return null;
22 |
23 | return XmlSerializer.XmlDeserialize(file);
24 | }
25 |
26 | public static void CreateDirectoryForFile(string file)
27 | {
28 | string directory = Path.GetDirectoryName(file)!;
29 | if (!Directory.Exists(directory))
30 | Directory.CreateDirectory(directory);
31 | }
32 |
33 | public static string GetInstalledAddinsDirectory()
34 | {
35 | return GetAppDataFile("Installed");
36 | }
37 |
38 | private static string GetGeneralOptionsFile()
39 | {
40 | return GetAppDataFile("GeneralOptions.xml");
41 | }
42 |
43 | private static string GetAppDataFile(string file)
44 | {
45 | return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"ExcelDna.AddInManager", file);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Source/Installer/ExcelAddInDeploy/CustomMessages.wxl:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Source/Installer/ExcelAddInDeploy/EnglishLoc.wxl:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1033
4 | Excel-DNA Add-In Manager
5 | ExcelDNAAddInManager
6 | The Excel-DNA Add-In Manager assists in distributing and managing Excel add-ins.
7 | Govert van Drimmelen
8 | 1
9 | (c) 2022 Govert van Drimmelen
10 | Uninstall !(loc.ProductName)
11 | !(loc.ProductName)
12 |
--------------------------------------------------------------------------------
/Source/Installer/ExcelAddInDeploy/ExcelAddInDeploy.wixproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 3.7
7 | {9787f6fe-2779-410b-b4ef-a04d2c6c7eea}
8 | 2.0
9 | ExcelAddInDeploy
10 | Package
11 | $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets
12 | $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets
13 |
14 |
15 | bin\$(Configuration)\
16 | obj\$(Configuration)\
17 | Debug;MSIProductVersion=$(MSIProductVersion)
18 |
19 |
20 | bin\$(Configuration)\
21 | obj\$(Configuration)\
22 |
23 |
24 | MSIProductVersion=$(MSIProductVersion)
25 |
26 |
27 |
28 |
29 |
30 |
31 | InstallerCA
32 | {f135d7b8-747c-4c4f-a9fc-1f3a25fbd403}
33 | True
34 | True
35 | Binaries;Content;Satellites
36 | INSTALLFOLDER
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | $(WixExtDir)\WixUIExtension.dll
51 | WixUIExtension
52 |
53 |
54 | $(WixExtDir)\WixNetFxExtension.dll
55 | WixNetFxExtension
56 |
57 |
58 |
59 |
60 |
61 |
62 |
70 |
--------------------------------------------------------------------------------
/Source/Installer/ExcelAddInDeploy/Product.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
11 |
12 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | Installed OR NETFRAMEWORK40CLIENT
52 |
53 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | NOT Installed
90 | NOT Installed
91 | Installed AND NOT UPGRADINGPRODUCTCODE
92 | Installed AND NOT UPGRADINGPRODUCTCODE
93 |
94 |
95 |
96 | NOT Installed
97 | NOT Installed
98 | Installed AND NOT UPGRADINGPRODUCTCODE
99 | Installed AND NOT UPGRADINGPRODUCTCODE
100 |
101 |
102 |
103 |
104 |
105 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/Source/Installer/ExcelAddInDeploy/Resources/Banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Excel-DNA/AddInManager/0798fe7ef31dedb59bfeec072dc6827e2f99b67e/Source/Installer/ExcelAddInDeploy/Resources/Banner.jpg
--------------------------------------------------------------------------------
/Source/Installer/ExcelAddInDeploy/Resources/Dialog.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Excel-DNA/AddInManager/0798fe7ef31dedb59bfeec072dc6827e2f99b67e/Source/Installer/ExcelAddInDeploy/Resources/Dialog.jpg
--------------------------------------------------------------------------------
/Source/Installer/ExcelAddInDeploy/Resources/EULA.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033\deflangfe1033{\fonttbl{\f0\fswiss\fprq2\fcharset0 Arial;}}
2 | {\*\generator Riched20 6.3.9600}{\*\mmathPr\mdispDef1\mwrapIndent1440 }\viewkind4\uc1
3 | \pard\sa200\sl276\slmult1\f0\fs20 MIT License\par
4 | \par
5 | Copyright (c) 2022 Excel-DNA\par
6 | \par
7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\par
8 | \par
9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\par
10 | \par
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\par
12 | }
13 |
--------------------------------------------------------------------------------
/Source/Installer/ExcelAddInDeploy/Resources/Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Excel-DNA/AddInManager/0798fe7ef31dedb59bfeec072dc6827e2f99b67e/Source/Installer/ExcelAddInDeploy/Resources/Icon.ico
--------------------------------------------------------------------------------
/Source/Installer/Installer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 11.00
3 | # Visual Studio 2010
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InstallerCA", "InstallerCA\InstallerCA.csproj", "{F135D7B8-747C-4C4F-A9FC-1F3A25FBD403}"
5 | EndProject
6 | Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ExcelAddInDeploy", "ExcelAddInDeploy\ExcelAddInDeploy.wixproj", "{9787F6FE-2779-410B-B4EF-A04D2C6C7EEA}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Debug|Mixed Platforms = Debug|Mixed Platforms
12 | Debug|x86 = Debug|x86
13 | Release|Any CPU = Release|Any CPU
14 | Release|Mixed Platforms = Release|Mixed Platforms
15 | Release|x86 = Release|x86
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {F135D7B8-747C-4C4F-A9FC-1F3A25FBD403}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {F135D7B8-747C-4C4F-A9FC-1F3A25FBD403}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {F135D7B8-747C-4C4F-A9FC-1F3A25FBD403}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
21 | {F135D7B8-747C-4C4F-A9FC-1F3A25FBD403}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
22 | {F135D7B8-747C-4C4F-A9FC-1F3A25FBD403}.Debug|x86.ActiveCfg = Debug|Any CPU
23 | {F135D7B8-747C-4C4F-A9FC-1F3A25FBD403}.Release|Any CPU.ActiveCfg = Release|Any CPU
24 | {F135D7B8-747C-4C4F-A9FC-1F3A25FBD403}.Release|Any CPU.Build.0 = Release|Any CPU
25 | {F135D7B8-747C-4C4F-A9FC-1F3A25FBD403}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
26 | {F135D7B8-747C-4C4F-A9FC-1F3A25FBD403}.Release|Mixed Platforms.Build.0 = Release|Any CPU
27 | {F135D7B8-747C-4C4F-A9FC-1F3A25FBD403}.Release|x86.ActiveCfg = Release|Any CPU
28 | {9787F6FE-2779-410B-B4EF-A04D2C6C7EEA}.Debug|Any CPU.ActiveCfg = Debug|x86
29 | {9787F6FE-2779-410B-B4EF-A04D2C6C7EEA}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
30 | {9787F6FE-2779-410B-B4EF-A04D2C6C7EEA}.Debug|Mixed Platforms.Build.0 = Debug|x86
31 | {9787F6FE-2779-410B-B4EF-A04D2C6C7EEA}.Debug|x86.ActiveCfg = Debug|x86
32 | {9787F6FE-2779-410B-B4EF-A04D2C6C7EEA}.Debug|x86.Build.0 = Debug|x86
33 | {9787F6FE-2779-410B-B4EF-A04D2C6C7EEA}.Release|Any CPU.ActiveCfg = Release|x86
34 | {9787F6FE-2779-410B-B4EF-A04D2C6C7EEA}.Release|Mixed Platforms.ActiveCfg = Release|x86
35 | {9787F6FE-2779-410B-B4EF-A04D2C6C7EEA}.Release|Mixed Platforms.Build.0 = Release|x86
36 | {9787F6FE-2779-410B-B4EF-A04D2C6C7EEA}.Release|x86.ActiveCfg = Release|x86
37 | {9787F6FE-2779-410B-B4EF-A04D2C6C7EEA}.Release|x86.Build.0 = Release|x86
38 | EndGlobalSection
39 | GlobalSection(SolutionProperties) = preSolution
40 | HideSolutionNode = FALSE
41 | EndGlobalSection
42 | EndGlobal
43 |
--------------------------------------------------------------------------------
/Source/Installer/InstallerCA/ClosePromptForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace InstallerCA
5 | {
6 | public partial class ClosePromptForm : Form {
7 | public ClosePromptForm(string text) {
8 | InitializeComponent();
9 | messageText.Text = text;
10 | }
11 |
12 | private void OkButtonClick(object sender, EventArgs e) {
13 | DialogResult = DialogResult.OK;
14 | Close();
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Source/Installer/InstallerCA/ClosePromptForm.designer.cs:
--------------------------------------------------------------------------------
1 | namespace InstallerCA
2 | {
3 |
4 | partial class ClosePromptForm {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing) {
15 | if (disposing && (components != null)) {
16 | components.Dispose();
17 | }
18 | base.Dispose(disposing);
19 | }
20 |
21 | #region Windows Form Designer generated code
22 |
23 | ///
24 | /// Required method for Designer support - do not modify
25 | /// the contents of this method with the code editor.
26 | ///
27 | private void InitializeComponent() {
28 | this.okButton = new System.Windows.Forms.Button();
29 | this.messageText = new System.Windows.Forms.Label();
30 | this.SuspendLayout();
31 | //
32 | // okButton
33 | //
34 | this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
35 | this.okButton.Location = new System.Drawing.Point(200, 67);
36 | this.okButton.Name = "okButton";
37 | this.okButton.Size = new System.Drawing.Size(75, 23);
38 | this.okButton.TabIndex = 0;
39 | this.okButton.Text = "OK";
40 | this.okButton.UseVisualStyleBackColor = true;
41 | this.okButton.Click += new System.EventHandler(this.OkButtonClick);
42 | //
43 | // messageText
44 | //
45 | this.messageText.AutoSize = true;
46 | this.messageText.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
47 | this.messageText.Location = new System.Drawing.Point(12, 25);
48 | this.messageText.MaximumSize = new System.Drawing.Size(460, 80);
49 | this.messageText.Name = "messageText";
50 | this.messageText.Size = new System.Drawing.Size(0, 13);
51 | this.messageText.TabIndex = 1;
52 | //
53 | // ClosePromptForm
54 | //
55 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
56 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
57 | this.ClientSize = new System.Drawing.Size(474, 102);
58 | this.Controls.Add(this.messageText);
59 | this.Controls.Add(this.okButton);
60 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
61 | this.MaximizeBox = false;
62 | this.MinimizeBox = false;
63 | this.Name = "ClosePromptForm";
64 | this.ShowIcon = false;
65 | this.ShowInTaskbar = false;
66 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
67 | this.Text = "Application needs to be closed";
68 | this.ResumeLayout(false);
69 | this.PerformLayout();
70 |
71 | }
72 |
73 | #endregion
74 |
75 | private System.Windows.Forms.Button okButton;
76 | private System.Windows.Forms.Label messageText;
77 | }
78 |
79 | }
--------------------------------------------------------------------------------
/Source/Installer/InstallerCA/CustomAction.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Source/Installer/InstallerCA/CustomAction.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Deployment.WindowsInstaller;
2 | using Microsoft.Win32;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Globalization;
9 |
10 | namespace InstallerCA
11 | {
12 | public class CustomActions
13 | {
14 | #region Methods
15 |
16 | #region CaRegisterAddIn
17 | [CustomAction]
18 | public static ActionResult CaRegisterAddIn(Session session)
19 | {
20 | string szOfficeRegKeyVersions = string.Empty;
21 | string szBaseAddInKey = @"Software\Microsoft\Office\";
22 | string szXll32Bit = string.Empty;
23 | string szXll64Bit = string.Empty;
24 | string szXllToRegister = string.Empty;
25 | string szFolder = string.Empty;
26 | int nOpenVersion;
27 | double nVersion;
28 | bool bFoundOffice = false;
29 | List lstVersions;
30 |
31 | try
32 | {
33 | session.Log("Enter try block of CaRegisterAddIn");
34 |
35 | szOfficeRegKeyVersions = session["OFFICEREGKEYS"];
36 | szXll32Bit = session["XLL32"];
37 | szXll64Bit = session["XLL64"];
38 | szFolder = session["AddinFolder"];
39 |
40 | szXll32Bit = szFolder.ToString() + szXll32Bit.ToString();
41 | szXll64Bit = szFolder.ToString() + szXll64Bit.ToString();
42 |
43 | if (szOfficeRegKeyVersions.Length > 0)
44 | {
45 | lstVersions = szOfficeRegKeyVersions.Split(',').ToList();
46 |
47 | foreach (string szOfficeVersionKey in lstVersions)
48 | {
49 | nVersion = double.Parse(szOfficeVersionKey, NumberStyles.Any, CultureInfo.InvariantCulture);
50 |
51 | session.Log("Retrieving Registry Information for : " + szBaseAddInKey + szOfficeVersionKey);
52 |
53 | // get the OPEN keys from the Software\Microsoft\Office\[Version]\Excel\Options key, skip if office version not found.
54 | if (Registry.CurrentUser.OpenSubKey(szBaseAddInKey + szOfficeVersionKey, false) != null)
55 | {
56 | string szKeyName = szBaseAddInKey + szOfficeVersionKey + @"\Excel\Options";
57 |
58 | szXllToRegister = GetAddInName(szXll32Bit, szXll64Bit, szOfficeVersionKey, nVersion);
59 |
60 | RegistryKey rkExcelXll = Registry.CurrentUser.OpenSubKey(szKeyName, true);
61 |
62 | if (szXllToRegister != string.Empty && rkExcelXll != null)
63 | {
64 | string[] szValueNames = rkExcelXll.GetValueNames();
65 | bool bIsOpen = false;
66 | int nMaxOpen = -1;
67 |
68 | // check every value for OPEN keys
69 | foreach (string szValueName in szValueNames)
70 | {
71 | // if there are already OPEN keys, determine if our key is installed
72 | if (szValueName.StartsWith("OPEN"))
73 | {
74 | nOpenVersion = int.TryParse(szValueName.Substring(4), NumberStyles.Any, CultureInfo.InvariantCulture, out nOpenVersion) ? nOpenVersion : 0;
75 | int nNewOpen = szValueName == "OPEN" ? 0 : nOpenVersion;
76 | if (nNewOpen > nMaxOpen)
77 | {
78 | nMaxOpen = nNewOpen;
79 | }
80 |
81 | // if the key is our key, set the open flag
82 | //NOTE: this line means if the user has changed its office from 32 to 64 (or conversly) without removing the addin then we will not update the key properly
83 | //The user will have to uninstall addin before installing it again
84 | if (rkExcelXll.GetValue(szValueName).ToString().Contains(szXllToRegister))
85 | {
86 | bIsOpen = true;
87 | }
88 | }
89 | }
90 |
91 | // if adding a new key
92 | if (!bIsOpen)
93 | {
94 | if (nMaxOpen == -1)
95 | {
96 | rkExcelXll.SetValue("OPEN", "/R \"" + szXllToRegister + "\"");
97 | }
98 | else
99 | {
100 | rkExcelXll.SetValue("OPEN" + (nMaxOpen + 1).ToString(), "/R \"" + szXllToRegister + "\"");
101 | }
102 | rkExcelXll.Close();
103 | }
104 | bFoundOffice = true;
105 | }
106 | else
107 | {
108 | session.Log("Unable to retrieve key for : " + szKeyName);
109 | }
110 | }
111 | else
112 | {
113 | session.Log("Unable to retrieve registry Information for : " + szBaseAddInKey + szOfficeVersionKey);
114 | }
115 | }
116 | }
117 |
118 | session.Log("End CaRegisterAddIn");
119 | }
120 | catch (System.Security.SecurityException ex)
121 | {
122 | session.Log("CaRegisterAddIn SecurityException" + ex.Message);
123 | bFoundOffice = false;
124 | }
125 | catch (System.UnauthorizedAccessException ex)
126 | {
127 | session.Log("CaRegisterAddIn UnauthorizedAccessException" + ex.Message);
128 | bFoundOffice = false;
129 | }
130 | catch (Exception ex)
131 | {
132 | session.Log("CaRegisterAddIn Exception" + ex.Message);
133 | bFoundOffice = false;
134 | }
135 |
136 | return bFoundOffice ? ActionResult.Success : ActionResult.Failure;
137 | }
138 | #endregion
139 |
140 | #region CaUnRegisterAddIn
141 | [CustomAction]
142 | public static ActionResult CaUnRegisterAddIn(Session session)
143 | {
144 | string szOfficeRegKeyVersions = string.Empty;
145 | string szBaseAddInKey = @"Software\Microsoft\Office\";
146 | string szXll32Bit = string.Empty;
147 | string szXll64Bit = string.Empty;
148 | string szFolder = string.Empty;
149 | bool bFoundOffice = false;
150 | List lstVersions;
151 |
152 | try
153 | {
154 | session.Log("Begin CaUnRegisterAddIn");
155 |
156 | szOfficeRegKeyVersions = session["OFFICEREGKEYS"];
157 | szXll32Bit = session["XLL32"];
158 | szXll64Bit = session["XLL64"];
159 | szFolder = session["AddinFolder"];
160 |
161 | szXll32Bit = szFolder.ToString() + szXll32Bit.ToString();
162 | szXll64Bit = szFolder.ToString() + szXll64Bit.ToString();
163 |
164 |
165 | if (szOfficeRegKeyVersions.Length > 0)
166 | {
167 | lstVersions = szOfficeRegKeyVersions.Split(',').ToList();
168 |
169 | foreach (string szOfficeVersionKey in lstVersions)
170 | {
171 | // only remove keys where office version is found
172 | if (Registry.CurrentUser.OpenSubKey(szBaseAddInKey + szOfficeVersionKey, false) != null)
173 | {
174 | bFoundOffice = true;
175 |
176 | string szKeyName = szBaseAddInKey + szOfficeVersionKey + @"\Excel\Options";
177 |
178 | var rkAddInKey = Registry.CurrentUser.OpenSubKey(szKeyName, true);
179 | if (rkAddInKey == null) continue;
180 |
181 | var szValueNames = rkAddInKey.GetValueNames();
182 | var allOpenKeyValues = new List();
183 | foreach (string szValueName in szValueNames)
184 | {
185 | //unregister both 32 and 64 xll
186 | if (szValueName.StartsWith("OPEN") && (rkAddInKey.GetValue(szValueName).ToString().Contains(szXll32Bit) || rkAddInKey.GetValue(szValueName).ToString().Contains(szXll64Bit)))
187 | {
188 | //Delete the current KEY
189 | rkAddInKey.DeleteValue(szValueName);
190 | }
191 | else if (szValueName.StartsWith("OPEN"))
192 | {
193 | //Collect all other OPEN KEYs that will need to be inserted back with adjusted OPEN counter
194 | //Since we are going to preserve all in the allOpenKeyValues, we can delete the original once,
195 | //so we can insert / create new with correc tcounters.
196 | allOpenKeyValues.Add(rkAddInKey.GetValue(szValueName).ToString());
197 | rkAddInKey.DeleteValue(szValueName);
198 | }
199 | }
200 |
201 | var i = 0;
202 | //Here all OPEN KEYs are GONE!!! Lets insert new once with correct OPEN counters
203 | foreach (var szValueName in allOpenKeyValues)
204 | {
205 | if (i > 0)
206 | rkAddInKey.SetValue("OPEN" + i, szValueName);
207 | else
208 | rkAddInKey.SetValue("OPEN", szValueName);
209 |
210 | ++i;
211 | }
212 | }
213 | }
214 | }
215 |
216 | session.Log("End CaUnRegisterAddIn");
217 | }
218 | catch (Exception ex)
219 | {
220 | session.Log(ex.Message);
221 | }
222 |
223 | return bFoundOffice ? ActionResult.Success : ActionResult.Failure;
224 | }
225 | #endregion
226 |
227 | #region ClosePrompt
228 | [CustomAction]
229 | public static ActionResult ClosePrompt(Session session)
230 | {
231 | session.Log("Begin PromptToCloseApplications");
232 | try
233 | {
234 | var productName = session["ProductName"];
235 | var processes = session["PromptToCloseProcesses"].Split(',');
236 | var displayNames = session["PromptToCloseDisplayNames"].Split(',');
237 |
238 | if (processes.Length != displayNames.Length)
239 | {
240 | session.Log(@"Please check that 'PromptToCloseProcesses' and 'PromptToCloseDisplayNames' exist and have same number of items.");
241 | return ActionResult.Failure;
242 | }
243 |
244 | for (var i = 0; i < processes.Length; i++)
245 | {
246 | session.Log("Prompting process {0} with name {1} to close.", processes[i], displayNames[i]);
247 | using (var prompt = new PromptCloseApplication(productName, processes[i], displayNames[i]))
248 | {
249 | if (!prompt.Prompt())
250 | {
251 | return ActionResult.Failure;
252 | }
253 | }
254 | }
255 | }
256 | catch (Exception ex)
257 | {
258 | session.Log("Missing properties or wrong values. Please check that 'PromptToCloseProcesses' and 'PromptToCloseDisplayNames' exist and have same number of items. \nException:" + ex.Message);
259 | return ActionResult.Failure;
260 | }
261 |
262 | session.Log("End PromptToCloseApplications");
263 | return ActionResult.Success;
264 | }
265 | #endregion
266 |
267 | #region GetAddInName
268 | public static string GetAddInName(string szXll32Name, string szXll64Name, string szOfficeVersionKey, double nVersion)
269 | {
270 | string szXllToRegister = string.Empty;
271 |
272 | if (nVersion >= 14)
273 | {
274 | // determine if office is 32-bit or 64-bit
275 | RegistryKey localMachineRegistry = // 64bit machines need to determine correct hive.
276 | RegistryKey.OpenBaseKey(RegistryHive.LocalMachine,
277 | Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32);
278 | RegistryKey rkBitness = localMachineRegistry.OpenSubKey(@"Software\Microsoft\Office\" + szOfficeVersionKey + @"\Outlook", false);
279 | if (rkBitness != null)
280 | {
281 | object oBitValue = rkBitness.GetValue("Bitness");
282 | if (oBitValue != null)
283 | {
284 | if (oBitValue.ToString() == "x64")
285 | {
286 | szXllToRegister = szXll64Name;
287 | }
288 | else
289 | {
290 | szXllToRegister = szXll32Name;
291 | }
292 | }
293 | else
294 | {
295 | szXllToRegister = szXll32Name;
296 | }
297 | }
298 | else
299 | {
300 | if (Environment.Is64BitOperatingSystem)
301 | {
302 | localMachineRegistry = //64bit machines need to check 32bit registry too!
303 | RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
304 | rkBitness =
305 | localMachineRegistry.OpenSubKey(
306 | @"Software\Microsoft\Office\" + szOfficeVersionKey + @"\Outlook", false);
307 | if (rkBitness != null)
308 | {
309 | var oBitValue = rkBitness.GetValue("Bitness");
310 | if (oBitValue != null)
311 | {
312 | if (oBitValue.ToString() == "x64")
313 | {
314 | szXllToRegister = szXll64Name;
315 | }
316 | else
317 | {
318 | szXllToRegister = szXll32Name;
319 | }
320 | }
321 | else
322 | {
323 | szXllToRegister = szXll32Name;
324 | }
325 | }
326 | else
327 | {
328 | szXllToRegister = szXll32Name;
329 | }
330 | }
331 | else
332 | szXllToRegister = szXll32Name;
333 | }
334 | }
335 | else
336 | {
337 | szXllToRegister = szXll32Name;
338 | }
339 |
340 | return szXllToRegister;
341 | }
342 | #endregion
343 |
344 | #endregion
345 | }
346 | }
347 |
--------------------------------------------------------------------------------
/Source/Installer/InstallerCA/InstallerCA.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 8.0.30703
7 | 2.0
8 | {F135D7B8-747C-4C4F-A9FC-1F3A25FBD403}
9 | Library
10 | Properties
11 | InstallerCA
12 | InstallerCA
13 | v4.0
14 | 512
15 | $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.CA.targets
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 | False
37 | ..\..\..\..\Program Files (x86)\ISWIX LLC\IsWiX\Microsoft.Deployment.WindowsInstaller.dll
38 | True
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | Form
53 |
54 |
55 | ClosePromptForm.cs
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
73 |
74 |
--------------------------------------------------------------------------------
/Source/Installer/InstallerCA/InstallerClass.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace ExcelXLL
2 | {
3 | partial class InstallerClass
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Component Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | components = new System.ComponentModel.Container();
32 | }
33 |
34 | #endregion
35 | }
36 | }
--------------------------------------------------------------------------------
/Source/Installer/InstallerCA/InstallerClass.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32;
2 |
3 | using System;
4 | using System.Collections;
5 | using System.Collections.Generic;
6 | using System.ComponentModel;
7 | using System.Configuration.Install;
8 | using System.IO;
9 | using System.Linq;
10 | using System.Reflection;
11 |
12 | namespace ExcelXLL
13 | {
14 | [RunInstaller(true)]
15 | public partial class InstallerClass : System.Configuration.Install.Installer
16 | {
17 | #region Constants
18 | private const string m_szRegistryValue = "/R " + "\"ExcelXLL-packed.xll\"";
19 | private const string m_szKeyRoot = "OPEN";
20 | #endregion
21 |
22 | public InstallerClass()
23 | {
24 | // Attach the 'Committed' event.
25 | this.Committed += new InstallEventHandler(MyInstaller_Committed);
26 | // Attach the 'Committing' event.
27 | //this.Committing += new InstallEventHandler(MyInstaller_Committing);
28 |
29 | InitializeComponent();
30 | }
31 |
32 | // Event handler for 'Committing' event.
33 | private void MyInstaller_Committing(object sender, InstallEventArgs e)
34 | {
35 | //Console.WriteLine("");
36 | //Console.WriteLine("Committing Event occurred.");
37 | //Console.WriteLine("");
38 | }
39 |
40 | // Event handler for 'Committed' event.
41 | private void MyInstaller_Committed(object sender, InstallEventArgs e)
42 | {
43 | try
44 | {
45 | MaintainRegistryKey(true);
46 | }
47 | catch
48 | {
49 | // Do nothing...
50 | }
51 | }
52 |
53 | ///
54 | /// Creates a registry string or deletes one in the context of registering an Excel Automation Addin written in C#
55 | ///
56 | /// Number of Excel Version, e.g. 14.0 if Excel 2010
57 | /// true stands for installing, false stands for deinstalling
58 | public void UpdateRegistry(string szExcelVersion, bool bInstallTheAddin)
59 | {
60 | string szKeyName = string.Empty;
61 | string szKeyValue = string.Empty;
62 | bool bKeyExists = false;
63 | string szRegKeyOptions = string.Empty;
64 |
65 | try
66 | {
67 | szRegKeyOptions = "Software\\Microsoft\\Office\\" + szExcelVersion + "\\Excel\\Options";
68 |
69 | RegistryKey regKeyOptions = Registry.CurrentUser.OpenSubKey(szRegKeyOptions, true);
70 | string[] szValueOpenNames = regKeyOptions.GetValueNames().Where(item => item.ToUpper().Contains(m_szKeyRoot)).ToArray();
71 | szKeyName = GetKeyName(regKeyOptions, szValueOpenNames, ref bKeyExists);
72 |
73 | if (bInstallTheAddin)
74 | {
75 | if (!bKeyExists)
76 | {
77 | regKeyOptions.SetValue(szKeyName, m_szRegistryValue, RegistryValueKind.String);
78 | }
79 | }
80 | else
81 | {
82 | if (bKeyExists)
83 | {
84 | regKeyOptions.DeleteValue(szKeyName, false);
85 | }
86 | }
87 | }
88 | catch
89 | {
90 | }
91 | }
92 |
93 | ///
94 | /// Retrieves an OPEN key from the list of Option keys. If no key exists, then the key will be OPEN.
95 | ///
96 | /// Registry keys found under HKEY_CURRENT_USER\Software\Microsoft\Office\[Version]\Excel\Options
97 | /// All keys that begin with OPEN
98 | /// Indicator passed by ref of whether the key has been found. This indicator will be set within this method.
99 | /// The registry key name to be created or deleted.
100 | private string GetKeyName(RegistryKey regKeyOptions, string[] szKeyNames, ref bool bKeyExists)
101 | {
102 | string szKeyName = string.Empty;
103 | string szKeyValue = string.Empty;
104 | int nOpenVersion = 0;
105 |
106 | for (int i = 0; i < szKeyNames.Length; i++)
107 | {
108 | szKeyValue = (string)regKeyOptions.GetValue(szKeyNames[i], m_szRegistryValue);
109 |
110 | if (string.Compare(szKeyValue, m_szRegistryValue) == 0)
111 | {
112 | szKeyName = szKeyNames[i];
113 | bKeyExists = true;
114 | }
115 | else
116 | {
117 | nOpenVersion = i + 1;
118 | }
119 | }
120 |
121 | if (szKeyNames.Length > 0 && !bKeyExists)
122 | {
123 | szKeyName = m_szKeyRoot + nOpenVersion.ToString();
124 | }
125 | else if (szKeyNames.Length == 0)
126 | {
127 | szKeyName = m_szKeyRoot;
128 | }
129 |
130 | return szKeyName;
131 | }
132 |
133 | private void MaintainRegistryKey(bool bInsertKey)
134 | {
135 | UpdateRegistry("11.0", bInsertKey); // Excel 2003 32-bit
136 | UpdateRegistry("12.0", bInsertKey); // Excel 2007 32-bit
137 | UpdateRegistry("14.0", bInsertKey); // Excel 2010 32-bit
138 | UpdateRegistry("15.0", bInsertKey); // Excel 2013 32-bit
139 | }
140 |
141 | // Override the 'Install' method.
142 | public override void Install(IDictionary savedState)
143 | {
144 | base.Install(savedState);
145 | }
146 |
147 | // Override the 'Commit' method.
148 | public override void Commit(IDictionary savedState)
149 | {
150 | base.Commit(savedState);
151 | }
152 |
153 | // Override the 'Rollback' method.
154 | public override void Rollback(IDictionary savedState)
155 | {
156 | base.Rollback(savedState);
157 | }
158 |
159 | public override void Uninstall(IDictionary savedState)
160 | {
161 | MaintainRegistryKey(false);
162 |
163 | base.Uninstall(savedState);
164 | }
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/Source/Installer/InstallerCA/PromptCloseApplication.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Deployment.WindowsInstaller;
2 | using System;
3 | using System.Diagnostics;
4 | using System.Runtime.InteropServices;
5 | using System.Windows.Forms;
6 |
7 | namespace InstallerCA
8 | {
9 | public class PromptCloseApplication : IDisposable
10 | {
11 | #region Instance Variables
12 | private readonly string m_szProductName;
13 | private readonly string m_szProcessName;
14 | private readonly string m_szDisplayName;
15 | private System.Threading.Timer m_timer;
16 | private Form m_form;
17 | private IntPtr m_mainWindowHanle;
18 |
19 | [DllImport("user32.dll", SetLastError = true)]
20 | public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
21 | #endregion
22 |
23 | #region Constructor
24 | public PromptCloseApplication(string productName, string processName, string displayName)
25 | {
26 | m_szProductName = productName;
27 | m_szProcessName = processName;
28 | m_szDisplayName = displayName;
29 | }
30 | #endregion
31 |
32 | #region Prompt
33 | public bool Prompt()
34 | {
35 | bool bReturn = false;
36 | bool bRunning = IsRunning(m_szProcessName);
37 |
38 | if (IsRunning(m_szProcessName))
39 | {
40 | m_form = new ClosePromptForm(String.Format("Please close running instances of {0} before running {1} setup.", m_szDisplayName, m_szProductName));
41 | m_mainWindowHanle = FindWindow(null, m_szProductName + " Setup");
42 | if (m_mainWindowHanle == IntPtr.Zero)
43 | {
44 | m_mainWindowHanle = FindWindow("#32770", m_szProductName);
45 | }
46 |
47 | m_timer = new System.Threading.Timer(TimerElapsed, m_form, 200, 200);
48 |
49 | bReturn = ShowDialog();
50 | }
51 | else
52 | {
53 | bReturn = true;
54 | }
55 | return bReturn;
56 | }
57 | #endregion
58 |
59 | #region ShowDialog
60 | bool ShowDialog()
61 | {
62 | bool bReturn = false;
63 |
64 | if (m_form.ShowDialog(new WindowWrapper(m_mainWindowHanle)) == DialogResult.OK)
65 | {
66 | bReturn = !IsRunning(m_szProcessName) || ShowDialog();
67 | }
68 | return bReturn;
69 | }
70 | #endregion
71 |
72 | #region TimerElapsed
73 | private void TimerElapsed(object sender)
74 | {
75 | if (m_form == null || IsRunning(m_szProcessName) || !m_form.Visible)
76 | {
77 | return;
78 | }
79 | m_form.DialogResult = DialogResult.OK;
80 | m_form.Close();
81 | }
82 | #endregion
83 |
84 | #region IsRunning
85 | private bool IsRunning(string processName)
86 | {
87 | bool bReturn = false;
88 | Process[] procList = Process.GetProcesses();
89 | foreach (Process p in procList)
90 | {
91 | if (p.ProcessName.ToUpper() == processName.ToUpper())
92 | {
93 | return true;
94 | }
95 | }
96 | return bReturn;
97 | }
98 | #endregion
99 |
100 | #region Destructor
101 | public void Dispose()
102 | {
103 | if (m_timer != null)
104 | {
105 | m_timer.Dispose();
106 | }
107 | if (m_form != null && m_form.Visible)
108 | {
109 | m_form.Close();
110 | }
111 | }
112 | #endregion
113 | }
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/Source/Installer/InstallerCA/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("InstallerCA")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("InstallerCA")]
12 | [assembly: AssemblyCopyright("Copyright © 2013")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("bd89da70-0317-4659-a58d-5789bc59af03")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/Source/Installer/InstallerCA/WindowWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace InstallerCA
5 | {
6 | public class WindowWrapper : IWin32Window {
7 | public WindowWrapper(IntPtr handle) {
8 | _hwnd = handle;
9 | }
10 |
11 | public IntPtr Handle {
12 | get { return _hwnd; }
13 | }
14 |
15 | private readonly IntPtr _hwnd;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Source/Installer/WiRunSQL.vbs:
--------------------------------------------------------------------------------
1 | ' This script can be used to update the installer database after it is built - see CustomMessages.wxl for related code.
2 | ' Add a Post-build event to the ExcelAddInDeploy project:
3 | ' Cscript "{Path to WiRunSQL.vbs}\WiRunSQL.vbs" "$(TargetDir)en-us\$(TargetFileName)" "UPDATE `TextStyle` SET `Color` = 16777215 WHERE `TextStyle` = 'WixUI_Font_Title'"
4 | ' This updates the installer database after it is built and changes the colour of the title text. Note the use of ` not ' around field names. Colour value calculated as:
5 |
6 | ' "65536 * blue + 256 * green + red, where red, green, and blue are each in the range of 0-255. The value must not exceed 16777215, which is the value for white.
7 | ' The value is 0 for black, 255 for red, 65280 for green, 16711680 for blue and 8421504 for grey. Leaving the field empty specifies the default color." - copied from
8 | ' http://msdn.microsoft.com/en-ca/library/windows/desktop/aa372074%28v=vs.85%29.aspx
9 | ' See also, http://stackoverflow.com/questions/17574141/customise-fonts-with-wix-extensions and http://msdn.microsoft.com/en-us/library/windows/desktop/aa368568%28v=vs.85%29.aspx
10 |
11 | ' Windows Installer utility to execute SQL statements against an installer database
12 | ' For use with Windows Scripting Host, CScript.exe or WScript.exe
13 | ' Copyright (c) Microsoft Corporation. All rights reserved.
14 | ' Demonstrates the script-driven database queries and updates
15 | '
16 | Option Explicit
17 |
18 | Const msiOpenDatabaseModeReadOnly = 0
19 | Const msiOpenDatabaseModeTransact = 1
20 |
21 | Dim argNum, argCount:argCount = Wscript.Arguments.Count
22 | If (argCount < 2) Then
23 | Wscript.Echo "Windows Installer utility to execute SQL queries against an installer database." &_
24 | vbLf & " The 1st argument specifies the path to the MSI database, relative or full path" &_
25 | vbLf & " Subsequent arguments specify SQL queries to execute - must be in double quotes" &_
26 | vbLf & " SELECT queries will display the rows of the result list specified in the query" &_
27 | vbLf & " Binary data columns selected by a query will not be displayed" &_
28 | vblf &_
29 | vblf & "Copyright (C) Microsoft Corporation. All rights reserved."
30 | Wscript.Quit 1
31 | End If
32 |
33 | ' Scan arguments for valid SQL keyword and to determine if any update operations
34 | Dim openMode : openMode = msiOpenDatabaseModeReadOnly
35 | For argNum = 1 To argCount - 1
36 | Dim keyword : keyword = Wscript.Arguments(argNum)
37 | Dim keywordLen : keywordLen = InStr(1, keyword, " ", vbTextCompare)
38 | If (keywordLen) Then keyword = UCase(Left(keyword, keywordLen - 1))
39 | If InStr(1, "UPDATE INSERT DELETE CREATE ALTER DROP", keyword, vbTextCompare) Then
40 | openMode = msiOpenDatabaseModeTransact
41 | ElseIf keyword <> "SELECT" Then
42 | Fail "Invalid SQL statement type: " & keyword
43 | End If
44 | Next
45 |
46 | ' Connect to Windows installer object
47 | On Error Resume Next
48 | Dim installer : Set installer = Nothing
49 | Set installer = Wscript.CreateObject("WindowsInstaller.Installer") : CheckError
50 |
51 | ' Open database
52 | Dim databasePath:databasePath = Wscript.Arguments(0)
53 | Dim database : Set database = installer.OpenDatabase(databasePath, openMode) : CheckError
54 |
55 | ' Process SQL statements
56 | Dim query, view, record, message, rowData, columnCount, delim, column
57 | For argNum = 1 To argCount - 1
58 | query = Wscript.Arguments(argNum)
59 | Set view = database.OpenView(query) : CheckError
60 | view.Execute : CheckError
61 | If Ucase(Left(query, 6)) = "SELECT" Then
62 | Do
63 | Set record = view.Fetch
64 | If record Is Nothing Then Exit Do
65 | columnCount = record.FieldCount
66 | rowData = Empty
67 | delim = " "
68 | For column = 1 To columnCount
69 | If column = columnCount Then delim = vbLf
70 | rowData = rowData & record.StringData(column) & delim
71 | Next
72 | message = message & rowData
73 | Loop
74 | End If
75 | Next
76 | If openMode = msiOpenDatabaseModeTransact Then database.Commit
77 | If Not IsEmpty(message) Then Wscript.Echo message
78 | Wscript.Quit 0
79 |
80 | Sub CheckError
81 | Dim message, errRec
82 | If Err = 0 Then Exit Sub
83 | message = Err.Source & " " & Hex(Err) & ": " & Err.Description
84 | If Not installer Is Nothing Then
85 | Set errRec = installer.LastErrorRecord
86 | If Not errRec Is Nothing Then message = message & vbLf & errRec.FormatText
87 | End If
88 | Fail message
89 | End Sub
90 |
91 | Sub Fail(message)
92 | Wscript.Echo message
93 | Wscript.Quit 2
94 | End Sub
95 |
96 |
--------------------------------------------------------------------------------