OLE automation: Difference between revisions

From Rosetta Code
Content added Content deleted
No edit summary
m (→‎{{header|Wren}}: Changed to Wren S/H)
 
(8 intermediate revisions by 6 users not shown)
Line 1: Line 1:
{{task|OLE Automation Client and Server}}
{{task|OLE Automation Client and Server}}
[http://en.wikipedia.org/wiki/OLE_Automation OLE Automation] is an inter-process communication mechanism based on [http://en.wikipedia.org/wiki/Component_Object_Model Component Object Model] (COM) on Microsoft Windows.
[http://en.wikipedia.org/wiki/OLE_Automation OLE Automation]   is an inter-process communication mechanism based on   [http://en.wikipedia.org/wiki/Component_Object_Model Component Object Model]   (COM) on Microsoft Windows.



Provide an automation server implementing objects that can be accessed by a client running in a separate process. The client gets a proxy-object that can call methods on the object.
;Task:
Provide an automation server implementing objects that can be accessed by a client running in a separate process.

The client gets a proxy-object that can call methods on the object.
The communication should be able to handle conversions of [http://en.wikipedia.org/wiki/Variant_type variants] to and from the native value types.
The communication should be able to handle conversions of [http://en.wikipedia.org/wiki/Variant_type variants] to and from the native value types.
<br><br>


=={{header|AutoHotkey}}==
=={{header|AutoHotkey}}==
Line 9: Line 15:


client: using the ahk ole server as well as the python ole server implemented below
client: using the ahk ole server as well as the python ole server implemented below
<lang autohotkey>ahk := comobjactive("ahkdemo.ahk")
<syntaxhighlight lang="autohotkey">ahk := comobjactive("ahkdemo.ahk")
ahk.hello("hello world")
ahk.hello("hello world")
py := ComObjActive("python.server")
py := ComObjActive("python.server")
py.write("hello")
py.write("hello")
return</lang>
return</syntaxhighlight>
server:
server:
<lang autohotkey>#Persistent
<syntaxhighlight lang="autohotkey">#Persistent
CLSID_ThisScript := "{38A3EB13-D0C4-478b-9720-4D0B2D361DB9}"
CLSID_ThisScript := "{38A3EB13-D0C4-478b-9720-4D0B2D361DB9}"
APPID_ThisScript := "ahkdemo.ahk"
APPID_ThisScript := "ahkdemo.ahk"
Line 70: Line 76:
#include lib\ComDispTable.ahk
#include lib\ComDispTable.ahk
#include lib\ComDispatch.ahk
#include lib\ComDispatch.ahk
#include lib\ComVar.ahk</lang>
#include lib\ComVar.ahk</syntaxhighlight>

=={{header|Go}}==
{{libheader|Go OLE}}
{{works with|Windows 10}}
<br>
This uses OLE automation to create a new Microsoft Word document, write some text to it and after 10 seconds close the document without saving and quit Word.
<syntaxhighlight lang="go">package main

import (
"time"
ole "github.com/go-ole/go-ole"
"github.com/go-ole/go-ole/oleutil"
)

func main() {
ole.CoInitialize(0)
unknown, _ := oleutil.CreateObject("Word.Application")
word, _ := unknown.QueryInterface(ole.IID_IDispatch)
oleutil.PutProperty(word, "Visible", true)
documents := oleutil.MustGetProperty(word, "Documents").ToIDispatch()
document := oleutil.MustCallMethod(documents, "Add").ToIDispatch()
content := oleutil.MustGetProperty(document, "Content").ToIDispatch()
paragraphs := oleutil.MustGetProperty(content, "Paragraphs").ToIDispatch()
paragraph := oleutil.MustCallMethod(paragraphs, "Add").ToIDispatch()
rnge := oleutil.MustGetProperty(paragraph, "Range").ToIDispatch()
oleutil.PutProperty(rnge, "Text", "This is a Rosetta Code test document.")

time.Sleep(10 * time.Second)

oleutil.PutProperty(document, "Saved", true)
oleutil.CallMethod(document, "Close", false)
oleutil.CallMethod(word, "Quit")
word.Release()

ole.CoUninitialize()
}</syntaxhighlight>


=={{header|M2000 Interpreter}}==
=={{header|M2000 Interpreter}}==
M2000 Interpreter is an ActiveX dll (a COM object) so can be start from any language which can handle com objects. Also M2000 can handle other Com Objects, like MS Word. We have to Declare the object (in a Windows OS), form ready made objects in registry, which we have permissions to handle.

We can use Events, from declared objects and from objects that we get as result from methods, using WithEvents.
We can use Events, from declared objects and from objects that we get as result from methods, using WithEvents.
<syntaxhighlight lang="m2000 interpreter">
<lang M2000 Interpreter>
Module CheckAutomation {
Module CheckAutomation {
ExitNow=false
ExitNow=false
Line 106: Line 150:
}
}
CheckAutomation
CheckAutomation
</syntaxhighlight>
</lang>

=={{header|Phix}}==
Some Microsft Excel OLE automation routines can be found [http://phix.x10.mx/pmwiki/pmwiki.php?n=Main.ManageExcelFiles here] but since I don't have Excel installed I have not tested them.<br>
There are also some older (also untested) routines [http://phix.x10.mx/pmwiki/pmwiki.php?n=Main.CodeScrapsForMsExcelDocuments here].


=={{header|Python}}==
=={{header|Python}}==
Line 112: Line 160:
Server uses a client of the ahk server above to register the clsid in the windows registry.
Server uses a client of the ahk server above to register the clsid in the windows registry.
Translated from <tt>win32com/test/testDynamic.py</tt>
Translated from <tt>win32com/test/testDynamic.py</tt>
<lang python>#!/usr/bin/env python
<syntaxhighlight lang="python">#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
import win32com.client
import win32com.client
Line 167: Line 215:
# autohotkey.exe ahkside.ahk
# autohotkey.exe ahkside.ahk
# python /c/Python26/Scripts/ipython.py -wthread -i pythonside.py
# python /c/Python26/Scripts/ipython.py -wthread -i pythonside.py
# must use -wthread otherwise calling com client hangs</lang>
# must use -wthread otherwise calling com client hangs</syntaxhighlight>
client
client
<lang Python>import win32com.client
<syntaxhighlight lang="python">import win32com.client
client = win32com.client.Dispatch("python.server")
client = win32com.client.Dispatch("python.server")
client.write("hello world")</lang>
client.write("hello world")</syntaxhighlight>

=={{header|Wren}}==
{{trans|Go}}
{{libheader|WrenGo}}
{{libheader|Go OLE}}
{{works with|Windows 10}}
An embedded application with a Go host so we can use the Go OLE library.
<syntaxhighlight lang="wren">/* OLE_automation.wren */

class Ole {
foreign static coInitialize(p)
foreign static coUninitialize()
}

class OleUtil {
static createObject(programID) {
return IUnknown.new(programID)
}

foreign static putProperty(disp, name, param)
foreign static mustGetProperty(disp, name)
foreign static mustCallMethod(disp, name)
foreign static mustCallMethod2(disp, name, param)
}

foreign class GUID {
construct new(guid) {}
}

var IID_DISPATCH = GUID.new("{00020400-0000-0000-C000-000000000046}")

foreign class IUnknown {
construct new(programID) {}

foreign queryInterface(iid, name)
foreign static release(name)
}

class Time {
foreign static sleep(secs)
}

Ole.coInitialize(0)
var unknown = OleUtil.createObject("Word.application")
var word = unknown.queryInterface(IID_DISPATCH, "word")
OleUtil.putProperty(word, "Visible", true)
var documents = OleUtil.mustGetProperty(word, "Documents")
var document = OleUtil.mustCallMethod(documents, "Add")
var content = OleUtil.mustGetProperty(document, "Content")
var paragraphs = OleUtil.mustGetProperty(content, "Paragraphs")
var paragraph = OleUtil.mustCallMethod(paragraphs, "Add")
var range = OleUtil.mustGetProperty(paragraph, "Range")

OleUtil.putProperty(range, "Text", "This is a Rosetta Code test document.")

Time.sleep(10)

OleUtil.putProperty(document, "Saved", true)
OleUtil.mustCallMethod2(document, "Close", false)
OleUtil.mustCallMethod(word, "Quit")
IUnknown.release(word)

Ole.coUninitialize()</syntaxhighlight>
<br>
We now embed this script in the following Go program and run it.
<syntaxhighlight lang="go">/* go run OLE_automation.go */

package main

import (
ole "github.com/go-ole/go-ole"
"github.com/go-ole/go-ole/oleutil"
"strings"
"time"
wren "github.com/crazyinfin8/WrenGo"
)

type any = interface{}

var dispMap = make(map[string]*ole.IDispatch)

func coInitialize(vm *wren.VM, parameters []any) (any, error) {
p := uintptr(parameters[1].(float64))
ole.CoInitialize(p)
return nil, nil
}

func coUninitialize(vm *wren.VM, parameters []any) (any, error) {
ole.CoUninitialize()
return nil, nil
}

func putProperty(vm *wren.VM, parameters []any) (any, error) {
disp := dispMap[parameters[1].(string)]
propName := parameters[2].(string)
param := parameters[3]
oleutil.PutProperty(disp, propName, param)
return nil, nil
}

func mustGetProperty(vm *wren.VM, parameters []any) (any, error) {
disp := dispMap[parameters[1].(string)]
propName := parameters[2].(string)
disp2 := oleutil.MustGetProperty(disp, propName).ToIDispatch()
disp2Name := strings.ToLower(propName)
dispMap[disp2Name] = disp2
return disp2Name, nil
}

func mustCallMethod(vm *wren.VM, parameters []any) (any, error) {
dispName := parameters[1].(string)
disp := dispMap[dispName]
methName := parameters[2].(string)
disp2 := oleutil.MustCallMethod(disp, methName).ToIDispatch()
disp2Name := dispName[0:len(dispName)-1]
dispMap[disp2Name] = disp2
return disp2Name, nil
}

func mustCallMethod2(vm *wren.VM, parameters []any) (any, error) {
dispName := parameters[1].(string)
disp := dispMap[dispName]
methName := parameters[2].(string)
param := parameters[3]
disp2 := oleutil.MustCallMethod(disp, methName, param).ToIDispatch()
disp2Name := dispName[0:len(dispName)-1]
dispMap[disp2Name] = disp2
return disp2Name, nil
}

func newGUID(vm *wren.VM, parameters []any) (any, error) {
param := parameters[1].(string)
guid := ole.NewGUID(param)
return &guid, nil
}

func newIUnknown(vm *wren.VM, parameters []any) (any, error) {
programID := parameters[1].(string)
unknown, _ := oleutil.CreateObject(programID)
return &unknown, nil
}

func queryInterface(vm *wren.VM, parameters []any) (any, error) {
handle := parameters[0].(*wren.ForeignHandle)
ifc, _ := handle.Get()
unknown := ifc.(**ole.IUnknown)
handle2 := parameters[1].(*wren.ForeignHandle)
ifc2, _ := handle2.Get()
guid := ifc2.(**ole.GUID)
disp, _ := (*unknown).QueryInterface(*guid)
name := parameters[2].(string)
dispMap[name] = disp
return name, nil
}

func release(vm *wren.VM, parameters []any) (any, error) {
unknown := dispMap[parameters[1].(string)]
unknown.Release()
return nil, nil
}

func sleep(vm *wren.VM, parameters []any) (any, error) {
secs := time.Duration(parameters[1].(float64))
time.Sleep(secs * time.Second)
return nil, nil
}

func main() {
vm := wren.NewVM()
fileName := "OLE_automation.wren"

oleMethodMap := wren.MethodMap {
"static coInitialize(_)" : coInitialize,
"static coUninitialize()": coUninitialize,
}

oleUtilMethodMap := wren.MethodMap {
"static putProperty(_,_,_)" : putProperty,
"static mustGetProperty(_,_)" : mustGetProperty,
"static mustCallMethod(_,_)" : mustCallMethod,
"static mustCallMethod2(_,_,_)": mustCallMethod2,
}

iUnknownMethodMap := wren.MethodMap {
"queryInterface(_,_)": queryInterface,
"static release(_)" : release,
}

timeMethodMap := wren.MethodMap {
"static sleep(_)": sleep,
}

classMap := wren.ClassMap {
"Ole" : wren.NewClass(nil, nil, oleMethodMap),
"OleUtil" : wren.NewClass(nil, nil, oleUtilMethodMap),
"GUID" : wren.NewClass(newGUID, nil, nil),
"IUnknown" : wren.NewClass(newIUnknown, nil, iUnknownMethodMap),
"Time" : wren.NewClass(nil, nil, timeMethodMap),
}

module := wren.NewModule(classMap)
vm.SetModule(fileName, module)
vm.InterpretFile(fileName)
vm.Free()
}</syntaxhighlight>


{{omit from|Ada|Too involved to implement from scratch, and OS specific}}
{{omit from|Ada|Too involved to implement from scratch, and OS specific}}
Line 189: Line 442:
{{omit from|PicoLisp}}
{{omit from|PicoLisp}}
{{omit from|Retro}}
{{omit from|Retro}}
{{omit from|SQL PL|It does not handle COM}}
{{omit from|TPP}}
{{omit from|TPP}}
{{omit from|UNIX Shell}}<!--Wrong OS! -->
{{omit from|UNIX Shell}}<!--Wrong OS! -->

Latest revision as of 09:30, 8 January 2024

Task
OLE automation
You are encouraged to solve this task according to the task description, using any language you may know.

OLE Automation   is an inter-process communication mechanism based on   Component Object Model   (COM) on Microsoft Windows.


Task

Provide an automation server implementing objects that can be accessed by a client running in a separate process.

The client gets a proxy-object that can call methods on the object.

The communication should be able to handle conversions of variants to and from the native value types.

AutoHotkey

Library: ComDispatch

by fincs:discussion

client: using the ahk ole server as well as the python ole server implemented below

ahk := comobjactive("ahkdemo.ahk")
ahk.hello("hello world")
py := ComObjActive("python.server")
py.write("hello")
return

server:

#Persistent
CLSID_ThisScript := "{38A3EB13-D0C4-478b-9720-4D0B2D361DB9}"
APPID_ThisScript := "ahkdemo.ahk"
funcs := ["aRegisterIDs", "aGetObject", "aCallFunc", "hello"]
server := ahkComServer(CLSID_ThisScript, APPID_ThisScript, funcs)   
return

aRegisterIDs(this, CLSID, APPID){
RegisterIDs(CLSID, APPID)
}

hello(this, message){
msgbox % message
}
aGetObject(this, name){
global
return %name%
}

aCallFunc(this, func, args){
return %func%(args)
}

;; ahkcomserver()
ahkComServer(CLSID_ThisScript, APPID_ThisScript, funcs)
{
global serverReady
server := object()
 ; CLSID_ThisScript := "{38A3EB13-D0C4-478b-9720-4D0B2D361DB9}"
 ; APPID_ThisScript := "Garglet.QueryServer"
  
  RegisterIDs(CLSID_ThisScript, APPID_ThisScript)
for i, func in funcs
{
str .= func . ", "
}
str := SubStr(str, 1, strlen(str) - 2)

  myObj := ComDispatch("", str)
; Expose it
  if !(hRemote := ComRemote(myObj, CLSID_ThisScript))
  {
    MsgBox, 16, %A_ScriptName%, Can't remote the object!
    ExitApp
  }
server.CLSID := CLSID_ThisScript
server.APPID := APPID_ThisScript
server.hRemote := hRemote
serverReady := 1
  return server
}

#Include ComRemote.ahk
#include lib\ComDispTable.ahk
#include lib\ComDispatch.ahk
#include lib\ComVar.ahk

Go

Library: Go OLE
Works with: Windows 10


This uses OLE automation to create a new Microsoft Word document, write some text to it and after 10 seconds close the document without saving and quit Word.

package main

import (
    "time"
    ole "github.com/go-ole/go-ole"
    "github.com/go-ole/go-ole/oleutil"
)

func main() {
    ole.CoInitialize(0)
    unknown, _ := oleutil.CreateObject("Word.Application")
    word, _ := unknown.QueryInterface(ole.IID_IDispatch)
    oleutil.PutProperty(word, "Visible", true)
    documents := oleutil.MustGetProperty(word, "Documents").ToIDispatch()
    document := oleutil.MustCallMethod(documents, "Add").ToIDispatch()
    content := oleutil.MustGetProperty(document, "Content").ToIDispatch()
    paragraphs := oleutil.MustGetProperty(content, "Paragraphs").ToIDispatch()
    paragraph := oleutil.MustCallMethod(paragraphs, "Add").ToIDispatch()
    rnge := oleutil.MustGetProperty(paragraph, "Range").ToIDispatch()
    oleutil.PutProperty(rnge, "Text", "This is a Rosetta Code test document.")

    time.Sleep(10 * time.Second)

    oleutil.PutProperty(document, "Saved", true)
    oleutil.CallMethod(document, "Close", false)
    oleutil.CallMethod(word, "Quit")
    word.Release()

    ole.CoUninitialize()
}

M2000 Interpreter

M2000 Interpreter is an ActiveX dll (a COM object) so can be start from any language which can handle com objects. Also M2000 can handle other Com Objects, like MS Word. We have to Declare the object (in a Windows OS), form ready made objects in registry, which we have permissions to handle.

We can use Events, from declared objects and from objects that we get as result from methods, using WithEvents.

Module CheckAutomation {
      ExitNow=false
      Declare WithEvents Alfa "WORD.APPLICATION"
      \\ minimize console
      Title "Minimized- Waiting", 0
      Wait 300
      Print "ok"
      With Alfa, "Visible", True
      Function ALFA_QUIT {
                  Print "Why you close Word?"
                  ExitNow=True
      }
      M=0
      Every 20 {
            If ExitNow then exit
            M++
            If M>500 then exit
      }
      Try {
            Method Alfa, "QUIT"
      }
      Declare Alfa Nothing
      if ExitNow then {
            Print format$("Finish  {0:2} sec", M/1000)
      } Else {
            Print "Close Word manually"
      }
      \\ show again console
      Title "ok"
}
CheckAutomation

Phix

Some Microsft Excel OLE automation routines can be found here but since I don't have Excel installed I have not tested them.
There are also some older (also untested) routines here.

Python

Library: pywin32

Server uses a client of the ahk server above to register the clsid in the windows registry. Translated from win32com/test/testDynamic.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import win32com.client
from win32com.server.util import wrap, unwrap
from win32com.server.dispatcher import DefaultDebugDispatcher
from ctypes import *
import commands
import pythoncom
import winerror
from win32com.server.exception import Exception

clsid = "{55C2F76F-5136-4614-A397-12214CC011E5}"
iid = pythoncom.MakeIID(clsid)
appid = "python.server"

class VeryPermissive:
    def __init__(self):
        self.data = []
        self.handle = 0
        self.dobjects = {}        
    def __del__(self):
        pythoncom.RevokeActiveObject(self.handle)
    def _dynamic_(self, name, lcid, wFlags, args):
        if wFlags & pythoncom.DISPATCH_METHOD:
            return getattr(self,name)(*args)
        if wFlags & pythoncom.DISPATCH_PROPERTYGET:
            try:
                # to avoid problems with byref param handling, tuple results are converted to lists.
                ret = self.__dict__[name]
                if type(ret)==type(()):
                    ret = list(ret)
                return ret
            except KeyError: # Probably a method request.
                raise Exception(scode=winerror.DISP_E_MEMBERNOTFOUND)
        if wFlags & (pythoncom.DISPATCH_PROPERTYPUT | pythoncom.DISPATCH_PROPERTYPUTREF):
            setattr(self, name, args[0])
            return
        raise Exception(scode=winerror.E_INVALIDARG, desc="invalid wFlags")
    def write(self, x):
        print x
        return 0
import win32com.server.util, win32com.server.policy
child = VeryPermissive()
ob = win32com.server.util.wrap(child, usePolicy=win32com.server.policy.DynamicPolicy)
try:
    handle = pythoncom.RegisterActiveObject(ob, iid, 0)
except pythoncom.com_error, details:
    print "Warning - could not register the object in the ROT:", details
    handle = None    
child.handle = handle  
          
ahk = win32com.client.Dispatch("ahkdemo.ahk")
ahk.aRegisterIDs(clsid, appid)
# autohotkey.exe ahkside.ahk
# python /c/Python26/Scripts/ipython.py -wthread -i pythonside.py
# must use -wthread otherwise calling com client hangs

client

import win32com.client
client = win32com.client.Dispatch("python.server")
client.write("hello world")

Wren

Translation of: Go
Library: WrenGo
Library: Go OLE
Works with: Windows 10

An embedded application with a Go host so we can use the Go OLE library.

/* OLE_automation.wren */

class Ole {
    foreign static coInitialize(p)
    foreign static coUninitialize()
}

class OleUtil {
    static createObject(programID) {
        return IUnknown.new(programID)
    }

    foreign static putProperty(disp, name, param)
    foreign static mustGetProperty(disp, name)
    foreign static mustCallMethod(disp, name)
    foreign static mustCallMethod2(disp, name, param)
}

foreign class GUID {
    construct new(guid) {}
}

var IID_DISPATCH = GUID.new("{00020400-0000-0000-C000-000000000046}")

foreign class IUnknown {
    construct new(programID) {}

    foreign queryInterface(iid, name)
    foreign static release(name)
}

class Time {
    foreign static sleep(secs)
}

Ole.coInitialize(0)
var unknown = OleUtil.createObject("Word.application")
var word = unknown.queryInterface(IID_DISPATCH, "word")
OleUtil.putProperty(word, "Visible", true)
var documents  = OleUtil.mustGetProperty(word, "Documents")
var document   = OleUtil.mustCallMethod(documents, "Add")
var content    = OleUtil.mustGetProperty(document, "Content")
var paragraphs = OleUtil.mustGetProperty(content, "Paragraphs")
var paragraph  = OleUtil.mustCallMethod(paragraphs, "Add")
var range      = OleUtil.mustGetProperty(paragraph, "Range")

OleUtil.putProperty(range, "Text", "This is a Rosetta Code test document.")

Time.sleep(10)

OleUtil.putProperty(document, "Saved", true)
OleUtil.mustCallMethod2(document, "Close", false)
OleUtil.mustCallMethod(word, "Quit")
IUnknown.release(word)

Ole.coUninitialize()


We now embed this script in the following Go program and run it.

/* go run OLE_automation.go */

package main

import (
    ole "github.com/go-ole/go-ole"
    "github.com/go-ole/go-ole/oleutil"
    "strings"
    "time"
    wren "github.com/crazyinfin8/WrenGo"
)

type any = interface{}

var dispMap = make(map[string]*ole.IDispatch)

func coInitialize(vm *wren.VM, parameters []any) (any, error) {
    p := uintptr(parameters[1].(float64))
    ole.CoInitialize(p)
    return nil, nil
}

func coUninitialize(vm *wren.VM, parameters []any) (any, error) {
    ole.CoUninitialize()
    return nil, nil
}

func putProperty(vm *wren.VM, parameters []any) (any, error) {
    disp := dispMap[parameters[1].(string)]
    propName := parameters[2].(string)
    param := parameters[3]
    oleutil.PutProperty(disp, propName, param)
    return nil, nil
}

func mustGetProperty(vm *wren.VM, parameters []any) (any, error) {
    disp := dispMap[parameters[1].(string)]
    propName := parameters[2].(string)
    disp2 := oleutil.MustGetProperty(disp, propName).ToIDispatch()
    disp2Name := strings.ToLower(propName)
    dispMap[disp2Name] = disp2
    return disp2Name, nil
}

func mustCallMethod(vm *wren.VM, parameters []any) (any, error) {
    dispName := parameters[1].(string)
    disp := dispMap[dispName]
    methName := parameters[2].(string)
    disp2 := oleutil.MustCallMethod(disp, methName).ToIDispatch()
    disp2Name := dispName[0:len(dispName)-1]
    dispMap[disp2Name] = disp2
    return disp2Name, nil
}

func mustCallMethod2(vm *wren.VM, parameters []any) (any, error) {
    dispName := parameters[1].(string)
    disp := dispMap[dispName]
    methName := parameters[2].(string)
    param := parameters[3]
    disp2 := oleutil.MustCallMethod(disp, methName, param).ToIDispatch()
    disp2Name := dispName[0:len(dispName)-1]
    dispMap[disp2Name] = disp2
    return disp2Name, nil
}

func newGUID(vm *wren.VM, parameters []any) (any, error) {
    param := parameters[1].(string)
    guid := ole.NewGUID(param)
    return &guid, nil
}

func newIUnknown(vm *wren.VM, parameters []any) (any, error) {
    programID := parameters[1].(string)
    unknown, _ := oleutil.CreateObject(programID)
    return &unknown, nil
}

func queryInterface(vm *wren.VM, parameters []any) (any, error) {
    handle  := parameters[0].(*wren.ForeignHandle)
    ifc, _  := handle.Get()
    unknown := ifc.(**ole.IUnknown)
    handle2 := parameters[1].(*wren.ForeignHandle)
    ifc2, _ := handle2.Get()
    guid    := ifc2.(**ole.GUID)
    disp, _ := (*unknown).QueryInterface(*guid)
    name    := parameters[2].(string)
    dispMap[name] = disp
    return name, nil
}

func release(vm *wren.VM, parameters []any) (any, error) {
    unknown := dispMap[parameters[1].(string)]
    unknown.Release()
    return nil, nil
}

func sleep(vm *wren.VM, parameters []any) (any, error) {
    secs := time.Duration(parameters[1].(float64))
    time.Sleep(secs * time.Second)
    return nil, nil
}

func main() {
    vm := wren.NewVM()
    fileName := "OLE_automation.wren"

    oleMethodMap := wren.MethodMap {
        "static coInitialize(_)" : coInitialize,
        "static coUninitialize()": coUninitialize,
    }

    oleUtilMethodMap := wren.MethodMap {
        "static putProperty(_,_,_)"    : putProperty,
        "static mustGetProperty(_,_)"  : mustGetProperty,
        "static mustCallMethod(_,_)"   : mustCallMethod,
        "static mustCallMethod2(_,_,_)": mustCallMethod2,
    }

    iUnknownMethodMap := wren.MethodMap {
        "queryInterface(_,_)": queryInterface,
        "static release(_)"  : release,
    }

    timeMethodMap := wren.MethodMap {
	"static sleep(_)": sleep,
    }

    classMap := wren.ClassMap {
        "Ole"      : wren.NewClass(nil, nil, oleMethodMap),
        "OleUtil"  : wren.NewClass(nil, nil, oleUtilMethodMap),
        "GUID"     : wren.NewClass(newGUID, nil, nil),
        "IUnknown" : wren.NewClass(newIUnknown, nil, iUnknownMethodMap),
        "Time"     : wren.NewClass(nil, nil, timeMethodMap),
    }

    module := wren.NewModule(classMap)
    vm.SetModule(fileName, module)
    vm.InterpretFile(fileName)
    vm.Free()
}