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