OLE automation: Difference between revisions
m (added a ;Task: section header, add whitespace.) |
(Added Wren) |
||
Line 220: | Line 220: | ||
client = win32com.client.Dispatch("python.server") |
client = win32com.client.Dispatch("python.server") |
||
client.write("hello world")</lang> |
client.write("hello world")</lang> |
||
=={{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. |
|||
<lang ecmascript>/* 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()</lang> |
|||
<br> |
|||
We now embed this script in the following Go program and run it. |
|||
<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() |
|||
}</lang> |
|||
{{omit from|Ada|Too involved to implement from scratch, and OS specific}} |
{{omit from|Ada|Too involved to implement from scratch, and OS specific}} |
Revision as of 16:56, 7 March 2022
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
by fincs:discussion
client: using the ahk ole server as well as the python ole server implemented below <lang autohotkey>ahk := comobjactive("ahkdemo.ahk") ahk.hello("hello world") py := ComObjActive("python.server") py.write("hello") return</lang> server: <lang autohotkey>#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</lang>
Go
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.
<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()
}</lang>
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. <lang M2000 Interpreter> 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 </lang>
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
Server uses a client of the ahk server above to register the clsid in the windows registry. Translated from win32com/test/testDynamic.py <lang python>#!/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</lang>
client <lang Python>import win32com.client client = win32com.client.Dispatch("python.server") client.write("hello world")</lang>
Wren
An embedded application with a Go host so we can use the Go OLE library. <lang ecmascript>/* 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()</lang>
We now embed this script in the following Go program and run it.
<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()
}</lang>
- Programming Tasks
- OLE Automation Client and Server
- AutoHotkey
- ComDispatch
- Go
- Go OLE
- M2000 Interpreter
- Phix
- Python
- Pywin32
- Wren
- WrenGo
- Ada/Omit
- AWK/Omit
- BBC BASIC/Omit
- Blast/Omit
- Brlcad/Omit
- GUISS/Omit
- HTML/Omit
- Locomotive Basic/Omit
- Logtalk/Omit
- Mathematica/Omit
- Maxima/Omit
- ML/I/Omit
- JavaScript/Omit
- Openscad/Omit
- PicoLisp/Omit
- Retro/Omit
- SQL PL/Omit
- TPP/Omit
- UNIX Shell/Omit
- Zkl/Omit
- ZX Spectrum Basic/Omit