blob: 75ebbf13f6c755eabb5ca055169bad81f4d3cb38 [file] [log] [blame]
Serge Bazanskicc25bdf2018-10-25 14:02:58 +02001// +build windows
2
3package ole
4
5import (
6 "errors"
7 "syscall"
8 "time"
9 "unicode/utf16"
10 "unsafe"
11)
12
13var (
14 procCoInitialize, _ = modole32.FindProc("CoInitialize")
15 procCoInitializeEx, _ = modole32.FindProc("CoInitializeEx")
16 procCoUninitialize, _ = modole32.FindProc("CoUninitialize")
17 procCoCreateInstance, _ = modole32.FindProc("CoCreateInstance")
18 procCoTaskMemFree, _ = modole32.FindProc("CoTaskMemFree")
19 procCLSIDFromProgID, _ = modole32.FindProc("CLSIDFromProgID")
20 procCLSIDFromString, _ = modole32.FindProc("CLSIDFromString")
21 procStringFromCLSID, _ = modole32.FindProc("StringFromCLSID")
22 procStringFromIID, _ = modole32.FindProc("StringFromIID")
23 procIIDFromString, _ = modole32.FindProc("IIDFromString")
24 procGetUserDefaultLCID, _ = modkernel32.FindProc("GetUserDefaultLCID")
25 procCopyMemory, _ = modkernel32.FindProc("RtlMoveMemory")
26 procVariantInit, _ = modoleaut32.FindProc("VariantInit")
27 procVariantClear, _ = modoleaut32.FindProc("VariantClear")
28 procVariantTimeToSystemTime, _ = modoleaut32.FindProc("VariantTimeToSystemTime")
29 procSysAllocString, _ = modoleaut32.FindProc("SysAllocString")
30 procSysAllocStringLen, _ = modoleaut32.FindProc("SysAllocStringLen")
31 procSysFreeString, _ = modoleaut32.FindProc("SysFreeString")
32 procSysStringLen, _ = modoleaut32.FindProc("SysStringLen")
33 procCreateDispTypeInfo, _ = modoleaut32.FindProc("CreateDispTypeInfo")
34 procCreateStdDispatch, _ = modoleaut32.FindProc("CreateStdDispatch")
35 procGetActiveObject, _ = modoleaut32.FindProc("GetActiveObject")
36
37 procGetMessageW, _ = moduser32.FindProc("GetMessageW")
38 procDispatchMessageW, _ = moduser32.FindProc("DispatchMessageW")
39)
40
41// coInitialize initializes COM library on current thread.
42//
43// MSDN documentation suggests that this function should not be called. Call
44// CoInitializeEx() instead. The reason has to do with threading and this
45// function is only for single-threaded apartments.
46//
47// That said, most users of the library have gotten away with just this
48// function. If you are experiencing threading issues, then use
49// CoInitializeEx().
50func coInitialize() (err error) {
51 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx
52 // Suggests that no value should be passed to CoInitialized.
53 // Could just be Call() since the parameter is optional. <-- Needs testing to be sure.
54 hr, _, _ := procCoInitialize.Call(uintptr(0))
55 if hr != 0 {
56 err = NewError(hr)
57 }
58 return
59}
60
61// coInitializeEx initializes COM library with concurrency model.
62func coInitializeEx(coinit uint32) (err error) {
63 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms695279(v=vs.85).aspx
64 // Suggests that the first parameter is not only optional but should always be NULL.
65 hr, _, _ := procCoInitializeEx.Call(uintptr(0), uintptr(coinit))
66 if hr != 0 {
67 err = NewError(hr)
68 }
69 return
70}
71
72// CoInitialize initializes COM library on current thread.
73//
74// MSDN documentation suggests that this function should not be called. Call
75// CoInitializeEx() instead. The reason has to do with threading and this
76// function is only for single-threaded apartments.
77//
78// That said, most users of the library have gotten away with just this
79// function. If you are experiencing threading issues, then use
80// CoInitializeEx().
81func CoInitialize(p uintptr) (err error) {
82 // p is ignored and won't be used.
83 // Avoid any variable not used errors.
84 p = uintptr(0)
85 return coInitialize()
86}
87
88// CoInitializeEx initializes COM library with concurrency model.
89func CoInitializeEx(p uintptr, coinit uint32) (err error) {
90 // Avoid any variable not used errors.
91 p = uintptr(0)
92 return coInitializeEx(coinit)
93}
94
95// CoUninitialize uninitializes COM Library.
96func CoUninitialize() {
97 procCoUninitialize.Call()
98}
99
100// CoTaskMemFree frees memory pointer.
101func CoTaskMemFree(memptr uintptr) {
102 procCoTaskMemFree.Call(memptr)
103}
104
105// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier.
106//
107// The Programmatic Identifier must be registered, because it will be looked up
108// in the Windows Registry. The registry entry has the following keys: CLSID,
109// Insertable, Protocol and Shell
110// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx).
111//
112// programID identifies the class id with less precision and is not guaranteed
113// to be unique. These are usually found in the registry under
114// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of
115// "Program.Component.Version" with version being optional.
116//
117// CLSIDFromProgID in Windows API.
118func CLSIDFromProgID(progId string) (clsid *GUID, err error) {
119 var guid GUID
120 lpszProgID := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId)))
121 hr, _, _ := procCLSIDFromProgID.Call(lpszProgID, uintptr(unsafe.Pointer(&guid)))
122 if hr != 0 {
123 err = NewError(hr)
124 }
125 clsid = &guid
126 return
127}
128
129// CLSIDFromString retrieves Class ID from string representation.
130//
131// This is technically the string version of the GUID and will convert the
132// string to object.
133//
134// CLSIDFromString in Windows API.
135func CLSIDFromString(str string) (clsid *GUID, err error) {
136 var guid GUID
137 lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str)))
138 hr, _, _ := procCLSIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid)))
139 if hr != 0 {
140 err = NewError(hr)
141 }
142 clsid = &guid
143 return
144}
145
146// StringFromCLSID returns GUID formated string from GUID object.
147func StringFromCLSID(clsid *GUID) (str string, err error) {
148 var p *uint16
149 hr, _, _ := procStringFromCLSID.Call(uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(&p)))
150 if hr != 0 {
151 err = NewError(hr)
152 }
153 str = LpOleStrToString(p)
154 return
155}
156
157// IIDFromString returns GUID from program ID.
158func IIDFromString(progId string) (clsid *GUID, err error) {
159 var guid GUID
160 lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId)))
161 hr, _, _ := procIIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid)))
162 if hr != 0 {
163 err = NewError(hr)
164 }
165 clsid = &guid
166 return
167}
168
169// StringFromIID returns GUID formatted string from GUID object.
170func StringFromIID(iid *GUID) (str string, err error) {
171 var p *uint16
172 hr, _, _ := procStringFromIID.Call(uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(&p)))
173 if hr != 0 {
174 err = NewError(hr)
175 }
176 str = LpOleStrToString(p)
177 return
178}
179
180// CreateInstance of single uninitialized object with GUID.
181func CreateInstance(clsid *GUID, iid *GUID) (unk *IUnknown, err error) {
182 if iid == nil {
183 iid = IID_IUnknown
184 }
185 hr, _, _ := procCoCreateInstance.Call(
186 uintptr(unsafe.Pointer(clsid)),
187 0,
188 CLSCTX_SERVER,
189 uintptr(unsafe.Pointer(iid)),
190 uintptr(unsafe.Pointer(&unk)))
191 if hr != 0 {
192 err = NewError(hr)
193 }
194 return
195}
196
197// GetActiveObject retrieves pointer to active object.
198func GetActiveObject(clsid *GUID, iid *GUID) (unk *IUnknown, err error) {
199 if iid == nil {
200 iid = IID_IUnknown
201 }
202 hr, _, _ := procGetActiveObject.Call(
203 uintptr(unsafe.Pointer(clsid)),
204 uintptr(unsafe.Pointer(iid)),
205 uintptr(unsafe.Pointer(&unk)))
206 if hr != 0 {
207 err = NewError(hr)
208 }
209 return
210}
211
212// VariantInit initializes variant.
213func VariantInit(v *VARIANT) (err error) {
214 hr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v)))
215 if hr != 0 {
216 err = NewError(hr)
217 }
218 return
219}
220
221// VariantClear clears value in Variant settings to VT_EMPTY.
222func VariantClear(v *VARIANT) (err error) {
223 hr, _, _ := procVariantClear.Call(uintptr(unsafe.Pointer(v)))
224 if hr != 0 {
225 err = NewError(hr)
226 }
227 return
228}
229
230// SysAllocString allocates memory for string and copies string into memory.
231func SysAllocString(v string) (ss *int16) {
232 pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v))))
233 ss = (*int16)(unsafe.Pointer(pss))
234 return
235}
236
237// SysAllocStringLen copies up to length of given string returning pointer.
238func SysAllocStringLen(v string) (ss *int16) {
239 utf16 := utf16.Encode([]rune(v + "\x00"))
240 ptr := &utf16[0]
241
242 pss, _, _ := procSysAllocStringLen.Call(uintptr(unsafe.Pointer(ptr)), uintptr(len(utf16)-1))
243 ss = (*int16)(unsafe.Pointer(pss))
244 return
245}
246
247// SysFreeString frees string system memory. This must be called with SysAllocString.
248func SysFreeString(v *int16) (err error) {
249 hr, _, _ := procSysFreeString.Call(uintptr(unsafe.Pointer(v)))
250 if hr != 0 {
251 err = NewError(hr)
252 }
253 return
254}
255
256// SysStringLen is the length of the system allocated string.
257func SysStringLen(v *int16) uint32 {
258 l, _, _ := procSysStringLen.Call(uintptr(unsafe.Pointer(v)))
259 return uint32(l)
260}
261
262// CreateStdDispatch provides default IDispatch implementation for IUnknown.
263//
264// This handles default IDispatch implementation for objects. It haves a few
265// limitations with only supporting one language. It will also only return
266// default exception codes.
267func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (disp *IDispatch, err error) {
268 hr, _, _ := procCreateStdDispatch.Call(
269 uintptr(unsafe.Pointer(unk)),
270 v,
271 uintptr(unsafe.Pointer(ptinfo)),
272 uintptr(unsafe.Pointer(&disp)))
273 if hr != 0 {
274 err = NewError(hr)
275 }
276 return
277}
278
279// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch.
280//
281// This will not handle the full implementation of the interface.
282func CreateDispTypeInfo(idata *INTERFACEDATA) (pptinfo *IUnknown, err error) {
283 hr, _, _ := procCreateDispTypeInfo.Call(
284 uintptr(unsafe.Pointer(idata)),
285 uintptr(GetUserDefaultLCID()),
286 uintptr(unsafe.Pointer(&pptinfo)))
287 if hr != 0 {
288 err = NewError(hr)
289 }
290 return
291}
292
293// copyMemory moves location of a block of memory.
294func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) {
295 procCopyMemory.Call(uintptr(dest), uintptr(src), uintptr(length))
296}
297
298// GetUserDefaultLCID retrieves current user default locale.
299func GetUserDefaultLCID() (lcid uint32) {
300 ret, _, _ := procGetUserDefaultLCID.Call()
301 lcid = uint32(ret)
302 return
303}
304
305// GetMessage in message queue from runtime.
306//
307// This function appears to block. PeekMessage does not block.
308func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, err error) {
309 r0, _, err := procGetMessageW.Call(uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax))
310 ret = int32(r0)
311 return
312}
313
314// DispatchMessage to window procedure.
315func DispatchMessage(msg *Msg) (ret int32) {
316 r0, _, _ := procDispatchMessageW.Call(uintptr(unsafe.Pointer(msg)))
317 ret = int32(r0)
318 return
319}
320
321// GetVariantDate converts COM Variant Time value to Go time.Time.
322func GetVariantDate(value float64) (time.Time, error) {
323 var st syscall.Systemtime
324 r, _, _ := procVariantTimeToSystemTime.Call(uintptr(value), uintptr(unsafe.Pointer(&st)))
325 if r != 0 {
326 return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
327 }
328 return time.Now(), errors.New("Could not convert to time, passing current time.")
329}