Hi Dave,
That looks pretty cool!
You might now need to look at this Excel bug report:
http://social.msdn.microsoft.com/Forums/is/exceldev/thread/24353308-b4ba-48dc-8fa3-a27fcfa6aaac
It suggests there might be an issue when you are on manual
recalculation, and deleting a row or column, and have RTD formulae on
the sheet (which you now have behind the scenes). I'm not sure whether
it impacts on this RTD-based handle manager, or the way you'd use it -
any comments you might have would be helpful.
Regards,
Govert
On Oct 8, 6:02 am, DC <
da...@minnesotacarlson.com> wrote:
> ngm,
>
> Thanks for the feedback. I took your advice and added a parameter
> to pass a human readable "tag" to the register function. It still should
> stand out as a different result than a typical function while still being
> a little more useful than "N2342323". Since Govert's new push this
> week adds the "ExcelRtdSever" class I was able to remove a lot of
> what I had in my RTD class and also added an example below that
> creates an object handle asynchronously. I find often that when there
> is something that takes a while to run, that I often need to call multiple
> functions using the same result set ... hence the async/handle desire.
> I would think that this would be easy enough to extend the method to
> cache SQL or web service results.
>
> Hopefully someone knows enough of what is going on behind the
> scenes to tell me if there is some "gotcha" that I am missing. Any
> feedback on thread/memory issues that I might be overlooking would
> be great. I am by no means a programmer or tester, but when I tried
> this on some larger/slower algorithms and while holding down Ctrl+Alt+F9
> I saw my CPU jump to 100%, but no jump on threads or RAM.
>
> *Revised Object Handler*
>
> namespace ExcelDna.FsObjectHandleropen Systemopen System.Textopen System.Collections.Genericopen ExcelDna.Integrationopen ExcelDna.Integration.Rtd
> type public ExcelInterface() =
> interface IExcelAddIn with
> member x.AutoOpen() = ExcelAsyncUtil.Initialize()
> member x.AutoClose() = ExcelAsyncUtil.Uninitialize()
> /// Excel object handler / cache / async wrappermodule XlCache =
> let private _objects = new Dictionary<string,obj>()
> let private _tags = new Dictionary<string, int>()
> let lookup handle =
> match _objects.ContainsKey(handle) with
> |true -> _objects.[handle]
> |false -> failwith "object not found"
> let register tag (o:obj) =
> let counter =
> match _tags.ContainsKey(tag) with
> |true -> _tags.[tag] + 1
> |false -> 1
> _tags.[tag] <- counter
> let handle =
> new StringBuilder()
> |> fun s -> s.AppendFormat("[{0}.{1}]", tag, string counter).ToString()
> _objects.[handle] <- o
> handle
> let unregister handle =
> if _objects.ContainsKey(handle) then
> _objects.Remove(handle) |> ignore
> let asyncRun myName myFunction myInputs =
> ExcelAsyncUtil.Run(
> myName,
> myInputs,
> fun () -> (myFunction myInputs) |> box)
> |> unbox
> /// Excel RTD server to handle the registering/unregistering of object handlestype public StaticRTD() =
> inherit ExcelRtdServer()
> let _topics = new Dictionary<ExcelRtdServer.Topic, string>()
> override x.ConnectData(topic:ExcelRtdServer.Topic, topicInfo:IList<string>, newValues:bool byref) =
> let name = topicInfo.Item(0)
> _topics.[topic] <- name
> name |> box
> override x.DisconnectData(topic:ExcelRtdServer.Topic) =
> _topics.[topic] |> XlCache.unregister
> _topics.Remove(topic) |> ignore
>
> *Revised Example*
>
> namespace ExcelDna.FsObjectHandleropen ExcelDna.Integrationopen ExcelDna.Integration.Rtdopen System.Threading
> module FsObjectHandlerExample =
>
> // class example
>
> // class we want to create an object handle for
> type Person(firstName:string, lastName:string) =
> let _first = firstName
> let _last = lastName
> member x.FirstName with get() = _first
> member x.LastName with get() = _last
>
> // function to create object and pass it's handle to excel
> [<ExcelFunction(Name="Person.create")>]
> let createPerson (firstName, lastName) =
> new Person(firstName, lastName)
> |> XlCache.register "Person"
> |> fun handle -> XlCall.RTD("ExcelDna.FsObjectHandler.StaticRTD", null, handle)
>
> // function that uses the object handle
> [<ExcelFunction(Name="Person.getFirstName")>]
> let getPersonFirstName personHandle =
> personHandle
> |> XlCache.lookup :?> Person
> |> fun person -> person.FirstName
> // slow function example
>
> // slow running function
> let myArray (n,a) =
> Thread.Sleep 2000
> [| 0 .. n |]
> |> Array.map ((+) a)
>
> // creates object asynchronously and returns handle when done
> [<ExcelFunction(Name="MyArray.create")>]
> let createMyArray (n:int,a:int) =
> // need to restate function to accept object array input
> let f (p:obj[]) = myArray (p.[0] :?> int, p.[1] :?> int)
> [| box n; box a |]
> |> XlCache.asyncRun "MyArray" f
> |> fun result ->
> if result = box ExcelError.ExcelErrorNA then
> "hold on"
> else
> result
> |> XlCache.register "MyArray"
> |> fun handle ->
> XlCall.RTD("ExcelDna.FsObjectHandler.StaticRTD", null, handle)
> |> string
>
> [<ExcelFunction(Name="MyArray.getSum")>]
> let getMyArraySum myArrayHandle =
> myArrayHandle
> |> XlCache.lookup :?> int[]
> |> Array.sum