github.com/hspan/go-ole@v0.0.0/com.go (about)

     1  // +build windows
     2  
     3  package ole
     4  
     5  import (
     6  	"syscall"
     7  	"unicode/utf16"
     8  	"unsafe"
     9  )
    10  
    11  var (
    12  	procCoInitialize, _            = modole32.FindProc("CoInitialize")
    13  	procCoInitializeEx, _          = modole32.FindProc("CoInitializeEx")
    14  	procCoUninitialize, _          = modole32.FindProc("CoUninitialize")
    15  	procCoCreateInstance, _        = modole32.FindProc("CoCreateInstance")
    16  	procCoTaskMemFree, _           = modole32.FindProc("CoTaskMemFree")
    17  	procCLSIDFromProgID, _         = modole32.FindProc("CLSIDFromProgID")
    18  	procCLSIDFromString, _         = modole32.FindProc("CLSIDFromString")
    19  	procStringFromCLSID, _         = modole32.FindProc("StringFromCLSID")
    20  	procStringFromIID, _           = modole32.FindProc("StringFromIID")
    21  	procIIDFromString, _           = modole32.FindProc("IIDFromString")
    22  	procCoGetObject, _             = modole32.FindProc("CoGetObject")
    23  	procGetUserDefaultLCID, _      = modkernel32.FindProc("GetUserDefaultLCID")
    24  	procCopyMemory, _              = modkernel32.FindProc("RtlMoveMemory")
    25  	procVariantInit, _             = modoleaut32.FindProc("VariantInit")
    26  	procVariantClear, _            = modoleaut32.FindProc("VariantClear")
    27  	procVariantTimeToSystemTime, _ = modoleaut32.FindProc("VariantTimeToSystemTime")
    28  	procSysAllocString, _          = modoleaut32.FindProc("SysAllocString")
    29  	procSysAllocStringLen, _       = modoleaut32.FindProc("SysAllocStringLen")
    30  	procSysFreeString, _           = modoleaut32.FindProc("SysFreeString")
    31  	procSysStringLen, _            = modoleaut32.FindProc("SysStringLen")
    32  	procCreateDispTypeInfo, _      = modoleaut32.FindProc("CreateDispTypeInfo")
    33  	procCreateStdDispatch, _       = modoleaut32.FindProc("CreateStdDispatch")
    34  	procGetActiveObject, _         = modoleaut32.FindProc("GetActiveObject")
    35  
    36  	procGetMessageW, _      = moduser32.FindProc("GetMessageW")
    37  	procDispatchMessageW, _ = moduser32.FindProc("DispatchMessageW")
    38  )
    39  
    40  // coInitialize initializes COM library on current thread.
    41  //
    42  // MSDN documentation suggests that this function should not be called. Call
    43  // CoInitializeEx() instead. The reason has to do with threading and this
    44  // function is only for single-threaded apartments.
    45  //
    46  // That said, most users of the library have gotten away with just this
    47  // function. If you are experiencing threading issues, then use
    48  // CoInitializeEx().
    49  func coInitialize() (err error) {
    50  	// http://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx
    51  	// Suggests that no value should be passed to CoInitialized.
    52  	// Could just be Call() since the parameter is optional. <-- Needs testing to be sure.
    53  	hr, _, _ := procCoInitialize.Call(uintptr(0))
    54  	if hr != 0 {
    55  		err = NewError(hr)
    56  	}
    57  	return
    58  }
    59  
    60  // coInitializeEx initializes COM library with concurrency model.
    61  func coInitializeEx(coinit uint32) (err error) {
    62  	// http://msdn.microsoft.com/en-us/library/windows/desktop/ms695279(v=vs.85).aspx
    63  	// Suggests that the first parameter is not only optional but should always be NULL.
    64  	hr, _, _ := procCoInitializeEx.Call(uintptr(0), uintptr(coinit))
    65  	if hr != 0 {
    66  		err = NewError(hr)
    67  	}
    68  	return
    69  }
    70  
    71  // CoInitialize initializes COM library on current thread.
    72  //
    73  // MSDN documentation suggests that this function should not be called. Call
    74  // CoInitializeEx() instead. The reason has to do with threading and this
    75  // function is only for single-threaded apartments.
    76  //
    77  // That said, most users of the library have gotten away with just this
    78  // function. If you are experiencing threading issues, then use
    79  // CoInitializeEx().
    80  func CoInitialize(p uintptr) (err error) {
    81  	// p is ignored and won't be used.
    82  	// Avoid any variable not used errors.
    83  	p = uintptr(0)
    84  	return coInitialize()
    85  }
    86  
    87  // CoInitializeEx initializes COM library with concurrency model.
    88  func CoInitializeEx(p uintptr, coinit uint32) (err error) {
    89  	// Avoid any variable not used errors.
    90  	p = uintptr(0)
    91  	return coInitializeEx(coinit)
    92  }
    93  
    94  // CoUninitialize uninitializes COM Library.
    95  func CoUninitialize() {
    96  	procCoUninitialize.Call()
    97  }
    98  
    99  // CoTaskMemFree frees memory pointer.
   100  func CoTaskMemFree(memptr uintptr) {
   101  	procCoTaskMemFree.Call(memptr)
   102  }
   103  
   104  // CLSIDFromProgID retrieves Class Identifier with the given Program Identifier.
   105  //
   106  // The Programmatic Identifier must be registered, because it will be looked up
   107  // in the Windows Registry. The registry entry has the following keys: CLSID,
   108  // Insertable, Protocol and Shell
   109  // (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx).
   110  //
   111  // programID identifies the class id with less precision and is not guaranteed
   112  // to be unique. These are usually found in the registry under
   113  // HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of
   114  // "Program.Component.Version" with version being optional.
   115  //
   116  // CLSIDFromProgID in Windows API.
   117  func CLSIDFromProgID(progId string) (clsid *GUID, err error) {
   118  	var guid GUID
   119  	lpszProgID := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId)))
   120  	hr, _, _ := procCLSIDFromProgID.Call(lpszProgID, uintptr(unsafe.Pointer(&guid)))
   121  	if hr != 0 {
   122  		err = NewError(hr)
   123  	}
   124  	clsid = &guid
   125  	return
   126  }
   127  
   128  // CLSIDFromString retrieves Class ID from string representation.
   129  //
   130  // This is technically the string version of the GUID and will convert the
   131  // string to object.
   132  //
   133  // CLSIDFromString in Windows API.
   134  func CLSIDFromString(str string) (clsid *GUID, err error) {
   135  	var guid GUID
   136  	lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str)))
   137  	hr, _, _ := procCLSIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid)))
   138  	if hr != 0 {
   139  		err = NewError(hr)
   140  	}
   141  	clsid = &guid
   142  	return
   143  }
   144  
   145  // StringFromCLSID returns GUID formated string from GUID object.
   146  func StringFromCLSID(clsid *GUID) (str string, err error) {
   147  	var p *uint16
   148  	hr, _, _ := procStringFromCLSID.Call(uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(&p)))
   149  	if hr != 0 {
   150  		err = NewError(hr)
   151  	}
   152  	str = LpOleStrToString(p)
   153  	return
   154  }
   155  
   156  // IIDFromString returns GUID from program ID.
   157  func IIDFromString(progId string) (clsid *GUID, err error) {
   158  	var guid GUID
   159  	lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId)))
   160  	hr, _, _ := procIIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid)))
   161  	if hr != 0 {
   162  		err = NewError(hr)
   163  	}
   164  	clsid = &guid
   165  	return
   166  }
   167  
   168  // StringFromIID returns GUID formatted string from GUID object.
   169  func StringFromIID(iid *GUID) (str string, err error) {
   170  	var p *uint16
   171  	hr, _, _ := procStringFromIID.Call(uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(&p)))
   172  	if hr != 0 {
   173  		err = NewError(hr)
   174  	}
   175  	str = LpOleStrToString(p)
   176  	return
   177  }
   178  
   179  // CreateInstance of single uninitialized object with GUID.
   180  func CreateInstance(clsid *GUID, iid *GUID) (unk *IUnknown, err error) {
   181  	if iid == nil {
   182  		iid = IID_IUnknown
   183  	}
   184  	hr, _, _ := procCoCreateInstance.Call(
   185  		uintptr(unsafe.Pointer(clsid)),
   186  		0,
   187  		CLSCTX_SERVER,
   188  		uintptr(unsafe.Pointer(iid)),
   189  		uintptr(unsafe.Pointer(&unk)))
   190  	if hr != 0 {
   191  		err = NewError(hr)
   192  	}
   193  	return
   194  }
   195  
   196  // GetActiveObject retrieves pointer to active object.
   197  func GetActiveObject(clsid *GUID, iid *GUID) (unk *IUnknown, err error) {
   198  	if iid == nil {
   199  		iid = IID_IUnknown
   200  	}
   201  	hr, _, _ := procGetActiveObject.Call(
   202  		uintptr(unsafe.Pointer(clsid)),
   203  		uintptr(unsafe.Pointer(iid)),
   204  		uintptr(unsafe.Pointer(&unk)))
   205  	if hr != 0 {
   206  		err = NewError(hr)
   207  	}
   208  	return
   209  }
   210  
   211  type BindOpts struct {
   212  	CbStruct          uint32
   213  	GrfFlags          uint32
   214  	GrfMode           uint32
   215  	TickCountDeadline uint32
   216  }
   217  
   218  // GetObject retrieves pointer to active object.
   219  func GetObject(programID string, bindOpts *BindOpts, iid *GUID) (unk *IUnknown, err error) {
   220  	if bindOpts != nil {
   221  		bindOpts.CbStruct = uint32(unsafe.Sizeof(BindOpts{}))
   222  	}
   223  	if iid == nil {
   224  		iid = IID_IUnknown
   225  	}
   226  	hr, _, _ := procCoGetObject.Call(
   227  		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(programID))),
   228  		uintptr(unsafe.Pointer(bindOpts)),
   229  		uintptr(unsafe.Pointer(iid)),
   230  		uintptr(unsafe.Pointer(&unk)))
   231  	if hr != 0 {
   232  		err = NewError(hr)
   233  	}
   234  	return
   235  }
   236  
   237  // VariantInit initializes variant.
   238  func VariantInit(v *VARIANT) (err error) {
   239  	hr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v)))
   240  	if hr != 0 {
   241  		err = NewError(hr)
   242  	}
   243  	return
   244  }
   245  
   246  // VariantClear clears value in Variant settings to VT_EMPTY.
   247  func VariantClear(v *VARIANT) (err error) {
   248  	hr, _, _ := procVariantClear.Call(uintptr(unsafe.Pointer(v)))
   249  	if hr != 0 {
   250  		err = NewError(hr)
   251  	}
   252  	return
   253  }
   254  
   255  // SysAllocString allocates memory for string and copies string into memory.
   256  func SysAllocString(v string) (ss *int16) {
   257  	pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v))))
   258  	ss = (*int16)(unsafe.Pointer(pss))
   259  	return
   260  }
   261  
   262  // SysAllocStringLen copies up to length of given string returning pointer.
   263  func SysAllocStringLen(v string) (ss *int16) {
   264  	utf16 := utf16.Encode([]rune(v + "\x00"))
   265  	ptr := &utf16[0]
   266  
   267  	pss, _, _ := procSysAllocStringLen.Call(uintptr(unsafe.Pointer(ptr)), uintptr(len(utf16)-1))
   268  	ss = (*int16)(unsafe.Pointer(pss))
   269  	return
   270  }
   271  
   272  // SysFreeString frees string system memory. This must be called with SysAllocString.
   273  func SysFreeString(v *int16) (err error) {
   274  	hr, _, _ := procSysFreeString.Call(uintptr(unsafe.Pointer(v)))
   275  	if hr != 0 {
   276  		err = NewError(hr)
   277  	}
   278  	return
   279  }
   280  
   281  // SysStringLen is the length of the system allocated string.
   282  func SysStringLen(v *int16) uint32 {
   283  	l, _, _ := procSysStringLen.Call(uintptr(unsafe.Pointer(v)))
   284  	return uint32(l)
   285  }
   286  
   287  // CreateStdDispatch provides default IDispatch implementation for IUnknown.
   288  //
   289  // This handles default IDispatch implementation for objects. It haves a few
   290  // limitations with only supporting one language. It will also only return
   291  // default exception codes.
   292  func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (disp *IDispatch, err error) {
   293  	hr, _, _ := procCreateStdDispatch.Call(
   294  		uintptr(unsafe.Pointer(unk)),
   295  		v,
   296  		uintptr(unsafe.Pointer(ptinfo)),
   297  		uintptr(unsafe.Pointer(&disp)))
   298  	if hr != 0 {
   299  		err = NewError(hr)
   300  	}
   301  	return
   302  }
   303  
   304  // CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch.
   305  //
   306  // This will not handle the full implementation of the interface.
   307  func CreateDispTypeInfo(idata *INTERFACEDATA) (pptinfo *IUnknown, err error) {
   308  	hr, _, _ := procCreateDispTypeInfo.Call(
   309  		uintptr(unsafe.Pointer(idata)),
   310  		uintptr(GetUserDefaultLCID()),
   311  		uintptr(unsafe.Pointer(&pptinfo)))
   312  	if hr != 0 {
   313  		err = NewError(hr)
   314  	}
   315  	return
   316  }
   317  
   318  // copyMemory moves location of a block of memory.
   319  func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) {
   320  	procCopyMemory.Call(uintptr(dest), uintptr(src), uintptr(length))
   321  }
   322  
   323  // GetUserDefaultLCID retrieves current user default locale.
   324  func GetUserDefaultLCID() (lcid uint32) {
   325  	ret, _, _ := procGetUserDefaultLCID.Call()
   326  	lcid = uint32(ret)
   327  	return
   328  }
   329  
   330  // GetMessage in message queue from runtime.
   331  //
   332  // This function appears to block. PeekMessage does not block.
   333  func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, err error) {
   334  	r0, _, err := procGetMessageW.Call(uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax))
   335  	ret = int32(r0)
   336  	return
   337  }
   338  
   339  // DispatchMessage to window procedure.
   340  func DispatchMessage(msg *Msg) (ret int32) {
   341  	r0, _, _ := procDispatchMessageW.Call(uintptr(unsafe.Pointer(msg)))
   342  	ret = int32(r0)
   343  	return
   344  }