Hello, COM(Go) World!

Posted on 5月 31st, 2012 by cx20

COM(Go)

COM(Component Object Model)はマイクロソフトの提唱するプログラム部品の仕様である。
COM を用いて開発された部品であれば言語を問わず利用することができる。
以下は Go言語 による COM クライアントの例となっている。

ソースコード

package main
 
import (
    "syscall" 
    "unsafe" 
)
 
const (
    CLSCTX_INPROC_SERVER   = 1
    CLSCTX_LOCAL_SERVER    = 4
 
    DISPATCH_METHOD        = 1
    DISPATCH_PROPERTYGET   = 2
    DISPATCH_PROPERTYPUT   = 4
 
    VT_EMPTY           = 0x0
    VT_NULL            = 0x1
    VT_I2              = 0x2
    VT_I4              = 0x3
    VT_R4              = 0x4
    VT_R8              = 0x5
    VT_CY              = 0x6
    VT_DATE            = 0x7
    VT_BSTR            = 0x8
    VT_DISPATCH        = 0x9
    VT_ERROR           = 0xa
    VT_BOOL            = 0xb
    VT_VARIANT         = 0xc
    VT_UNKNOWN         = 0xd
    VT_DECIMAL         = 0xe
    VT_I1              = 0x10
    VT_UI1             = 0x11
    VT_UI2             = 0x12
    VT_UI4             = 0x13
    VT_I8              = 0x14
    VT_UI8             = 0x15
    VT_INT             = 0x16
    VT_UINT            = 0x17
    VT_VOID            = 0x18
    VT_VECTOR          = 0x1000
    VT_ARRAY           = 0x2000
    VT_BYREF           = 0x4000
    VT_RESERVED        = 0x8000
)
 
type DISPPARAMS struct {
    rgvarg              uintptr
    rgdispidNamedArgs   uintptr
    cArgs               uint32
    cNamedArgs          uint32
}
 
type VARIANT struct {
    VT                  uint16
    wReserved1          uint16
    wReserved2          uint16
    wReserved3          uint16
    Val                 int64
}
 
type GUID struct {
    Data1               uint32
    Data2               uint16
    Data3               uint16
    Data4               [8]byte
}
 
type IDispatch struct {
    lpVtbl *pIDispatchVtbl
}
 
type pIDispatchVtbl struct {
    pQueryInterface     uintptr
    pAddRef             uintptr
    pRelease            uintptr
    pGetTypeInfoCount   uintptr
    pGetTypeInfo        uintptr
    pGetIDsOfNames      uintptr
    pInvoke             uintptr
}
 
var ( 
    ole32, _                  = syscall.LoadLibrary("ole32.dll")
    oleaut32, _               = syscall.LoadLibrary("oleaut32.dll")
    kernel32, _               = syscall.LoadLibrary("kernel32.dll")
 
    procCoInitialize, _       = syscall.GetProcAddress(ole32, "CoInitialize")
    procCoUninitialize, _     = syscall.GetProcAddress(ole32, "CoUninitialize")
    procCoCreateInstance, _   = syscall.GetProcAddress(ole32, "CoCreateInstance")
    procCLSIDFromProgID, _    = syscall.GetProcAddress(ole32, "CLSIDFromProgID")
 
    procVariantInit, _        = syscall.GetProcAddress(oleaut32, "VariantInit")
    procSysAllocString, _     = syscall.GetProcAddress(oleaut32, "SysAllocString")
    procSysFreeString, _      = syscall.GetProcAddress(oleaut32, "SysFreeString")
 
    procGetUserDefaultLCID, _ = syscall.GetProcAddress(kernel32, "GetUserDefaultLCID")
 
    IID_NULL                  = &GUID{ 0x00000000, 0x0000, 0x0000, [8]byte{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
    IID_IDispatch             = &GUID{ 0x00020400, 0x0000, 0x0000, [8]byte{ 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }
)
 
func CoInitialize(p uintptr) (hr uintptr) {
    hr, _, _ = syscall.Syscall(uintptr(procCoInitialize), 1, p, 0, 0)
    return
}
 
func CoUninitialize() {
    syscall.Syscall(uintptr(procCoUninitialize), 0, 0, 0, 0)
}
 
func CLSIDFromProgID(progId string) (clsid *GUID, hr uintptr) {
    var guid GUID
    hr, _, _ = syscall.Syscall( uintptr(procCLSIDFromProgID), 2,
        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId))),
        uintptr(unsafe.Pointer(&guid)), 0)
    clsid = &guid
    return
}
 
