├── .gitignore ├── docs └── addin-ribbon.png ├── JuliaInXL ├── juliaicon.png ├── juliafileopen.png ├── juliafileinclude.png ├── JuliaComputing_Icon.ico ├── juliafileselection.png ├── resources │ ├── Examples.xlsx │ ├── sim.jl │ └── License.rtf ├── JuliaInExcel_TemporaryKey.pfx ├── setassemblyversion.bat ├── app.config ├── JuliaInXL.csproj.user ├── packages.config ├── Properties │ ├── AssemblyInfo.noversion │ └── AssemblyInfo.cs ├── JuliaInXL-AddIn.dna ├── julia.svg ├── JuliaInXL.csproj └── AddInUtilities.cs ├── .gitattributes ├── LICENSE ├── JuliaInXL.sln └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | bin 3 | obj 4 | Setup/JuliaInXLSetup 5 | packages/* 6 | ~* -------------------------------------------------------------------------------- /docs/addin-ribbon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaComputing/JuliaInXL/HEAD/docs/addin-ribbon.png -------------------------------------------------------------------------------- /JuliaInXL/juliaicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaComputing/JuliaInXL/HEAD/JuliaInXL/juliaicon.png -------------------------------------------------------------------------------- /JuliaInXL/juliafileopen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaComputing/JuliaInXL/HEAD/JuliaInXL/juliafileopen.png -------------------------------------------------------------------------------- /JuliaInXL/juliafileinclude.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaComputing/JuliaInXL/HEAD/JuliaInXL/juliafileinclude.png -------------------------------------------------------------------------------- /JuliaInXL/JuliaComputing_Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaComputing/JuliaInXL/HEAD/JuliaInXL/JuliaComputing_Icon.ico -------------------------------------------------------------------------------- /JuliaInXL/juliafileselection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaComputing/JuliaInXL/HEAD/JuliaInXL/juliafileselection.png -------------------------------------------------------------------------------- /JuliaInXL/resources/Examples.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaComputing/JuliaInXL/HEAD/JuliaInXL/resources/Examples.xlsx -------------------------------------------------------------------------------- /JuliaInXL/JuliaInExcel_TemporaryKey.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaComputing/JuliaInXL/HEAD/JuliaInXL/JuliaInExcel_TemporaryKey.pfx -------------------------------------------------------------------------------- /JuliaInXL/setassemblyversion.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | @echo //>> AssemblyInfo.cs 3 | @echo [assembly: AssemblyVersion("%JuliaPro_Version%")] >> AssemblyInfo.cs 4 | @echo [assembly: AssemblyFileVersion("%JuliaPro_Version%")] >> AssemblyInfo.cs -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /JuliaInXL/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /JuliaInXL/JuliaInXL.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Program 5 | C:\Program Files (x86)\Microsoft Office\Office14\EXCEL.EXE 6 | JuliaInXL-32bit.xll 7 | C:\Users\andy\Documents\GitHub\JuliaInXL\JuliaInXL\bin\Debug\ 8 | 9 | -------------------------------------------------------------------------------- /JuliaInXL/resources/sim.jl: -------------------------------------------------------------------------------- 1 | using Distributions 2 | using JuliaWebAPI 3 | using Logging 4 | Logging.configure(level=DEBUG) 5 | 6 | f(u) = exp(-u^2/2)/√(2pi) 7 | 8 | const u1=Uniform(-5.0,+5.0) 9 | const u2=Uniform(0.0, 0.5) 10 | 11 | function simulate_pt() 12 | x=rand(u1) 13 | y=rand(u2) 14 | y 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Julia Computing, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /JuliaInXL/Properties/AssemblyInfo.noversion: -------------------------------------------------------------------------------- 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("JuliaInXL")] 9 | [assembly: AssemblyDescription("Julia in Excel")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Julia Computing Inc")] 12 | [assembly: AssemblyProduct("JuliaInXL")] 13 | [assembly: AssemblyCopyright("Copyright © 2016 Julia Computing Inc")] 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("a1829377-7fc9-42ea-b92b-26b2b2f83500")] 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.*")] -------------------------------------------------------------------------------- /JuliaInXL/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("JuliaInXL")] 9 | [assembly: AssemblyDescription("Julia in Excel")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Julia Computing Inc")] 12 | [assembly: AssemblyProduct("JuliaInXL")] 13 | [assembly: AssemblyCopyright("Copyright © 2016 Julia Computing Inc")] 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("a1829377-7fc9-42ea-b92b-26b2b2f83500")] 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("0.5.0.4")] 36 | [assembly: AssemblyFileVersion("0.5.0.4")] 37 | 38 | -------------------------------------------------------------------------------- /JuliaInXL.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JuliaInXL", "JuliaInXL\JuliaInXL.csproj", "{A1829377-7FC9-42EA-B92B-26B2B2F83500}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | CD_ROM|Any CPU = CD_ROM|Any CPU 11 | Debug|Any CPU = Debug|Any CPU 12 | DVD-5|Any CPU = DVD-5|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | SingleImage|Any CPU = SingleImage|Any CPU 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {A1829377-7FC9-42EA-B92B-26B2B2F83500}.CD_ROM|Any CPU.ActiveCfg = Release|Any CPU 18 | {A1829377-7FC9-42EA-B92B-26B2B2F83500}.CD_ROM|Any CPU.Build.0 = Release|Any CPU 19 | {A1829377-7FC9-42EA-B92B-26B2B2F83500}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {A1829377-7FC9-42EA-B92B-26B2B2F83500}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {A1829377-7FC9-42EA-B92B-26B2B2F83500}.DVD-5|Any CPU.ActiveCfg = Debug|Any CPU 22 | {A1829377-7FC9-42EA-B92B-26B2B2F83500}.DVD-5|Any CPU.Build.0 = Debug|Any CPU 23 | {A1829377-7FC9-42EA-B92B-26B2B2F83500}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {A1829377-7FC9-42EA-B92B-26B2B2F83500}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {A1829377-7FC9-42EA-B92B-26B2B2F83500}.SingleImage|Any CPU.ActiveCfg = Release|Any CPU 26 | {A1829377-7FC9-42EA-B92B-26B2B2F83500}.SingleImage|Any CPU.Build.0 = Release|Any CPU 27 | EndGlobalSection 28 | GlobalSection(SolutionProperties) = preSolution 29 | HideSolutionNode = FALSE 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /JuliaInXL/resources/License.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033{\fonttbl{\f0\fmodern\fprq1\fcharset0 Courier New;}{\f1\fnil\fcharset0 Calibri;}} 2 | {\colortbl ;\red0\green0\blue0;} 3 | {\*\generator Riched20 10.0.22000}\viewkind4\uc1 4 | \pard\widctlpar\qc\tx916\tx1832\tx2748\tx3664\tx4580\tx5496\tx6412\tx7328\tx8244\tx9160\tx10076\tx10992\tx11908\tx12824\tx13740\tx14656\cf1\b\f0\fs20\lang2057 MIT License\par 5 | 6 | \pard\widctlpar\tx916\tx1832\tx2748\tx3664\tx4580\tx5496\tx6412\tx7328\tx8244\tx9160\tx10076\tx10992\tx11908\tx12824\tx13740\tx14656\b0\par 7 | Copyright (c) 2015-2022 Julia Computing, Inc.\par 8 | \par 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\par 10 | \par 11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\par 12 | \par 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\par 14 | 15 | \pard\sa200\sl276\slmult1\cf0\f1\fs22\lang9\par 16 | } 17 | -------------------------------------------------------------------------------- /JuliaInXL/JuliaInXL-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 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /JuliaInXL/julia.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 25 | 26 | 27 | 35 | 36 | 37 | 41 | 42 | 43 | 47 | 48 | 49 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Call Julia functions from Microsoft Excel 2 | 3 | ### Installation 4 | 5 | The JuliaInXL system is composed of two parts, a Julia package and an Excel Plugin. The Excel plugin itself consists of an XLL add-in. 6 | 7 | The Excel plugin is distributed as an installable exe: JuliaProfessional_JuliaInXL_Addin_vW.X.Y.Z.exe, where vW.X.Y.Z is the associated version number of Julia Professional. Running this installer will install the XLL into `\[APPDATA]\Roaming\Microsoft\AddIns\`. Use of the JuliaInXL plug-in depends on .NET v4.0 or later being available on the system. After installation, the add-in needs to be enabled via either [File -> Options -> Add-ins](https://support.office.com/en-us/article/View-manage-and-install-add-ins-in-Office-programs-16278816-1948-4028-91E5-76DCA5380F8D) menu entry or the [Developer/Add-In](https://msdn.microsoft.com/en-us/library/bb608625.aspx) ribbon button. 8 | 9 | The Julia language package can be installed by doing (on the Julia REPL) `Pkg.add("JuliaInXL")`, if you have the rights to that repository. However, you will most likely be provided this package as part of your Julia Professional Bundle. This package depends on two public packages, `JuliaWebAPI` and `Reexport`. 10 | 11 | ### Usage Workflow 12 | 13 | #### Getting Started 14 | The primary supported workflow is for interactive development of Julia programs alongside Excel. Once the packages are installed, start Excel, and a Julia REPL. On the Julia repl, type `using JuliaInXL` to make the environment ready for calling via Excel. After that, define your functions as normal on the REPL. To try an an example, use a demo file included with the package: `include("C:\\Users\\\\AppData\\Local\\JuliaPro-W.X.Y.Z\\Julia-W.X.Y\\share\\julia\\site\\vW.X\\JuliaInXL\\test\\sim.jl")` where `` is your current Windows username and W.X.Y.Z are integers representing the current release of Julia Professional. (When using theses examples, you will need the `Distributions.jl` package installed. Please run `Pkg.add("Distributions")` on the Julia REPL if you do not have this already installed.) 15 | 16 | Once you have the functions that you need to call from Excel, expose them via the `process_async` call. This will start the server listening to requests from Excel in the background, and return a connection object that can be used for exposing more functions later. The primary arguments to this function is an array of functions to be exposed, and the endpoint on which to listen to messages. The `bind=true` parameter tells the function to start listening at the endpoint and wait for client connections. 17 | 18 | ```julia 19 | conn = process_async([simulate, simulateTime], "tcp://127.0.0.1:9999", bind=true) 20 | ``` 21 | 22 | There is a corresponding `process` function with exactly the same API which listens synchronously, and thus doesn't return. This function should be used if the process is started from a command line, rather than via an iteractive REPL. 23 | 24 | #### Calling functions from Excel 25 | 26 | Once the server is started, julia functions can be called from Excel using the `jlcall` worksheet function. The first argument to jlcall is a string, which is the name of the function to be called. Subsequent arguments to the `jlcall` function are passed as parameters to the Julia function being called. These can be constant literals, or cell references. Arrays can be passed via cell ranges. 27 | 28 | If the Julia function returns an array (1d or 2d), then use `jlcall` as an Excel Array function by selecting a range before entering the function, and pressing `Shift-Ctrl-Enter` to finish. 29 | 30 | Functions exposed to Excel should take floats or strings, or their arrays as arguments. In general, it is a good idea to keep the function arguments as loosely typed as possible. Therefore functions should return integers, floats, or strings; or their arrays. However, arrays of dimensions greater than two are not supported. 31 | 32 | Note that [Excel stores all numbers as 64 bit IEEE floats](https://support.microsoft.com/en-us/kb/78113). Therefore, be aware of the possibility of truncation if returning large, or high precision, numbers. 33 | 34 | Dates are passed in from excel as floating point numbers in its internal encoding (fractional days since 1/1/1900 or 1/1/1904). Thus, they are recieved in Julia functions as floats. They can be converted to Julia DateTime values using the `xldate` function. 35 | 36 | #### Making changes 37 | 38 | Remember that we started the Julia server in async mode. This means that the REPL is available for interactive use when working with Excel. Creating new definitions of functions that are already exposed will replace them, and the new versions will get called when the sheet is next recalculated. 39 | 40 | New functions can be added to the listener interface using the connection object saved from the original `process_async` call, via the `register` function. The arguments to `register` are the connection object returned from `process_async`, and the function name to expose. 41 | 42 | ```julia 43 | register(conn, simulateArray) 44 | ``` 45 | #### Controlling Julia from within Excel 46 | ![Ribbon](https://raw.githubusercontent.com/JuliaComputing/JuliaInXL/master/docs/addin-ribbon.png?token=AAXIJjVyMx7f5eYINZh9p0OAMleG68Luks5WmFXAwA%3D%3D) 47 | 48 | ---- 49 | _Microsoft and Excel are registered trademarks of Microsoft Corporation_ 50 | -------------------------------------------------------------------------------- /JuliaInXL/JuliaInXL.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {A1829377-7FC9-42EA-B92B-26B2B2F83500} 8 | Library 9 | Properties 10 | JuliaInXL 11 | JuliaInXL 12 | v4.0 13 | 512 14 | Client 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | none 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 0 32 | 33 | 34 | false 35 | 36 | 37 | 38 | ..\packages\AsyncIO.0.1.18.0\lib\net40\AsyncIO.dll 39 | True 40 | 41 | 42 | ..\packages\ExcelDna.Integration.0.33.9\lib\ExcelDna.Integration.dll 43 | False 44 | 45 | 46 | ..\packages\ExcelDna.Registration.0.33.9\lib\net40\ExcelDna.Registration.dll 47 | True 48 | 49 | 50 | ..\packages\NetMQ.3.3.3.1\lib\net40\NetMQ.dll 51 | True 52 | 53 | 54 | ..\packages\Newtonsoft.Json.8.0.3\lib\net40\Newtonsoft.Json.dll 55 | True 56 | 57 | 58 | 59 | 60 | ..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll 61 | True 62 | 63 | 64 | ..\packages\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.dll 65 | True 66 | 67 | 68 | ..\packages\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.Extensions.dll 69 | True 70 | 71 | 72 | ..\packages\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.Primitives.dll 73 | True 74 | 75 | 76 | ..\packages\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.WebRequest.dll 77 | True 78 | 79 | 80 | ..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll 81 | True 82 | 83 | 84 | ..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll 85 | True 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | Always 102 | 103 | 104 | Always 105 | 106 | 107 | Always 108 | 109 | 110 | Always 111 | 112 | 113 | PreserveNewest 114 | Designer 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | xcopy "$(SolutionDir)packages\ExcelDna.AddIn.0.33.9\tools\ExcelDna.Integration.dll" "$(TargetDir)ExcelDna.Integration.dll*" /C /Y 123 | xcopy "$(SolutionDir)packages\ExcelDna.AddIn.0.33.9\tools\ExcelDna.xll" "$(TargetDir)JuliaInXL-AddIn.xll*" /C /Y 124 | xcopy "$(ProjectDir)JuliaInXL-AddIn.dna" "$(TargetDir)JuliaInXL-AddIn.dna*" /C /Y 125 | xcopy "$(TargetDir)JuliaInXL-AddIn.dna" "$(TargetDir)JuliaInXL-AddIn64.dna*" /C /Y 126 | xcopy "$(SolutionDir)packages\ExcelDna.AddIn.0.33.9\tools\ExcelDna64.xll" "$(TargetDir)JuliaInXL-AddIn64.xll*" /C /Y 127 | "$(SolutionDir)packages\ExcelDna.AddIn.0.33.9\tools\ExcelDnaPack.exe" "$(TargetDir)JuliaInXL-AddIn.dna" /Y 128 | "$(SolutionDir)packages\ExcelDna.AddIn.0.33.9\tools\ExcelDnaPack.exe" "$(TargetDir)JuliaInXL-AddIn64.dna" /Y 129 | del /Q "$(TargetDir)ExcelDna.Integration.dll" 130 | del /Q "$(TargetDir)ExcelDna.Registration.dll" 131 | del /Q "$(TargetDir)AsyncIO.dll" 132 | del /Q "$(TargetDir)JuliaInXL.dll" 133 | del /Q "$(TargetDir)JuliaInXL.dll.config" 134 | del /Q "$(TargetDir)NetMQ.dll" 135 | del /Q "$(TargetDir)NetMQ.xml" 136 | del /Q "$(TargetDir)Newtonsoft.Json.dll" 137 | del /Q "$(TargetDir)Newtonsoft.Json.xml" 138 | del /Q "$(TargetDir)JuliaInXL-AddIn.dna" 139 | del /Q "$(TargetDir)JuliaInXL-AddIn64.dna" 140 | del /Q "$(TargetDir)System.IO.dll" 141 | del /Q "$(TargetDir)System.IO.xml" 142 | del /Q "$(TargetDir)System.Net.Http.dll" 143 | del /Q "$(TargetDir)System.Net.Http.xml" 144 | del /Q "$(TargetDir)System.Net.Http.Extensions.dll" 145 | del /Q "$(TargetDir)System.Net.Http.Extensions.xml" 146 | del /Q "$(TargetDir)System.Net.Http.Primitives.dll" 147 | del /Q "$(TargetDir)System.Net.Http.Primitives.xml" 148 | del /Q "$(TargetDir)System.Net.Http.WebRequest.dll" 149 | del /Q "$(TargetDir)System.Net.Http.WebRequest.xml" 150 | del /Q "$(TargetDir)System.Runtime.dll" 151 | del /Q "$(TargetDir)System.Runtime.xml" 152 | del /Q "$(TargetDir)System.Threading.Tasks.dll" 153 | del /Q "$(TargetDir)System.Threading.Tasks.xml" 154 | del /Q "$(TargetDir)JuliaInXL-AddIn.xll" 155 | del /Q "$(TargetDir)JuliaInXL-AddIn64.xll" 156 | del /Q "$(TargetDir)juliaicon.png" 157 | del /Q "$(TargetDir)juliafileopen.png" 158 | del /Q "$(TargetDir)juliafileinclude.png" 159 | del /Q "$(TargetDir)juliafileselection.png" 160 | rename "$(TargetDir)JuliaInXL-AddIn-packed.xll" "JuliaInXL-32bit.xll" 161 | rename "$(TargetDir)JuliaInXL-AddIn64-packed.xll" "JuliaInXL-64bit.xll" 162 | 163 | 164 | del /Q "$(TargetDir)JuliaInXL-32bit.xll" 165 | del /Q "$(TargetDir)JuliaInXL-64bit.xll" 166 | xcopy /y "$(ProjectDir)Properties\AssemblyInfo.noversion" "$(ProjectDir)\Properties\AssemblyInfo.cs" 167 | cd "$(ProjectDir)Properties" 168 | CALL "$(ProjectDir)setassemblyversion.bat" 169 | 170 | 171 | 172 | 173 | 174 | 175 | 182 | -------------------------------------------------------------------------------- /JuliaInXL/AddInUtilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Runtime.InteropServices; 7 | using System.Text.RegularExpressions; 8 | using System.Threading; 9 | using System.Windows.Forms; 10 | using ExcelDna.Integration; 11 | using ExcelDna.Integration.Extensibility; 12 | using ExcelDna.ComInterop; 13 | using ExcelDna.Integration.CustomUI; 14 | using ExcelDna.Registration; 15 | using ExcelDna.Logging; 16 | 17 | using NetMQ; 18 | using Newtonsoft.Json.Linq; 19 | using NetMQ.Sockets; 20 | using Microsoft.Win32; 21 | using System.Collections.Generic; 22 | 23 | namespace JuliaInXL 24 | { 25 | 26 | [Serializable()] 27 | public class JuliaException : System.Exception 28 | { 29 | public JuliaException() : base() { } 30 | public JuliaException(string message) : base(message) { } 31 | public JuliaException(string message, System.Exception inner) : base(message, inner) { } 32 | 33 | // A constructor is needed for serialization when an 34 | // exception propagates from a remoting server to the client. 35 | protected JuliaException(System.Runtime.Serialization.SerializationInfo info, 36 | System.Runtime.Serialization.StreamingContext context) 37 | { } 38 | } 39 | 40 | [ComVisible(true)] 41 | public interface IAddInUtilities 42 | { 43 | object CallJulia(string fn, object[] arguments,int JuliaInXL_timeout=30); 44 | void Terminate(); 45 | void Reconnect(); 46 | Process LaunchLocalJulia(); 47 | void ShutdownJulia(Process p); 48 | } 49 | 50 | [ComVisible(true)] 51 | [ClassInterface(ClassInterfaceType.AutoDual)] 52 | public class AddInUtilities : IAddInUtilities 53 | { 54 | private RequestSocket client; 55 | private NetMQContext context; 56 | private Process juliaprocess = new Process(); 57 | private string juliaversion; 58 | private string juliaproversion; 59 | private string endpoint = "tcp://localhost:9999"; 60 | private string juliafile = ""; 61 | private bool connected = false; 62 | 63 | public string Endpoint 64 | { 65 | get 66 | { 67 | return endpoint; 68 | } 69 | 70 | set 71 | { 72 | endpoint = value; 73 | } 74 | } 75 | 76 | public Process JuliaProcess 77 | { 78 | get 79 | { 80 | return juliaprocess; 81 | } 82 | 83 | set 84 | { 85 | juliaprocess = value; 86 | } 87 | } 88 | 89 | public string JuliaProVersion 90 | { 91 | get 92 | { 93 | return juliaproversion; 94 | } 95 | 96 | set 97 | { 98 | juliaproversion = value; 99 | } 100 | } 101 | 102 | public string JuliaVersion 103 | { 104 | get 105 | { 106 | return juliaversion; 107 | } 108 | 109 | set 110 | { 111 | juliaversion = value; 112 | } 113 | } 114 | 115 | public string JuliaFile 116 | { 117 | get 118 | { 119 | return juliafile; 120 | } 121 | set 122 | { 123 | juliafile = value; 124 | } 125 | } 126 | 127 | public bool Connected 128 | { 129 | get 130 | { 131 | return connected; 132 | } 133 | set 134 | { 135 | connected = value; 136 | } 137 | } 138 | 139 | public static bool registryValueExists(string hive_HKLM_or_HKCU, string registryRoot, string valueName) 140 | { 141 | RegistryKey root; 142 | 143 | switch (hive_HKLM_or_HKCU.ToUpper()) 144 | { 145 | case "HKLM": 146 | RegistryKey localMachineRegistry 147 | = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, 148 | Environment.Is64BitOperatingSystem 149 | ? RegistryView.Registry64 150 | : RegistryView.Registry32); 151 | 152 | root = localMachineRegistry.OpenSubKey(registryRoot); 153 | 154 | break; 155 | case "HKCU": 156 | 157 | root = Registry.CurrentUser.OpenSubKey(registryRoot); 158 | 159 | break; 160 | default: 161 | throw new System.InvalidOperationException("parameter registryRoot must be either \"HKLM\" or \"HKCU\""); 162 | } 163 | 164 | bool test = (root != null) && (root.GetValue(valueName) != null); 165 | 166 | return test; 167 | } 168 | 169 | public AddInUtilities() 170 | { 171 | 172 | Version version = Assembly.GetExecutingAssembly().GetName().Version; 173 | JuliaVersion = version.Major + "." + version.Minor + "." + version.Build; 174 | JuliaProVersion = JuliaVersion + "." + version.Revision; 175 | 176 | // Check for either JuliaInXL or JuliaPro contained within the registry path to the JuliaPro installation. 177 | if (registryValueExists("HKCU", "Software\\JuliaPro\\JuliaInXL", "JuliaInXL_Default_Endpoint") == true) 178 | { 179 | endpoint = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\JuliaPro\JuliaInXL", "JuliaInXL_Default_Endpoint", ""); 180 | } 181 | else if (registryValueExists("HKCU", "Software\\JuliaPro\\" + JuliaProVersion, "JuliaInXL_Default_Endpoint") == true) 182 | { 183 | endpoint = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\JuliaPro\" + JuliaProVersion, "JuliaInXL_Default_Endpoint", ""); 184 | } 185 | else if (registryValueExists("HKLM", "Software\\JuliaPro\\JuliaInXL", "JuliaInXL_Default_Endpoint") == true) 186 | { 187 | RegistryKey localMachineRegistry 188 | = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, 189 | Environment.Is64BitOperatingSystem 190 | ? RegistryView.Registry64 191 | : RegistryView.Registry32); 192 | 193 | RegistryKey key = localMachineRegistry.OpenSubKey("Software\\JuliaPro\\JuliaInXL"); 194 | 195 | endpoint = (string)key.GetValue("JuliaInXL_Default_Endpoint"); 196 | } 197 | else if (registryValueExists("HKLM", "Software\\JuliaPro\\" + JuliaProVersion, "JuliaInXL_Default_Endpoint") == true) 198 | { 199 | RegistryKey localMachineRegistry 200 | = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, 201 | Environment.Is64BitOperatingSystem 202 | ? RegistryView.Registry64 203 | : RegistryView.Registry32); 204 | 205 | RegistryKey key = localMachineRegistry.OpenSubKey("Software\\JuliaPro\\" + JuliaProVersion); 206 | 207 | endpoint = (string)key.GetValue("JuliaInXL_Default_Endpoint"); 208 | } 209 | else if (Environment.GetEnvironmentVariable("JULIAINXL_DEFAULT_ENDPOINT") != null) 210 | { 211 | endpoint = Environment.GetEnvironmentVariable("JULIAINXL_DEFAULT_ENDPOINT"); 212 | } 213 | else 214 | { 215 | endpoint = "tcp://localhost:9999"; 216 | } 217 | 218 | context = NetMQ.NetMQContext.Create(); 219 | client = context.CreateRequestSocket(); 220 | client.Connect(endpoint); 221 | Connected = true; 222 | } 223 | 224 | public Process LaunchLocalJulia() 225 | { 226 | string julia_exe; 227 | Version version = Assembly.GetExecutingAssembly().GetName().Version; 228 | JuliaVersion = version.Major + "." + version.Minor + "." + version.Build; 229 | JuliaProVersion = JuliaVersion + "." + version.Revision; 230 | 231 | // Check for either JuliaInXL or JuliaPro contained within the registry path to the JuliaPro installation. 232 | if (registryValueExists("HKCU", "Software\\JuliaPro\\JuliaInXL\\", "Install_Dir") == true) 233 | { 234 | string juliaBaseDir = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\JuliaPro\JuliaInXL\", "Install_Dir", ""); 235 | julia_exe = juliaBaseDir + "\\julia.exe"; 236 | } 237 | else if (registryValueExists("HKCU", "Software\\JuliaPro\\" + JuliaProVersion, "Install_Dir") == true) 238 | { 239 | string juliaBaseDir = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\JuliaPro\" + JuliaProVersion, "Install_Dir", ""); 240 | julia_exe = juliaBaseDir + "\\Julia-" + JuliaVersion + "\\bin\\julia.exe"; 241 | } 242 | else if (registryValueExists("HKLM", "Software\\JuliaInXL\\" + JuliaProVersion, "Install_Dir") == true) 243 | { 244 | RegistryKey localMachineRegistry 245 | = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, 246 | Environment.Is64BitOperatingSystem 247 | ? RegistryView.Registry64 248 | : RegistryView.Registry32); 249 | RegistryKey key = localMachineRegistry.OpenSubKey("Software\\JuliaPro\\JuliaInXL"); 250 | string juliaBaseDir = (string) key.GetValue("Install_Dir"); 251 | julia_exe = juliaBaseDir + "\\julia.exe"; 252 | } 253 | else if (registryValueExists("HKLM", "Software\\JuliaPro\\" + JuliaProVersion, "Install_Dir") == true) 254 | { 255 | RegistryKey localMachineRegistry 256 | = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, 257 | Environment.Is64BitOperatingSystem 258 | ? RegistryView.Registry64 259 | : RegistryView.Registry32); 260 | RegistryKey key = localMachineRegistry.OpenSubKey("Software\\JuliaPro\\" + JuliaProVersion); 261 | string juliaBaseDir = (string)key.GetValue("Install_Dir"); 262 | julia_exe = juliaBaseDir + "\\Julia-" + JuliaVersion + "\\bin\\julia.exe"; 263 | } 264 | else 265 | { 266 | julia_exe = "julia.exe"; 267 | } 268 | 269 | Process p = new Process(); 270 | 271 | try 272 | { 273 | string endpoint = Endpoint; 274 | char[] delimiter = { ':' }; 275 | string[] words = endpoint.Split(delimiter); 276 | string port = words[words.Length - 1]; 277 | string host = words[words.Length - 2]; 278 | 279 | string address; 280 | if (host.Equals("//localhost")) 281 | { 282 | address = "//127.0.0.1"; 283 | } 284 | else 285 | { 286 | address = host; 287 | } 288 | 289 | string commandlineargs = String.Join("", "-i -e \"using JuliaInXL; JuliaInXL.start_async_server(", port, ")\""); 290 | 291 | p.StartInfo.FileName = julia_exe; 292 | p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized; // ProcessWindowStyle.Hidden; 293 | p.StartInfo.Arguments = commandlineargs; 294 | 295 | p.Start(); 296 | 297 | Thread.Sleep(1000); 298 | Process.GetProcessById(p.Id); 299 | Connected = true; 300 | } 301 | catch (Exception e) 302 | { 303 | MessageBox.Show("Exception = " + e.ToString()); 304 | p = null; 305 | } 306 | 307 | return p; 308 | 309 | } 310 | 311 | public void ShutdownJulia(Process p) 312 | { 313 | p.Kill(); 314 | } 315 | 316 | 317 | public object CallJulia(string fn, object[] arguments, int JuliaInXL_timeout=30) 318 | { 319 | 320 | try 321 | { 322 | Version version = Assembly.GetExecutingAssembly().GetName().Version; 323 | JuliaVersion = version.Major + "." + version.Minor + "." + version.Build; 324 | JuliaProVersion = JuliaVersion + "." + version.Revision; 325 | RegistryKey localMachineRegistry 326 | = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, 327 | Environment.Is64BitOperatingSystem 328 | ? RegistryView.Registry64 329 | : RegistryView.Registry32); 330 | RegistryKey key = localMachineRegistry.OpenSubKey("Software\\JuliaPro\\" + JuliaProVersion + "\\JuliaInXL"); 331 | 332 | if ((registryValueExists("HKCU", "Software\\JuliaPro\\" + JuliaProVersion + "\\JuliaInXL", "Timeout") == true) && (int.TryParse((string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\JuliaPro\" + JuliaProVersion + "\\JuliaInXL", "Timeout", ""), out JuliaInXL_timeout)) && (JuliaInXL_timeout > 0)) {} 333 | else if ((registryValueExists("HKLM", "Software\\JuliaPro\\" + JuliaProVersion + "\\JuliaInXL", "Timeout") == true) && (int.TryParse((string)key.GetValue("Timeout"), out JuliaInXL_timeout)) && (JuliaInXL_timeout > 0)) {} 334 | else if (Environment.GetEnvironmentVariable("JuliaInXL_timeout") != null && (int.TryParse(Environment.GetEnvironmentVariable("JuliaInXL_timeout"), out JuliaInXL_timeout)) && (JuliaInXL_timeout > 0)) {} 335 | else 336 | { 337 | JuliaInXL_timeout = 30; 338 | } 339 | JObject o = new JObject(); 340 | o["cmd"] = fn; 341 | if (arguments.Length > 1 || arguments[0] != null) 342 | { 343 | JArray args = new JArray(); 344 | for (int i = 0; i < arguments.Length; i++) 345 | { 346 | 347 | if (arguments[i] is object[]) 348 | { 349 | 350 | object[] arg = (object[])arguments[i]; 351 | 352 | JArray jarg = new JArray(); 353 | for (int k = 0; k < arg.Length; k++) 354 | { 355 | jarg.Add(arg[k]); 356 | } 357 | 358 | args.Add(jarg); 359 | 360 | } 361 | else if (arguments[i] is object[,]) 362 | { 363 | object[,] arg = (object[,])arguments[i]; 364 | 365 | int cols = arg.GetUpperBound(1) + 1; 366 | int rows = arg.GetUpperBound(0) + 1; 367 | 368 | JArray jarg_cols = new JArray(); 369 | for (int n = 0; n < cols; n++) 370 | { 371 | object[] arg_col = new object[rows]; 372 | for (int m = 0; m < rows; m++) 373 | { 374 | arg_col[m] = arg[m, n]; 375 | } 376 | jarg_cols.Add(new JArray(arg_col)); 377 | } 378 | 379 | args.Add(jarg_cols); 380 | 381 | } 382 | else if (arguments[i] is Array) 383 | { 384 | //It seems JArray flattens its arguments by default 385 | //wrapping it in another JArray prevents flattening 386 | args.Add(new JArray(arguments[i])); 387 | } 388 | else 389 | { 390 | args.Add(arguments[i]); 391 | } 392 | } 393 | o["args"] = args; 394 | } 395 | client.SendFrame(o.ToString()); 396 | 397 | // Receive the response from the client socket 398 | string m2=""; 399 | if (Connected==true) 400 | { 401 | client.TryReceiveFrameString(TimeSpan.FromSeconds(JuliaInXL_timeout), out m2); 402 | } 403 | object retval = ProcessResult(m2); 404 | return retval; 405 | } 406 | catch 407 | { 408 | this.Reconnect(); 409 | return "#ERR"; 410 | } 411 | } 412 | 413 | /** 414 | * Convert the JSON string into native objects that can be returned to Excel 415 | * Arrays are supported only upto dimension 2 416 | **/ 417 | private dynamic ProcessResult(string m2) 418 | { 419 | dynamic retval = ""; 420 | if (m2 == null) 421 | { 422 | client.Close(); 423 | client = context.CreateRequestSocket(); 424 | client.Connect(endpoint); 425 | Connected = false; 426 | retval = "#ERR"; 427 | } 428 | else 429 | { 430 | JObject result = JObject.Parse(m2); 431 | retval = result["data"]; 432 | 433 | if (retval is JArray) 434 | { 435 | Object[] t = ((JArray)retval).ToObject(); 436 | if ((t).Length > 0 && t[0] is JArray) 437 | { 438 | int cols = (t).Length; 439 | Object[] col = ((JArray)t[0]).ToObject(); 440 | int rows = (col).Length; 441 | object[,] r = new object[rows, cols]; 442 | //Array is two Dimensional 443 | for (int j = 0; j < cols; j++) 444 | { 445 | col = ((JArray)t[j]).ToObject(); 446 | for (int i = 0; i < rows; i++) 447 | { 448 | r[i, j] = col[i]; 449 | } 450 | t[j] = ((JArray)t[j]).ToObject(); 451 | } 452 | retval = r; 453 | 454 | } 455 | else //if ((t).Length > 0 && t[0] is double) 456 | { 457 | object[] r = new object[(t).Length]; 458 | for (int i = 0; i < t.Length; i++) 459 | { 460 | r[i] = t[i]; 461 | } 462 | retval = r; 463 | } 464 | } 465 | 466 | } 467 | 468 | return retval; 469 | } 470 | 471 | public void Terminate() 472 | { 473 | JObject o = new JObject(); 474 | o["cmd"] = ":terminate"; 475 | client.SendFrame(o.ToString()); 476 | this.Reconnect(); 477 | } 478 | 479 | public void Reconnect() 480 | { 481 | client.Close(); 482 | client = context.CreateRequestSocket(); 483 | client.Connect(endpoint); 484 | Connected = true; 485 | 486 | } 487 | 488 | public void KeepAlive() 489 | { 490 | string KeepAlive_endpoint = Endpoint; 491 | char[] KeepAlive_delimiter = { ':' }; 492 | string[] KeepAlive_words = KeepAlive_endpoint.Split(KeepAlive_delimiter); 493 | string KeepAlive_port = KeepAlive_words[KeepAlive_words.Length - 1]; 494 | string KeepAlive_host = KeepAlive_words[KeepAlive_words.Length - 2]; 495 | int KeepAlive_Interger_port = (Int32.Parse(KeepAlive_port)) - 1; 496 | string KeepAlive_connection_string = $"tcp:{KeepAlive_host}:{KeepAlive_Interger_port}"; 497 | try 498 | { 499 | while (true) 500 | { 501 | using (var keep_alive_client = new RequestSocket()) 502 | { 503 | keep_alive_client.Connect(KeepAlive_connection_string); 504 | keep_alive_client.SendFrame("Hello"); 505 | string keep_alive_sampletext; 506 | keep_alive_client.TryReceiveFrameString(TimeSpan.FromSeconds(1), out keep_alive_sampletext); 507 | if (keep_alive_sampletext == "Hello") 508 | { 509 | Connected = true; 510 | } 511 | else 512 | { 513 | JObject o = new JObject(); 514 | o["cmd"] = "JuliaInXL.start_heartbeat"; 515 | o["args"] = KeepAlive_Interger_port; 516 | client.SendFrame(o.ToString()); 517 | string m2; 518 | client.TryReceiveFrameString(TimeSpan.FromSeconds(1), out m2); 519 | object retval = ProcessResult(m2); 520 | if (retval.ToString() == "#ERR" || retval.ToString() == "") 521 | { 522 | Connected = false; 523 | } 524 | else 525 | { 526 | Connected = true; 527 | } 528 | } 529 | // Making the thread wait 530 | Thread.CurrentThread.Join(1000); 531 | } 532 | } 533 | } 534 | catch {} 535 | } 536 | 537 | public void close() 538 | { 539 | if (client != null) { client.Close(); } 540 | if (context != null) { try { context.Terminate(); } catch { } } 541 | } 542 | } 543 | 544 | [ComVisible(true)] 545 | [ClassInterface(ClassInterfaceType.AutoDual)] 546 | [ProgId("JuliaInXLutilities")] 547 | public class JuliaInXLutilities 548 | { 549 | 550 | private AddInUtilities utilities; 551 | 552 | public AddInUtilities Utilities 553 | { 554 | get 555 | { 556 | return utilities; 557 | } 558 | 559 | set 560 | { 561 | utilities = value; 562 | } 563 | 564 | } 565 | 566 | public object LaunchUtilities() 567 | { 568 | if (utilities == null) 569 | utilities = new AddInUtilities(); 570 | 571 | return utilities; 572 | } 573 | 574 | } 575 | 576 | public static class JuliaInXLutilities_global 577 | { 578 | static JuliaInXLutilities _jxlu; 579 | 580 | public static JuliaInXLutilities jxlu 581 | { 582 | get 583 | { 584 | return _jxlu; 585 | } 586 | 587 | set 588 | { 589 | _jxlu = value; 590 | } 591 | } 592 | } 593 | 594 | [ComVisible(true)] 595 | [ProgId("JuliaComAddIn")] 596 | public class JuliaComAddIn : ExcelComAddIn 597 | { 598 | 599 | public JuliaComAddIn() 600 | { 601 | } 602 | public override void OnConnection(object Application, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom) 603 | { 604 | } 605 | public override void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom) 606 | { 607 | } 608 | public override void OnAddInsUpdate(ref Array custom) 609 | { 610 | } 611 | public override void OnStartupComplete(ref Array custom) 612 | { 613 | JuliaInXLutilities_global.jxlu.Utilities.JuliaProcess = JuliaInXLutilities_global.jxlu.Utilities.LaunchLocalJulia(); 614 | Thread thread = new Thread(new ThreadStart(JuliaInXLutilities_global.jxlu.Utilities.KeepAlive)); 615 | thread.Start(); 616 | } 617 | public override void OnBeginShutdown(ref Array custom) 618 | { 619 | JuliaInXLutilities_global.jxlu.Utilities.ShutdownJulia(JuliaInXLutilities_global.jxlu.Utilities.JuliaProcess); 620 | } 621 | } 622 | 623 | [ComVisible(false)] 624 | [ClassInterface(ClassInterfaceType.None)] 625 | [ProgId("JuliaAddIn")] 626 | public class JuliaAddIn : IExcelAddIn 627 | { 628 | 629 | ExcelDna.Integration.ExcelComAddIn _comAddIn; 630 | 631 | public void AutoOpen() 632 | { 633 | JuliaInXLutilities_global.jxlu = new JuliaInXLutilities(); 634 | JuliaInXLutilities_global.jxlu.LaunchUtilities(); 635 | ExcelRegistration.GetExcelFunctions(). 636 | ProcessParamsRegistrations(). 637 | RegisterFunctions(); 638 | ComServer.DllRegisterServer(); 639 | _comAddIn = new JuliaComAddIn(); 640 | ExcelComAddInHelper.LoadComAddIn(_comAddIn); 641 | ExcelIntegration.RegisterUnhandledExceptionHandler( 642 | delegate (object ex) { 643 | Exception ex2 = (Exception)ex; 644 | return string.Format("#{0}!", ex2.Message); 645 | } 646 | ); 647 | } 648 | 649 | public void AutoClose() 650 | { 651 | ComServer.DllUnregisterServer(); 652 | } 653 | 654 | [ExcelFunction(ExplicitRegistration = true, IsMacroType = false)] 655 | public static object[,] jlcall( 656 | [ExcelArgument(Name = "function", Description = "Name of Julia function", AllowReference = true)] object fn, 657 | [ExcelArgument(Name = "arguments", Description = "List input arguments to Julia function", AllowReference = true)] params object[] args) 658 | 659 | { 660 | bool xlErrorPresent = false; 661 | ExcelReference caller = (ExcelReference)XlCall.Excel(XlCall.xlfCaller); 662 | int rows = caller.RowLast - caller.RowFirst + 1; 663 | int cols = caller.ColumnLast - caller.ColumnFirst + 1; 664 | ExcelReference cellRef, a; 665 | dynamic retval; 666 | object[,] ret; 667 | object b; 668 | ExcelError err = ExcelError.ExcelErrorValue; 669 | 670 | // List of numeric types used later for comparison 671 | List numerictypes = new List(); 672 | numerictypes.Add(typeof(double)); 673 | numerictypes.Add(typeof(float)); 674 | numerictypes.Add(typeof(int)); 675 | numerictypes.Add(typeof(Int16)); 676 | numerictypes.Add(typeof(Int32)); 677 | numerictypes.Add(typeof(Int64)); 678 | numerictypes.Add(typeof(UInt16)); 679 | numerictypes.Add(typeof(UInt32)); 680 | numerictypes.Add(typeof(UInt64)); 681 | 682 | string f; 683 | 684 | if (JuliaInXLutilities_global.jxlu.Utilities.Connected) 685 | { 686 | try 687 | { 688 | if (fn is ExcelReference) 689 | { 690 | f = ((ExcelReference)fn).GetValue() as string; 691 | } 692 | else 693 | { 694 | f = (string)fn; 695 | } 696 | 697 | int numargs = args.Length; 698 | if (numargs > 0) 699 | { 700 | object[] jargs = new object[numargs]; 701 | for (int i = 0; i < numargs; i++) 702 | { 703 | if (args[i] is ExcelReference) 704 | { 705 | a = (ExcelReference)args[i]; 706 | int rf = a.RowFirst; 707 | int rl = a.RowLast; 708 | int cf = a.ColumnFirst; 709 | int cl = a.ColumnLast; 710 | int rowsarg = rl - rf + 1; 711 | int colsarg = cl - cf + 1; 712 | if (rl == rf && cl == cf) 713 | { 714 | cellRef = new ExcelReference(rf, rf, cf, cf, a.SheetId); 715 | b = cellRef.GetValue(); 716 | if (b is ExcelEmpty) 717 | { 718 | throw new JuliaException("JuliaEmptyCell"); 719 | } 720 | if (b is ExcelError) 721 | { 722 | xlErrorPresent = true; 723 | err = (ExcelError)b; 724 | break; 725 | } 726 | jargs[i] = b; 727 | } 728 | else if (rl == rf && cl != cf) 729 | { 730 | object[] arg = new object[colsarg]; 731 | for (int n = 0; n < colsarg; n++) 732 | { 733 | cellRef = new ExcelReference(rf, rf, cf + n, cf + n, a.SheetId); 734 | b = cellRef.GetValue(); 735 | if (b is ExcelEmpty) 736 | { 737 | throw new JuliaException("JuliaEmptyCell"); 738 | } 739 | if (b is ExcelError) 740 | { 741 | xlErrorPresent = true; 742 | err = (ExcelError) b; 743 | break; 744 | } 745 | arg[n] = b; 746 | } 747 | jargs[i] = arg; 748 | } 749 | else if (rl != rf && cl == cf) 750 | { 751 | object[] arg = new object[rowsarg]; 752 | for (int m = 0; m < rowsarg; m++) 753 | { 754 | cellRef = new ExcelReference(rf + m, rf + m, cf, cf, a.SheetId); 755 | b = cellRef.GetValue(); 756 | if (b is ExcelDna.Integration.ExcelEmpty) 757 | { 758 | throw new JuliaException("JuliaEmptyCell"); 759 | } 760 | if (b is ExcelError) 761 | { 762 | xlErrorPresent = true; 763 | err = (ExcelError) b; 764 | break; 765 | } 766 | 767 | arg[m] = b; 768 | } 769 | if (xlErrorPresent == false) 770 | { 771 | jargs[i] = arg; 772 | } 773 | } 774 | else 775 | { 776 | object[,] arg = new object[rowsarg, colsarg]; 777 | for (int m = 0; m < rowsarg; m++) 778 | { 779 | for (int n = 0; n < colsarg; n++) 780 | { 781 | cellRef = new ExcelReference(rf + m, rf + m, cf + n, cf + n, a.SheetId); 782 | b = cellRef.GetValue(); 783 | if (b is ExcelEmpty) 784 | { 785 | throw new JuliaException("JuliaEmptyCell"); 786 | } 787 | if (b is ExcelError) 788 | { 789 | xlErrorPresent = true; 790 | err = (ExcelError) b; 791 | break; 792 | } 793 | arg[m, n] = b; 794 | } 795 | } 796 | if (xlErrorPresent == false) 797 | { 798 | jargs[i] = arg; 799 | } 800 | } 801 | } 802 | else 803 | { 804 | 805 | if (args[i] is ExcelEmpty) 806 | { 807 | throw new JuliaException("JuliaEmptyCell"); 808 | } 809 | if (args[i] is ExcelError) 810 | { 811 | xlErrorPresent = true; 812 | err = (ExcelError)args[i]; 813 | break; 814 | } 815 | 816 | jargs[i] = args[i]; 817 | } 818 | } 819 | 820 | if (xlErrorPresent == false) 821 | { 822 | retval = JuliaInXLutilities_global.jxlu.Utilities.CallJulia(f, jargs); 823 | } 824 | else 825 | { 826 | retval = new object[rows, cols]; 827 | for (int i = 0; i < rows; ++i) 828 | { 829 | for (int j = 0; j < cols; ++j) 830 | { 831 | retval[i, j] = err; 832 | } 833 | } 834 | } 835 | } 836 | else 837 | { 838 | object[] zargs = new object[1]; 839 | zargs[0] = null; 840 | retval = JuliaInXLutilities_global.jxlu.Utilities.CallJulia(f, zargs); 841 | } 842 | 843 | if (retval is object[]) 844 | { 845 | // Size of the data returned by Julia 846 | int M = ((object[])retval).GetUpperBound(0) + 1; 847 | 848 | // rows and cols are the size of the active cells in Excel 849 | ret = new object[rows, cols]; 850 | object r; 851 | for (int i = 0; i < rows; ++i) 852 | { 853 | for (int j = 0; j < cols; ++j) 854 | { 855 | int k = j * rows + i; 856 | r = retval[k]; 857 | if (k < M) 858 | { 859 | if (r is JValue) 860 | { 861 | Type t = ((JValue) r).Value.GetType(); 862 | if (numerictypes.Contains(t)) 863 | { 864 | ret[i, j] = (double) r; 865 | } 866 | else if (t == typeof(bool)) 867 | { 868 | ret[i, j] = (bool) r; 869 | } 870 | else if (t == typeof(string)) 871 | { 872 | ret[i, j] = (String) r; 873 | } 874 | else 875 | { 876 | ret[i, j] = r; 877 | } 878 | } 879 | else 880 | { 881 | ret[i, j] = r; 882 | } 883 | } 884 | else 885 | { 886 | ret[i, j] = ExcelError.ExcelErrorNA; 887 | } 888 | } 889 | } 890 | return ret; 891 | } 892 | else if (retval is object[,]) 893 | { 894 | // Size of the data returned by Julia 895 | int M = ((object[,])retval).GetUpperBound(0) + 1; 896 | int N = ((object[,])retval).GetUpperBound(1) + 1; 897 | 898 | ret = new object[rows, cols]; 899 | object r; 900 | for (int i = 0; i < rows; ++i) 901 | { 902 | for (int j = 0; j < cols; ++j) 903 | { 904 | if (i < M && j < N) 905 | { 906 | r = retval[i, j]; 907 | if (r is JValue) 908 | { 909 | Type t = ((JValue)r).Value.GetType(); 910 | if (numerictypes.Contains(t)) 911 | { 912 | ret[i, j] = (double) r; 913 | } 914 | else if (t == typeof(bool)) 915 | { 916 | ret[i, j] = (bool) r; 917 | } 918 | else if (t == typeof(string)) 919 | { 920 | ret[i, j] = (String) r; 921 | } 922 | else 923 | { 924 | ret[i, j] = r; 925 | } 926 | } 927 | else 928 | { 929 | ret[i, j] = r; 930 | } 931 | } 932 | else 933 | { 934 | ret[i, j] = ExcelError.ExcelErrorNA; 935 | } 936 | } 937 | } 938 | 939 | return ret; 940 | } 941 | else if (retval is JValue) 942 | { 943 | Type t = retval.Value.GetType(); 944 | if (numerictypes.Contains(t)) 945 | { 946 | ret = new object[rows, cols]; 947 | ret[0, 0] = (double) retval; 948 | return ret; 949 | } 950 | else if (t == typeof(bool)) 951 | { 952 | ret = new object[rows, cols]; 953 | ret[0, 0] = (bool) retval; 954 | return ret; 955 | } 956 | else if (t == typeof(string)) 957 | { 958 | ret = new object[rows, cols]; 959 | ret[0, 0] = (String) retval; 960 | return ret; 961 | } 962 | } 963 | return retval; 964 | } 965 | catch (JuliaException e) 966 | { 967 | throw e; 968 | } 969 | catch 970 | { 971 | retval = new object[rows, cols]; 972 | for (int i = 0; i < rows; ++i) 973 | { 974 | for (int j = 0; j < cols; ++j) 975 | { 976 | retval[i, j] = ExcelError.ExcelErrorValue; 977 | } 978 | } 979 | return retval; 980 | } 981 | } 982 | else 983 | { 984 | throw new JuliaException("JuliaNotConnected"); 985 | } 986 | } 987 | 988 | [ExcelFunction(ExplicitRegistration = true, IsMacroType = false)] 989 | public static object[,] jleval([ExcelArgument(Name = "argument", Description = "String of Julia expression to be executed by eval(parse(expr))", AllowReference = true)] string arg) 990 | { 991 | object[] args = { arg }; 992 | return jlcall("parse_and_eval", args); 993 | } 994 | 995 | [ExcelFunction(ExplicitRegistration = true, IsMacroType = false)] 996 | public static object[,] jlsetvar([ExcelArgument(Name = "variable", Description = "Name of a global variable to set in the Julia process", AllowReference = true)] object v, 997 | [ExcelArgument(Name = "arguments", Description = "One or more values to assign to this global variable", AllowReference = true)] params object[] args) 998 | { 999 | object[] a = new object[args.Length + 1]; 1000 | a[0] = v; 1001 | for (int i = 0; i< args.Length; ++i) 1002 | { 1003 | a[i + 1] = args[i]; 1004 | } 1005 | return jlcall("jlsetvar", a); 1006 | } 1007 | 1008 | } 1009 | 1010 | 1011 | 1012 | [ComVisible(true)] 1013 | public class JuliaRibbon : ExcelRibbon 1014 | { 1015 | 1016 | private IRibbonUI ribbon = null; 1017 | 1018 | private IRibbonUI Ribbon 1019 | { 1020 | get 1021 | { 1022 | return ribbon; 1023 | } 1024 | 1025 | set 1026 | { 1027 | ribbon = value; 1028 | } 1029 | } 1030 | 1031 | public void OnLoad_JuliaInXL(IRibbonUI ribbon) 1032 | { 1033 | this.ribbon = ribbon; 1034 | JuliaInXLutilities_global.jxlu.LaunchUtilities(); 1035 | ribbon.Invalidate(); 1036 | } 1037 | 1038 | public void OnButtonLaunchJulia_JuliaInXL(IRibbonControl control) 1039 | { 1040 | char[] delimiter = { ':' }; 1041 | string[] words = JuliaInXLutilities_global.jxlu.Utilities.Endpoint.Split(delimiter); 1042 | if (!words[1].Equals("//localhost")) 1043 | { 1044 | words[1] = "//localhost"; 1045 | JuliaInXLutilities_global.jxlu.Utilities.Endpoint = words[0] + ":" + words[1] + ":" + words[2]; 1046 | ribbon.Invalidate(); 1047 | } 1048 | 1049 | if (words[0].Equals("tcp") && words[1].Equals("//localhost")) 1050 | { 1051 | if (JuliaInXLutilities_global.jxlu.Utilities.JuliaProcess != null) 1052 | { 1053 | try 1054 | { 1055 | Process.GetProcessById(JuliaInXLutilities_global.jxlu.Utilities.JuliaProcess.Id); 1056 | JuliaInXLutilities_global.jxlu.Utilities.JuliaProcess.Kill(); 1057 | JuliaInXLutilities_global.jxlu.Utilities.JuliaProcess = null; 1058 | } 1059 | catch (Exception) 1060 | { 1061 | JuliaInXLutilities_global.jxlu.Utilities.JuliaProcess = null; 1062 | } 1063 | } 1064 | JuliaInXLutilities_global.jxlu.Utilities.JuliaProcess = JuliaInXLutilities_global.jxlu.Utilities.LaunchLocalJulia(); 1065 | } 1066 | 1067 | } 1068 | 1069 | public String GetEndpointText_JuliaInXL(IRibbonControl control) 1070 | { 1071 | return JuliaInXLutilities_global.jxlu.Utilities.Endpoint; 1072 | } 1073 | 1074 | public String GetJuliaFileText_JuliaInXL(IRibbonControl control) 1075 | { 1076 | return JuliaInXLutilities_global.jxlu.Utilities.JuliaFile; 1077 | } 1078 | 1079 | public void SetConnectionInfo_JuliaInXL(IRibbonControl control, string text) 1080 | { 1081 | //TODO: Determine proper string validation for an endpoint. 1082 | 1083 | char[] delimiter = { ':' }; 1084 | string[] words = text.Split(delimiter); 1085 | string portdigits = words[2]; 1086 | Regex rgx = new Regex("^\\d{" + portdigits.Length.ToString() + "}"); 1087 | if (words.Length == 3 && 1088 | words[0].Equals("tcp") && 1089 | words[1].Substring(0, 2).Equals("//") && 1090 | rgx.IsMatch(portdigits)) 1091 | { 1092 | JuliaInXLutilities_global.jxlu.Utilities.Endpoint = text; 1093 | JuliaInXLutilities_global.jxlu.Utilities.Reconnect(); 1094 | } 1095 | 1096 | MessageBox.Show("Current endpoint " + JuliaInXLutilities_global.jxlu.Utilities.Endpoint); 1097 | } 1098 | 1099 | public void SetJuliaFile_JuliaInXL(IRibbonControl control, string text) 1100 | { 1101 | JuliaInXLutilities_global.jxlu.Utilities.JuliaFile = text; 1102 | } 1103 | 1104 | public void OnButtonReconnect_JuliaInXL(IRibbonControl control) 1105 | { 1106 | JuliaInXLutilities_global.jxlu.Utilities.Reconnect(); 1107 | } 1108 | 1109 | public static void OnButtonTerminate_JuliaInXL() 1110 | { 1111 | JuliaInXLutilities_global.jxlu.Utilities.Terminate(); 1112 | } 1113 | 1114 | public void OnButtonSelectJuliaFile_JuliaInXL(IRibbonControl control) 1115 | { 1116 | string juliaVersion = JuliaInXLutilities_global.jxlu.Utilities.JuliaVersion; 1117 | string juliaProVersion = JuliaInXLutilities_global.jxlu.Utilities.JuliaProVersion; 1118 | string initialDirectory; 1119 | 1120 | if (AddInUtilities.registryValueExists("HKCU", "Software\\JuliaPro\\JuliaInXL", "Install_Dir") == true) 1121 | { 1122 | string juliaBaseDir = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\JuliaPro\JuliaInXL", "Install_Dir", ""); 1123 | initialDirectory = juliaBaseDir; 1124 | } 1125 | else if (AddInUtilities.registryValueExists("HKLM", "Software\\JuliaPro\\JuliaInXL", "Install_Dir") == true) 1126 | { 1127 | RegistryKey localMachineRegistry 1128 | = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, 1129 | Environment.Is64BitOperatingSystem 1130 | ? RegistryView.Registry64 1131 | : RegistryView.Registry32); 1132 | RegistryKey key = localMachineRegistry.OpenSubKey("Software\\JuliaPro\\JuliaInXL"); 1133 | string juliaBaseDir = (string)key.GetValue("Install_Dir"); 1134 | 1135 | initialDirectory = juliaBaseDir; 1136 | } 1137 | else 1138 | { 1139 | initialDirectory = Environment.GetEnvironmentVariable("HOMEDRIVE") + "\\" + Environment.GetEnvironmentVariable("HOMEPATH"); 1140 | } 1141 | 1142 | OpenFileDialog openFileDialog1 = new OpenFileDialog(); 1143 | 1144 | openFileDialog1.InitialDirectory = initialDirectory; 1145 | openFileDialog1.Filter = "Julia files (*.jl)|*.jl|All files (*.*)|*.*"; 1146 | openFileDialog1.FilterIndex = 0; 1147 | openFileDialog1.RestoreDirectory = true; 1148 | 1149 | if (openFileDialog1.ShowDialog() == DialogResult.OK) 1150 | { 1151 | JuliaInXLutilities_global.jxlu.Utilities.JuliaFile = openFileDialog1.FileName; 1152 | this.ribbon.InvalidateControl("JuliaSelectedFile"); 1153 | } 1154 | 1155 | } 1156 | 1157 | public void OnButtonIncludeJuliaFile_JuliaInXL(IRibbonControl control) 1158 | { 1159 | string file = (string)JuliaInXLutilities_global.jxlu.Utilities.JuliaFile.Clone(); 1160 | object[] arg = { file }; 1161 | if (System.IO.File.Exists(file) && JuliaInXLutilities_global.jxlu.Utilities.Connected == true) 1162 | { 1163 | JuliaInXLutilities_global.jxlu.Utilities.CallJulia("include", arg); 1164 | } 1165 | } 1166 | 1167 | public override string GetCustomUI(string RibbonID) 1168 | { 1169 | 1170 | string customUIXml = 1171 | @" 1172 | 1173 | 1174 | 1175 | 1176 |