-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathIUnknown.go
131 lines (116 loc) · 3.77 KB
/
IUnknown.go
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package wingo
import (
"github.com/rogeecn/wingo/proc"
"syscall"
"unsafe"
"github.com/rogeecn/wingo/co"
"github.com/rogeecn/wingo/errco"
)
// Typically uses CLSCTX_INPROC_SERVER. Panics if the COM object cannot be
// created.
//
// ⚠️ The returned IUnknown can be used to construct another COM object; either
// way you must defer Release().
//
// Example for an ordinary COM object:
//
// comObject := shell.NewITaskbarList(
// win.CoCreateInstance(
// shellco.CLSID_TaskbarList, nil,
// co.CLSCTX_INPROC_SERVER,
// shellco.IID_ITaskbarList),
// )
// defer comObject.Release()
//
// Example for COM Automation:
//
// clsId, _ := win.CLSIDFromProgID("Excel.Application")
// root := win.CoCreateInstance(
// clsId, nil, co.CLSCTX_SERVER, co.IID_IUNKNOWN)
// defer root.Release()
//
// excel := autom.NewIDispatch(
// root.QueryInterface(automco.IID_IDispatch))
// defer excel.Release()
//
// for _, f := range excel.ListFunctions() {
// println(f.Name, f.NumParams, f.FuncKind, f.Flags)
// }
//
// 📑 https://docs.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cocreateinstance
func CoCreateInstance(
rclsid co.CLSID, pUnkOuter *IUnknown,
dwClsContext co.CLSCTX, riid co.IID) IUnknown {
var ppvQueried **IUnknownVtbl
var ppvOuter ***IUnknownVtbl
if pUnkOuter != nil { // was the outer pointer requested?
pUnkOuter.Release()
ppvOuter = &pUnkOuter.ppv
}
ret, _, _ := syscall.Syscall6(proc.CoCreateInstance.Addr(), 5,
uintptr(unsafe.Pointer(GuidFromClsid(rclsid))),
uintptr(unsafe.Pointer(ppvOuter)),
uintptr(dwClsContext),
uintptr(unsafe.Pointer(GuidFromIid(riid))),
uintptr(unsafe.Pointer(&ppvQueried)), 0)
if hr := errco.ERROR(ret); hr == errco.S_OK {
return IUnknown{ppv: ppvQueried}
} else {
panic(hr)
}
}
//------------------------------------------------------------------------------
// IUnknown virtual table, base to all COM virtual tables.
//
// 📑 https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown
type IUnknownVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
}
// IUnknown COM interface, base to all COM interfaces.
//
// 📑 https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown
type IUnknown struct{ ppv **IUnknownVtbl }
// Returns the underlying pointer to the COM virtual table.
func (me *IUnknown) Ptr() **IUnknownVtbl {
return me.ppv
}
// ⚠️ You must defer IUnknown.Release() on the new object.
//
// 📑 https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-addref
func (me *IUnknown) AddRef() IUnknown {
syscall.Syscall((*me.ppv).AddRef, 1,
uintptr(unsafe.Pointer(me.ppv)), 0, 0)
return IUnknown{ppv: me.ppv} // simply copy the pointer into a new object
}
// ⚠️ The returned pointer must be used to construct a COM object; you must
// defer its Release().
//
// 📑 https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void)
func (me *IUnknown) QueryInterface(riid co.IID) IUnknown {
var ppvQueried **IUnknownVtbl
ret, _, _ := syscall.Syscall((*me.ppv).QueryInterface, 3,
uintptr(unsafe.Pointer(me.ppv)),
uintptr(unsafe.Pointer(GuidFromIid(riid))),
uintptr(unsafe.Pointer(&ppvQueried)))
if hr := errco.ERROR(ret); hr == errco.S_OK {
return IUnknown{ppv: ppvQueried}
} else {
panic(hr)
}
}
// Releases the COM pointer. Never fails, can be called any number of times.
//
// 📑 https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-release
func (me *IUnknown) Release() uint32 {
ret := uintptr(0)
if me.Ptr() != nil {
ret, _, _ = syscall.Syscall((*me.ppv).Release, 1,
uintptr(unsafe.Pointer(me.ppv)), 0, 0)
if ret == 0 { // COM pointer was released
me.ppv = nil
}
}
return uint32(ret)
}