F# Excel Interop: Marshal.GetActiveObject("Excel.Application") does not work - f#

I am trying to use F# to automate some Excel tasks. I actually got two issues:
1. If I open a workbook using an Excel.Application instance, I would miss all the add-ins that would have been automatically loaded if I had just opened the workbook in Excel. So I try to open the workbook in Excel first, with all add-ins loaded, and then hand the instance to F#. Then I got the second issue:
2. The Marshal.GetActiveObject("Excel.Application") actually open a new instance, instead of getting the existing instance. The code is below:
#r "Microsoft.Office.Interop.Excel"
#r "office"
open Microsoft.Office.Interop
open System
open System.Runtime.InteropServices
let app = Marshal.GetActiveObject("Excel.Application")
let app1 = app :?> Excel.Application
let wb = app1.ActiveWorkbook
let visible = app1.Visible
let n = app1.Workbooks.Count
and the output is:
val app : obj
val app1 : Excel.Application
val wb : Excel.Workbook = null
val visible : bool = false
val n : int = 0
But I am very sure I have an Excel instance running and it is visible. Thanks!

I have double checked my code and this is the way to use Excel from F# script (use Excel-DNA for other cases). However, very often (especially when using add-ins) Excel does not close properly and some semi-dead Excel processes hang in the task manager until they are manually killed. Probably you are getting a wrong process, not the one where you open your workbook.

Related

#load a package in F# interactive (FSharpChart.fsx)

