github.com/scalingdata/go-ole@v1.2.0/com.go (about)

     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(unsafe.Pointer(&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), nil), nil
   327  	}
   328  	return time.Now(), errors.New("Could not convert to time, passing current time.")
   329  }