Serge Bazanski | cc25bdf | 2018-10-25 14:02:58 +0200 | [diff] [blame^] | 1 | // +build windows |
| 2 | |
| 3 | package ole |
| 4 | |
| 5 | import ( |
| 6 | "errors" |
| 7 | "syscall" |
| 8 | "time" |
| 9 | "unicode/utf16" |
| 10 | "unsafe" |
| 11 | ) |
| 12 | |
| 13 | var ( |
| 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(). |
| 50 | func 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. |
| 62 | func 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(). |
| 81 | func 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. |
| 89 | func 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. |
| 96 | func CoUninitialize() { |
| 97 | procCoUninitialize.Call() |
| 98 | } |
| 99 | |
| 100 | // CoTaskMemFree frees memory pointer. |
| 101 | func 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. |
| 118 | func 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. |
| 135 | func 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. |
| 147 | func 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. |
| 158 | func 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. |
| 170 | func 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. |
| 181 | func 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. |
| 198 | func 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. |
| 213 | func 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. |
| 222 | func 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. |
| 231 | func 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. |
| 238 | func 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. |
| 248 | func 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. |
| 257 | func 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. |
| 267 | func 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. |
| 282 | func 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. |
| 294 | func 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. |
| 299 | func 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. |
| 308 | func 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. |
| 315 | func 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. |
| 322 | func 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 | } |