func CreateInstance(clsid *GUID, iid *GUID) (unk *IDispatch, hr uintptr) {
    hr, _, _ = syscall.Syscall6( uintptr(procCoCreateInstance), 5,
        uintptr(unsafe.Pointer(clsid)),
        0,
        CLSCTX_INPROC_SERVER,
        uintptr(unsafe.Pointer(iid)),
        uintptr(unsafe.Pointer(&unk)),
        0)
    return
}
 
func (disp *IDispatch) GetIDsOfNames(names []string) (dispid []int32, hr uintptr) {
    wnames := make([]*uint16, len(names))
    for i := 0; i < len(names); i++ {
        wnames[i] = syscall.StringToUTF16Ptr(names[i])
    }
    dispid = make([]int32, len(names))
    hr, _, _ = syscall.Syscall6( disp.lpVtbl.pGetIDsOfNames, 6,
        uintptr(unsafe.Pointer(disp)),
        uintptr(unsafe.Pointer(IID_NULL)),
        uintptr(unsafe.Pointer(&wnames[0])),
        uintptr(len(names)),
        uintptr(GetUserDefaultLCID()),
        uintptr(unsafe.Pointer(&dispid[0])) )
    return
}
 
func (disp *IDispatch) Invoke(dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, hr uintptr) {
    var dispparams DISPPARAMS
    var vargs []VARIANT
    if len(params) > 0 {
        vargs = make([]VARIANT, len(params))
        for i, v := range params {
            n := len(params) - i - 1
            VariantInit(&vargs[n])
            switch v.(type) {
            case bool:
                if v.(bool) {
                    vargs[n] = VARIANT{VT_BOOL, 0, 0, 0, 0xffff}
                } else {
                    vargs[n] = VARIANT{VT_BOOL, 0, 0, 0, 0}
                }
            case int16:
                vargs[n] = VARIANT{VT_I2, 0, 0, 0, int64(v.(int16))}
            case float32:
                vargs[n] = VARIANT{VT_R4, 0, 0, 0, int64(v.(float32))}
            case float64:
                vargs[n] = VARIANT{VT_R8, 0, 0, 0, int64(v.(float64))}
            case byte:
                vargs[n] = VARIANT{VT_I1, 0, 0, 0, int64(v.(byte))}
            case uint16:
                vargs[n] = VARIANT{VT_UI2, 0, 0, 0, int64(v.(int16))}
            case int, int32:
                vargs[n] = VARIANT{VT_UI4, 0, 0, 0, int64(v.(int))}
            case uint, uint32:
                vargs[n] = VARIANT{VT_UI4, 0, 0, 0, int64(v.(uint))}
            case string:
                vargs[n] = VARIANT{VT_BSTR, 0, 0, 0, int64(uintptr(unsafe.Pointer(SysAllocString(v.(string)))))}
            case *IDispatch:
                vargs[n] = VARIANT{VT_DISPATCH, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*IDispatch))))}
            case *bool:
                vargs[n] = VARIANT{VT_BOOL | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*bool))))}
            case *byte:
                vargs[n] = VARIANT{VT_I1 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*byte))))}
            case *int16:
                vargs[n] = VARIANT{VT_I2 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*int16))))}
            case *uint16:
                vargs[n] = VARIANT{VT_UI2 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*uint16))))}
            case *int, *int32:
                vargs[n] = VARIANT{VT_I4 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*int))))}
            case *uint, *uint32:
                vargs[n] = VARIANT{VT_UI4 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*uint))))}
            case *float32:
                vargs[n] = VARIANT{VT_R4 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*float32))))}
            case *float64:
                vargs[n] = VARIANT{VT_R8 | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*float64))))}
            case *string:
                vargs[n] = VARIANT{VT_BSTR | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*string))))}
            case **IDispatch:
                vargs[n] = VARIANT{VT_DISPATCH | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(**IDispatch))))}
            case *VARIANT:
                vargs[n] = VARIANT{VT_VARIANT | VT_BYREF, 0, 0, 0, int64(uintptr(unsafe.Pointer(v.(*VARIANT))))}
            case nil:
                vargs[n] = VARIANT{VT_NULL, 0, 0, 0, 0}
            default:
                panic("unknown type")
            }
        }
        dispparams.rgvarg = uintptr(unsafe.Pointer(&vargs[0]))
        dispparams.cArgs = uint32(len(params))
    }
 
    result = new(VARIANT)
    VariantInit(result)
    hr, _, _ = syscall.Syscall9( disp.lpVtbl.pInvoke, 9,
        uintptr(unsafe.Pointer(disp)),
        uintptr(dispid),
        uintptr(unsafe.Pointer(IID_NULL)),
        uintptr(GetUserDefaultLCID()),
        uintptr(dispatch),
        uintptr(unsafe.Pointer(&dispparams)),
        uintptr(unsafe.Pointer(result)),
        0,
        0)
    for _, varg := range vargs {
        if varg.VT == VT_BSTR && varg.Val != 0 {
            SysFreeString(((*int16)(unsafe.Pointer(uintptr(varg.Val)))))
        }
    }
    return
}
 
