330 lines
11 KiB
Go
330 lines
11 KiB
Go
// +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.")
|
|
}
|