1// Copyright 2010-2012 The W32 Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build windows
6
7package w32
8
9import (
10	"syscall"
11	"unicode/utf16"
12	"unsafe"
13)
14
15func MakeIntResource(id uint16) *uint16 {
16	return (*uint16)(unsafe.Pointer(uintptr(id)))
17}
18
19func LOWORD(dw uint32) uint16 {
20	return uint16(dw)
21}
22
23func HIWORD(dw uint32) uint16 {
24	return uint16(dw >> 16 & 0xffff)
25}
26
27func BoolToBOOL(value bool) BOOL {
28	if value {
29		return 1
30	}
31
32	return 0
33}
34
35func UTF16PtrToString(cstr *uint16) string {
36	if cstr != nil {
37		us := make([]uint16, 0, 256)
38		for p := uintptr(unsafe.Pointer(cstr)); ; p += 2 {
39			u := *(*uint16)(unsafe.Pointer(p))
40			if u == 0 {
41				return string(utf16.Decode(us))
42			}
43			us = append(us, u)
44		}
45	}
46
47	return ""
48}
49
50func ComAddRef(unknown *IUnknown) int32 {
51	ret, _, _ := syscall.Syscall(unknown.lpVtbl.pAddRef, 1,
52		uintptr(unsafe.Pointer(unknown)),
53		0,
54		0)
55	return int32(ret)
56}
57
58func ComRelease(unknown *IUnknown) int32 {
59	ret, _, _ := syscall.Syscall(unknown.lpVtbl.pRelease, 1,
60		uintptr(unsafe.Pointer(unknown)),
61		0,
62		0)
63	return int32(ret)
64}
65
66func ComQueryInterface(unknown *IUnknown, id *GUID) *IDispatch {
67	var disp *IDispatch
68	hr, _, _ := syscall.Syscall(unknown.lpVtbl.pQueryInterface, 3,
69		uintptr(unsafe.Pointer(unknown)),
70		uintptr(unsafe.Pointer(id)),
71		uintptr(unsafe.Pointer(&disp)))
72	if hr != 0 {
73		panic("Invoke QieryInterface error.")
74	}
75	return disp
76}
77
78func ComGetIDsOfName(disp *IDispatch, names []string) []int32 {
79	wnames := make([]*uint16, len(names))
80	dispid := make([]int32, len(names))
81	for i := 0; i < len(names); i++ {
82		wnames[i] = syscall.StringToUTF16Ptr(names[i])
83	}
84	hr, _, _ := syscall.Syscall6(disp.lpVtbl.pGetIDsOfNames, 6,
85		uintptr(unsafe.Pointer(disp)),
86		uintptr(unsafe.Pointer(IID_NULL)),
87		uintptr(unsafe.Pointer(&wnames[0])),
88		uintptr(len(names)),
89		uintptr(GetUserDefaultLCID()),
90		uintptr(unsafe.Pointer(&dispid[0])))
91	if hr != 0 {
92		panic("Invoke GetIDsOfName error.")
93	}
94	return dispid
95}
96
97func ComInvoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (result *VARIANT) {
98	var dispparams DISPPARAMS
99
100	if dispatch&DISPATCH_PROPERTYPUT != 0 {
101		dispnames := [1]int32{DISPID_PROPERTYPUT}
102		dispparams.RgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0]))
103		dispparams.CNamedArgs = 1
104	}
105	var vargs []VARIANT
106	if len(params) > 0 {
107		vargs = make([]VARIANT, len(params))
108		for i, v := range params {
109			//n := len(params)-i-1
110			n := len(params) - i - 1
111			VariantInit(&vargs[n])
112			switch v.(type) {
113			case bool:
114				if v.(bool) {
115					vargs[n] = VARIANT{VT_BOOL, 0, 0, 0, 0xffff}
116				} else {
117					vargs[n] = VARIANT{VT_BOOL, 0, 0, 0, 0}
118				}
119			case *bool:
120				vargs[n] = VARIANT{VT_BOOL | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*bool))))}
121			case byte:
122				vargs[n] = VARIANT{VT_I1, 0, 0, 0, int64(v.(byte))}
123			case *byte:
124				vargs[n] = VARIANT{VT_I1 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*byte))))}
125			case int16:
126				vargs[n] = VARIANT{VT_I2, 0, 0, 0, int64(v.(int16))}
127			case *int16:
128				vargs[n] = VARIANT{VT_I2 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*int16))))}
129			case uint16:
130				vargs[n] = VARIANT{VT_UI2, 0, 0, 0, int64(v.(int16))}
131			case *uint16:
132				vargs[n] = VARIANT{VT_UI2 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*uint16))))}
133			case int, int32:
134				vargs[n] = VARIANT{VT_UI4, 0, 0, 0, int64(v.(int))}
135			case *int, *int32:
136				vargs[n] = VARIANT{VT_I4 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*int))))}
137			case uint, uint32:
138				vargs[n] = VARIANT{VT_UI4, 0, 0, 0, int64(v.(uint))}
139			case *uint, *uint32:
140				vargs[n] = VARIANT{VT_UI4 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*uint))))}
141			case int64:
142				vargs[n] = VARIANT{VT_I8, 0, 0, 0, v.(int64)}
143			case *int64:
144				vargs[n] = VARIANT{VT_I8 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*int64))))}
145			case uint64:
146				vargs[n] = VARIANT{VT_UI8, 0, 0, 0, int64(v.(uint64))}
147			case *uint64:
148				vargs[n] = VARIANT{VT_UI8 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*uint64))))}
149			case float32:
150				vargs[n] = VARIANT{VT_R4, 0, 0, 0, int64(v.(float32))}
151			case *float32:
152				vargs[n] = VARIANT{VT_R4 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*float32))))}
153			case float64:
154				vargs[n] = VARIANT{VT_R8, 0, 0, 0, int64(v.(float64))}
155			case *float64:
156				vargs[n] = VARIANT{VT_R8 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*float64))))}
157			case string:
158				vargs[n] = VARIANT{VT_BSTR, 0, 0, 0, int64(uintptr(unsafe.Pointer(SysAllocString(v.(string)))))}
159			case *string:
160				vargs[n] = VARIANT{VT_BSTR | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*string))))}
161			case *IDispatch:
162				vargs[n] = VARIANT{VT_DISPATCH, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*IDispatch))))}
163			case **IDispatch:
164				vargs[n] = VARIANT{VT_DISPATCH | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(**IDispatch))))}
165			case nil:
166				vargs[n] = VARIANT{VT_NULL, 0, 0, 0, 0}
167			case *VARIANT:
168				vargs[n] = VARIANT{VT_VARIANT | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*VARIANT))))}
169			default:
170				panic("unknown type")
171			}
172		}
173		dispparams.Rgvarg = uintptr(unsafe.Pointer(&vargs[0]))
174		dispparams.CArgs = uint32(len(params))
175	}
176
177	var ret VARIANT
178	var excepInfo EXCEPINFO
179	VariantInit(&ret)
180	hr, _, _ := syscall.Syscall9(disp.lpVtbl.pInvoke, 8,
181		uintptr(unsafe.Pointer(disp)),
182		uintptr(dispid),
183		uintptr(unsafe.Pointer(IID_NULL)),
184		uintptr(GetUserDefaultLCID()),
185		uintptr(dispatch),
186		uintptr(unsafe.Pointer(&dispparams)),
187		uintptr(unsafe.Pointer(&ret)),
188		uintptr(unsafe.Pointer(&excepInfo)),
189		0)
190	if hr != 0 {
191		if excepInfo.BstrDescription != nil {
192			bs := UTF16PtrToString(excepInfo.BstrDescription)
193			panic(bs)
194		}
195	}
196	for _, varg := range vargs {
197		if varg.VT == VT_BSTR && varg.Val != 0 {
198			SysFreeString(((*int16)(unsafe.Pointer(uintptr(varg.Val)))))
199		}
200	}
201	result = &ret
202	return
203}
204