func GetUserDefaultLCID() (lcid uint32) {
    ret, _, _ := syscall.Syscall(uintptr(procGetUserDefaultLCID), 0, 0, 0, 0)
    lcid = uint32(ret)
    return
}
 
func VariantInit(v *VARIANT) (hr uintptr) {
    hr, _, _ = syscall.Syscall( uintptr(procVariantInit), 1, uintptr(unsafe.Pointer(v)), 0, 0)
    return
}
 
func SysAllocString(v string) (ss *int16) {
    pss, _, _ := syscall.Syscall( uintptr(procSysAllocString), 1, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v))), 0, 0)
    ss = (*int16)(unsafe.Pointer(pss))
    return
}
 
func SysFreeString(v *int16) (hr uintptr) {
    hr, _, _ = syscall.Syscall( uintptr(procSysFreeString), 1, uintptr(unsafe.Pointer(v)), 0, 0)
    return
}
 
func main() {
    CoInitialize(0)
 
    clsid, _ := CLSIDFromProgID("Shell.Application")
    disp, _ := CreateInstance(clsid, IID_IDispatch)
    dispid, _ := disp.GetIDsOfNames([]string{"BrowseForFolder"})
    disp.Invoke(dispid[0], DISPATCH_METHOD, 0, "Hello, COM(Go) World!", 0, 36)
 
    CoUninitialize()
}

実行方法

C:¥> go build hello.go

実行結果

+----------------------------------------+
|Browse For Folder                    [X]|
+----------------------------------------+
| Hello, COM(Go) Wolrd!                  |
|                                        |
| +------------------------------------+ |
| |[Windows]                           | |
| | +[addins]                          | |
| | +[AppCompat]                       | |
| | +[AppPatch]                        | |
| | +[assembly]                        | |
| |     :                              | |
| |     :                              | |
| |     :                              | |
| +------------------------------------+ |
| [Make New Folder]    [  OK  ] [Cancel] |
+----------------------------------------+

Tags:

Categories: COM, Go, library

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

WP-SpamFree by Pole Position Marketing