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