Hi i'm a noob and asking this newbie question, please forgive me.
I've installed successfully FSharpChart in my local directory
...
Added package 'MSDN.FSharpChart.dll.0.60.0' to folder 'C:\Users\Fagui\Documents\GitHub\Learning Fsharp\Expert in F\packages'
Added package 'MSDN.FSharpChart.dll.0.60.0' to 'packages.config'
Successfully installed 'MSDN.FSharpChart.dll 0.60.0' to Expert in F
now, if i do
#load "FSharpChart.fsx";;
^^^^^^^^^^^^^^^^^^^^^^^
stdin(4,1): error FS0078: Unable to find the file 'FSharpChart.fsx' in any of
C:\Users\Fagui\AppData\Local\Temp
additional info:
inside this folder, i see a nupkg file, and a lib directory
Inside the lib directory, there is a dll, and a pdf file,
but i don't see any .fsx file.
basically, F# has installed the package in the active folder for the current project, and F#interactive is in another folder ?? bit strange ?
should i install another time the package ? or what is the way around it ?
thanks
UPDATE:
i don't know why, but apparently when i installed the FSharpChart package, I only got the dll, no fsx file
i managed to load it doing
#I #"C:\Users\Fagui\Documents\GitHub\Learning Fsharp\Expert in F"
#r #"packages\MSDN.FSharpChart.dll.0.60\lib\MSDN.FSharpChart.dll";;
unfortunately, typing the script in F# interactive
open MSDN.FSharp.Charting
let rnd = System.Random()
let rand() = rnd.NextDouble()
let randomPoints = [for i in 0 .. 1000 -> 10.0 * rand(), 10.0 * rand()]
randomPoints |> FSharpChart.Point;;
doesn't yield any chart, but just returns a list
val rnd : Random
val rand : unit -> float
val randomPoints : (float * float) list =
[(9.765916457, 2.272289941); (0.8211438594, 1.625466995);
...
(7.783786034, 7.572208311); (6.497914692, 3.66987128); ...]
val it : ChartTypes.PointChart
this may be due to the fact that the library is not supported anymore, and that i should use a newer library like Thomas Petricek indicated.
So, i did manage to install FSharp.Charting instead
let rnd = System.Random()
let rand() = rnd.NextDouble()
let randomPoints = [for i in 0 .. 1000 -> 10.0 * rand(), 10.0 * rand()]
randomPoints |> Chart.Point;;
and it did work
There is a newer version of the FSharpChart.fsx library which is called F# Charting, so first of all, I would recommend using this newer library instead (the API is quite similar, but F# Charting has a number of improvements).
The documentation for F# Charting also has a detailed page on referencing the library.
Typically, when you reference the library using NuGet, you'll need to specify relative reference:
// On Mac OSX use packages/FSharp.Charting.Gtk.0.90.13/FSharp.Charting.Gtk.fsx
#load "packages/FSharp.Charting.0.90.13/FSharp.Charting.fsx"
Where 0.90.13 is the version of the library that you got from NuGet (you may need to check the folder name - the path references in #load are relative to the place where your script lives).
Yes, F# Interactive is independent of the current project.
Use:
#load #"C:\Users\Fagui\Documents\GitHub\Learning Fsharp\Expert in F\packages\FSharpChart.fsx";;
Also you can use the #I directive if you need to reference assemblies of a specific folder, see the reference.

Hyperlinks without file://... with openpyxl to open a ws from the same wb?

I want open ws2 inside the same libreoffice wb with a hyperlink in a cell on ws1.
My code is:
wb["test"].cell(row = 1, column = 2).hyperlink = '#%s' % "test2"
This works, but openpyxl adds file://... to the link and a new instance of libreoffice starts (and auto close) with every click on my hyperlink.
If i add manually a hyperlink to libreoffice calc no file:// is added and no other instances of libreoffice starts.
What target_modes are supported?
I believe target_mode = "External is hard coded inside the openpyxl cell hyperlink attr. wb.cell()._hyperlink_rel.target_mode = "External
It's fair to say that support for hyperlinks isn't perfect. However, starting with version 2.3 there is at least full support for the type. The relevant code for serialising could easily be adapted to reflect this.

How to implement a read table function in F#? (like Vlookup)

I need a function.
When I enter 0~15 and Protection, it'll return 45%.
Just like Vlookup function in Excel.
Is there a function like this one in F#?
(At website try F#, Learn -> Financial Modeling -> Using the Yahoo Finance Type Provider
It recommended us to use Samples.Csv.dll. However, I failed to install it and don't want to install that package just for a function :(.. )
I followed the tutorial (http://fsharp.github.io/FSharp.Data/library/CsvProvider.html)
and tried to run the program on my computer. But I am in trouble now
It couldn't identify the type CsvProvider (So I can't use the function Stocks.Load.)
What's the problem..?
This is how the code looks when using the CSV type provider in F# Data. To get this to work, you'll need to add reference to FSharp.Data.dll. The best way to do this is to install the package from NuGet. In Visual Studio, it will add reference for you, and in command line you can say:
nuget install FSharp.Data
Alternatively, if you are in an F# script file, then you need to install the nuget package and then add #r #"C:\path\to\FSharp.Data.dll". Then you can write the following:
open FSharp.Data
// Generate type based on a local copy with sample data
type Data = CsvProvider<"sample.csv">
// Load actual data from a file (this can be a different file with the same structure)
let loaded = Data.Load("runtime/file/name.csv")
// Find row for a specified age range & look at the properties
let row = loaded.Data |> Seq.find (fun r -> r.Age = "0~15")
row.Protection
row.Saving
row.Specified
A very simple way to do this is with a DataTable:
open System.Data
open System.IO
open LumenWorks.Framework.IO.Csv
let vlookup =
let table = new DataTable()
do
use streamReader = new StreamReader(#"C:\data.csv")
use csvReader = new CsvReader(streamReader, hasHeaders=true)
table.Load(csvReader)
table.PrimaryKey <- [|table.Columns.["Age"]|]
fun age (column: string) -> table.Rows.Find([|age|]).[column]
//Usage
vlookup "0~15" "Protection" |> printfn "%A"
There's no lack of CSV readers out there. I used this especially fast one (also available on NuGet).

F# Module/Namespace Error

My first program with F#.
I have one file like so:
namespace LanguageMapper.Data
#if INTERACTIVE
#r "System.Data"
#r "System.Data.Linq"
#r "FSharp.Data.TypeProviders"
#endif
open System.Data
open System.Data.Linq
open Microsoft.FSharp.Data.TypeProviders
module Data =
// You can use Server Explorer to build your ConnectionString.
type SqlConnection = Microsoft.FSharp.Data.TypeProviders.SqlDataConnection<ConnectionString = #"connstring">
let db = SqlConnection.GetDataContext()
Then i have another file like so
namespace LanguageMapper.Program
open Data
module Program =
[<EntryPoint>]
let main argv =
let getLocale x =
match x with
| [|"live"|] -> "live"
| [|"dev"|] -> "dev"
| _ -> "local"
Over top of the open Data i get a red squiggly in VS telling me:
"Error 1 This declaration opens the namespace or module
'Microsoft.FSharp.Data' through a partially qualified path. Adjust
this code to use the full path of the namespace. This change will make
your code more robust as new constructs are added to the F# and CLI
libraries."
What am i doing wrong? I just want to reference one file from the other.
You need to open the module using its fully qualified name, that is including its namespace. So in LanguageMapper.Program you need to open LanguageMapper.Data.Data (only the last bit is the module name).
The Compiler is complaining on your open definition because it only specifies to open a namespace or module named Data - and it finds one in Microsoft.FSharp.Data, probably because there are some 'automatic' opens for the Microsoft.FSharp namespaces.

control file structure of a zip archive in FSharp

the following is a code sample that takes a list of file names and zips them into a single archive. The problem I'm having is that I'd like for the file described by filname be in the top level of the zip archive (i.e. when the archive is opened, "clientName....xml" is the first thing you see, instead of the folder "XML").
let filename = sprintf "C:\\XML\\ClientName_%s.xml" (System.DateTime.Now.ToString("ddMMyyyy"))
use fs = new FileStream(filename, FileMode.Create)
let xmlSerializer = XmlSerializer(typeof<log>)
xmlSerializer.Serialize(fs,logObj)
fs.Close()
use zipfile = new ZipFile()
let basePath = path.Replace("/", "\\")
for fileObj in files do
let relativeFilePath = basePath + (fileObj.Filename).Replace("/", "\\")
printfn "%s" relativeFilePath
zipfile.AddFile(relativeFilePath) |> ignore
()
zipfile.AddFile(filename) |> ignore
let zipFileName = sprintf "C:\\XML\\Compliance_%s.zip" (System.DateTime.Now.ToString("ddMMyyyy"))
zipfile.Save(zipFileName)
Where does the ZipFile type come from? I don't think this is a standard .NET class... I tried searching and found this library http://dotnetzip.codeplex.com/ which has a class matching to your sample :-)
The mentioned library also has AddFile overload that takes two string - the source file name and a relative file name in the ZIP file. This seems exactly like what you're looking for. I guess the call would be something like zipfile.AddFile(absolutePath, "/")...

Resources