├── .gitattributes ├── .gitignore ├── CommonUtilities ├── CommonUtilities.csproj ├── Properties │ └── AssemblyInfo.cs └── XmlSerializationExtensions.cs ├── ExcelScript.sln ├── ExcelScript ├── AddinFunctions │ ├── Create.cs │ ├── Deserialize.cs │ ├── DisplayCode.cs │ ├── Evaluate.cs │ ├── FormatCode.cs │ ├── OptionsCreate.cs │ ├── ParameterCreate.cs │ ├── Parse.cs │ ├── Register.cs │ ├── Run.cs │ └── Serialize.cs ├── ExcelScript-AddIn.dna ├── ExcelScript-AddIn.xll.config ├── ExcelScript.csproj ├── ExcelScriptAddin.cs ├── HandleNames.cs ├── Internal │ ├── DirtyRangeFlagger.cs │ ├── ManageDirtyFlagsAttribute.cs │ ├── ParameterExtensions.cs │ ├── ParameterValueFactory.cs │ └── SerializationWrapper.cs ├── NinjectModules │ └── ScriptingModule.cs ├── Properties │ └── AssemblyInfo.cs ├── Registration │ ├── FunctionLogging.cs │ ├── RangeParameterConversion.cs │ └── SuppressInDialog.cs ├── Todos and Bugs.txt ├── XlScriptOptions.cs ├── app.config └── packages.config ├── ObjectStorage ├── ObjectStorage.csproj ├── ObjectStore.cs ├── Properties │ └── AssemblyInfo.cs ├── StoredObject.cs ├── VersionProviders │ ├── ConstantVersionProvider.cs │ ├── HashVersionProvider.cs │ ├── IVersionProvider.cs │ ├── NotifyPropertyChangedVersionProvider.cs │ └── NullObjectVersionProvider.cs └── WeaklyStoredObject.cs ├── ObjectStorageAbstractions ├── IObjectStore.cs ├── IStoredObject.cs ├── ObjectStorageAbstractions.csproj └── Properties │ └── AssemblyInfo.cs ├── ObjectStorageTests ├── Generators │ └── StoredTypeGenerator.cs ├── ObjectStorageTests.csproj ├── ObjectStoreTestBase.cs ├── ObjectStoreTests_Hashed.cs ├── ObjectStoreTests_Mixed.cs ├── ObjectStoreTests_NotifyPropertyChanged.cs ├── Properties │ └── AssemblyInfo.cs └── Types │ ├── IStoredType.cs │ ├── StoredTypeHashed.cs │ ├── StoredTypeNotifyPropertyChanged.cs │ └── StoredTypeUnsupported.cs ├── README.md ├── Resources └── Examples.xlsm ├── RoslynScriptGlobals ├── Globals.cs ├── GlobalsFactory.cs ├── Properties │ └── AssemblyInfo.cs ├── RoslynScriptGlobals-AddIn.dna ├── RoslynScriptGlobals.csproj └── packages.config ├── RoslynScripting ├── Factory │ ├── ParameterFactory.cs │ └── ScriptFactory.cs ├── FormatColorScheme.cs ├── FormattedText.cs ├── HostedScriptRunner.cs ├── Internal │ ├── DelegateDisposable.cs │ ├── HashAttribute.cs │ ├── HashEnumerable.cs │ ├── HashHelper.cs │ ├── HashValueAttribute.cs │ ├── IScriptRunner.cs │ └── Marshalling │ │ ├── RemoteTask.cs │ │ └── RemoteTaskCompletionSource.cs ├── Parameter.cs ├── ParameterValue.cs ├── ParseResult.cs ├── Properties │ └── AssemblyInfo.cs ├── RoslynScripting.csproj ├── Script.cs ├── ScriptRunResult.cs ├── app.config └── packages.config ├── RoslynScriptingTests ├── Generators │ ├── ParameterGenerator.cs │ └── ScriptGenerator.cs ├── Properties │ └── AssemblyInfo.cs ├── RoslynScriptingTests.csproj ├── SerializationTests.cs ├── TestGlobals.cs ├── Types │ └── SimpleSerializableType.cs ├── app.config └── packages.config └── ScriptingAbstractions ├── Factory ├── IParameterFactory.cs ├── IParameterValueFactory.cs └── IScriptFactory.cs ├── IGlobalsFactory.cs ├── IHasDescription.cs ├── IParameter.cs ├── IParameterValue.cs ├── IScript.cs ├── IScriptGlobals.cs ├── IScriptRunResult.cs ├── Properties └── AssemblyInfo.cs ├── ScriptingAbstractions.csproj └── ScriptingOptions.cs /.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 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 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 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | -------------------------------------------------------------------------------- /CommonUtilities/CommonUtilities.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {DC268D3E-5FB8-4BD0-93B6-588001FA8EAE} 8 | Library 9 | Properties 10 | ExcelScript.CommonUtilities 11 | CommonUtilities 12 | v4.6.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 54 | -------------------------------------------------------------------------------- /CommonUtilities/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("CommonUtilities")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("CommonUtilities")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("dc268d3e-5fb8-4bd0-93b6-588001fa8eae")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /ExcelScript.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExcelScript", "ExcelScript\ExcelScript.csproj", "{F489EDF9-1286-463B-8C76-3844169BC2D3}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectStorage", "ObjectStorage\ObjectStorage.csproj", "{8AB1CE36-E883-4A98-9713-9F6A849376ED}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectStorageTests", "ObjectStorageTests\ObjectStorageTests.csproj", "{3D4B72A2-1959-4429-8FC6-233CC7A81F8C}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ObjectStorageAbstractions", "ObjectStorageAbstractions\ObjectStorageAbstractions.csproj", "{7013FBC4-3D9B-48DC-986E-31B39BCAD70B}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Object-Storage", "Object-Storage", "{F4288F4B-7251-4A48-A3D5-C2F0AC54C149}" 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripting", "Scripting", "{2FBA8A7A-07E1-4D62-BD9E-EF392C5AECE5}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptingAbstractions", "ScriptingAbstractions\ScriptingAbstractions.csproj", "{9C15D62F-889E-4C74-93D3-AAC38D27031F}" 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynScripting", "RoslynScripting\RoslynScripting.csproj", "{81C9CA40-28C5-490B-A32A-DEFFCA247613}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynScriptGlobals", "RoslynScriptGlobals\RoslynScriptGlobals.csproj", "{EB8FEE81-6DE4-4BE3-A14C-22988D1F23B1}" 23 | EndProject 24 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynScriptingTests", "RoslynScriptingTests\RoslynScriptingTests.csproj", "{0BE3325A-547C-4AC7-9AE5-EEB7BCBB4F22}" 25 | EndProject 26 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonUtilities", "CommonUtilities\CommonUtilities.csproj", "{DC268D3E-5FB8-4BD0-93B6-588001FA8EAE}" 27 | EndProject 28 | Global 29 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 30 | Debug|Any CPU = Debug|Any CPU 31 | Release|Any CPU = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 34 | {F489EDF9-1286-463B-8C76-3844169BC2D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {F489EDF9-1286-463B-8C76-3844169BC2D3}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {F489EDF9-1286-463B-8C76-3844169BC2D3}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {F489EDF9-1286-463B-8C76-3844169BC2D3}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {8AB1CE36-E883-4A98-9713-9F6A849376ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {8AB1CE36-E883-4A98-9713-9F6A849376ED}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {8AB1CE36-E883-4A98-9713-9F6A849376ED}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {8AB1CE36-E883-4A98-9713-9F6A849376ED}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {3D4B72A2-1959-4429-8FC6-233CC7A81F8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {3D4B72A2-1959-4429-8FC6-233CC7A81F8C}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {3D4B72A2-1959-4429-8FC6-233CC7A81F8C}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {3D4B72A2-1959-4429-8FC6-233CC7A81F8C}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {7013FBC4-3D9B-48DC-986E-31B39BCAD70B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {7013FBC4-3D9B-48DC-986E-31B39BCAD70B}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {7013FBC4-3D9B-48DC-986E-31B39BCAD70B}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {7013FBC4-3D9B-48DC-986E-31B39BCAD70B}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {9C15D62F-889E-4C74-93D3-AAC38D27031F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {9C15D62F-889E-4C74-93D3-AAC38D27031F}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {9C15D62F-889E-4C74-93D3-AAC38D27031F}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {9C15D62F-889E-4C74-93D3-AAC38D27031F}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {81C9CA40-28C5-490B-A32A-DEFFCA247613}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {81C9CA40-28C5-490B-A32A-DEFFCA247613}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {81C9CA40-28C5-490B-A32A-DEFFCA247613}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {81C9CA40-28C5-490B-A32A-DEFFCA247613}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {EB8FEE81-6DE4-4BE3-A14C-22988D1F23B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {EB8FEE81-6DE4-4BE3-A14C-22988D1F23B1}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {EB8FEE81-6DE4-4BE3-A14C-22988D1F23B1}.Release|Any CPU.ActiveCfg = Release|Any CPU 61 | {EB8FEE81-6DE4-4BE3-A14C-22988D1F23B1}.Release|Any CPU.Build.0 = Release|Any CPU 62 | {0BE3325A-547C-4AC7-9AE5-EEB7BCBB4F22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {0BE3325A-547C-4AC7-9AE5-EEB7BCBB4F22}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {0BE3325A-547C-4AC7-9AE5-EEB7BCBB4F22}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {0BE3325A-547C-4AC7-9AE5-EEB7BCBB4F22}.Release|Any CPU.Build.0 = Release|Any CPU 66 | {DC268D3E-5FB8-4BD0-93B6-588001FA8EAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 67 | {DC268D3E-5FB8-4BD0-93B6-588001FA8EAE}.Debug|Any CPU.Build.0 = Debug|Any CPU 68 | {DC268D3E-5FB8-4BD0-93B6-588001FA8EAE}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {DC268D3E-5FB8-4BD0-93B6-588001FA8EAE}.Release|Any CPU.Build.0 = Release|Any CPU 70 | EndGlobalSection 71 | GlobalSection(SolutionProperties) = preSolution 72 | HideSolutionNode = FALSE 73 | EndGlobalSection 74 | GlobalSection(NestedProjects) = preSolution 75 | {8AB1CE36-E883-4A98-9713-9F6A849376ED} = {F4288F4B-7251-4A48-A3D5-C2F0AC54C149} 76 | {3D4B72A2-1959-4429-8FC6-233CC7A81F8C} = {F4288F4B-7251-4A48-A3D5-C2F0AC54C149} 77 | {7013FBC4-3D9B-48DC-986E-31B39BCAD70B} = {F4288F4B-7251-4A48-A3D5-C2F0AC54C149} 78 | {9C15D62F-889E-4C74-93D3-AAC38D27031F} = {2FBA8A7A-07E1-4D62-BD9E-EF392C5AECE5} 79 | {81C9CA40-28C5-490B-A32A-DEFFCA247613} = {2FBA8A7A-07E1-4D62-BD9E-EF392C5AECE5} 80 | {EB8FEE81-6DE4-4BE3-A14C-22988D1F23B1} = {2FBA8A7A-07E1-4D62-BD9E-EF392C5AECE5} 81 | {0BE3325A-547C-4AC7-9AE5-EEB7BCBB4F22} = {2FBA8A7A-07E1-4D62-BD9E-EF392C5AECE5} 82 | EndGlobalSection 83 | EndGlobal 84 | -------------------------------------------------------------------------------- /ExcelScript/AddinFunctions/Create.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Integration; 2 | using ExcelScript.Internal; 3 | using ExcelScript.Registration; 4 | using ObjectStorage.Abstractions; 5 | using RoslynScriptGlobals; 6 | using ScriptingAbstractions; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.ComponentModel; 10 | using System.Linq; 11 | using System.Reflection; 12 | using Excel = NetOffice.ExcelApi; 13 | 14 | namespace ExcelScript 15 | { 16 | public partial class ExcelScriptAddin 17 | { 18 | [ExcelFunction(Name = FunctionPrefix + nameof(Create), Description = "Creates a reusable script, and returns its Handle - which can then by invoked with the ExcelScript.Run method", IsVolatile = false)] 19 | [SuppressInDialog] 20 | [ManageDirtyFlags] 21 | public static object Create( 22 | [ExcelArgument("Name for a handle under which the script object will be stored in memory")] string HandleName, 23 | [ExcelArgument("The code for this script. Multiple lines are allowed.")] string[] code, 24 | [ExcelArgument("A handle to script option definitions as created by ExcelScript.Options.Create. Can be left blank to use defaults.")] string OptionsHandle = "", 25 | [ExcelArgument("An arbitrary number of handles to parameter definitions (as created by ExcelScript.Parameters.Create), which define input parameters for the script")] params string[] ParameterHandles) 26 | { 27 | if (code == null) 28 | throw new ArgumentNullException(nameof(code)); 29 | 30 | string _code = String.Join(Environment.NewLine, code); 31 | 32 | // Get Options 33 | XlScriptOptions xlScriptOptions = (String.IsNullOrEmpty(OptionsHandle)) ? XlScriptOptions.Default : GetFromStoreOrThrow(OptionsHandle); 34 | 35 | // Create Script object 36 | var script = CreateScript(xlScriptOptions); 37 | script.Code = _code; 38 | script.ReturnType = xlScriptOptions.ReturnType; 39 | 40 | foreach (var parameterHandle in ParameterHandles) 41 | { 42 | var parameterHandleName = HandleNames.GetNameFrom(parameterHandle); 43 | IParameter storedParameter = GetFromStoreOrThrow(parameterHandleName); 44 | script.Parameters.Add(storedParameter); 45 | } 46 | 47 | IStoredObject storedScript; 48 | if (m_ObjectStore.GetByName(HandleName, out storedScript) && storedScript.Object.GetHashCode() == script.GetHashCode()) 49 | { 50 | return HandleNames.ToHandle(storedScript); 51 | } 52 | else 53 | { 54 | return AddOrUpdateInStoreOrThrow(HandleName, script, () => TryDispose(script)); 55 | } 56 | } 57 | 58 | private static IScript CreateScript(XlScriptOptions xlScriptOptions) 59 | { 60 | var options = CreateScriptingOptionsFrom(xlScriptOptions); 61 | var script = m_ScriptFactory.Create(m_GlobalsFactory, options); 62 | return script; 63 | } 64 | 65 | private static ScriptingOptions CreateScriptingOptionsFrom(XlScriptOptions xlScriptOptions) 66 | { 67 | var options = new ScriptingOptions(); 68 | 69 | foreach (var reference in GetScriptDefaultReferences()) 70 | options.References.Add(reference); 71 | 72 | foreach (var import in GetScriptDefaultImports()) 73 | options.Imports.Add(import); 74 | 75 | options.HostingType = xlScriptOptions.HostingType; 76 | 77 | return options; 78 | } 79 | 80 | private static IEnumerable GetScriptDefaultReferences() 81 | { 82 | yield return typeof(string).Assembly; // mscorlib 83 | yield return typeof(IComponent).Assembly; // System 84 | yield return typeof(Enumerable).Assembly; // System.Core 85 | yield return typeof(Globals).Assembly; // script globals 86 | yield return typeof(Excel.Application).Assembly; // NetOffice 87 | } 88 | 89 | 90 | private static IEnumerable GetScriptDefaultImports() 91 | { 92 | yield return "System"; 93 | yield return "System.Collections.Generic"; 94 | yield return "System.Linq"; 95 | yield return "NetOffice.ExcelApi"; 96 | yield return "NetOffice"; 97 | } 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /ExcelScript/AddinFunctions/Deserialize.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Integration; 2 | using ExcelScript.Internal; 3 | using ExcelScript.Registration; 4 | using ObjectStorage.Abstractions; 5 | using RoslynScriptGlobals; 6 | using ScriptingAbstractions; 7 | using System; 8 | using System.IO; 9 | using System.IO.Compression; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Xml.Linq; 13 | using System.Xml.Serialization; 14 | 15 | namespace ExcelScript 16 | { 17 | public partial class ExcelScriptAddin 18 | { 19 | [ExcelFunction(Name = FunctionPrefix + nameof(Deserialize), IsVolatile = false, Description = "Deserializes data that was previously serialized with ExcelScript.Serialzie()")] 20 | [SuppressInDialog] 21 | public static object Deserialize( 22 | [ExcelArgument("Data input. Can either be the raw data as it was generated by ExcelScript.Serialize(), or can be a filepath from which to load the data")] string input) 23 | { 24 | var obj = InternalDeserialize(input); 25 | 26 | IStoredObject storedObj = (IStoredObject)obj; 27 | 28 | // Re-inject dependencies into the object 29 | var underlyingObj = storedObj.Object; 30 | InjectDependencies(underlyingObj); 31 | 32 | Type objType = storedObj.GetType().GetGenericArguments().Single(); 33 | IStoredObject reStoredObj; 34 | 35 | // todo: maybe reflect & call correct generic method? 36 | if (!m_ObjectStore.AddOrUpdate(storedObj.Name, underlyingObj, objType, out reStoredObj, () => TryDispose(underlyingObj))) 37 | throw new InvalidOperationException("Object could not be re-added to the data store"); 38 | 39 | return HandleNames.ToHandle(reStoredObj); 40 | } 41 | 42 | private static object InternalDeserialize(string data) 43 | { 44 | if (IsValidFilePath(data) || IsValidFilePath(Environment.ExpandEnvironmentVariables(data))) 45 | { 46 | data = Environment.ExpandEnvironmentVariables(data); 47 | 48 | if (!File.Exists(data)) 49 | throw new FileNotFoundException("The given file with serialized data was not found", data); 50 | 51 | data = File.ReadAllText(data); 52 | } 53 | 54 | XDocument doc; 55 | using (var sr = new StringReader(data)) 56 | { 57 | doc = XDocument.Load(sr); 58 | } 59 | 60 | if (doc.Root.Name == "CompressedData") 61 | { 62 | string algorithm = doc.Root.Attribute("Algorithm").Value; 63 | 64 | switch (algorithm) 65 | { 66 | case "gzip": 67 | var compressedData = doc.Root.Value; 68 | Byte[] bytes = Convert.FromBase64String(compressedData); 69 | data = Unzip(bytes); 70 | break; 71 | default: 72 | throw new InvalidOperationException($"Cannot decompress data compressed with algorithm {algorithm}"); 73 | } 74 | } 75 | 76 | var deserialized = XmlDeserialize(data); 77 | var obj = deserialized.ContainedObject; 78 | 79 | return obj; 80 | } 81 | 82 | private static void InjectDependencies(object DeserializedObject) 83 | { 84 | if (DeserializedObject is ScriptingAbstractions.IScript) 85 | InjectDependencies((RoslynScripting.Script)DeserializedObject); 86 | } 87 | 88 | private static void InjectDependencies(IScript Script) 89 | { 90 | m_ScriptFactory.Inject(Script, m_GlobalsFactory); 91 | } 92 | 93 | private static bool IsValidFilePath(string input) 94 | { 95 | try 96 | { 97 | string value = Path.GetFullPath(input); 98 | return !String.IsNullOrWhiteSpace(value); 99 | } 100 | catch (ArgumentException) 101 | { 102 | return false; 103 | } 104 | catch (NotSupportedException) 105 | { 106 | return false; 107 | } 108 | catch (PathTooLongException) 109 | { 110 | return false; 111 | } 112 | } 113 | 114 | // From http://stackoverflow.com/questions/7343465/compression-decompression-string-with-c-sharp 115 | private static string Unzip(byte[] bytes) 116 | { 117 | using (var msi = new MemoryStream(bytes)) 118 | using (var mso = new MemoryStream()) 119 | { 120 | using (var gs = new GZipStream(msi, CompressionMode.Decompress)) 121 | { 122 | CopyTo(gs, mso); 123 | } 124 | 125 | return Encoding.UTF8.GetString(mso.ToArray()); 126 | } 127 | } 128 | 129 | private static T XmlDeserialize(string xml) 130 | { 131 | XmlSerializer deserializer = new XmlSerializer(typeof(T)); 132 | TextReader reader = new StringReader(xml); 133 | object obj = deserializer.Deserialize(reader); 134 | T result = (T)obj; 135 | reader.Close(); 136 | 137 | return result; 138 | } 139 | 140 | private static void CopyTo(Stream src, Stream dest) 141 | { 142 | byte[] bytes = new byte[4096]; 143 | 144 | int cnt; 145 | 146 | while ((cnt = src.Read(bytes, 0, bytes.Length)) != 0) 147 | { 148 | dest.Write(bytes, 0, cnt); 149 | } 150 | } 151 | 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /ExcelScript/AddinFunctions/DisplayCode.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Integration; 2 | using ScriptingAbstractions; 3 | using System; 4 | using System.Linq; 5 | 6 | namespace ExcelScript 7 | { 8 | public partial class ExcelScriptAddin 9 | { 10 | [ExcelFunction(Name = FunctionPrefix + nameof(DisplayCode), Description = "Displays either the entire code, or specific lines from a script as given by the script handle. Can be useful for debugging.", IsVolatile = false)] 11 | public static object DisplayCode( 12 | [ExcelArgument(Name = "ScriptHandle", Description = "A stored handled to the script which shall be displayed")] string ScriptHandle, 13 | [ExcelArgument(Name = "DisplayLineNumbers", Description = "If true, prepends line numbers to each line of the code")] bool DisplayLineNumbers = false, 14 | [ExcelArgument(Name = "LineStart", Description = "Line number of the first line to be displayed (1-based). Optional.")] int LineStart = 0, 15 | [ExcelArgument(Name = "LineEnd", Description = "Line number of the last line to be displayed (1-based). Optional. If LineStart is given, but LineEnd is not, LineEnd will be = LineStart (i.e. will display exactly one line).")] int LineEnd = 0 16 | ) 17 | { 18 | var script = GetFromStoreOrThrow(ScriptHandle); 19 | 20 | var scriptLines = script 21 | .Code 22 | .Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None); 23 | 24 | if (!scriptLines.Any()) 25 | return ""; 26 | 27 | int LineNumDigits = (int)Math.Floor(Math.Log10(scriptLines.Count()) + 1); 28 | string LineNumFormat = "D" + LineNumDigits; 29 | 30 | int _LineStart = (LineStart < 1) ? 1 : LineStart; 31 | int _LineEnd = Math.Min(scriptLines.Length, (LineEnd < 1) ? ((LineStart < 1) ? scriptLines.Length : (int)LineStart) : (int)LineEnd); 32 | 33 | var result1d = scriptLines 34 | .Select((x, i) => new { LineNumber = i + 1, CodeLine = x }) 35 | .Where(x => x.LineNumber >= _LineStart && x.LineNumber <= _LineEnd) 36 | .Select(x => DisplayLineNumbers ? x.LineNumber.ToString(LineNumFormat) + ": " + x.CodeLine : x.CodeLine) 37 | .ToArray(); 38 | 39 | string[,] result = new string[result1d.Length, 1]; 40 | for (int i = 0; i < result1d.Length; i++) 41 | { 42 | result[i, 0] = result1d[i]; 43 | } 44 | 45 | 46 | return result; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ExcelScript/AddinFunctions/Evaluate.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Integration; 2 | using ExcelScript.Registration; 3 | using ScriptingAbstractions; 4 | using System; 5 | using System.Diagnostics; 6 | using System.Linq; 7 | 8 | namespace ExcelScript 9 | { 10 | public partial class ExcelScriptAddin 11 | { 12 | [ExcelFunction(Name = FunctionPrefix + nameof(Evaluate), Description = "Evaluates the given expression and returns its result", IsVolatile = false)] 13 | [SuppressInDialog] 14 | public static object Evaluate( 15 | [ExcelArgument(Description = "The code that is to be evaluated")] string[] code 16 | ) 17 | { 18 | if (code == null) 19 | throw new ArgumentNullException(nameof(code)); 20 | 21 | string _code = String.Join(Environment.NewLine, code); 22 | 23 | var script = CreateScript(XlScriptOptions.Default); 24 | 25 | script.Code = _code; 26 | script.ReturnType = typeof(object); 27 | var result = script.Run(Enumerable.Empty()); 28 | 29 | if (result.IsSuccess) 30 | { 31 | return result.ReturnValue; 32 | } 33 | else 34 | { 35 | throw new AggregateException("There were errors running the script", result.Errors); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ExcelScript/AddinFunctions/FormatCode.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Integration; 2 | using RoslynScriptGlobals; 3 | using RoslynScripting; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Drawing; 7 | using System.Linq; 8 | using Excel = NetOffice.ExcelApi; 9 | 10 | namespace ExcelScript 11 | { 12 | public partial class ExcelScriptAddin 13 | { 14 | [ExcelFunction(Name = FunctionPrefix + nameof(FormatCode), IsMacroType = true, Description = "Formats the code in the input range, and replaces the input ranges' data with the formatted code")] 15 | public static object FormatCode( 16 | [ExcelArgument(AllowReference = true, Description = "A cell reference to the range containing C# code")] object excelRef) 17 | { 18 | Excel.Range cellRef = ToRange((ExcelReference)excelRef); 19 | 20 | if (cellRef == null) 21 | throw new ArgumentException("Input was not an excel reference", nameof(excelRef)); 22 | 23 | if (cellRef.Columns.Count != 1) 24 | throw new ArgumentException("The range must have exactly one column", nameof(excelRef)); 25 | 26 | IEnumerable codeLines = cellRef.Cells.Select(x => Convert.ToString(x.Value)); 27 | 28 | // Remove all empty lines at the beginning and end 29 | codeLines = codeLines.SkipWhile(x => String.IsNullOrWhiteSpace(x)) 30 | .Reverse() 31 | .SkipWhile(x => String.IsNullOrWhiteSpace(x)) 32 | .Reverse(); 33 | 34 | var code = String.Join(Environment.NewLine, codeLines); 35 | 36 | XlScriptOptions xlScriptOptions = XlScriptOptions.Default; 37 | var options = CreateScriptingOptionsFrom(xlScriptOptions); 38 | 39 | var formattedCode = Script.GetFormattedCodeAsync(code, options).Result; 40 | 41 | if (formattedCode.TextLines.Count > cellRef.Rows.Count) 42 | throw new ArgumentException($"The formatted result has {formattedCode.TextLines.Count} lines, but the input range has only {cellRef.Rows.Count}; please expant your input range.", nameof(excelRef)); 43 | 44 | for (int i = 0; i < cellRef.Rows.Count; i++) 45 | { 46 | Excel.Range target = cellRef[i + 1, 1]; 47 | 48 | if (i >= formattedCode.TextLines.Count) 49 | { 50 | // The target range is bigger than what we need; just clear out the unused cells 51 | target.ClearContents(); 52 | } 53 | else 54 | { 55 | var line = formattedCode.TextLines.ElementAt(i); 56 | target.Value = line.Text; 57 | 58 | foreach (var linePart in line.Parts) 59 | { 60 | var characters = target.Characters(linePart.LineSpan.Start + 1, linePart.LineSpan.Length); 61 | var textColor = linePart.TextFormat.TextColor; 62 | var excelColor = Color.FromArgb(textColor.B, textColor.G, textColor.R); // excel uses BGR format, so we convert RGB to BGR here 63 | characters.Font.Color = excelColor.ToArgb(); 64 | } 65 | } 66 | } 67 | 68 | return $"({DateTime.Now.ToShortTimeString()}) Formatted"; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ExcelScript/AddinFunctions/OptionsCreate.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Integration; 2 | using ExcelScript.Internal; 3 | using ExcelScript.Registration; 4 | using System; 5 | 6 | namespace ExcelScript 7 | { 8 | public partial class ExcelScriptAddin 9 | { 10 | [ExcelFunction(Name = FunctionPrefix + "Options.Create", Description = "Can be used to create/set options for user scripts. The result handle of this can be passed to the ExcelScript.Create() function's OptionsHandle parameter", IsVolatile = false)] 11 | [SuppressInDialog] 12 | [ManageDirtyFlags] 13 | public static object OptionsCreate( 14 | [ExcelArgument(Description = "Name of the object handle to be created")] string HandleName, 15 | [ExcelArgument(Description = "Return type of the script. Object by default.")] string ReturnType = "object", 16 | [ExcelArgument(Description = "Hosting type for script execution. Possible values: Shared = Same for all scripts (default). Individual = Seperate AppDomain for each script. Global = Same AppDomain as ExcelScript-Addin.")] string HostingType = "Shared" 17 | ) 18 | { 19 | Type _ReturnType = ToType(ReturnType); 20 | ScriptingAbstractions.HostingType _HostingType = ToHostingType(HostingType); 21 | 22 | var options = new XlScriptOptions(_ReturnType, _HostingType); 23 | return AddOrUpdateInStoreOrThrow(HandleName, options, () => TryDispose(options)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ExcelScript/AddinFunctions/ParameterCreate.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Integration; 2 | using ExcelScript.Internal; 3 | using ExcelScript.Registration; 4 | using ObjectStorage.Abstractions; 5 | using ScriptingAbstractions; 6 | using System; 7 | 8 | namespace ExcelScript 9 | { 10 | public partial class ExcelScriptAddin 11 | { 12 | [ExcelFunction(Name = FunctionPrefix + "Parameter.Create", Description = "Creates a definition for an input parameter for a user script. This can then be passed on to ExcelSCript.Create() ParameterHandles.", IsVolatile = false)] 13 | [SuppressInDialog] 14 | [ManageDirtyFlags] 15 | public static object Parameter_Create( 16 | [ExcelArgument(Description = "Name under which the parameter will be usable in scripts")] string ParameterName, 17 | [ExcelArgument(Description = "Type of the parameter")] string ParameterType, 18 | [ExcelArgument(Description = "If true, this parameter can be ommitted when calling the script, and will then default to its defaultValue (next parameter)")] bool isOptional = false, 19 | [ExcelArgument(Description = "Default value if the parameter is ommitted - only specify this when isOptional (previous parameter) = true")] object defaultValue = null, 20 | string Description = null, 21 | string HandleName = null) 22 | { 23 | if (String.IsNullOrWhiteSpace(ParameterName)) 24 | throw new ArgumentException("Parameter name was empty", nameof(ParameterName)); 25 | 26 | if (!Microsoft.CodeAnalysis.CSharp.SyntaxFacts.IsValidIdentifier(ParameterName)) 27 | throw new ArgumentException("Parameter name was not in a valid format; must be in the format of a valid C# identifier", nameof(ParameterName)); 28 | 29 | if (!isOptional && defaultValue != null) 30 | throw new ArgumentException("Please omit the default value when specifying the parameter as non-optional (isOptional = false).", nameof(defaultValue)); 31 | 32 | if (HandleName == null) 33 | HandleName = ParameterName; 34 | 35 | var parameter = m_ParameterFactory.Create(); 36 | 37 | parameter.Name = ParameterName; 38 | parameter.Type = ToType(ParameterType); 39 | parameter.IsOptional = isOptional; 40 | parameter.Description = Description; 41 | 42 | if (isOptional) 43 | parameter.DefaultValue = defaultValue; 44 | 45 | IStoredObject storedParameter; 46 | string result = AddOrUpdateInStoreOrThrow(HandleName, parameter, out storedParameter, () => TryDispose(parameter)); 47 | 48 | return result; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ExcelScript/AddinFunctions/Parse.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Integration; 2 | using ExcelScript.Internal; 3 | using ExcelScript.Registration; 4 | using ObjectStorage.Abstractions; 5 | using RoslynScriptGlobals; 6 | using RoslynScripting; 7 | using ScriptingAbstractions; 8 | using System; 9 | using System.Linq; 10 | using System.Reflection; 11 | 12 | namespace ExcelScript 13 | { 14 | public partial class ExcelScriptAddin 15 | { 16 | [ExcelFunction(Name = FunctionPrefix + nameof(Parse), Description = "Creates a reusable script, and returns its Handle - which can then by invoked with the ExcelScript.Run method", IsVolatile = false)] 17 | [SuppressInDialog] 18 | [ManageDirtyFlags] 19 | public static object Parse( 20 | [ExcelArgument("The code for this script. Multiple lines are allowed.")] string[] code, 21 | [ExcelArgument("A handle to script option definitions as created by ExcelScript.Options.Create. Can be left blank to use defaults.")] string OptionsHandle = "", 22 | [ExcelArgument("Name for a handle under which the script object will be stored in memory, Can be ommitted, in which case the function name will be used.")] string HandleName = "") 23 | { 24 | if (code == null) 25 | throw new ArgumentNullException(nameof(code)); 26 | 27 | string _code = String.Join(Environment.NewLine, code); 28 | 29 | // CAREFUL: This method may be run in a different AppDomain! Don't reference anything from outside of the delegate 30 | Func EntryMethodSelector = (methods) => 31 | { 32 | MethodInfo result = null; 33 | 34 | // When only one method is available, we'll just return that one... 35 | if (methods.Count() == 1) 36 | { 37 | result = methods.Single(); 38 | } 39 | else 40 | { 41 | // Otherwise, we'll try to find a (one) function marked with an [ExcelFunction] attribute 42 | var markedMethods = methods 43 | .Where(x => x.GetCustomAttribute() != null); 44 | 45 | if (markedMethods.Count() == 0) 46 | throw new ArgumentException($"Your script defines {methods.Count()} methods; please mark one of them with the [ExcelFunction] attribute, to mark which function shall be used as an entry point to your script."); 47 | 48 | if (markedMethods.Count() > 1) 49 | throw new ArgumentException($"Please only mark one function with an [ExcelFunction] attribute; you have currently marked {markedMethods.Count()} functions: " + String.Join(", ", markedMethods.Select(x => x.Name))); 50 | 51 | result = markedMethods.Single(); 52 | } 53 | 54 | return result; 55 | }; 56 | 57 | // CAREFUL: This method may be run in a different AppDomain! Don't reference anything from outside of the delegate 58 | Func EntryMethodParameterFactory = (method => 59 | { 60 | return method 61 | .GetParameters() 62 | .Select(x => 63 | { 64 | var excelArgumentAttribute = x.GetCustomAttribute(); 65 | 66 | return new Parameter 67 | { 68 | Name = (excelArgumentAttribute == null || String.IsNullOrWhiteSpace(excelArgumentAttribute.Name)) ? x.Name : excelArgumentAttribute.Name, 69 | Type = x.ParameterType, 70 | IsOptional = x.IsOptional, 71 | DefaultValue = x.DefaultValue, 72 | Description = (excelArgumentAttribute == null || String.IsNullOrWhiteSpace(excelArgumentAttribute.Description)) ? String.Empty : excelArgumentAttribute.Description 73 | }; 74 | }) 75 | .ToArray(); 76 | }); 77 | 78 | // Create Script object 79 | XlScriptOptions xlScriptOptions = (String.IsNullOrEmpty(OptionsHandle)) ? XlScriptOptions.Default : GetFromStoreOrThrow(OptionsHandle); 80 | var options = CreateScriptingOptionsFrom(xlScriptOptions); 81 | options.References.Add(typeof(ExcelFunctionAttribute).Assembly); 82 | options.Imports.Add(typeof(ExcelFunctionAttribute).Namespace); 83 | 84 | var parseResult = m_ScriptFactory.ParseFromAsync(m_GlobalsFactory, options, _code, EntryMethodSelector, EntryMethodParameterFactory).Result; 85 | var script = parseResult.Script; 86 | script.ReturnType = xlScriptOptions.ReturnType; 87 | 88 | if (String.IsNullOrWhiteSpace(HandleName)) 89 | HandleName = parseResult.EntryMethodName; 90 | 91 | IStoredObject storedScript; 92 | if (m_ObjectStore.GetByName(HandleName, out storedScript) && storedScript.Object.GetHashCode() == script.GetHashCode()) 93 | { 94 | return HandleNames.ToHandle(storedScript); 95 | } 96 | else 97 | { 98 | return AddOrUpdateInStoreOrThrow(HandleName, script, () => TryDispose(script)); 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /ExcelScript/AddinFunctions/Serialize.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Integration; 2 | using ExcelScript.Internal; 3 | using ExcelScript.Registration; 4 | using ObjectStorage.Abstractions; 5 | using System; 6 | using System.IO; 7 | using System.IO.Compression; 8 | using System.Text; 9 | using System.Xml.Linq; 10 | using System.Xml.Serialization; 11 | 12 | namespace ExcelScript 13 | { 14 | public partial class ExcelScriptAddin 15 | { 16 | [ExcelFunction(Name = FunctionPrefix + nameof(Serialize), IsVolatile = false, Description = "Serializes the object into a format which can later be re-loaded (with ExcelScript.Deserialize)")] 17 | [SuppressInDialog] 18 | public static object Serialize( 19 | [ExcelArgument(Description = "Handle to the object which shall be serialized")] string Handle, 20 | [ExcelArgument(Description = "If true, compressed the output. Signitifantly reduces the size of the output, but obfuscates it (makes it unreadable to humans)")] bool Compress = false, 21 | [ExcelArgument(Description = "Optional. If a file path is given, the deserialized data will be saved to a file to the given target destination")] string OutputFilePath = null) 22 | { 23 | return InternalSerialize(Handle, Compress, OutputFilePath); 24 | } 25 | 26 | private static object InternalSerialize(string HandleName, bool Compress = false, string OutputFilePath = null) 27 | { 28 | HandleName = HandleNames.GetNameFrom(HandleName); 29 | IStoredObject storedObj; 30 | 31 | if (!m_ObjectStore.GetByName(HandleName, out storedObj)) 32 | throw new Exception($"No object named {HandleName} found"); 33 | 34 | bool WriteToFile = !String.IsNullOrWhiteSpace(OutputFilePath); 35 | 36 | var obj = new SerializationWrapper { ContainedObject = storedObj }; 37 | var result = XmlSerialize(obj); 38 | 39 | if (Compress) 40 | { 41 | var compressed = Zip(result); 42 | XDocument doc = new XDocument(new XElement("CompressedData", new XAttribute("Algorithm", "gzip"), Convert.ToBase64String(compressed))); 43 | 44 | if (WriteToFile) 45 | { 46 | OutputFilePath = Environment.ExpandEnvironmentVariables(OutputFilePath); 47 | 48 | using (var fileWriter = new FileStream(OutputFilePath, FileMode.Create)) 49 | { 50 | doc.Save(fileWriter); 51 | } 52 | return $"Written to '{OutputFilePath}' in compressed format."; 53 | } 54 | else 55 | { 56 | using (var stringWriter = new StringWriter()) 57 | { 58 | doc.Save(stringWriter); 59 | return stringWriter.ToString(); 60 | } 61 | } 62 | } 63 | else 64 | { 65 | if (WriteToFile) 66 | { 67 | File.WriteAllLines(OutputFilePath, new string[] { result }); 68 | return $"Written to '{OutputFilePath}' in uncompressed XML format."; 69 | } 70 | else 71 | { 72 | return result; 73 | } 74 | } 75 | } 76 | 77 | private static string XmlSerialize(T obj) 78 | { 79 | XmlSerializer serializer = new XmlSerializer(typeof(T)); 80 | using (var writer = new StringWriter()) 81 | { 82 | serializer.Serialize(writer, obj); 83 | return writer.ToString(); 84 | } 85 | } 86 | 87 | 88 | // From http://stackoverflow.com/questions/7343465/compression-decompression-string-with-c-sharp 89 | private static byte[] Zip(string str) 90 | { 91 | var bytes = Encoding.UTF8.GetBytes(str); 92 | 93 | using (var msi = new MemoryStream(bytes)) 94 | using (var mso = new MemoryStream()) 95 | { 96 | using (var gs = new GZipStream(mso, CompressionMode.Compress)) 97 | { 98 | CopyTo(msi, gs); 99 | } 100 | 101 | return mso.ToArray(); 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /ExcelScript/ExcelScript-AddIn.dna: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 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 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /ExcelScript/ExcelScript-AddIn.xll.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 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 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /ExcelScript/HandleNames.cs: -------------------------------------------------------------------------------- 1 | using ObjectStorage.Abstractions; 2 | using System; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace ExcelScript 6 | { 7 | public static class HandleNames 8 | { 9 | private static readonly Regex HandleRegex = new Regex("^(.*)(:[0-9]*)$"); 10 | private static readonly Regex InvalidChars = new Regex("[^0-9a-zA-Z_]"); // only allow alphabetic characters, letters and underscores 11 | 12 | public static string ToHandle(IStoredObject storedObject) 13 | { 14 | return $"{storedObject.Name}:{storedObject.Version}"; 15 | } 16 | 17 | /// 18 | /// Returns the Handle name from the given Handle 19 | /// e.g. Handle = Test:123, return value = Test 20 | /// Removes invalid characters. 21 | /// 22 | public static string GetNameFrom(string Text) 23 | { 24 | // Remove the :12345 at the end, if there is any 25 | var match = HandleRegex.Match(Text); 26 | 27 | if (match.Success) 28 | Text = match.Groups[1].Value; 29 | 30 | // Remove al invalid chars 31 | Text = InvalidChars.Replace(Text, String.Empty); // Remove invalid chars 32 | 33 | return Text; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ExcelScript/Internal/ManageDirtyFlagsAttribute.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 ExcelScript.Internal 8 | { 9 | [AttributeUsage(AttributeTargets.Method)] 10 | public class ManageDirtyFlagsAttribute : System.Attribute 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ExcelScript/Internal/ParameterExtensions.cs: -------------------------------------------------------------------------------- 1 | using ExcelScript.Internal; 2 | using ScriptingAbstractions; 3 | using ScriptingAbstractions.Factory; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace ExcelScript 11 | { 12 | internal static class ParameterExtensions 13 | { 14 | private static readonly IParameterValueFactory m_ParameterValueFactory = new ParameterValueFactory(); 15 | 16 | public static IParameterValue WithValue(this IParameter parameter, object Value) 17 | { 18 | return m_ParameterValueFactory.CreateFor(parameter, Value); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ExcelScript/Internal/ParameterValueFactory.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Integration; 2 | using RoslynScripting; 3 | using ScriptingAbstractions; 4 | using ScriptingAbstractions.Factory; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using Excel = NetOffice.ExcelApi; 11 | 12 | namespace ExcelScript.Internal 13 | { 14 | internal class ConverterInfo 15 | { 16 | public readonly TransferableValueToOriginalValueConverter transferableValueToOriginalValueConverter; 17 | public readonly OriginalValueToTransferableValueConverter originalValueToTransferableValueConverter; 18 | public readonly Type TransferableValueType; 19 | 20 | public ConverterInfo(TransferableValueToOriginalValueConverter transferableValueToOriginalValueConverter, OriginalValueToTransferableValueConverter originalValueToTransferableValueConverter, Type TransferableValueType) 21 | { 22 | this.transferableValueToOriginalValueConverter = transferableValueToOriginalValueConverter; 23 | this.originalValueToTransferableValueConverter = originalValueToTransferableValueConverter; 24 | this.TransferableValueType = TransferableValueType; 25 | } 26 | 27 | } 28 | 29 | public class ParameterValueFactory : IParameterValueFactory 30 | { 31 | private static readonly IDictionary Converters = CreateConverters(); 32 | 33 | public IParameterValue CreateFor(IParameter Parameter, object WithValue) 34 | { 35 | var converter = GetMarshalConverterFor(Parameter); 36 | IParameterValue result; 37 | 38 | if(converter == null) 39 | { 40 | result = new ParameterValue(Parameter, WithValue); 41 | } else 42 | { 43 | result = new ParameterValue(Parameter, WithValue, converter.TransferableValueType, converter.originalValueToTransferableValueConverter, converter.transferableValueToOriginalValueConverter); 44 | } 45 | 46 | return result; 47 | } 48 | 49 | 50 | 51 | /// 52 | /// Gets the ConverterInfo needed to convert for marsalling the given parameter. 53 | /// If the type needs conversion but no appropriate converter is registered, throws an exception 54 | /// 55 | /// 56 | /// 57 | private static ConverterInfo GetMarshalConverterFor(IParameter Parameter) 58 | { 59 | if (!NeedsMarshalConverter(Parameter)) 60 | return null; 61 | 62 | if (!Converters.ContainsKey(Parameter.Type)) 63 | throw new InvalidOperationException($"Parameter value needs conversion, however did not find any converter to convert parameter type {Parameter.Type.Name} into a marshallable value"); 64 | 65 | var result = Converters[Parameter.Type]; 66 | return result; 67 | } 68 | 69 | 70 | private static bool NeedsMarshalConverter(IParameter Parameter) 71 | { 72 | if (Parameter.Type.IsValueType) 73 | return false; // marshals by value 74 | 75 | if (Parameter.Type == typeof(string)) 76 | return false; // marshals by bleed 77 | 78 | if (typeof(MarshalByRefObject).IsAssignableFrom(Parameter.Type)) 79 | return false; // marshals by ref 80 | 81 | if (Parameter.Type.IsSerializable) 82 | return false; // marshal by value - todo: double check this ALWAYS works with any .IsSerializable type 83 | 84 | return true; 85 | } 86 | 87 | private static IDictionary CreateConverters() 88 | { 89 | var result = new Dictionary(); 90 | 91 | var rangeConverter = new ConverterInfo(TransferableValueToRange, RangeToTransferableValue, typeof(string)); 92 | result.Add(typeof(Excel.Range), rangeConverter); 93 | return result; 94 | } 95 | 96 | 97 | #region Converter Functions 98 | 99 | // this will be executing on the host's appdomain 100 | private static Excel.Range TransferableValueToRange(object transferableValue) 101 | { 102 | string address = (string)transferableValue; 103 | var application = new Excel.Application(null, ExcelDnaUtil.Application); 104 | var range = application.Range(address); 105 | 106 | range.OnDispose += (args) => 107 | { 108 | range.Application.Dispose(); 109 | }; 110 | 111 | return range; 112 | } 113 | 114 | private static string RangeToTransferableValue(object OriginalValue) 115 | { 116 | var range = (Excel.Range)OriginalValue; 117 | string result = range.Address(true, true, NetOffice.ExcelApi.Enums.XlReferenceStyle.xlA1, true); 118 | return result; 119 | } 120 | 121 | #endregion 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /ExcelScript/Internal/SerializationWrapper.cs: -------------------------------------------------------------------------------- 1 | using ExcelScript.CommonUtilities; 2 | using System.Xml; 3 | using System.Xml.Schema; 4 | using System.Xml.Serialization; 5 | 6 | namespace ExcelScript.Internal 7 | { 8 | public class SerializationWrapper : IXmlSerializable 9 | { 10 | public object ContainedObject { get; set; } 11 | 12 | #region IXmlSerializable 13 | 14 | public XmlSchema GetSchema() 15 | { 16 | return null; 17 | } 18 | 19 | public void WriteXml(XmlWriter writer) 20 | { 21 | writer.Write(nameof(ContainedObject), ContainedObject); 22 | } 23 | 24 | public void ReadXml(XmlReader reader) 25 | { 26 | reader.MoveToCustomStart(); 27 | 28 | object _ContainedObject; 29 | 30 | reader.Read(nameof(ContainedObject), out _ContainedObject); 31 | 32 | this.ContainedObject = _ContainedObject; 33 | 34 | reader.ReadEndElement(); 35 | } 36 | 37 | #endregion 38 | 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ExcelScript/NinjectModules/ScriptingModule.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Integration; 2 | using ExcelScript.Internal; 3 | using Ninject.Modules; 4 | using ObjectStorage; 5 | using ObjectStorage.Abstractions; 6 | using RoslynScriptGlobals; 7 | using RoslynScripting.Factory; 8 | using ScriptingAbstractions; 9 | using ScriptingAbstractions.Factory; 10 | using System; 11 | using Excel = NetOffice.ExcelApi; 12 | 13 | namespace ExcelScript.NinjectModules 14 | { 15 | public class ScriptingModule : NinjectModule 16 | { 17 | public override void Load() 18 | { 19 | Bind().To().InSingletonScope(); 20 | Bind().To().InSingletonScope(); 21 | Bind().To().InSingletonScope(); 22 | Bind>().ToConstant>(GetApplication).WhenInjectedInto(); 23 | Bind>().To().InSingletonScope(); 24 | Bind().ToSelf().InSingletonScope(); 25 | } 26 | 27 | private static Excel.Application GetApplication() 28 | { 29 | return new Excel.Application(null, ExcelDnaUtil.Application); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ExcelScript/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("ExcelScript")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ExcelScript")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("f489edf9-1286-463b-8c76-3844169bc2d3")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /ExcelScript/Registration/FunctionLogging.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Registration; 2 | using System.Linq; 3 | 4 | namespace ExcelScript.Registration 5 | { 6 | public class FunctionLoggingHandler : FunctionExecutionHandler 7 | { 8 | int Index; 9 | public override void OnEntry(FunctionExecutionArgs args) 10 | { 11 | // FunctionExecutionArgs gives access to the function name and parameters, 12 | // and gives some options for flow redirection. 13 | 14 | // Tag will flow through the whole handler 15 | args.Tag = args.FunctionName + ":" + Index; 16 | 17 | ExcelScriptAddin.m_Log.Debug("{0} - OnEntry - Args: {1}", args.Tag, string.Join(",", args.Arguments.Select(arg => arg.ToString()))); 18 | } 19 | 20 | public override void OnSuccess(FunctionExecutionArgs args) 21 | { 22 | ExcelScriptAddin.m_Log.Debug("{0} - OnSuccess - Result: {1}", args.Tag, args.ReturnValue); 23 | } 24 | 25 | public override void OnException(FunctionExecutionArgs args) 26 | { 27 | ExcelScriptAddin.m_Log.Error("{0} - OnException - Message: {1}", args.Tag, args.Exception); 28 | } 29 | 30 | public override void OnExit(FunctionExecutionArgs args) 31 | { 32 | ExcelScriptAddin.m_Log.Debug("{0} - OnExit", args.Tag); 33 | } 34 | 35 | // The configuration part - maybe move somewhere else. 36 | // (Add a registration index just to show we can attach arbitrary data to the captured handler instance which may be created for each function.) 37 | // If we return the same object for every function, the object needs to be re-entrancy safe is used by IsThreadSafe functions. 38 | static int _index = 0; 39 | internal static FunctionExecutionHandler LoggingHandlerSelector(ExcelFunctionRegistration functionRegistration) 40 | { 41 | return new FunctionLoggingHandler { Index = _index++ }; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ExcelScript/Registration/RangeParameterConversion.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Integration; 2 | using ExcelDna.Registration; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Linq.Expressions; 7 | using Excel = NetOffice.ExcelApi; 8 | 9 | namespace ExcelScript.Registration 10 | { 11 | // based on https://github.com/Excel-DNA/Registration/blob/master/Source/ExcelDna.Registration.VisualBasic/RangeParameterConversion.vb 12 | public static class RangeParameterConversion 13 | { 14 | public static IEnumerable UpdateRegistrationForRangeParameters(this IEnumerable reg) 15 | { 16 | return reg.Select(UpdateAttributesForRangeParameters); 17 | } 18 | 19 | public static Expression> ParameterConversion(Type paramType, ExcelParameterRegistration paramRegistration) 20 | { 21 | if (paramType == typeof(Excel.Range)) 22 | return (Expression>)((object input) => ReferenceToRange(input)); 23 | else 24 | return null; 25 | } 26 | 27 | private static ExcelFunctionRegistration UpdateAttributesForRangeParameters(ExcelFunctionRegistration reg) 28 | { 29 | var rangeParams = reg.FunctionLambda.Parameters.Select((x, i) => new { _Parameter = x, _Index = i }) 30 | .Where(x => x._Parameter.Type == typeof(Excel.Range)); 31 | 32 | bool hasRangeParam = false; 33 | foreach(var param in rangeParams) 34 | { 35 | reg.ParameterRegistrations[param._Index].ArgumentAttribute.AllowReference = true; 36 | hasRangeParam = true; 37 | } 38 | 39 | if (hasRangeParam) 40 | reg.FunctionAttribute.IsMacroType = true; 41 | 42 | return reg; 43 | } 44 | 45 | private static Excel.Range ReferenceToRange(object input) 46 | { 47 | ExcelReference xlRef = (ExcelReference)input; 48 | return ExcelScriptAddin.FromExcelReferenceToRange(xlRef); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ExcelScript/Registration/SuppressInDialog.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Integration; 2 | using ExcelDna.Registration; 3 | using System; 4 | using System.Linq; 5 | 6 | namespace ExcelScript.Registration 7 | { 8 | // Alternative pattern - make this an attribute directly 9 | // CONSIDER: In this case we never need the parameters. It would be nice never have to pull them into the FunctionExecutionArgs. 10 | 11 | // TODO: Only works for functions that return string or object. Automatically add a return value conversion otherwise? 12 | 13 | [AttributeUsage(AttributeTargets.Method)] 14 | public class SuppressInDialogAttribute : Attribute, IFunctionExecutionHandler 15 | { 16 | readonly string _dialogResult; 17 | public SuppressInDialogAttribute(string dialogMessage = "!!! NOT CALCULATED IN DIALOG !!!") 18 | { 19 | _dialogResult = dialogMessage; 20 | } 21 | 22 | public void OnEntry(FunctionExecutionArgs args) 23 | { 24 | if (ExcelDnaUtil.IsInFunctionWizard()) 25 | { 26 | args.ReturnValue = _dialogResult; 27 | args.FlowBehavior = FlowBehavior.Return; 28 | } 29 | // Otherwise we do not interfere 30 | } 31 | 32 | // Implemented just to satisfy the interface 33 | public void OnSuccess(FunctionExecutionArgs args) { } 34 | public void OnException(FunctionExecutionArgs args) { } 35 | public void OnExit(FunctionExecutionArgs args) { } 36 | } 37 | 38 | public static class SuppressInDialogFunctionExecutionHandler 39 | { 40 | /// 41 | /// Currently only applied to functions that return object or string. 42 | /// 43 | /// 44 | /// 45 | public static IFunctionExecutionHandler SuppressInDialogSelector(ExcelFunctionRegistration functionRegistration) 46 | { 47 | // Eat the TimingAttributes, and return a timer handler if there were any 48 | if (functionRegistration.CustomAttributes.OfType().Any() && 49 | (functionRegistration.FunctionLambda.ReturnType == typeof(object) || 50 | functionRegistration.FunctionLambda.ReturnType == typeof(string))) 51 | { 52 | // Get the first cache attribute, and remove all of them 53 | var suppressAtt = functionRegistration.CustomAttributes.OfType().First(); 54 | functionRegistration.CustomAttributes.RemoveAll(att => att is SuppressInDialogAttribute); 55 | 56 | return suppressAtt; 57 | } 58 | return null; 59 | 60 | } 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ExcelScript/Todos and Bugs.txt: -------------------------------------------------------------------------------- 1 | 1. Do return-value conversion / cross domain boxing 2 | 2. Do conversion from Range to Array types2 3 | 3. Implement an RunAsync() excel function 4 | 4. Extend Unit Tests -------------------------------------------------------------------------------- /ExcelScript/XlScriptOptions.cs: -------------------------------------------------------------------------------- 1 | using ScriptingAbstractions; 2 | using System; 3 | 4 | namespace ExcelScript 5 | { 6 | public class XlScriptOptions 7 | { 8 | public Type ReturnType { get; private set; } 9 | public HostingType HostingType { get; private set; } 10 | 11 | 12 | public XlScriptOptions() 13 | { 14 | this.ReturnType = typeof(Object); 15 | this.HostingType = HostingType.SharedSandboxAppDomain; 16 | } 17 | 18 | public XlScriptOptions(Type ReturnType, HostingType HostingType) 19 | { 20 | this.ReturnType = ReturnType; 21 | this.HostingType = HostingType; 22 | } 23 | 24 | public static XlScriptOptions Default 25 | { 26 | get 27 | { 28 | return new XlScriptOptions(); 29 | } 30 | } 31 | 32 | public override int GetHashCode() 33 | { 34 | unchecked 35 | { 36 | int hash = (int)2166136261; 37 | 38 | hash = (hash * 16777619) ^ ReturnType.GetHashCode(); 39 | hash = (hash * 16777619) ^ HostingType.GetHashCode(); 40 | 41 | return hash; 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ExcelScript/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /ExcelScript/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 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 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /ObjectStorage/ObjectStorage.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {8AB1CE36-E883-4A98-9713-9F6A849376ED} 8 | Library 9 | Properties 10 | ObjectStorage 11 | ObjectStorage 12 | v4.6.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | {dc268d3e-5fb8-4bd0-93b6-588001fa8eae} 56 | CommonUtilities 57 | 58 | 59 | {7013fbc4-3d9b-48dc-986e-31b39bcad70b} 60 | ObjectStorageAbstractions 61 | 62 | 63 | 64 | 71 | -------------------------------------------------------------------------------- /ObjectStorage/ObjectStore.cs: -------------------------------------------------------------------------------- 1 | using ObjectStorage.Abstractions; 2 | using System; 3 | using System.Collections.Concurrent; 4 | using System.Linq; 5 | using System.Reflection; 6 | 7 | namespace ObjectStorage 8 | { 9 | public class ObjectStore : IObjectStore 10 | { 11 | private readonly ConcurrentDictionary _StoredObjects = new ConcurrentDictionary(); 12 | 13 | public bool GetByName(string name, out IStoredObject result) 14 | { 15 | IStoredObject temp_result = null; 16 | bool success = GetByName(name, out temp_result); 17 | 18 | result = temp_result as IStoredObject; 19 | return success; 20 | } 21 | 22 | 23 | public bool GetByName(string name, out IStoredObject result) 24 | where T : class 25 | { 26 | IStoredObject return_value = null; 27 | 28 | if (!_StoredObjects.TryGetValue(name, out return_value)) 29 | { 30 | result = null; 31 | return false; 32 | } 33 | else 34 | { 35 | if(return_value is IStoredObject) 36 | { 37 | result = (IStoredObject)return_value; 38 | return true; 39 | } else if(return_value.Object is T && return_value is IUnregisterAware) 40 | { 41 | result = Wrap(name, (T)return_value.Object, ((IUnregisterAware)return_value).OnUnregister); 42 | return true; 43 | } else 44 | { 45 | result = null; 46 | return false; 47 | } 48 | } 49 | } 50 | 51 | public bool Exists(string name) 52 | { 53 | return _StoredObjects.ContainsKey(name); 54 | } 55 | 56 | public bool Remove(string name) 57 | { 58 | IStoredObject _unused; 59 | return Remove(name, out _unused); 60 | } 61 | 62 | public bool Remove(string name, out IStoredObject RemovedItem) 63 | { 64 | bool success = _StoredObjects.TryRemove(name, out RemovedItem); 65 | TryDispose(RemovedItem); 66 | return success; 67 | } 68 | 69 | // MethodInfo of AddOrUpdate(string name, T Object, out IStoredObject StoredObject, Action OnUnregistered) 70 | private static readonly MethodInfo _GenericAddOrUpdateMi = typeof(ObjectStore).GetMethods() 71 | .Where(m => m.Name == nameof(AddOrUpdate)) 72 | .Select(m => new 73 | { 74 | Method = m, 75 | Params = m.GetParameters(), 76 | GenericArgs = m.GetGenericArguments() 77 | }) 78 | .Where(x => x.Params.Length == 4 79 | && x.GenericArgs.Length == 1) 80 | .Select(x => x.Method) 81 | .Single(); 82 | 83 | public bool AddOrUpdate(string name, object Object, Type objectType, out IStoredObject StoredObject, Action OnUnregistered = null) 84 | { 85 | var parameters = new object[] { name, Object, null, OnUnregistered }; 86 | var mi = _GenericAddOrUpdateMi.MakeGenericMethod(objectType); 87 | bool result = (bool)mi.Invoke(this, parameters); 88 | StoredObject = (IStoredObject)parameters[2]; // the out IStoredObject parameter 89 | return result; 90 | } 91 | 92 | public bool AddOrUpdate(string name, T Object, out IStoredObject StoredObject, Action OnUnregistered = null) 93 | where T : class 94 | { 95 | StoredObject = (IStoredObject)_StoredObjects.AddOrUpdate( 96 | name, 97 | 98 | // AddValueFactory 99 | (_name) => 100 | { 101 | return Wrap(name, Object, OnUnregistered); 102 | }, 103 | 104 | // UpdateValueFactory 105 | (_name, _oldValue) => 106 | { 107 | // When updating, we must ensure to carry on the version of the previously existing object 108 | var result = Wrap(name, Object, _oldValue, OnUnregistered); 109 | TryDispose(_oldValue); 110 | return result; 111 | } 112 | ); 113 | 114 | return true; 115 | } 116 | 117 | public bool AddOrUpdate(string name, T Object, Action OnUnregistered = null) 118 | where T : class 119 | { 120 | IStoredObject _unused; 121 | return AddOrUpdate(name, Object, out _unused, OnUnregistered); 122 | } 123 | 124 | private void TryDispose(IStoredObject storedObject) 125 | { 126 | IDisposable disposable = storedObject as IDisposable; 127 | if (disposable != null) 128 | disposable.Dispose(); 129 | } 130 | 131 | private IStoredObject Wrap(string Name, T Item, Action OnUnregistered) 132 | where T : class 133 | { 134 | return new StoredObject(Name, Item, OnUnregistered); 135 | } 136 | 137 | private IStoredObject Wrap(string Name, T Item, IStoredObject oldItem, Action OnUnregistered) 138 | where T : class 139 | { 140 | return new StoredObject(Name, Item, oldItem, OnUnregistered); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /ObjectStorage/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("ObjectStorage")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ObjectStorage")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("8ab1ce36-e883-4a98-9713-9f6a849376ed")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /ObjectStorage/StoredObject.cs: -------------------------------------------------------------------------------- 1 | using ExcelScript.CommonUtilities; 2 | using ObjectStorage.Abstractions; 3 | using ObjectStorage.VersionProviders; 4 | using System; 5 | using System.Xml; 6 | using System.Xml.Schema; 7 | using System.Xml.Serialization; 8 | 9 | namespace ObjectStorage 10 | { 11 | internal interface IUnregisterAware 12 | { 13 | Action OnUnregister { get; } 14 | } 15 | 16 | /// 17 | /// Implementation of IStoredObject. 18 | /// For Versioning to work correctly, objets stored with this class should propertly implement GetHashCode() for value equality 19 | /// 20 | /// Type of the stored object 21 | public class StoredObject : IStoredObject, IDisposable, IXmlSerializable, IUnregisterAware 22 | where T : class 23 | { 24 | public Action OnUnregister { get; private set; } 25 | 26 | public virtual string Name { get; protected set; } 27 | 28 | private T m_Object; 29 | public virtual T Object 30 | { 31 | get 32 | { 33 | return m_Object; 34 | } 35 | protected set 36 | { 37 | if (m_Object != value) 38 | { 39 | OnSetObject(m_Object, value); 40 | m_Object = value; 41 | } 42 | } 43 | } 44 | 45 | protected IVersionProvider m_VersionProvider; 46 | 47 | public virtual int Version 48 | { 49 | get 50 | { 51 | return m_VersionProvider.GetVersionFor(this.Object); 52 | } 53 | } 54 | 55 | // IStoredObject 56 | Object IStoredObject.Object 57 | { 58 | get 59 | { 60 | return Object; 61 | } 62 | } 63 | 64 | // IStoredObject 65 | T IStoredObject.Object 66 | { 67 | get 68 | { 69 | return Object; 70 | } 71 | } 72 | 73 | protected void OnSetObject(T oldValue, T newValue) 74 | { 75 | IDisposable oldProviderDisposable = this.m_VersionProvider as IDisposable; 76 | 77 | this.m_VersionProvider = CreateVersionProviderFor(newValue, oldValue); 78 | 79 | if (oldProviderDisposable != null) 80 | oldProviderDisposable.Dispose(); 81 | } 82 | 83 | public void Dispose() 84 | { 85 | if (OnUnregister != null) 86 | OnUnregister(); 87 | 88 | IDisposable disposable = this.m_VersionProvider as IDisposable; 89 | 90 | if (disposable != null) 91 | disposable.Dispose(); 92 | } 93 | 94 | public StoredObject(string Name, T Object, Action OnUnregister) 95 | : this(Name, Object, null, OnUnregister) 96 | { 97 | } 98 | 99 | // This constructor is needed for Deserialization 100 | private StoredObject() 101 | : this(null, null, null, null) 102 | { 103 | } 104 | 105 | /// 106 | /// This constructor can be called when creating a new StoredObject which is intended to replace the OldObject in the store. 107 | /// The constructor will ensure the new object's versioning carries forward the old version's versioning. 108 | /// 109 | /// Name under which the object is added to the store 110 | /// The actual (new) object 111 | /// The old object which is being replaced 112 | internal StoredObject(string Name, T Object, IStoredObject OldObject, Action OnUnregister) 113 | { 114 | this.Name = Name; 115 | this.OnUnregister = OnUnregister; 116 | 117 | int initial_version = (OldObject == null) ? 0 : OldObject.Version; 118 | this.m_VersionProvider = new ConstantVersionProvider(initial_version); // we'll start with a ConstantVersionProvider to supply the initial version. Each time this.Objec is set (such as later in this constructor), this.m_VersionProvider will be replaced with a provider that actually suits the runtime type anyway. 119 | 120 | this.Object = Object; 121 | } 122 | 123 | 124 | private IVersionProvider CreateVersionProviderFor(T newValue, T oldValue) 125 | { 126 | int initial_version = (m_VersionProvider.GetVersionFor(oldValue)+1); 127 | 128 | if (newValue == null) 129 | return new NullObjectVersionProvider(initial_version); 130 | 131 | Type runtimeType = newValue.GetType(); 132 | 133 | if (HashVersionProvider.CanHandle(runtimeType)) 134 | return new HashVersionProvider(newValue, initial_version); 135 | else if (NotifyPropertyChangedVersionProvider.CanHandle(runtimeType)) 136 | return new NotifyPropertyChangedVersionProvider(newValue, initial_version); 137 | else 138 | throw new InvalidOperationException(String.Format("No IVersionProvider found that can handle type '{0}'", typeof(T).FullName)); 139 | } 140 | 141 | #region IXmlSerializable 142 | 143 | public XmlSchema GetSchema() 144 | { 145 | return null; 146 | } 147 | 148 | public void WriteXml(XmlWriter writer) 149 | { 150 | writer.Write(nameof(Name), Name); 151 | writer.Write(nameof(Version), Version); 152 | writer.Write(nameof(Object), (object)Object); 153 | } 154 | 155 | public void ReadXml(XmlReader reader) 156 | { 157 | reader.MoveToCustomStart(); 158 | 159 | string _Name; 160 | int _Version; 161 | object _Obj; 162 | 163 | reader.Read(nameof(Name), out _Name); 164 | reader.Read(nameof(Version), out _Version); 165 | reader.Read(nameof(Object), out _Obj); 166 | 167 | this.Name = _Name; 168 | this.Object = (T)_Obj; 169 | // no need to set version 170 | 171 | reader.ReadEndElement(); 172 | } 173 | 174 | #endregion 175 | 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /ObjectStorage/VersionProviders/ConstantVersionProvider.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 ObjectStorage.VersionProviders 8 | { 9 | internal class ConstantVersionProvider : IVersionProvider 10 | { 11 | private int m_Version; 12 | 13 | public ConstantVersionProvider(int Version) 14 | { 15 | this.m_Version = Version; 16 | } 17 | 18 | public int GetVersionFor(object obj) 19 | { 20 | return this.m_Version; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ObjectStorage/VersionProviders/HashVersionProvider.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 ObjectStorage.VersionProviders 8 | { 9 | internal class HashVersionProvider : IVersionProvider 10 | { 11 | private int m_Version = 1; 12 | private int m_lastHash = -1; 13 | 14 | public int GetVersionFor(object obj) 15 | { 16 | int new_hash = GetHashFor(obj); 17 | 18 | if(new_hash != m_lastHash) 19 | { 20 | m_lastHash = new_hash; 21 | return ++m_Version; 22 | } else 23 | { 24 | return m_Version; 25 | } 26 | } 27 | 28 | public static bool CanHandle(Type t) 29 | { 30 | return (t.GetMethod("GetHashCode").DeclaringType == t); 31 | } 32 | 33 | private static int GetHashFor(object obj) 34 | { 35 | if (obj == null) 36 | return -1; 37 | else 38 | return obj.GetHashCode(); 39 | } 40 | 41 | /// Initial value. The value of this will not be stored in this class / will not store any reference to this 42 | public HashVersionProvider(object InitialObject) 43 | { 44 | this.m_lastHash = GetHashFor(InitialObject); 45 | } 46 | 47 | public HashVersionProvider(object InitialObject, int initial_version) 48 | : this(InitialObject) 49 | { 50 | this.m_Version = initial_version; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ObjectStorage/VersionProviders/IVersionProvider.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 ObjectStorage.VersionProviders 8 | { 9 | public interface IVersionProvider 10 | { 11 | int GetVersionFor(object obj); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ObjectStorage/VersionProviders/NotifyPropertyChangedVersionProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ObjectStorage.VersionProviders 9 | { 10 | internal class NotifyPropertyChangedVersionProvider : IVersionProvider, IDisposable 11 | { 12 | private WeakReference m_EventSource; 13 | private bool m_isDirty = false; 14 | private int m_Version; 15 | 16 | public int GetVersionFor(object obj) 17 | { 18 | if(m_EventSource != null) 19 | { 20 | INotifyPropertyChanged oldSource; 21 | if(m_EventSource.TryGetTarget(out oldSource) && oldSource == obj) 22 | { 23 | if (m_isDirty) 24 | { 25 | m_isDirty = false; 26 | this.m_Version += 1; 27 | } 28 | 29 | return this.m_Version; 30 | } 31 | } 32 | 33 | TrySubscribeTo(obj); 34 | this.m_Version += 1; 35 | return this.m_Version; 36 | } 37 | 38 | public void Dispose() 39 | { 40 | TryUnsubscribe(); 41 | } 42 | 43 | public static bool CanHandle(Type t) 44 | { 45 | return typeof(INotifyPropertyChanged).IsAssignableFrom(t); 46 | } 47 | 48 | public NotifyPropertyChangedVersionProvider(object InitialValue, int InitialVersion = 1) 49 | { 50 | TrySubscribeTo(InitialValue); 51 | this.m_Version = InitialVersion; 52 | } 53 | 54 | private void TrySubscribeTo(object obj) 55 | { 56 | TryUnsubscribe(); 57 | 58 | INotifyPropertyChanged _obj = obj as INotifyPropertyChanged; 59 | if(_obj != null) 60 | { 61 | _obj.PropertyChanged += EventSourcePropertyChanged; 62 | this.m_EventSource = new WeakReference(_obj); 63 | } 64 | } 65 | 66 | private void EventSourcePropertyChanged(object sender, PropertyChangedEventArgs e) 67 | { 68 | m_isDirty = true; 69 | } 70 | 71 | private void TryUnsubscribe() 72 | { 73 | INotifyPropertyChanged oldEventSource; 74 | if (m_EventSource != null && m_EventSource.TryGetTarget(out oldEventSource)) 75 | { 76 | oldEventSource.PropertyChanged -= EventSourcePropertyChanged; 77 | oldEventSource = null; 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /ObjectStorage/VersionProviders/NullObjectVersionProvider.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 ObjectStorage.VersionProviders 8 | { 9 | internal class NullObjectVersionProvider : IVersionProvider 10 | { 11 | private int m_Version; 12 | 13 | public NullObjectVersionProvider(int initial_version) 14 | { 15 | this.m_Version = initial_version; 16 | } 17 | 18 | public int GetVersionFor(object obj) 19 | { 20 | if (obj != null) 21 | throw new InvalidOperationException($"NullObjectVersionProvider may only be called with Null-values; Non-null parameter was {obj}"); 22 | 23 | return m_Version; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ObjectStorage/WeaklyStoredObject.cs: -------------------------------------------------------------------------------- 1 | using ObjectStorage.Abstractions; 2 | using System; 3 | using System.ComponentModel; 4 | 5 | namespace ObjectStorage 6 | { 7 | public class WeaklyStoredObject : StoredObject, IStoredObject 8 | where T : class, INotifyPropertyChanged 9 | { 10 | private WeakReference m_Object; 11 | 12 | 13 | public override T Object 14 | { 15 | get 16 | { 17 | T result = default(T); 18 | bool success = m_Object.TryGetTarget(out result); 19 | 20 | if (!success) 21 | return default(T); 22 | 23 | return result; 24 | } 25 | protected set 26 | { 27 | if (m_Object != value) 28 | { 29 | OnSetObject(Object, value); 30 | m_Object = new WeakReference(value); 31 | } 32 | } 33 | 34 | } 35 | 36 | public WeaklyStoredObject(string Name, T Object, Action OnUnregister) 37 | : base(Name, Object, OnUnregister) 38 | { 39 | } 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ObjectStorageAbstractions/IObjectStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ObjectStorage.Abstractions 4 | { 5 | public interface IObjectStore 6 | { 7 | /// 8 | /// Gets an object by name 9 | /// 10 | /// Name of the object 11 | /// Result of the operation 12 | /// True if an object with the given name was found in the obejct store. False otherwise. 13 | bool GetByName(string name, out IStoredObject result); 14 | 15 | /// 16 | /// Gets an object by name 17 | /// 18 | /// Name of the object 19 | /// Result of the operation 20 | /// True if an object with the given name was found in the obejct store. False otherwise. 21 | bool GetByName(string name, out IStoredObject result) where T : class; 22 | 23 | /// 24 | /// Checks if an object with the given name exists in the store. 25 | /// Use the result of this operation with care in multi-threaded scenarios, as the object may be removed before the next call. 26 | /// 27 | /// Name of the Object 28 | /// True if the object store contains an item with the given name. False otherwise. 29 | bool Exists(string name); 30 | 31 | /// 32 | /// Removes an object with the given name from the object store 33 | /// 34 | /// Name of the object which is to be removed 35 | /// True if the item was successfully removed. False otherwise (e.g. because no item with the name existed) 36 | bool Remove(string name); 37 | 38 | /// 39 | /// Removes an object with the given name from the object store 40 | /// 41 | /// Name of the object which is to be removed 42 | /// Object which was removed from the store 43 | /// True if the item was successfully removed. False otherwise (e.g. because no item with the name existed) 44 | bool Remove(string name, out IStoredObject RemovedItem); 45 | 46 | /// 47 | /// Adds an object with the given name to the object store, or updates the record if one with the same name already exists 48 | /// 49 | /// Name under which the given OBject shall be added 50 | /// Object which shall be added to the object store 51 | /// Callback to execute when the object is remoed from the store (explicitly or because its replaced by a new object) 52 | /// True if the object was added or updated to the store 53 | bool AddOrUpdate(string name, T Object, Action OnUnregistered = null) where T : class; 54 | 55 | /// 56 | /// Adds an object with the given name to the object store, or updates the record if one with the same name already exists 57 | /// 58 | /// Name under which the given OBject shall be added 59 | /// Object which shall be added to the object store 60 | /// Callback to execute when the object is remoed from the store (explicitly or because its replaced by a new object) 61 | /// Returns the IStoredObject that was stored 62 | /// True if the object was added or updated to the store 63 | bool AddOrUpdate(string name, T Object, out IStoredObject StoredObject, Action OnUnregistered = null) where T : class; 64 | 65 | /// 66 | /// Adds an object with the given name to the object store, or updates the record if one with the same name already exists 67 | /// 68 | /// Name under which the given OBject shall be added 69 | /// Object which shall be added to the object store 70 | /// Runtime type of the object under which it shall be stored (will be stored as an IStoredObject<$objectType>) 71 | /// Callback to execute when the object is remoed from the store (explicitly or because its replaced by a new object) 72 | /// Returns the IStoredObject that was stored. This will actually be an IStoredObject<$objectType>. 73 | /// True if the object was added or updated to the store 74 | bool AddOrUpdate(string name, object Object, Type objectType, out IStoredObject StoredObject, Action OnUnregistered = null); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /ObjectStorageAbstractions/IStoredObject.cs: -------------------------------------------------------------------------------- 1 | namespace ObjectStorage.Abstractions 2 | { 3 | /// 4 | /// Represents a stored object; allows to retrieve the object itself, as well as meta-information about it 5 | /// 6 | public interface IStoredObject 7 | { 8 | /// 9 | /// Name under which the object has been stored 10 | /// 11 | string Name { get; } 12 | 13 | /// 14 | /// The actual stored object 15 | /// 16 | object Object { get; } 17 | 18 | /// 19 | /// Version of this object 20 | /// 21 | int Version { get; } 22 | } 23 | 24 | /// 25 | /// Represents a stored object; allows to retrieve the object itself, as well as meta-information about it 26 | /// 27 | public interface IStoredObject : IStoredObject 28 | where T : class 29 | { 30 | /// 31 | /// The actual stored object 32 | /// 33 | new T Object { get; } 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /ObjectStorageAbstractions/ObjectStorageAbstractions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7013FBC4-3D9B-48DC-986E-31B39BCAD70B} 8 | Library 9 | Properties 10 | ObjectStorage.Abstractions 11 | ObjectStorageAbstractions 12 | v4.6.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 55 | -------------------------------------------------------------------------------- /ObjectStorageAbstractions/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("ObjectStorageAbstractions")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ObjectStorageAbstractions")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7013fbc4-3d9b-48dc-986e-31b39bcad70b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /ObjectStorageTests/Generators/StoredTypeGenerator.cs: -------------------------------------------------------------------------------- 1 | using ObjectStorageTests.Types; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ObjectStorageTests.Generators 9 | { 10 | public class StoredTypeGenerator 11 | { 12 | public enum ImplementationMethod 13 | { 14 | Hash, 15 | NotifyPropertyChanged, 16 | NoneUnsupported 17 | } 18 | 19 | public static IStoredType Generate(int seed = 1, ImplementationMethod implementationMethod = ImplementationMethod.Hash) 20 | { 21 | string value1 = String.Format("Stored Type #{0}", seed); 22 | int value2 = seed; 23 | 24 | switch (implementationMethod) 25 | { 26 | case ImplementationMethod.Hash: 27 | return new StoredTypeNotifyPropertyChanged(value1, value2); 28 | case ImplementationMethod.NotifyPropertyChanged: 29 | return new StoredTypeNotifyPropertyChanged(value1, value2); 30 | case ImplementationMethod.NoneUnsupported: 31 | return new StoredTypeUnsupported(value1, value2); 32 | default: 33 | throw new NotImplementedException(); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ObjectStorageTests/ObjectStorageTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {3D4B72A2-1959-4429-8FC6-233CC7A81F8C} 7 | Library 8 | Properties 9 | ObjectStorageTests 10 | ObjectStorageTests 11 | v4.6.1 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | {7013fbc4-3d9b-48dc-986e-31b39bcad70b} 67 | ObjectStorageAbstractions 68 | 69 | 70 | {8ab1ce36-e883-4a98-9713-9f6a849376ed} 71 | ObjectStorage 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | False 80 | 81 | 82 | False 83 | 84 | 85 | False 86 | 87 | 88 | False 89 | 90 | 91 | 92 | 93 | 94 | 95 | 102 | -------------------------------------------------------------------------------- /ObjectStorageTests/ObjectStoreTests_Hashed.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using ObjectStorageTests.Types; 8 | using ObjectStorageTests.Generators; 9 | 10 | namespace ObjectStorageTests 11 | { 12 | [TestClass] 13 | public class ObjectStoreTests_Hashed : ObjectStoreTestBase 14 | { 15 | private Func m_generator = (i) => StoredTypeGenerator.Generate(i, StoredTypeGenerator.ImplementationMethod.Hash); 16 | protected override Func ValidObjectFactory 17 | { 18 | get 19 | { 20 | return m_generator; 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ObjectStorageTests/ObjectStoreTests_Mixed.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using ObjectStorage; 3 | using ObjectStorage.Abstractions; 4 | using ObjectStorageTests.Generators; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace ObjectStorageTests 12 | { 13 | [TestClass] 14 | public class ObjectStoreTests_Mixed 15 | { 16 | [TestMethod] 17 | public void TestReplaceAndVersioning_Mixed() 18 | { 19 | var store = new ObjectStore(); 20 | 21 | string key = "MyObject1"; 22 | var obj1 = StoredTypeGenerator.Generate(1, StoredTypeGenerator.ImplementationMethod.Hash); 23 | var obj2 = StoredTypeGenerator.Generate(1, StoredTypeGenerator.ImplementationMethod.NotifyPropertyChanged); 24 | 25 | ObjectStoreTestBase.CheckedAddToStore(store, key, obj1); 26 | 27 | { 28 | IStoredObject result; 29 | bool success = store.GetByName(key, out result); 30 | int version = result.Version; 31 | int expectedVersion = 1; 32 | 33 | Assert.AreEqual(expectedVersion, version, $"Expected version to be {expectedVersion}, but was {version} instead"); 34 | } 35 | 36 | // Replace the object 37 | ObjectStoreTestBase.CheckedAddToStore(store, key, obj2); 38 | 39 | { 40 | IStoredObject result; 41 | bool success = store.GetByName(key, out result); 42 | int version = result.Version; 43 | int expectedVersion = 2; 44 | 45 | Assert.AreEqual(expectedVersion, version, $"Expected version to be {expectedVersion}, but was {version} instead"); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ObjectStorageTests/ObjectStoreTests_NotifyPropertyChanged.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using ObjectStorageTests.Generators; 3 | using ObjectStorageTests.Types; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace ObjectStorageTests 11 | { 12 | [TestClass] 13 | public class ObjectStoreTests_NotifyPropertyChanged : ObjectStoreTestBase 14 | { 15 | private Func m_generator = (i) => StoredTypeGenerator.Generate(i, StoredTypeGenerator.ImplementationMethod.NotifyPropertyChanged); 16 | protected override Func ValidObjectFactory 17 | { 18 | get 19 | { 20 | return m_generator; 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ObjectStorageTests/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("ObjectStorageTests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ObjectStorageTests")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("3d4b72a2-1959-4429-8fc6-233cc7a81f8c")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /ObjectStorageTests/Types/IStoredType.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 ObjectStorageTests.Types 8 | { 9 | public interface IStoredType 10 | { 11 | string Value1 { get; set; } 12 | int Value2 { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ObjectStorageTests/Types/StoredTypeHashed.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 ObjectStorageTests.Types 8 | { 9 | public class StoredTypeHashed : IStoredType 10 | { 11 | public string Value1 { get; set; } 12 | public int Value2 { get; set; } 13 | 14 | public StoredTypeHashed(string Value1, int Value2) 15 | { 16 | this.Value1 = Value1; 17 | this.Value2 = Value2; 18 | } 19 | 20 | public override int GetHashCode() 21 | { 22 | int hash = 13; 23 | hash = (hash * 7) + Value1.GetHashCode(); 24 | hash = (hash * 7) + Value2.GetHashCode(); 25 | return hash; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ObjectStorageTests/Types/StoredTypeNotifyPropertyChanged.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Runtime.CompilerServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ObjectStorageTests.Types 10 | { 11 | public class StoredTypeNotifyPropertyChanged : INotifyPropertyChanged, IStoredType 12 | { 13 | private string _Value1; 14 | private int _Value2; 15 | 16 | public string Value1 17 | { 18 | get { return _Value1; } 19 | set { SetField(ref _Value1, value); } 20 | } 21 | public int Value2 22 | { 23 | get { return _Value2; } 24 | set { SetField(ref _Value2, value); } 25 | } 26 | 27 | public StoredTypeNotifyPropertyChanged(string Value1, int Value2) 28 | { 29 | this.Value1 = Value1; 30 | this.Value2 = Value2; 31 | } 32 | 33 | #region INotifyPropertyChanged 34 | 35 | public event PropertyChangedEventHandler PropertyChanged; 36 | protected virtual void OnPropertyChanged(string propertyName) 37 | { 38 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 39 | } 40 | protected bool SetField(ref T field, T value, [CallerMemberName] string propertyName = null) 41 | { 42 | if (EqualityComparer.Default.Equals(field, value)) return false; 43 | field = value; 44 | OnPropertyChanged(propertyName); 45 | return true; 46 | } 47 | 48 | #endregion 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ObjectStorageTests/Types/StoredTypeUnsupported.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 ObjectStorageTests.Types 8 | { 9 | /// 10 | /// This type should not be supported, as it does not override GetHashCode() 11 | /// 12 | public class StoredTypeUnsupported : IStoredType 13 | { 14 | public string Value1 { get; set; } 15 | public int Value2 { get; set; } 16 | 17 | public StoredTypeUnsupported(string Value1, int Value2) 18 | { 19 | this.Value1 = Value1; 20 | this.Value2 = Value2; 21 | } 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ExcelScript 2 | Roslyn-based Excel AddIn that allows to write, compile, and run C# code right in Excel using simple user-defined functions 3 | 4 | Aside from doing simple immediate evaluations, some of the core features are: 5 | 6 | - You can create re-usable scripts on the fly in your worksheets, which will be stored in an in-memory cache with a used-defined string key. 7 | - You can then invoke that script using its key anywhere as often as you like 8 | - You can define and pass parameters to your scripts, so they can be invoked with arguments 9 | - Range-objects are fully supported as input parameters (ExcelScript takes care of converting & marshalling these internally) 10 | - You can create the parameter definitions & script manually, or just parse a function and have the AddIn extract and generate all necessary handles automatically 11 | - You can even register your script, meaning the AddIn will register a User Defined function with the name and all parameters you defined to invoke your script 12 | - A helper function lets you format & syntax highlight your C#-code in Excel 13 | - You can serialize/deserialize your scripts to strings or files 14 | 15 | Some bonus features: 16 | 17 | - You can debug your scripts. To do so, try the following: 18 | 1. Open the addin and the Example.xlsm 19 | 2. Attach the debugger of your choice to the Excel.exe process (e.g. open Visual Studio -> Debug -> Attach to Process -> Excel) 20 | 3. In any script, e.g. lets say on Sheet "Parse" in cell B18, enter "System.Diagnostics.Debugger.Break();" 21 | 4. When this script is run, your debugger should behave as expected; break execution, show the code, show local variables, etc. 22 | - The library supports execution in different AppDomains. By default, a script will run in a shared appdomain between all other scripts; you could also pass in an option to run in a shared appdomain with the main addin (ExcelDna's default domain), or to create a seperate appdomain for that script. 23 | - By default, all functions are marked non-volatile. Unfortunately, that means that after opening a workbook, you need to refresh each formula that creates a script by hand or macro. ExcelScript offers an experimental way around this, though: 24 | 1. Open The ExcelScript-AddIn.xll.config 25 | 2. set TagDirtyMethodCalls to true 26 | 3. This will mark each cell creating a handle (script, parameter and so on) to dirty once. Could be particularly useful in manual calculation mode (in auto calculation mode, all scripts would run on the next recalculation, so not very useful). 27 | 28 | Project includes an exemplary Excel-file to demonstrate usage. Please see the Wiki on Github for further guidelines. 29 | -------------------------------------------------------------------------------- /Resources/Examples.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabianoliver/ExcelScript/5f50df81b83fc7e7b138ca09c8c9a8627be773fe/Resources/Examples.xlsm -------------------------------------------------------------------------------- /RoslynScriptGlobals/Globals.cs: -------------------------------------------------------------------------------- 1 | using ScriptingAbstractions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Excel = NetOffice.ExcelApi; 6 | 7 | namespace RoslynScriptGlobals 8 | { 9 | public class Globals : IScriptGlobals, IDisposable 10 | { 11 | private readonly Lazy _Application; 12 | 13 | public IDictionary Parameters { get; private set; } 14 | 15 | public Excel.Application Application { get { return GetApplication(); } } 16 | public Excel.Worksheet ActiveSheet { get { return GetActiveSheet(); } } 17 | public Excel.Workbook ActiveWorkbook { get { return GetActiveWorkbook(); } } 18 | 19 | public Globals(Func ApplicationFactory) 20 | { 21 | this.Parameters = new Dictionary(); 22 | this._Application = new Lazy(() => ApplicationFactory()); 23 | } 24 | 25 | private Excel.Worksheet GetActiveSheet() 26 | { 27 | var app = _Application.Value; 28 | var sheet = app.ActiveSheet; 29 | var result = (Excel.Worksheet)sheet; 30 | return result; 31 | } 32 | 33 | private Excel.Application GetApplication() 34 | { 35 | var result = _Application.Value; 36 | return result; 37 | } 38 | 39 | private Excel.Workbook GetActiveWorkbook() 40 | { 41 | var app = _Application.Value; 42 | var result = app.ActiveWorkbook; 43 | 44 | return result; 45 | } 46 | 47 | void IDisposable.Dispose() 48 | { 49 | foreach(var parameter in this.Parameters.Values) 50 | { 51 | var disposable = parameter as IDisposable; 52 | 53 | if (disposable != null) 54 | disposable.Dispose(); 55 | } 56 | 57 | if(_Application.IsValueCreated) 58 | { 59 | _Application.Value?.Dispose(); 60 | } 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /RoslynScriptGlobals/GlobalsFactory.cs: -------------------------------------------------------------------------------- 1 | using ScriptingAbstractions; 2 | using System; 3 | using Excel = NetOffice.ExcelApi; 4 | 5 | namespace RoslynScriptGlobals 6 | { 7 | public class GlobalsFactory : MarshalByRefObject, IGlobalsFactory 8 | { 9 | public Func _ApplicationFactory; 10 | 11 | public GlobalsFactory(Func ApplicationFactory) 12 | { 13 | this._ApplicationFactory = ApplicationFactory; 14 | } 15 | 16 | public GlobalsFactory() 17 | { 18 | } 19 | 20 | public Globals Create(AppDomain ExecutingDomain) 21 | { 22 | return new Globals(_ApplicationFactory); 23 | } 24 | 25 | object IGlobalsFactory.Create(AppDomain ExecutingDomain) 26 | { 27 | return Create(ExecutingDomain); 28 | } 29 | 30 | 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /RoslynScriptGlobals/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("RoslynScriptGlobals")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RoslynScriptGlobals")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("eb8fee81-6de4-4be3-a14c-22988d1f23b1")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /RoslynScriptGlobals/RoslynScriptGlobals-AddIn.dna: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /RoslynScriptGlobals/RoslynScriptGlobals.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {EB8FEE81-6DE4-4BE3-A14C-22988D1F23B1} 8 | Library 9 | Properties 10 | RoslynScriptGlobals 11 | RoslynScriptGlobals 12 | v4.6.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | ..\packages\NetOffice.Excel.1.7.3.0\lib\net45\ExcelApi.dll 35 | False 36 | 37 | 38 | ..\packages\ExcelDna.Integration.0.33.9\lib\ExcelDna.Integration.dll 39 | False 40 | 41 | 42 | ..\packages\NetOffice.Core.1.7.3.0\lib\net45\NetOffice.dll 43 | False 44 | 45 | 46 | ..\packages\NetOffice.Core.1.7.3.0\lib\net45\OfficeApi.dll 47 | False 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | ..\packages\NetOffice.Core.1.7.3.0\lib\net45\VBIDEApi.dll 59 | False 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | {9c15d62f-889e-4c74-93d3-aac38d27031f} 70 | ScriptingAbstractions 71 | 72 | 73 | 74 | 75 | PreserveNewest 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 91 | -------------------------------------------------------------------------------- /RoslynScriptGlobals/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RoslynScripting/Factory/ParameterFactory.cs: -------------------------------------------------------------------------------- 1 | using ScriptingAbstractions; 2 | using ScriptingAbstractions.Factory; 3 | 4 | namespace RoslynScripting.Factory 5 | { 6 | public class ParameterFactory : IParameterFactory 7 | { 8 | public IParameter Create() 9 | { 10 | var parameter = new Parameter(); 11 | return parameter; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /RoslynScripting/Factory/ScriptFactory.cs: -------------------------------------------------------------------------------- 1 | using ScriptingAbstractions; 2 | using ScriptingAbstractions.Factory; 3 | using System; 4 | using System.Reflection; 5 | using System.Threading.Tasks; 6 | 7 | namespace RoslynScripting.Factory 8 | { 9 | 10 | public class ScriptFactory : IScriptFactory 11 | { 12 | public IScript Create(Func GlobalsFactory) 13 | where TGlobals : class, IScriptGlobals 14 | { 15 | return Create(GlobalsFactory, null); 16 | } 17 | 18 | public IScript Create(Func GlobalsFactory, ScriptingOptions Options) 19 | where TGlobals : class, IScriptGlobals 20 | { 21 | return new Script(GlobalsFactory, Options); 22 | } 23 | 24 | public void Inject(IScript Instance, Func GlobalsFactory) 25 | where TGlobals : class, IScriptGlobals 26 | { 27 | if (Instance == null) 28 | throw new ArgumentNullException(nameof(Instance)); 29 | 30 | if (GlobalsFactory == null) 31 | throw new ArgumentNullException(nameof(GlobalsFactory)); 32 | 33 | var instance = Instance as Script; 34 | 35 | if(instance != null) 36 | { 37 | instance.GlobalsFactory = GlobalsFactory; 38 | } 39 | } 40 | 41 | public async Task> ParseFromAsync(Func GlobalsFactory, ScriptingOptions ScriptingOptions, string code, Func EntryMethodSelector, Func EntryMethodParameterFactory) 42 | where TGlobals : class, IScriptGlobals 43 | { 44 | if (GlobalsFactory == null) 45 | throw new ArgumentNullException(nameof(GlobalsFactory)); 46 | 47 | if (code == null) 48 | throw new ArgumentNullException(nameof(code)); 49 | 50 | if (EntryMethodSelector == null) 51 | throw new ArgumentNullException(nameof(EntryMethodSelector)); 52 | 53 | if (EntryMethodParameterFactory == null) 54 | throw new ArgumentNullException(nameof(EntryMethodParameterFactory)); 55 | 56 | if ((!EntryMethodSelector.Target?.GetType()?.IsSerializable) ?? false) 57 | throw new ArgumentException($"The {nameof(EntryMethodSelector)}'s target type must be serializable in order to use it accross AppDomains. This error often occurs if you reference any out-of-scope variables in your delegate, if its declaring type is not serializable.", nameof(EntryMethodSelector)); 58 | 59 | if ((!EntryMethodParameterFactory.Target?.GetType()?.IsSerializable) ?? false) 60 | throw new ArgumentException($"The {nameof(EntryMethodParameterFactory)}'s target type must be serializable in order to use it accross AppDomains. This error often occurs if you reference any out-of-scope variables in your delegate, if its declaring type is not serializable.", nameof(EntryMethodParameterFactory)); 61 | 62 | return await Script.ParseFromAsync(GlobalsFactory, ScriptingOptions, code, EntryMethodSelector, EntryMethodParameterFactory); 63 | } 64 | 65 | 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /RoslynScripting/FormatColorScheme.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Linq; 4 | 5 | namespace RoslynScripting 6 | { 7 | [Serializable] 8 | public struct FormatColorScheme 9 | { 10 | public readonly Color Keyword; 11 | public readonly Color ClassName; 12 | public readonly Color Text; 13 | public readonly Color StringVerbatim; 14 | public readonly Color EnumName; 15 | public readonly Color Comment; 16 | public readonly Color Number; 17 | public readonly Color Punctuation; 18 | public readonly Color StructName; 19 | public readonly Color Operator; 20 | public readonly Color Identifier; 21 | public readonly Color PreprocessorKeyword; 22 | public readonly Color Unknown; 23 | 24 | public FormatColorScheme(Color Keyword, Color ClassName, Color Text, Color StringVerbatim, Color EnumName, Color Comment, Color Number, Color Punctuation, Color StructName, Color Operator, Color Identifier, Color PreprocessorKeyword, Color Unknown) 25 | { 26 | this.Keyword = Keyword; 27 | this.ClassName = ClassName; 28 | this.Text = Text; 29 | this.StringVerbatim = StringVerbatim; 30 | this.EnumName = EnumName; 31 | this.Comment = Comment; 32 | this.Number = Number; 33 | this.Punctuation = Punctuation; 34 | this.StructName = StructName; 35 | this.Operator = Operator; 36 | this.Identifier = Identifier; 37 | this.PreprocessorKeyword = PreprocessorKeyword; 38 | this.Unknown = Unknown; 39 | 40 | } 41 | 42 | internal int GetColorIndexForKeyword(string keyword) 43 | { 44 | switch (keyword) 45 | { 46 | case "keyword": 47 | return 1; 48 | case "class name": 49 | return 2; 50 | case "string": 51 | return 3; 52 | case "string - verbatim": 53 | return 4; 54 | case "enum name": 55 | return 5; 56 | case "comment": 57 | return 6; 58 | case "number": 59 | return 7; 60 | case "punctuation": 61 | return 8; 62 | case "struct name": 63 | return 9; 64 | case "operator": 65 | return 10; 66 | case "identifier": 67 | return 11; 68 | case "preprocessor keyword": 69 | return 12; 70 | default: 71 | return 13; 72 | } 73 | } 74 | 75 | internal Color GetColorForKeyword(string keyword) 76 | { 77 | var index = GetColorIndexForKeyword(keyword) - 1; 78 | return GetColorsInRtfOrder()[index]; 79 | } 80 | 81 | internal Color[] GetColorsInRtfOrder() 82 | { 83 | return new Color[] { Keyword, ClassName, Text, StringVerbatim, EnumName, Comment, Number, Punctuation, StructName, Operator, Identifier, PreprocessorKeyword, Unknown }; 84 | } 85 | 86 | public readonly static FormatColorScheme LightTheme = new FormatColorScheme(Color.Blue, Color.LightSeaGreen, Color.DarkRed, Color.DarkRed, Color.LightSeaGreen, Color.Green, Color.Black, Color.Black, Color.LightSeaGreen, Color.Black, Color.Black, Color.Black, Color.Black); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /RoslynScripting/FormattedText.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Drawing; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace RoslynScripting 10 | { 11 | [Serializable] 12 | public struct TextSpan 13 | { 14 | public readonly int Start; 15 | public readonly int End; 16 | public readonly int Length; 17 | 18 | public TextSpan(int start, int length) 19 | : this(start, length, start+length) 20 | { 21 | } 22 | 23 | public static TextSpan FromBounds(int start, int end) 24 | { 25 | var result = new TextSpan(start, end-start, end); 26 | return result; 27 | } 28 | 29 | private TextSpan(int start, int length, int end) 30 | { 31 | this.Start = start; 32 | this.Length = length; 33 | this.End = end; 34 | } 35 | } 36 | 37 | //[Serializable] 38 | public class TextFormat : MarshalByRefObject 39 | { 40 | public readonly Color TextColor; 41 | 42 | public TextFormat(Color TextColor) 43 | { 44 | this.TextColor = TextColor; 45 | } 46 | } 47 | 48 | // [Serializable] 49 | public class FormattedTextLinePart : MarshalByRefObject 50 | { 51 | /// 52 | /// Line number of the text 53 | /// 54 | public FormattedTextLine Line { get; internal set; } 55 | 56 | /// 57 | /// Position of the text relative to its line 58 | /// 59 | public readonly TextSpan LineSpan; 60 | 61 | /// 62 | /// The actual text 63 | /// 64 | public readonly string Text; 65 | 66 | /// 67 | /// Format of the text 68 | /// 69 | public readonly TextFormat TextFormat; 70 | 71 | public FormattedTextLinePart(string Text, TextSpan LineSpan, TextFormat TextFormat) 72 | { 73 | this.Text = Text; 74 | this.LineSpan = LineSpan; 75 | this.TextFormat = TextFormat; 76 | } 77 | 78 | public override string ToString() 79 | { 80 | return Text; 81 | } 82 | } 83 | 84 | // [Serializable] 85 | public class FormattedTextLine : MarshalByRefObject 86 | { 87 | /// 88 | /// The text this line is part of 89 | /// 90 | public FormattedText FormattedText { get; internal set; } 91 | 92 | public string Text 93 | { 94 | get 95 | { 96 | return String.Join(String.Empty, Parts.Select(x => x.Text)); 97 | } 98 | } 99 | 100 | /// 101 | /// Number of the line 102 | /// 103 | public readonly int LineNumber; 104 | 105 | /// 106 | /// Formatted parts that make up this line 107 | /// 108 | public readonly IReadOnlyCollection Parts; 109 | private readonly IList _Parts = new List(); 110 | 111 | public FormattedTextLine() 112 | { 113 | this.Parts = new ReadOnlyCollection(_Parts); 114 | } 115 | 116 | public void AddPart(FormattedTextLinePart Part) 117 | { 118 | Part.Line = this; 119 | _Parts.Add(Part); 120 | } 121 | 122 | public FormattedTextLinePart AppendText(string Text, TextFormat Format) 123 | { 124 | var lastPart = Parts.LastOrDefault(); 125 | var lastPartLineSpan = lastPart?.LineSpan; 126 | int SpanStart = _Parts.Any() ? ((TextSpan)lastPartLineSpan).End : 0; 127 | int SpanEnd = SpanStart + Text.Length; 128 | 129 | var LineSpan = TextSpan.FromBounds(SpanStart, SpanEnd); 130 | var LinePart = new FormattedTextLinePart(Text, LineSpan, Format); 131 | 132 | AddPart(LinePart); 133 | return LinePart; 134 | } 135 | 136 | public override string ToString() 137 | { 138 | return Text; 139 | } 140 | } 141 | 142 | // [Serializable] 143 | public class FormattedText : MarshalByRefObject 144 | { 145 | /// 146 | /// The lines that make up this text 147 | /// 148 | public readonly IReadOnlyCollection TextLines; 149 | private readonly IList _TextLines = new List(); 150 | 151 | public FormattedText() 152 | { 153 | this.TextLines = new ReadOnlyCollection(_TextLines); 154 | } 155 | 156 | public void AddLine(FormattedTextLine Line) 157 | { 158 | Line.FormattedText = this; 159 | this._TextLines.Add(Line); 160 | } 161 | 162 | public FormattedTextLine AppendLine() 163 | { 164 | var line = new FormattedTextLine(); 165 | AddLine(line); 166 | return line; 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /RoslynScripting/Internal/DelegateDisposable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RoslynScripting.Internal 4 | { 5 | internal class DelegateDisposable : IDisposable 6 | { 7 | private readonly Action OnDisposing; 8 | private bool m_disposed = false; 9 | 10 | public DelegateDisposable(Action OnDisposing) 11 | { 12 | if (OnDisposing == null) 13 | throw new ArgumentNullException(nameof(OnDisposing)); 14 | 15 | this.OnDisposing = OnDisposing; 16 | } 17 | 18 | public void Dispose() 19 | { 20 | if (!m_disposed) 21 | { 22 | OnDisposing(); 23 | m_disposed = true; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /RoslynScripting/Internal/HashAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RoslynScripting.Internal 4 | { 5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 6 | internal abstract class HashAttribute : Attribute 7 | { 8 | public abstract void AddTo(ref int hash, object value); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /RoslynScripting/Internal/HashEnumerable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | 3 | namespace RoslynScripting.Internal 4 | { 5 | internal class HashEnumerable : HashAttribute 6 | { 7 | public override void AddTo(ref int hash, object value) 8 | { 9 | IEnumerable enumerable = value as IEnumerable; 10 | 11 | unchecked 12 | { 13 | if (enumerable == null) 14 | { 15 | hash = (hash * 23) + HashHelper.HashOf(null); 16 | } 17 | else 18 | { 19 | foreach (var item in enumerable) 20 | { 21 | hash = (hash * 23) + HashHelper.HashOf(item); 22 | } 23 | } 24 | } 25 | 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /RoslynScripting/Internal/HashHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Reflection; 3 | 4 | namespace RoslynScripting.Internal 5 | { 6 | internal static class HashHelper 7 | { 8 | public static int HashOf(object obj) 9 | { 10 | if (obj == null) 11 | return 0; 12 | else 13 | return obj.GetHashCode(); 14 | } 15 | 16 | // todo: maybe add caching here, build a dynamic method to do this so don't have to re-query all attributes/properties again every time 17 | public static int HashOfAnnotated(T obj) 18 | { 19 | var properties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) 20 | .Where(prop => prop.IsDefined(typeof(HashAttribute), false)); 21 | 22 | unchecked 23 | { 24 | int hash = 27; 25 | 26 | foreach(var property in properties) 27 | { 28 | var attribute = (HashAttribute)property.GetCustomAttributes(typeof(HashAttribute), false).Single(); 29 | object value = property.GetValue(obj); 30 | attribute.AddTo(ref hash, value); 31 | } 32 | 33 | return hash; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /RoslynScripting/Internal/HashValueAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace RoslynScripting.Internal 2 | { 3 | internal class HashValueAttribute : HashAttribute 4 | { 5 | public override void AddTo(ref int hash, object value) 6 | { 7 | hash = (hash*23) + HashHelper.HashOf(value); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /RoslynScripting/Internal/IScriptRunner.cs: -------------------------------------------------------------------------------- 1 | using RoslynScripting.Internal.Marshalling; 2 | using ScriptingAbstractions; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Reflection; 6 | using System.Threading.Tasks; 7 | 8 | namespace RoslynScripting.Internal 9 | { 10 | internal interface IParseResult 11 | { 12 | string RefactoredCode { get; } 13 | string EntryMethodName { get; } 14 | IEnumerable Parameters { get; } 15 | } 16 | 17 | internal interface IScriptRunner 18 | { 19 | /// 20 | /// Does the script, when run, need to be recompiled? 21 | /// 22 | bool NeedsRecompilationFor(IParameter[] parameters, string scriptCode, ScriptingOptions Options); 23 | 24 | /// 25 | /// Asynchronously compiles and runs the script, returns its result 26 | /// 27 | RemoteTask RunAsync(Func ScriptGlobalsFactory, IParameterValue[] parameters, string scriptCode, ScriptingOptions Options); 28 | 29 | RemoteTask ParseAsync(string scriptCode, ScriptingOptions Options, Func EntryMethodSelector, Func EntryMethodParameterFactory); 30 | 31 | RemoteTask GetRtfFormattedCodeAsync(string scriptCode, ScriptingOptions Options, FormatColorScheme ColorScheme); 32 | RemoteTask GetFormattedCodeAsync(string scriptCode, ScriptingOptions Options, FormatColorScheme ColorScheme); 33 | 34 | /* 35 | /// 36 | /// Asynchronously compiles and runs the script, returns its result 37 | /// 38 | /// Instance of the compiler-generated submission object (e.g. Submission#0) that the script is/was run on 39 | RemoteTask RunAsync(Func ScriptGlobalsFactory, IParameterValue[] parameters, string scriptCode, ScriptingOptions Options, out object submission);*/ 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /RoslynScripting/Internal/Marshalling/RemoteTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace RoslynScripting.Internal.Marshalling 6 | { 7 | /// 8 | /// This code is from http://stackoverflow.com/questions/15142507/deadlock-when-combining-app-domain-remoting-and-tasks 9 | /// 10 | internal static class RemoteTask 11 | { 12 | public static async Task ClientComplete(RemoteTask remoteTask, CancellationToken cancellationToken) 13 | { 14 | T result; 15 | 16 | using (cancellationToken.Register(remoteTask.Cancel)) 17 | { 18 | RemoteTaskCompletionSource tcs = new RemoteTaskCompletionSource(); 19 | remoteTask.Complete(tcs); 20 | result = await tcs.Task; 21 | } 22 | 23 | await Task.Yield(); 24 | 25 | return result; 26 | } 27 | 28 | public static RemoteTask ServerStart(Func> func) 29 | { 30 | return new RemoteTask(func); 31 | } 32 | } 33 | 34 | internal class RemoteTask : MarshalByRefObject 35 | { 36 | readonly CancellationTokenSource cts = new CancellationTokenSource(); 37 | readonly Task task; 38 | 39 | internal RemoteTask(Func> starter) 40 | { 41 | this.task = starter(cts.Token); 42 | } 43 | 44 | internal void Complete(RemoteTaskCompletionSource tcs) 45 | { 46 | task.ContinueWith(t => 47 | { 48 | if (t.IsFaulted) 49 | { 50 | tcs.TrySetException(t.Exception); 51 | } 52 | else if (t.IsCanceled) 53 | { 54 | tcs.TrySetCancelled(); 55 | } 56 | else 57 | { 58 | tcs.TrySetResult(t.Result); 59 | } 60 | }, TaskContinuationOptions.ExecuteSynchronously); 61 | } 62 | 63 | internal void Cancel() 64 | { 65 | cts.Cancel(); 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /RoslynScripting/Internal/Marshalling/RemoteTaskCompletionSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace RoslynScripting.Internal.Marshalling 5 | { 6 | /// 7 | /// This code is from http://stackoverflow.com/questions/15142507/deadlock-when-combining-app-domain-remoting-and-tasks 8 | /// 9 | internal class RemoteTaskCompletionSource : MarshalByRefObject 10 | { 11 | readonly TaskCompletionSource tcs = new TaskCompletionSource(); 12 | 13 | public bool TrySetResult(T result) { return tcs.TrySetResult(result); } 14 | public bool TrySetCancelled() { return tcs.TrySetCanceled(); } 15 | public bool TrySetException(Exception ex) { return tcs.TrySetException(ex); } 16 | 17 | public Task Task 18 | { 19 | get 20 | { 21 | return tcs.Task; 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /RoslynScripting/Parameter.cs: -------------------------------------------------------------------------------- 1 | using ExcelScript.CommonUtilities; 2 | using RoslynScripting.Internal; 3 | using ScriptingAbstractions; 4 | using System; 5 | using System.Xml; 6 | using System.Xml.Schema; 7 | using System.Xml.Serialization; 8 | 9 | namespace RoslynScripting 10 | { 11 | // [Serializable] 12 | public class Parameter : MarshalByRefObject, IXmlSerializable, IParameter 13 | { 14 | [HashValue] 15 | public string Name { get; set; } 16 | 17 | [HashValue] 18 | public Type Type { get; set; } 19 | 20 | [HashValue] 21 | public bool IsOptional { get; set; } 22 | 23 | [HashValue] 24 | public object DefaultValue { get; set; } 25 | 26 | [HashValue] 27 | public string Description { get; set; } 28 | 29 | /// 30 | /// Checks consistency of a potential parameter value with its parmater definition. Throws an InvalidOperationException if incompatible. 31 | /// 32 | /// Parameter definition that defines constraints 33 | /// Value candidate for the parameter 34 | /// Thrown if the parameter value is incompatible with the parameter definition 35 | public static void EnsureValueIsCompatible(IParameter parameter, object Value) 36 | { 37 | if (Value == null) 38 | { 39 | if (parameter.Type.IsValueType) 40 | throw new InvalidOperationException($"Value was null, but {parameter.Type.Name} is a value type"); 41 | } 42 | else 43 | { 44 | Type ValueType = Value.GetType(); 45 | 46 | if (!parameter.Type.IsAssignableFrom(ValueType)) 47 | { 48 | throw new InvalidOperationException($"Expected type {parameter.Type.Name}, but the assigned value was of type {ValueType.Name}"); 49 | } 50 | } 51 | } 52 | 53 | public override int GetHashCode() 54 | { 55 | return HashHelper.HashOfAnnotated(this); 56 | } 57 | 58 | public override string ToString() 59 | { 60 | return $"Paramter ({Type.Name} {Name})"; 61 | } 62 | 63 | #region IXmlSerializable 64 | 65 | public XmlSchema GetSchema() { 66 | return null; 67 | } 68 | 69 | public void WriteXml(XmlWriter writer) 70 | { 71 | writer.Write(nameof(Name), Name); 72 | writer.Write(nameof(Type), Type); 73 | writer.Write(nameof(IsOptional), IsOptional); 74 | writer.Write(nameof(DefaultValue), DefaultValue); 75 | writer.Write(nameof(Description), Description); 76 | } 77 | 78 | public void ReadXml(XmlReader reader) 79 | { 80 | reader.MoveToCustomStart(); 81 | 82 | string _Name; 83 | Type _Type; 84 | bool _IsOptional; 85 | object _DefaultValue; 86 | string _Description; 87 | 88 | reader.Read(nameof(Name), out _Name); 89 | reader.Read(nameof(Type), out _Type); 90 | reader.Read(nameof(IsOptional), out _IsOptional); 91 | reader.Read(nameof(DefaultValue), out _DefaultValue); 92 | reader.Read(nameof(Description), out _Description); 93 | 94 | this.Name = _Name; 95 | this.Type = _Type; 96 | this.IsOptional = _IsOptional; 97 | this.DefaultValue = _DefaultValue; 98 | this.Description = _Description; 99 | 100 | reader.ReadEndElement(); 101 | } 102 | 103 | #endregion 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /RoslynScripting/ParameterValue.cs: -------------------------------------------------------------------------------- 1 | using ScriptingAbstractions; 2 | using System; 3 | 4 | namespace RoslynScripting 5 | { 6 | public class ParameterValue : MarshalByRefObject, IParameterValue 7 | { 8 | public IParameter Parameter { get; private set; } 9 | public Type TransferableValueType { get; private set; } 10 | 11 | public readonly object Value; 12 | private readonly TransferableValueToOriginalValueConverter Converter; 13 | private Lazy TransferableValueGetter; 14 | private bool AllowNullTransferableValues; 15 | 16 | public ParameterValue(IParameter Parameter, object Value) 17 | : this(Parameter, Value, Parameter.Type, x => x, x => x, true) 18 | { 19 | } 20 | 21 | public ParameterValue(IParameter Parameter, object Value, Type TransferableType, OriginalValueToTransferableValueConverter originalValueToTransferableValueConverter, TransferableValueToOriginalValueConverter transferableValueToOriginalValueConverter, bool AllowNullTransferableValues = false) 22 | { 23 | if (Parameter == null) 24 | throw new ArgumentNullException(nameof(Parameter)); 25 | 26 | if (TransferableType == null) 27 | throw new ArgumentNullException(nameof(TransferableType)); 28 | 29 | if (originalValueToTransferableValueConverter == null) 30 | throw new ArgumentNullException(nameof(originalValueToTransferableValueConverter)); 31 | 32 | if (transferableValueToOriginalValueConverter == null) 33 | throw new ArgumentNullException(nameof(transferableValueToOriginalValueConverter)); 34 | 35 | if ((!originalValueToTransferableValueConverter.Target?.GetType()?.IsSerializable) ?? false) 36 | throw new ArgumentException("The converter's target type must be serializable in order to use it accross AppDomains. This error often occurs if you reference any out-of-scope variables in your delegate, if its declaring type is not serializable.", nameof(originalValueToTransferableValueConverter)); 37 | 38 | if ((!transferableValueToOriginalValueConverter.Target?.GetType()?.IsSerializable) ?? false) 39 | throw new ArgumentException("The converter's target type must be serializable in order to use it accross AppDomains. This error often occurs if you reference any out-of-scope variables in your delegate, if its declaring type is not serializable.", nameof(transferableValueToOriginalValueConverter)); 40 | 41 | this.Value = Value; 42 | this.TransferableValueType = TransferableType; 43 | this.AllowNullTransferableValues = AllowNullTransferableValues; 44 | this.Parameter = Parameter; 45 | this.Converter = transferableValueToOriginalValueConverter; 46 | this.TransferableValueGetter = new Lazy(() => originalValueToTransferableValueConverter(this.Value)); 47 | } 48 | /// 49 | /// Gets the original value of the parameter 50 | /// 51 | public object GetOriginalValue() 52 | { 53 | RoslynScripting.Parameter.EnsureValueIsCompatible(this.Parameter, this.Value); 54 | return Value; 55 | } 56 | 57 | /// 58 | /// Gets the value of the object in a format that can be marshalled in between AppDomains. 59 | /// The returned object will be of type , which in turn will be a type 60 | /// that is either marshallable by ference (MarshalByRefObject), by value (serializable), or by bleed (string, int etc.) 61 | /// 62 | public object GetTransferableValue() 63 | { 64 | var value = TransferableValueGetter.Value; 65 | 66 | if(value != null) 67 | { 68 | if (!TransferableValueType.IsAssignableFrom(value.GetType())) 69 | throw new InvalidCastException($"The transferable value was found to be of type {value.GetType().Name}, which is incompatible with expected type {TransferableValueType.Name}"); 70 | } else if(!AllowNullTransferableValues) 71 | { 72 | throw new InvalidOperationException("The transferable value was null, which is invalid"); 73 | } 74 | 75 | return value; 76 | } 77 | 78 | /// 79 | /// Gets a delegate that must by contract execute in the AppDomain of the caller of this function. 80 | /// Invoking the returned delegate will convert a value of type to one of type 81 | /// 82 | /// 83 | public TransferableValueToOriginalValueConverter GetTransferableValueToOriginalValueConverter() 84 | { 85 | return this.Converter; 86 | } 87 | 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /RoslynScripting/ParseResult.cs: -------------------------------------------------------------------------------- 1 | using ScriptingAbstractions; 2 | using ScriptingAbstractions.Factory; 3 | 4 | namespace RoslynScripting 5 | { 6 | public sealed class ParseResult : IParseResult 7 | where TGlobals : class, IScriptGlobals 8 | { 9 | public IScript Script { get; private set; } 10 | public string EntryMethodName { get; private set; } 11 | 12 | public ParseResult(IScript Script, string EntryMethodName) 13 | { 14 | this.Script = Script; 15 | this.EntryMethodName = EntryMethodName; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /RoslynScripting/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("RoslynScripting")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RoslynScripting")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("81c9ca40-28c5-490b-a32a-deffca247613")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /RoslynScripting/ScriptRunResult.cs: -------------------------------------------------------------------------------- 1 | using ScriptingAbstractions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace RoslynScripting 7 | { 8 | public class ScriptRunResult : IScriptRunResult 9 | { 10 | public bool IsSuccess { get; private set; } 11 | public object ReturnValue { get; private set; } 12 | public IEnumerable Errors { get; private set; } 13 | 14 | private ScriptRunResult(IEnumerable exceptions) 15 | { 16 | this.IsSuccess = false; 17 | this.ReturnValue = null; 18 | this.Errors = exceptions; 19 | } 20 | 21 | private ScriptRunResult(Exception exception) 22 | : this(new Exception[] { exception }) 23 | { 24 | } 25 | 26 | private ScriptRunResult(object ReturnValue) 27 | { 28 | this.IsSuccess = true; 29 | this.ReturnValue = ReturnValue; 30 | this.Errors = Enumerable.Empty(); 31 | } 32 | 33 | private ScriptRunResult() 34 | { 35 | 36 | } 37 | 38 | public static ScriptRunResult Success(object ReturnValue = null) 39 | { 40 | return new ScriptRunResult() 41 | { 42 | IsSuccess = true, 43 | ReturnValue = ReturnValue, 44 | Errors = Enumerable.Empty() 45 | }; 46 | } 47 | 48 | public static ScriptRunResult Failure(IEnumerable Exceptions) 49 | { 50 | return new ScriptRunResult(Exceptions); 51 | } 52 | 53 | public static ScriptRunResult Failure(Exception exception) 54 | { 55 | return new ScriptRunResult(exception); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /RoslynScripting/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /RoslynScripting/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 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 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /RoslynScriptingTests/Generators/ParameterGenerator.cs: -------------------------------------------------------------------------------- 1 | using RoslynScripting; 2 | using RoslynScriptingTests.Types; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace RoslynScriptingTests.Generators 10 | { 11 | public static class ParameterGenerator 12 | { 13 | public static Parameter Generate(object DefaultValue, int seed = 1) 14 | { 15 | var result = new Parameter 16 | { 17 | Name = $"Parameter{seed}", 18 | IsOptional = seed % 2 == 0, 19 | DefaultValue = DefaultValue, 20 | Description = $"Parameter Description {seed} with come Generate(int seed = 1) 14 | { 15 | var globalsFactory = new TestGlobalsFactory(); 16 | var script = new ScriptFactory().Create((x) => new TestGlobalsFactory().Create(x)); 17 | 18 | script.ReturnType = typeof(int); 19 | script.Code = $"1+{seed}"; 20 | script.Description = $"Description of script {seed}"; 21 | 22 | for(int i = 1; i <= 2 + (seed%2); i++) 23 | { 24 | var parameter = ParameterGenerator.Generate(i); 25 | script.Parameters.Add(parameter); 26 | } 27 | 28 | return (Script)script; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /RoslynScriptingTests/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("RoslynScriptingTests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RoslynScriptingTests")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("0be3325a-547c-4ac7-9ae5-eeb7bcbb4f22")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /RoslynScriptingTests/SerializationTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using RoslynScripting; 3 | using RoslynScriptingTests.Generators; 4 | using System.IO; 5 | using System.Xml.Serialization; 6 | 7 | namespace RoslynScriptingTests 8 | { 9 | [TestClass] 10 | public class SerializationTests 11 | { 12 | [TestMethod] 13 | public void SerializeDeserialize_Parameter() 14 | { 15 | var parameter = ParameterGenerator.Generate(1); 16 | 17 | string xml = XmlSerialize(parameter); 18 | var deserialized = XmlDeserialize(xml); 19 | 20 | Assert.IsNotNull(deserialized, "Deserialized object was null"); 21 | 22 | int hash1 = parameter.GetHashCode(); 23 | int hash2 = deserialized.GetHashCode(); 24 | 25 | Assert.AreEqual(hash1, hash2, "Hash of original object differs from deserialized object"); 26 | } 27 | 28 | [TestMethod] 29 | public void SerializeDeserialize_Script() 30 | { 31 | var script = ScriptGenerator.Generate(1); 32 | 33 | string xml = XmlSerialize>(script); 34 | var deserialized = XmlDeserialize>(xml); 35 | 36 | Assert.IsNotNull(deserialized, "Deserialized object was null"); 37 | 38 | int hash1 = script.GetHashCode(); 39 | int hash2 = deserialized.GetHashCode(); 40 | 41 | Assert.AreEqual(hash1, hash2, "Hash of original object differs from deserialized object"); 42 | } 43 | 44 | private static string XmlSerialize(T obj) 45 | { 46 | XmlSerializer serializer = new XmlSerializer(typeof(T)); 47 | using (var writer = new StringWriter()) 48 | { 49 | serializer.Serialize(writer, obj); 50 | return writer.ToString(); 51 | } 52 | } 53 | 54 | private static T XmlDeserialize(string xml) 55 | { 56 | XmlSerializer deserializer = new XmlSerializer(typeof(T)); 57 | TextReader reader = new StringReader(xml); 58 | object obj = deserializer.Deserialize(reader); 59 | T result = (T)obj; 60 | reader.Close(); 61 | 62 | return result; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /RoslynScriptingTests/TestGlobals.cs: -------------------------------------------------------------------------------- 1 | using ScriptingAbstractions; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace RoslynScriptingTests 6 | { 7 | public class TestGlobals : IScriptGlobals 8 | { 9 | public IDictionary Parameters { get; private set; } 10 | } 11 | 12 | public class TestGlobalsFactory : IGlobalsFactory 13 | { 14 | object IGlobalsFactory.Create(AppDomain ExecutingDomain) 15 | { 16 | return Create(ExecutingDomain); 17 | } 18 | 19 | public TestGlobals Create(AppDomain ExecutingDomain) 20 | { 21 | return new TestGlobals(); 22 | } 23 | } 24 | } 25 | 26 | 27 | -------------------------------------------------------------------------------- /RoslynScriptingTests/Types/SimpleSerializableType.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 RoslynScriptingTests.Types 8 | { 9 | [Serializable] 10 | public class SimpleSerializableType 11 | { 12 | public string Value1 { get; set; } 13 | public int Value2 { get; set; } 14 | 15 | public override int GetHashCode() 16 | { 17 | int hash; 18 | 19 | unchecked 20 | { 21 | hash = 27; 22 | hash = (hash * 23) + ((Value1 == null) ? 0 : Value1.GetHashCode()); 23 | hash = (hash * 23) + Value2.GetHashCode(); 24 | } 25 | 26 | return hash; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /RoslynScriptingTests/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /RoslynScriptingTests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 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 | 52 | -------------------------------------------------------------------------------- /ScriptingAbstractions/Factory/IParameterFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.Remoting.Lifetime; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ScriptingAbstractions.Factory 9 | { 10 | public interface IParameterFactory 11 | { 12 | IParameter Create(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ScriptingAbstractions/Factory/IParameterValueFactory.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 ScriptingAbstractions.Factory 8 | { 9 | public interface IParameterValueFactory 10 | { 11 | IParameterValue CreateFor(IParameter Parameter, object WithValue); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ScriptingAbstractions/Factory/IScriptFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ScriptingAbstractions.Factory 9 | { 10 | public interface IParseResult 11 | where TGlobals : class, IScriptGlobals 12 | { 13 | IScript Script { get; } 14 | string EntryMethodName { get; } 15 | } 16 | 17 | public interface IScriptFactory 18 | { 19 | IScript Create(Func GlobalsFactory) 20 | where TGlobals : class, IScriptGlobals; 21 | 22 | IScript Create(Func GlobalsFactory, ScriptingOptions Options) 23 | where TGlobals : class, IScriptGlobals; 24 | 25 | /// 26 | /// Injects dependencies into an existing instance. Can be used after deserialization. 27 | /// 28 | void Inject(IScript Instance, Func GlobalsFactory) 29 | where TGlobals : class, IScriptGlobals; 30 | 31 | Task> ParseFromAsync(Func GlobalsFactory, ScriptingOptions ScriptingOptions, string code, Func EntryMethodSelector, Func EntryMethodParameterFactory) 32 | where TGlobals : class, IScriptGlobals; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ScriptingAbstractions/IGlobalsFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ScriptingAbstractions 4 | { 5 | public interface IGlobalsFactory 6 | { 7 | object Create(AppDomain ExecutingDomain); 8 | } 9 | 10 | public interface IGlobalsFactory : IGlobalsFactory 11 | where TGlobals : class, IScriptGlobals 12 | { 13 | new TGlobals Create(AppDomain ExecutingDomain); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ScriptingAbstractions/IHasDescription.cs: -------------------------------------------------------------------------------- 1 | namespace ScriptingAbstractions 2 | { 3 | public interface IHasDescription 4 | { 5 | string Description { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ScriptingAbstractions/IParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ScriptingAbstractions 4 | { 5 | public interface IParameter : IHasDescription 6 | { 7 | /// 8 | /// Name of the parameter 9 | /// 10 | string Name { get; set; } 11 | 12 | /// 13 | /// Type the value of this parameter must habe 14 | /// 15 | Type Type { get; set; } 16 | 17 | /// 18 | /// Defines whether this parameter can be ommited, in which case the is returned 19 | /// 20 | bool IsOptional { get; set; } 21 | 22 | /// 23 | /// Default value, in case the parameter is optional (see ). By contract, returns value of type 24 | /// 25 | object DefaultValue { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ScriptingAbstractions/IParameterValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ScriptingAbstractions 4 | { 5 | /// 6 | /// Converts a transferblale value created by into a copy of 7 | /// 8 | /// 9 | /// A value of type representing a copy of the original object 10 | public delegate object TransferableValueToOriginalValueConverter(object TransferableValue); 11 | 12 | /// 13 | /// Converts a value into a transferable value, which must be of type 14 | /// the value which is to be converted 15 | /// A value of , which must be marshallable by reference, value or bleed 16 | public delegate object OriginalValueToTransferableValueConverter(object OriginalValue); 17 | 18 | 19 | 20 | /// 21 | /// Represents the value of a parameter. 22 | /// This may need some explaining: Why all the hassle with transferable values, converters and such? 23 | /// Well, because effectively, we are likely to instantiate implementations of IParameterValue on a certain appdomain, but consume its values in another. 24 | /// This would cause issues if the value is not marshallable (by type/reference/bleed). In that case, we need to pass over a converted value which IS marshallable, 25 | /// and then on the other app domain get a converter to convert this back to the original type - where "converting back" essentially means "creating a copy of the object in the new app domain" 26 | /// 27 | public interface IParameterValue 28 | { 29 | 30 | Type TransferableValueType { get; } 31 | IParameter Parameter { get; } 32 | 33 | 34 | /// 35 | /// Gets the original value of the parameter 36 | /// 37 | object GetOriginalValue(); 38 | 39 | /// 40 | /// Gets the value of the object in a format that can be marshalled in between AppDomains. 41 | /// The returned object will be of type , which in turn will be a type 42 | /// that is either marshallable by ference (MarshalByRefObject), by value (serializable), or by bleed (string, int etc.) 43 | /// 44 | object GetTransferableValue(); 45 | 46 | /// 47 | /// Gets a delegate that must by contract execute in the AppDomain of the caller of this function. 48 | /// Invoking the returned delegate will convert a value of type to one of type 49 | /// 50 | /// 51 | TransferableValueToOriginalValueConverter GetTransferableValueToOriginalValueConverter(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /ScriptingAbstractions/IScript.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace ScriptingAbstractions 6 | { 7 | public interface IScript : IHasDescription 8 | { 9 | /// 10 | /// Null in case of void 11 | /// 12 | Type ReturnType { get; set; } 13 | 14 | IList Parameters { get; } 15 | 16 | string Code { get; set; } 17 | 18 | IScriptRunResult Run(IEnumerable Parameters); 19 | Task RunAsync(IEnumerable Parameters); 20 | } 21 | 22 | public interface IScript : IScript 23 | where TGlobals : class, IScriptGlobals 24 | { 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ScriptingAbstractions/IScriptGlobals.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ScriptingAbstractions 4 | { 5 | public interface IScriptGlobals 6 | { 7 | IDictionary Parameters {get;} 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ScriptingAbstractions/IScriptRunResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ScriptingAbstractions 5 | { 6 | public interface IScriptRunResult 7 | { 8 | bool IsSuccess { get; } 9 | object ReturnValue { get; } 10 | IEnumerable Errors { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ScriptingAbstractions/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("ScriptingAbstractions")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ScriptingAbstractions")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("9c15d62f-889e-4c74-93d3-aac38d27031f")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /ScriptingAbstractions/ScriptingAbstractions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {9C15D62F-889E-4C74-93D3-AAC38D27031F} 8 | Library 9 | Properties 10 | ScriptingAbstractions 11 | ScriptingAbstractions 12 | v4.6.1 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | {DC268D3E-5FB8-4BD0-93B6-588001FA8EAE} 59 | CommonUtilities 60 | 61 | 62 | 63 | 70 | -------------------------------------------------------------------------------- /ScriptingAbstractions/ScriptingOptions.cs: -------------------------------------------------------------------------------- 1 | using ExcelScript.CommonUtilities; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Xml; 7 | using System.Xml.Schema; 8 | using System.Xml.Serialization; 9 | 10 | namespace ScriptingAbstractions 11 | { 12 | public enum HostingType 13 | { 14 | /// 15 | /// Run in the same AppDomain that the main Addin (ExcelScript) is hosted in 16 | /// 17 | GlobalAppDomain, 18 | 19 | /// 20 | /// Run in a domain seperate from the main Addin, but shared by all scripts 21 | /// 22 | SharedSandboxAppDomain, 23 | 24 | /// 25 | /// Create a seperate app domain for each script 26 | /// 27 | IndividualScriptAppDomain 28 | } 29 | 30 | // [Serializable] 31 | public class ScriptingOptions : MarshalByRefObject, IXmlSerializable 32 | { 33 | public virtual List References { get; protected set; } = new List(); 34 | public virtual List Imports { get; protected set; } = new List(); 35 | public HostingType HostingType { get; set; } = HostingType.SharedSandboxAppDomain; 36 | /* 37 | [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.Infrastructure)] 38 | public override object InitializeLifetimeService() 39 | { 40 | return null; 41 | }*/ 42 | 43 | public override int GetHashCode() 44 | { 45 | unchecked 46 | { 47 | int hash = (int)2166136261; 48 | 49 | foreach (var item in References.Cast().Concat(Imports.Cast())) 50 | hash = (hash * 16777619) ^ item.GetHashCode(); 51 | 52 | int hostingType = (int)this.HostingType; 53 | hash = (hash * 16777619) ^ hostingType.GetHashCode(); 54 | 55 | return hash; 56 | } 57 | } 58 | 59 | #region IXmlSerializable 60 | 61 | public XmlSchema GetSchema() 62 | { 63 | return null; 64 | } 65 | 66 | public void WriteXml(XmlWriter writer) 67 | { 68 | writer.WriteEnumerable(nameof(References), References, x => x.FullName); 69 | writer.WriteEnumerable(nameof(Imports), Imports); 70 | writer.Write(nameof(HostingType), HostingType); 71 | } 72 | 73 | public void ReadXml(XmlReader reader) 74 | { 75 | reader.MoveToCustomStart(); 76 | 77 | this.References = reader.ReadEnumerable(nameof(References), x => 78 | { 79 | string value = (string)x; 80 | Assembly assembly = Assembly.Load(value); 81 | return assembly; 82 | }).ToList(); 83 | 84 | this.Imports = reader.ReadEnumerable(nameof(Imports), x => (string)x).ToList(); 85 | 86 | object _hostingType; 87 | reader.Read(nameof(HostingType), out _hostingType); 88 | this.HostingType = (HostingType)_hostingType; 89 | 90 | reader.ReadEndElement(); 91 | } 92 | 93 | #endregion 94 | } 95 | } 96 | --------------------------------------------------------------------------------