diff --git a/error.go b/error.go new file mode 100644 index 0000000..096b456 --- /dev/null +++ b/error.go @@ -0,0 +1,51 @@ +package ole + +// OleError stores COM errors. +type OleError struct { + hr uintptr + description string + subError error +} + +// NewError creates new error with HResult. +func NewError(hr uintptr) *OleError { + return &OleError{hr: hr} +} + +// NewErrorWithDescription creates new COM error with HResult and description. +func NewErrorWithDescription(hr uintptr, description string) *OleError { + return &OleError{hr: hr, description: description} +} + +// NewErrorWithSubError creates new COM error with parent error. +func NewErrorWithSubError(hr uintptr, description string, err error) *OleError { + return &OleError{hr: hr, description: description, subError: err} +} + +// Code is the HResult. +func (v *OleError) Code() uintptr { + return uintptr(v.hr) +} + +// String description, either manually set or format message with error code. +func (v *OleError) String() string { + if v.description != "" { + return errstr(int(v.hr)) + " (" + v.description + ")" + } + return errstr(int(v.hr)) +} + +// Error implements error interface. +func (v *OleError) Error() string { + return v.String() +} + +// Description retrieves error summary, if there is one. +func (v *OleError) Description() string { + return v.description +} + +// SubError returns parent error, if there is one. +func (v *OleError) SubError() error { + return v.subError +} diff --git a/error_func.go b/error_func.go new file mode 100644 index 0000000..1b8de2d --- /dev/null +++ b/error_func.go @@ -0,0 +1,7 @@ +// +build !windows + +package ole + +func errstr(errno int) string { + return "" +} diff --git a/error_windows.go b/error_windows.go new file mode 100644 index 0000000..fbb8f70 --- /dev/null +++ b/error_windows.go @@ -0,0 +1,23 @@ +// +build windows + +package ole + +import ( + "fmt" + "syscall" + "unicode/utf16" +) + +func errstr(errno int) string { + // ask windows for the remaining errors + var flags uint32 = syscall.FORMAT_MESSAGE_FROM_SYSTEM | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS + b := make([]uint16, 300) + n, err := syscall.FormatMessage(flags, 0, uint32(errno), 0, b, nil) + if err != nil { + return fmt.Sprintf("error %d (FormatMessage failed with: %v)", errno, err) + } + // trim terminating \r and \n + for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- { + } + return string(utf16.Decode(b[:n])) +} diff --git a/guid.go b/guid.go new file mode 100644 index 0000000..8c05c3d --- /dev/null +++ b/guid.go @@ -0,0 +1,52 @@ +package ole + +var ( + // NullInterfaceID is null Interface ID, used when no other Interface ID is known. + IID_NULL = &GUID{0x00000000, 0x0000, 0x0000, [8]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} + + // IUnknownInterfaceID is for IUnknown interfaces. + IID_IUnknown = &GUID{0x00000000, 0x0000, 0x0000, [8]byte{0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}} + + // IDispatchInterfaceID is for IDispatch interfaces. + IID_IDispatch = &GUID{0x00020400, 0x0000, 0x0000, [8]byte{0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}} + + // IConnectionPointContainerInterfaceID is for IConnectionPointContainer interfaces. + IID_IConnectionPointContainer = &GUID{0xB196B284, 0xBAB4, 0x101A, [8]byte{0xB6, 0x9C, 0x00, 0xAA, 0x00, 0x34, 0x1D, 0x07}} + + // IConnectionPointInterfaceID is for IConnectionPoint interfaces. + IID_IConnectionPoint = &GUID{0xB196B286, 0xBAB4, 0x101A, [8]byte{0xB6, 0x9C, 0x00, 0xAA, 0x00, 0x34, 0x1D, 0x07}} + + // IInspectableInterfaceID is for IInspectable interfaces. + IID_IInspectable = &GUID{0xaf86e2e0, 0xb12d, 0x4c6a, [8]byte{0x9c, 0x5a, 0xd7, 0xaa, 0x65, 0x10, 0x1e, 0x90}} + + // IProvideClassInfoInterfaceID is for IProvideClassInfo interfaces. + IID_IProvideClassInfo = &GUID{0xb196b283, 0xbab4, 0x101a, [8]byte{0xB6, 0x9C, 0x00, 0xAA, 0x00, 0x34, 0x1D, 0x07}} +) + +// GUID is Windows API specific GUID type. +// +// This exists to match Windows GUID type for direct passing for COM. +// Format is in xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx. +type GUID struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} + +// IsEqualGUID compares two GUID. +// +// Not constant time comparison. +func IsEqualGUID(guid1 *GUID, guid2 *GUID) bool { + return guid1.Data1 == guid2.Data1 && + guid1.Data2 == guid2.Data2 && + guid1.Data3 == guid2.Data3 && + guid1.Data4[0] == guid2.Data4[0] && + guid1.Data4[1] == guid2.Data4[1] && + guid1.Data4[2] == guid2.Data4[2] && + guid1.Data4[3] == guid2.Data4[3] && + guid1.Data4[4] == guid2.Data4[4] && + guid1.Data4[5] == guid2.Data4[5] && + guid1.Data4[6] == guid2.Data4[6] && + guid1.Data4[7] == guid2.Data4[7] +} diff --git a/iunknown.go b/iunknown.go index 5fb765f..0cb8b66 100644 --- a/iunknown.go +++ b/iunknown.go @@ -1,11 +1,6 @@ -// +build windows - package ole -import ( - "syscall" - "unsafe" -) +import "unsafe" type IUnknown struct { RawVTable *interface{} @@ -27,14 +22,13 @@ func (v *IUnknown) VTable() *IUnknownVtbl { return (*IUnknownVtbl)(unsafe.Pointer(v.RawVTable)) } -func (v *IUnknown) QueryInterface(iid *GUID) (disp *IDispatch, err error) { - disp, err = queryInterface(v, iid) - return +func (v *IUnknown) QueryInterface(iid *GUID) (*IDispatch, error) { + return queryInterface(v, iid) } -func (v *IUnknown) MustQueryInterface(iid *GUID) (disp *IDispatch) { +func (v *IUnknown) MustQueryInterface(iid *GUID) *IDispatch { disp, _ = queryInterface(v, iid) - return + return disp } func (v *IUnknown) AddRef() int32 { @@ -44,36 +38,3 @@ func (v *IUnknown) AddRef() int32 { func (v *IUnknown) Release() int32 { return release(v) } - -func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) { - hr, _, _ := syscall.Syscall( - unk.VTable().QueryInterface, - 3, - uintptr(unsafe.Pointer(unk)), - uintptr(unsafe.Pointer(iid)), - uintptr(unsafe.Pointer(&disp))) - if hr != 0 { - err = NewError(hr) - } - return -} - -func addRef(unk *IUnknown) int32 { - ret, _, _ := syscall.Syscall( - unk.VTable().AddRef, - 1, - uintptr(unsafe.Pointer(unk)), - 0, - 0) - return int32(ret) -} - -func release(unk *IUnknown) int32 { - ret, _, _ := syscall.Syscall( - unk.VTable().Release, - 1, - uintptr(unsafe.Pointer(unk)), - 0, - 0) - return int32(ret) -} diff --git a/iunknown_func.go b/iunknown_func.go new file mode 100644 index 0000000..a1478d6 --- /dev/null +++ b/iunknown_func.go @@ -0,0 +1,15 @@ +// +build !windows + +package ole + +func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) { + return nil, NewError(E_NOTIMPL) +} + +func addRef(unk *IUnknown) int32 { + return 0 +} + +func release(unk *IUnknown) int32 { + return 0 +} diff --git a/iunknown_windows.go b/iunknown_windows.go new file mode 100644 index 0000000..f69fae2 --- /dev/null +++ b/iunknown_windows.go @@ -0,0 +1,41 @@ +// +build windows + +package ole + +import ( + "syscall" + "unsafe" +) + +func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) { + hr, _, _ := syscall.Syscall( + unk.VTable().QueryInterface, + 3, + uintptr(unsafe.Pointer(unk)), + uintptr(unsafe.Pointer(iid)), + uintptr(unsafe.Pointer(&disp))) + if hr != 0 { + err = NewError(hr) + } + return +} + +func addRef(unk *IUnknown) int32 { + ret, _, _ := syscall.Syscall( + unk.VTable().AddRef, + 1, + uintptr(unsafe.Pointer(unk)), + 0, + 0) + return int32(ret) +} + +func release(unk *IUnknown) int32 { + ret, _, _ := syscall.Syscall( + unk.VTable().Release, + 1, + uintptr(unsafe.Pointer(unk)), + 0, + 0) + return int32(ret) +} diff --git a/ole.go b/ole.go index d3a3dfa..9e72668 100644 --- a/ole.go +++ b/ole.go @@ -1,69 +1,10 @@ -// +build windows - package ole import ( "fmt" "strings" - "syscall" - "unicode/utf16" ) -type OleError struct { - hr uintptr - description string - subError error -} - -func errstr(errno int) string { - // ask windows for the remaining errors - var flags uint32 = syscall.FORMAT_MESSAGE_FROM_SYSTEM | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS - b := make([]uint16, 300) - n, err := syscall.FormatMessage(flags, 0, uint32(errno), 0, b, nil) - if err != nil { - return fmt.Sprintf("error %d (FormatMessage failed with: %v)", errno, err) - } - // trim terminating \r and \n - for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- { - } - return string(utf16.Decode(b[:n])) -} - -func NewError(hr uintptr) *OleError { - return &OleError{hr: hr} -} - -func NewErrorWithDescription(hr uintptr, description string) *OleError { - return &OleError{hr: hr, description: description} -} - -func NewErrorWithSubError(hr uintptr, description string, err error) *OleError { - return &OleError{hr: hr, description: description, subError: err} -} - -func (v *OleError) Code() uintptr { - return uintptr(v.hr) -} - -func (v *OleError) String() string { - if v.description != "" { - return errstr(int(v.hr)) + " (" + v.description + ")" - } - return errstr(int(v.hr)) -} - -func (v *OleError) Error() string { - return v.String() -} - -func (v *OleError) Description() string { - return v.description -} - -func (v *OleError) SubError() error { - return v.subError -} - type DISPPARAMS struct { rgvarg uintptr rgdispidNamedArgs uintptr @@ -71,6 +12,7 @@ type DISPPARAMS struct { cNamedArgs uint32 } +// EXCEPINFO defines exception info. type EXCEPINFO struct { wCode uint16 wReserved uint16 @@ -83,6 +25,7 @@ type EXCEPINFO struct { scode uint32 } +// String convert EXCEPINFO to string. func (e EXCEPINFO) String() string { var src, desc, hlp string if e.bstrSource == nil { @@ -109,6 +52,7 @@ func (e EXCEPINFO) String() string { ) } +// Error implements error interface and returns error string. func (e EXCEPINFO) Error() string { if e.bstrDescription != nil { return strings.TrimSpace(BstrToString(e.bstrDescription)) @@ -127,11 +71,13 @@ func (e EXCEPINFO) Error() string { return fmt.Sprintf("%v: %#x", src, code) } +// PARAMDATA defines parameter data type. type PARAMDATA struct { Name *int16 Vt uint16 } +// METHODDATA defines method info. type METHODDATA struct { Name *uint16 Data *PARAMDATA @@ -143,16 +89,19 @@ type METHODDATA struct { VtReturn uint32 } +// INTERFACEDATA defines interface info. type INTERFACEDATA struct { MethodData *METHODDATA CMembers uint32 } +// Point is 2D vector type. type Point struct { X int32 Y int32 } +// Msg is message between processes. type Msg struct { Hwnd uint32 Message uint32 @@ -162,16 +111,19 @@ type Msg struct { Pt Point } +// TYPEDESC defines data type. type TYPEDESC struct { Hreftype uint32 VT uint16 } +// IDLDESC defines IDL info. type IDLDESC struct { DwReserved uint32 WIDLFlags uint16 } +// TYPEATTR defines type info. type TYPEATTR struct { Guid GUID Lcid uint32 diff --git a/utility.go b/utility.go index 346a782..2c27235 100644 --- a/utility.go +++ b/utility.go @@ -1,5 +1,3 @@ -// +build windows - package ole import ( @@ -7,20 +5,7 @@ import ( "unsafe" ) -func IsEqualGUID(guid1 *GUID, guid2 *GUID) bool { - return guid1.Data1 == guid2.Data1 && - guid1.Data2 == guid2.Data2 && - guid1.Data3 == guid2.Data3 && - guid1.Data4[0] == guid2.Data4[0] && - guid1.Data4[1] == guid2.Data4[1] && - guid1.Data4[2] == guid2.Data4[2] && - guid1.Data4[3] == guid2.Data4[3] && - guid1.Data4[4] == guid2.Data4[4] && - guid1.Data4[5] == guid2.Data4[5] && - guid1.Data4[6] == guid2.Data4[6] && - guid1.Data4[7] == guid2.Data4[7] -} - +// BytePtrToString converts byte pointer to a Go string. func BytePtrToString(p *byte) string { a := (*[10000]uint8)(unsafe.Pointer(p)) i := 0 @@ -30,11 +15,14 @@ func BytePtrToString(p *byte) string { return string(a[:i]) } -// Alias for LpOleStrToString - kept for compatibility reasons +// UTF16PtrToString is alias for LpOleStrToString. +// +// Kept for compatibility reasons. func UTF16PtrToString(p *uint16) string { return LpOleStrToString(p) } +// LpOleStrToString converts COM Unicode to Go string. func LpOleStrToString(p *uint16) string { if p == nil { return "" @@ -53,6 +41,7 @@ func LpOleStrToString(p *uint16) string { return string(utf16.Decode(a)) } +// BstrToString converts COM binary string to Go string. func BstrToString(p *uint16) string { if p == nil { return "" @@ -69,6 +58,7 @@ func BstrToString(p *uint16) string { return string(utf16.Decode(a)) } +// lpOleStrLen returns the length of Unicode string. func lpOleStrLen(p *uint16) (length int64) { if p == nil { return 0 @@ -86,6 +76,7 @@ func lpOleStrLen(p *uint16) (length int64) { return } +// convertHresultToError converts syscall to error, if call is unsuccessful. func convertHresultToError(hr uintptr, r2 uintptr, ignore error) (err error) { if hr != 0 { err = NewError(hr) diff --git a/variables.go b/variables.go index e05134f..ebe00f1 100644 --- a/variables.go +++ b/variables.go @@ -13,19 +13,4 @@ var ( modoleaut32, _ = syscall.LoadDLL("oleaut32.dll") modmsvcrt, _ = syscall.LoadDLL("msvcrt.dll") moduser32, _ = syscall.LoadDLL("user32.dll") - - IID_NULL = &GUID{0x00000000, 0x0000, 0x0000, [8]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} - IID_IUnknown = &GUID{0x00000000, 0x0000, 0x0000, [8]byte{0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}} - IID_IDispatch = &GUID{0x00020400, 0x0000, 0x0000, [8]byte{0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}} - IID_IConnectionPointContainer = &GUID{0xB196B284, 0xBAB4, 0x101A, [8]byte{0xB6, 0x9C, 0x00, 0xAA, 0x00, 0x34, 0x1D, 0x07}} - IID_IConnectionPoint = &GUID{0xB196B286, 0xBAB4, 0x101A, [8]byte{0xB6, 0x9C, 0x00, 0xAA, 0x00, 0x34, 0x1D, 0x07}} - IID_IInspectable = &GUID{0xaf86e2e0, 0xb12d, 0x4c6a, [8]byte{0x9c, 0x5a, 0xd7, 0xaa, 0x65, 0x10, 0x1e, 0x90}} - IID_IProvideClassInfo = &GUID{0xb196b283, 0xbab4, 0x101a, [8]byte{0xB6, 0x9C, 0x00, 0xAA, 0x00, 0x34, 0x1D, 0x07}} ) - -type GUID struct { - Data1 uint32 - Data2 uint16 - Data3 uint16 - Data4 [8]byte -} diff --git a/variant.go b/variant.go index 46000c7..9d7f722 100644 --- a/variant.go +++ b/variant.go @@ -1,13 +1,13 @@ -// +build windows - package ole import "unsafe" +// NewVariant returns new variant based on type and value. func NewVariant(vt VT, val int64) VARIANT { return VARIANT{VT: vt, Val: val} } +// ToIUnknown converts Variant to Unknown object. func (v *VARIANT) ToIUnknown() *IUnknown { if v.VT != VT_UNKNOWN { return nil @@ -15,6 +15,7 @@ func (v *VARIANT) ToIUnknown() *IUnknown { return (*IUnknown)(unsafe.Pointer(uintptr(v.Val))) } +// ToIDispatch converts variant to dispatch object. func (v *VARIANT) ToIDispatch() *IDispatch { if v.VT != VT_DISPATCH { return nil @@ -22,6 +23,7 @@ func (v *VARIANT) ToIDispatch() *IDispatch { return (*IDispatch)(unsafe.Pointer(uintptr(v.Val))) } +// ToArray converts variant to SafeArray helper. func (v *VARIANT) ToArray() *SafeArrayConversion { if v.VT != VT_SAFEARRAY { return nil @@ -30,6 +32,7 @@ func (v *VARIANT) ToArray() *SafeArrayConversion { return &SafeArrayConversion{safeArray} } +// ToString converts variant to Go string. func (v *VARIANT) ToString() string { if v.VT != VT_BSTR { return "" @@ -37,14 +40,18 @@ func (v *VARIANT) ToString() string { return BstrToString(*(**uint16)(unsafe.Pointer(&v.Val))) } +// Clear the memory of variant object. func (v *VARIANT) Clear() error { return VariantClear(v) } -// Returns v's value based on its VALTYPE. +// Value returns variant value based on its type. +// // Currently supported types: 2- and 4-byte integers, strings, bools. // Note that 64-bit integers, datetimes, and other types are stored as strings // and will be returned as strings. +// +// Needs to be further converted, because this returns an interface{}. func (v *VARIANT) Value() interface{} { switch v.VT { case VT_I1: diff --git a/variant_386.go b/variant_386.go index fe66afe..e73736b 100644 --- a/variant_386.go +++ b/variant_386.go @@ -1,5 +1,4 @@ // +build 386 -// +build windows package ole diff --git a/variant_amd64.go b/variant_amd64.go index c48f6ea..dccdde1 100644 --- a/variant_amd64.go +++ b/variant_amd64.go @@ -1,5 +1,4 @@ // +build amd64 -// +build windows package ole diff --git a/winrt.go b/winrt.go index 7d3e5ae..4e9eca7 100644 --- a/winrt.go +++ b/winrt.go @@ -59,8 +59,10 @@ func RoGetActivationFactory(clsid string, iid *GUID) (ins *IInspectable, err err return } +// HString is handle string for pointers. type HString uintptr +// NewHString returns a new HString for Go string. func NewHString(s string) (hstring HString, err error) { u16 := syscall.StringToUTF16Ptr(s) len := uint32(utf8.RuneCountInString(s)) @@ -74,6 +76,7 @@ func NewHString(s string) (hstring HString, err error) { return } +// DeleteHString deletes HString. func DeleteHString(hstring HString) (err error) { hr, _, _ := procWindowsDeleteString.Call(uintptr(hstring)) if hr != 0 { @@ -82,6 +85,7 @@ func DeleteHString(hstring HString) (err error) { return } +// String returns Go string value of HString. func (h HString) String() string { var u16buf uintptr var u16len uint32 diff --git a/winrt_doc.go b/winrt_doc.go new file mode 100644 index 0000000..e59c776 --- /dev/null +++ b/winrt_doc.go @@ -0,0 +1,33 @@ +// +build !windows + +package ole + +func RoInitialize(thread_type uint32) (err error) { + return NewError(E_NOTIMPL) +} + +func RoActivateInstance(clsid string) (ins *IInspectable, err error) { + return nil, NewError(E_NOTIMPL) +} + +func RoGetActivationFactory(clsid string, iid *GUID) (ins *IInspectable, err error) { + return nil, NewError(E_NOTIMPL) +} + +// HString is handle string for pointers. +type HString uintptr + +// NewHString returns a new HString for Go string. +func NewHString(s string) (hstring HString, err error) { + return HString(uintptr(0)), NewError(E_NOTIMPL) +} + +// DeleteHString deletes HString. +func DeleteHString(hstring HString) (err error) { + return NewError(E_NOTIMPL) +} + +// String returns Go string value of HString. +func (h HString) String() string { + return "" +}