// +build windows package ole import ( "errors" "syscall" "time" "unicode/utf16" "unsafe" ) var ( procCoInitialize, _ = modole32.FindProc("CoInitialize") procCoInitializeEx, _ = modole32.FindProc("CoInitializeEx") procCoUninitialize, _ = modole32.FindProc("CoUninitialize") procCoCreateInstance, _ = modole32.FindProc("CoCreateInstance") procCoTaskMemFree, _ = modole32.FindProc("CoTaskMemFree") procCLSIDFromProgID, _ = modole32.FindProc("CLSIDFromProgID") procCLSIDFromString, _ = modole32.FindProc("CLSIDFromString") procStringFromCLSID, _ = modole32.FindProc("StringFromCLSID") procStringFromIID, _ = modole32.FindProc("StringFromIID") procIIDFromString, _ = modole32.FindProc("IIDFromString") procGetUserDefaultLCID, _ = modkernel32.FindProc("GetUserDefaultLCID") procCopyMemory, _ = modkernel32.FindProc("RtlMoveMemory") procVariantInit, _ = modoleaut32.FindProc("VariantInit") procVariantClear, _ = modoleaut32.FindProc("VariantClear") procVariantTimeToSystemTime, _ = modoleaut32.FindProc("VariantTimeToSystemTime") procSysAllocString, _ = modoleaut32.FindProc("SysAllocString") procSysAllocStringLen, _ = modoleaut32.FindProc("SysAllocStringLen") procSysFreeString, _ = modoleaut32.FindProc("SysFreeString") procSysStringLen, _ = modoleaut32.FindProc("SysStringLen") procCreateDispTypeInfo, _ = modoleaut32.FindProc("CreateDispTypeInfo") procCreateStdDispatch, _ = modoleaut32.FindProc("CreateStdDispatch") procGetActiveObject, _ = modoleaut32.FindProc("GetActiveObject") procGetMessageW, _ = moduser32.FindProc("GetMessageW") procDispatchMessageW, _ = moduser32.FindProc("DispatchMessageW") ) // coInitialize initializes COM library on current thread. // // MSDN documentation suggests that this function should not be called. Call // CoInitializeEx() instead. The reason has to do with threading and this // function is only for single-threaded apartments. // // That said, most users of the library have gotten away with just this // function. If you are experiencing threading issues, then use // CoInitializeEx(). func coInitialize() (err error) { // http://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx // Suggests that no value should be passed to CoInitialized. // Could just be Call() since the parameter is optional. <-- Needs testing to be sure. hr, _, _ := procCoInitialize.Call(uintptr(0)) if hr != 0 { err = NewError(hr) } return } // coInitializeEx initializes COM library with concurrency model. func coInitializeEx(coinit uint32) (err error) { // http://msdn.microsoft.com/en-us/library/windows/desktop/ms695279(v=vs.85).aspx // Suggests that the first parameter is not only optional but should always be NULL. hr, _, _ := procCoInitializeEx.Call(uintptr(0), uintptr(coinit)) if hr != 0 { err = NewError(hr) } return } // CoInitialize initializes COM library on current thread. // // MSDN documentation suggests that this function should not be called. Call // CoInitializeEx() instead. The reason has to do with threading and this // function is only for single-threaded apartments. // // That said, most users of the library have gotten away with just this // function. If you are experiencing threading issues, then use // CoInitializeEx(). func CoInitialize(p uintptr) (err error) { // p is ignored and won't be used. // Avoid any variable not used errors. p = uintptr(0) return coInitialize() } // CoInitializeEx initializes COM library with concurrency model. func CoInitializeEx(p uintptr, coinit uint32) (err error) { // Avoid any variable not used errors. p = uintptr(0) return coInitializeEx(coinit) } // CoUninitialize uninitializes COM Library. func CoUninitialize() { procCoUninitialize.Call() } // CoTaskMemFree frees memory pointer. func CoTaskMemFree(memptr uintptr) { procCoTaskMemFree.Call(memptr) } // CLSIDFromProgID retrieves Class Identifier with the given Program Identifier. // // The Programmatic Identifier must be registered, because it will be looked up // in the Windows Registry. The registry entry has the following keys: CLSID, // Insertable, Protocol and Shell // (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx). // // programID identifies the class id with less precision and is not guaranteed // to be unique. These are usually found in the registry under // HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of // "Program.Component.Version" with version being optional. // // CLSIDFromProgID in Windows API. func CLSIDFromProgID(progId string) (clsid *GUID, err error) { var guid GUID lpszProgID := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId))) hr, _, _ := procCLSIDFromProgID.Call(lpszProgID, uintptr(unsafe.Pointer(&guid))) if hr != 0 { err = NewError(hr) } clsid = &guid return } // CLSIDFromString retrieves Class ID from string representation. // // This is technically the string version of the GUID and will convert the // string to object. // // CLSIDFromString in Windows API. func CLSIDFromString(str string) (clsid *GUID, err error) { var guid GUID lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str))) hr, _, _ := procCLSIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid))) if hr != 0 { err = NewError(hr) } clsid = &guid return } // StringFromCLSID returns GUID formated string from GUID object. func StringFromCLSID(clsid *GUID) (str string, err error) { var p *uint16 hr, _, _ := procStringFromCLSID.Call(uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(&p))) if hr != 0 { err = NewError(hr) } str = LpOleStrToString(p) return } // IIDFromString returns GUID from program ID. func IIDFromString(progId string) (clsid *GUID, err error) { var guid GUID lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId))) hr, _, _ := procIIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid))) if hr != 0 { err = NewError(hr) } clsid = &guid return } // StringFromIID returns GUID formatted string from GUID object. func StringFromIID(iid *GUID) (str string, err error) { var p *uint16 hr, _, _ := procStringFromIID.Call(uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(&p))) if hr != 0 { err = NewError(hr) } str = LpOleStrToString(p) return } // CreateInstance of single uninitialized object with GUID. func CreateInstance(clsid *GUID, iid *GUID) (unk *IUnknown, err error) { if iid == nil { iid = IID_IUnknown } hr, _, _ := procCoCreateInstance.Call( uintptr(unsafe.Pointer(clsid)), 0, CLSCTX_SERVER, uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(&unk))) if hr != 0 { err = NewError(hr) } return } // GetActiveObject retrieves pointer to active object. func GetActiveObject(clsid *GUID, iid *GUID) (unk *IUnknown, err error) { if iid == nil { iid = IID_IUnknown } hr, _, _ := procGetActiveObject.Call( uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(&unk))) if hr != 0 { err = NewError(hr) } return } // VariantInit initializes variant. func VariantInit(v *VARIANT) (err error) { hr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v))) if hr != 0 { err = NewError(hr) } return } // VariantClear clears value in Variant settings to VT_EMPTY. func VariantClear(v *VARIANT) (err error) { hr, _, _ := procVariantClear.Call(uintptr(unsafe.Pointer(v))) if hr != 0 { err = NewError(hr) } return } // SysAllocString allocates memory for string and copies string into memory. func SysAllocString(v string) (ss *int16) { pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v)))) ss = (*int16)(unsafe.Pointer(pss)) return } // SysAllocStringLen copies up to length of given string returning pointer. func SysAllocStringLen(v string) (ss *int16) { utf16 := utf16.Encode([]rune(v + "\x00")) ptr := &utf16[0] pss, _, _ := procSysAllocStringLen.Call(uintptr(unsafe.Pointer(ptr)), uintptr(len(utf16)-1)) ss = (*int16)(unsafe.Pointer(pss)) return } // SysFreeString frees string system memory. This must be called with SysAllocString. func SysFreeString(v *int16) (err error) { hr, _, _ := procSysFreeString.Call(uintptr(unsafe.Pointer(v))) if hr != 0 { err = NewError(hr) } return } // SysStringLen is the length of the system allocated string. func SysStringLen(v *int16) uint32 { l, _, _ := procSysStringLen.Call(uintptr(unsafe.Pointer(v))) return uint32(l) } // CreateStdDispatch provides default IDispatch implementation for IUnknown. // // This handles default IDispatch implementation for objects. It haves a few // limitations with only supporting one language. It will also only return // default exception codes. func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (disp *IDispatch, err error) { hr, _, _ := procCreateStdDispatch.Call( uintptr(unsafe.Pointer(unk)), v, uintptr(unsafe.Pointer(ptinfo)), uintptr(unsafe.Pointer(&disp))) if hr != 0 { err = NewError(hr) } return } // CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch. // // This will not handle the full implementation of the interface. func CreateDispTypeInfo(idata *INTERFACEDATA) (pptinfo *IUnknown, err error) { hr, _, _ := procCreateDispTypeInfo.Call( uintptr(unsafe.Pointer(idata)), uintptr(GetUserDefaultLCID()), uintptr(unsafe.Pointer(&pptinfo))) if hr != 0 { err = NewError(hr) } return } // copyMemory moves location of a block of memory. func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) { procCopyMemory.Call(uintptr(dest), uintptr(src), uintptr(length)) } // GetUserDefaultLCID retrieves current user default locale. func GetUserDefaultLCID() (lcid uint32) { ret, _, _ := procGetUserDefaultLCID.Call() lcid = uint32(ret) return } // GetMessage in message queue from runtime. // // This function appears to block. PeekMessage does not block. func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, err error) { r0, _, err := procGetMessageW.Call(uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax)) ret = int32(r0) return } // DispatchMessage to window procedure. func DispatchMessage(msg *Msg) (ret int32) { r0, _, _ := procDispatchMessageW.Call(uintptr(unsafe.Pointer(msg))) ret = int32(r0) return } // GetVariantDate converts COM Variant Time value to Go time.Time. func GetVariantDate(value float64) (time.Time, error) { var st syscall.Systemtime r, _, _ := procVariantTimeToSystemTime.Call(uintptr(value), uintptr(unsafe.Pointer(&st))) if r != 0 { 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 } return time.Now(), errors.New("Could not convert to time, passing current time.") }