FFI を学ぶ(3) : 自前のCで書いたCOM で MS-Access、SQL server にアクセスする。

小さいMDBですが、10000回連続読み込みでもOKでした。
XP では正常に動作するが、VISTAではSEGV になった。
XP ではmalloc して LSICD の値を保持しているので ((IDispatch *)pDisp)->lpVtbl->Release( (void *)pDisp) で開放したあと free(pDisp) していたが、VISTA では free(pDisp)の瞬間にSEGV と判明。
free(pDisp)は重複していたのだけれども、XPではSEGVにならなかったのだ。

{-# LANGUAGE CPP #-}
-- ghc --make Wxmdb.hs  winole.c -lole32 -loleaut32 -luuid  -o ole
module Main where

import Win32ole (Vmaterial(..), VariantFunc(..), HaskellDt(..),OLEobj, VARIANTobj, getProperty, variantToBool,
                 getObject, variantToInteger, c_OleInitialize, createInstance, c_OleUninitialize, variantToString,
                 toVARIANTobj, toVariantLS, c_VariantTypeCode, variantToDateTime, c_VariantToDouble, variantType, 
                 c_VariantToLong, callMethod, c_processMessage, c_ReleaseObject)
import Foreign.C.String (CString, CWString, newCString, newCWString, peekCString, withCWString)
import Foreign.C.Types  (CLong, CInt)
import Foreign.Ptr      (Ptr, castPtr)
import Foreign.Marshal.Alloc (free)
import System.IO.Unsafe      (unsafePerformIO)

--  /C/HaskellPlatform/com
mdbconn = "DRIVER={Microsoft Access Driver (*.mdb)};Dbq=C:\\HaskellPlatform\\com\\sample1.mdb ;"

-- sql     = "SELECT * FROM earthquake WHERE NumOfDeaths=37;"
sql     = "SELECT * FROM earthquake;"

rep 0 = putStrLn "Done."
rep n = do 
   db <- dbRead mdbconn sql
   mapM_ (\[HString name,HDateTime d, HDouble n, HInteger deaths, HString b] ->
           putStrLn $ 
             concat[ take 20 (name ++ (repeat ' ')),
                     show d," ",show n ," ",
                     reverse $ take 7 ((reverse (show deaths)) ++ (repeat ' '))," ",
                     show b])
         db
   putStrLn (show n)
   rep (n-1)

main = rep 1

-- 関東大震災          1923-09-01 00:00:00 UTC 7.9 142807    True
-- 北海道東方沖地震    1994-10-04 00:00:00 UTC 8.1 0         False
-- 阪神淡路大震災      1995-01-17 00:00:00 UTC 7.2 6418      True
-- 新潟県中越地震      2004-10-23 00:00:00 UTC 6.8 37        True

dbRead conStr sqlStr = do
    c_OleInitialize 0
    conn   <- createInstance "ADODB.Connection"
    dbOpen conn conStr
    rs     <- executSQL conn sqlStr
    fields <- getFieldsObject rs
    fName  <- getFiledsName rs fields
    print =<< mapM variantToString fName      -- => ["Name","Day","Magnitude","NumOfDeaths","DeadOrAlive"]
    vAllDT <- readDB rs fields fName
    print =<< vListToVnameList (head vAllDT)  -- => ["string","datetime","double","long","bool"]
    ret    <- convToHaskellType vAllDT
    dbClose conn fields rs fName
    c_OleUninitialize
    return ret

dbOpen :: OLEobj -> String -> IO()
dbOpen conn str = do
    variantSTR  <- toVARIANTobj (VString str)
    withCWString "Open" (\x -> callMethod conn x [variantSTR])
    free variantSTR

executSQL :: OLEobj -> String -> IO OLEobj
executSQL conn sql = do
    variant <- toVariantLS [VString sql] []
    objRs   <- withCWString "Execute" (\x -> getObject conn x variant)
    mapM_ free variant
    return objRs


cwsFields = unsafePerformIO $ newCWString "Fields"

getFieldsObject :: OLEobj -> IO OLEobj
getFieldsObject rs = getObject rs cwsFields []

dbClose :: OLEobj ->  OLEobj ->  OLEobj -> [VARIANTobj] -> IO ()
dbClose conn rs fields fName = do
    withCWString "Close" (\x-> callMethod  conn x [])
    mapM_ c_ReleaseObject [fields, rs, conn]
    mapM_ free fName

convToHaskellType :: [[VARIANTobj]] -> IO [[HaskellDt]]
convToHaskellType []     = return []
convToHaskellType vAllDT = do
    fArray <- variantToVfunc (head vAllDT) []
    vArrayToHarray fArray vAllDT 

vArrayToHarray :: [VariantFunc] -> [[VARIANTobj]] -> IO [[HaskellDt]]
vArrayToHarray fArray vArray = mapM  (\x-> applyFunc fArray x []) vArray

applyFunc :: [VariantFunc] -> [VARIANTobj] -> [HaskellDt] -> IO [HaskellDt]
applyFunc (func:funcs) (dt:dts) acc =
    case func of
      (FString    toString)  -> do str <- toString dt
                                   free dt
                                   applyFunc funcs dts ((HString str):acc)
      (FLong      toInteger) -> do int  <- toInteger dt
                                   free dt
                                   applyFunc funcs dts ((HInteger int):acc)
      (FDateTime toDateTime) -> do dtime  <- toDateTime dt
                                   free dt
                                   applyFunc funcs dts ((HDateTime dtime):acc)
      (FBool     toBool)     -> do bool <- toBool dt
                                   free dt
                                   applyFunc funcs dts ((HBool bool):acc)
      (FDouble   toDouble)   -> do double <- toDouble dt
                                   free dt
                                   applyFunc funcs dts ((HDouble double):acc)
      (FNull      toNull)    -> do null  <- toNull dt
                                   free dt
                                   applyFunc funcs dts ((HString null):acc)

applyFunc []     _        acc = return (reverse acc)

#define VT_EMPTY   0
#define VT_NULL    1
#define VT_I2      2 
#define VT_I4      3 
#define VT_R4      4 
#define VT_R8      5 
#define VT_DATE    7 
#define VT_BSTR    8 
#define VT_BOOL    0x0b 
#define VT_ARRAY   0x2000 
#define VT_VARIANT 0x0c 

variantToVfunc :: [VARIANTobj] -> [VariantFunc]-> IO [VariantFunc]
variantToVfunc []     acc = return (reverse acc)
variantToVfunc (x:xs) acc = do
    iTypeCD <- c_VariantTypeCode x
    case iTypeCD of 
      VT_BSTR -> variantToVfunc xs ((FString   variantToString) :acc)
      VT_I4   -> variantToVfunc xs ((FLong     variantToInteger):acc)
      VT_DATE -> variantToVfunc xs ((FDateTime variantToDateTime):acc)
      VT_BOOL -> variantToVfunc xs ((FBool     variantToBool):acc)
      VT_R8   -> variantToVfunc xs ((FDouble   c_VariantToDouble):acc)
      _       -> variantToVfunc xs ((FNull     variantType):acc)

vListToVnameList :: [VARIANTobj] -> IO [String]
vListToVnameList vList = mapM variantType vList

-- Filed 名取得
getFiledsName :: OLEobj -> OLEobj -> IO [VARIANTobj]
getFiledsName rs fields = do
    vCount <- getProperty fields cwsCount []
    fCount <- c_VariantToLong vCount
    free vCount 
    readFiledName rs cwsName (fCount -1) []
        where
            cwsCount  = unsafePerformIO $ newCWString "Count"
            cwsName   = unsafePerformIO $ newCWString "Name"

            readFiledName :: OLEobj -> CWString -> CLong -> [VARIANTobj] -> IO [VARIANTobj]
            readFiledName _  _    (-1)  out = do return out
            readFiledName rs name count out = do
                vList    <- toVariantLS [VLong count] []
                field    <- getObject rs cwsFields vList
                vColName <- getProperty field name []
                c_ReleaseObject field
                readFiledName rs name (count-1) (vColName:out)

cwsValue = unsafePerformIO $ newCWString "Value"
cwsItem  = unsafePerformIO $ newCWString "Item"

readDB :: OLEobj -> OLEobj -> [VARIANTobj]-> IO [[VARIANTobj]]
readDB rs fields nameList = readDB_ rs fields nameList []

readDB_ :: OLEobj -> OLEobj -> [VARIANTobj] ->  [[VARIANTobj]] -> IO [[VARIANTobj]]
readDB_ rs fields nameList acc = do
    eofBof <- isEOForBOF rs
    case eofBof of
        False -> do line <- readLine fields nameList
                    callMethod rs cwsMoveNext []
                    readDB_ rs fields nameList (line:acc)
        True  -> return (reverse acc)
      where  cwsMoveNext = unsafePerformIO $ newCWString "MoveNext"

isEOForBOF :: OLEobj -> IO Bool
isEOForBOF rs = do
    vEOF <- getProperty rs eof []
    vBOF <- getProperty rs bof []
    bEOF <- variantToBool vEOF
    bBOF <- variantToBool vBOF
    mapM_ free [vEOF, vBOF]
    return $ bEOF || bBOF
    where  eof = unsafePerformIO $ newCWString "EOF"
           bof = unsafePerformIO $ newCWString "BOF"

readLine :: OLEobj -> [VARIANTobj] -> IO [VARIANTobj]
readLine fields fieldsNames = mapM readField fieldsNames
    where
      readField field = do
        obj   <- getObject fields cwsItem [field]
        vData <- getProperty obj cwsValue []
        c_processMessage
        c_ReleaseObject obj
        return vData

Win32ole.hs

{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE CPP #-}

-- ghc --make win32ole.hs  winole.c -lole32 -loleaut32 -luuid  -o ole

module Win32ole (
     Vmaterial(..), HaskellDt(..), VariantFunc(..), Obj(..),
     OLEobj, BSTRobj, VARIANTobj, VARGobj, SYSTime, 
     stringToBSTR,
     toVariantLS,
     toVARIANTobj,
     tupleDateToVariant,
     bSTRToString,
     boolToVariant, 
     callMethod ,
     createInstance, 
     destroyOBJ, 
     invoke, 
     getFullPathName, 
     getObject, 
     getProperty, 
     putProperty, 
     methodTuple ,
     setWxVariantARG, 
     stringToBSTRvariant, 
     variantType ,
     c_VariantTypeCode,
     variantToString,
     variantToInteger,
     c_VariantToLong,
     variant2Str, 
     variantToDateTime, 
     variantToDate, 
     variantToTime, 
     variantToBool, 
     c_VariantToDouble,
     c_DoubleToVariant,
     c_CStringToBSTR, 
     c_BSTRtoCString, 
     c_variantToBSTR,
     c_longToVariant,
     c_VariantToSystemTime,
     c_SystemTimeToVariant,
     c_setSYSTEMTIME,
     c_getVsysTime,
     c_getVsysDate,
     c_getVariantType,
     c_BSTRToVariant,
     c_Variant2bool,
     c_bool2Variant,
     c_createVARIANTARG,
     c_setVARIANTARG,
     c_freeBSTR,
     c_invoke,
     c_createInstance,
     c_variantToString,
     c_Variant2Dispatch,
     c_processMessage,
     c_ReleaseObject,
     c_OleInitialize,
     c_OleUninitialize
) where

import Foreign.Ptr           (Ptr)
import Foreign.C.String      (CString, CWString, newCString, newCWString , withCString, withCWString, peekCString)
import Foreign.C.Types       (CLong, CInt, CUInt,CShort, CUShort)
import Foreign.Marshal.Alloc (free)
import Cinnamon.Ucs          (ucs4ToSjis)
import Data.Bits             ((.|.))
import Data.DateTime         (DateTime, toGregorian, fromGregorian, toGregorian', 
                               fromGregorian', fromSqlString, toSqlString)


data IDispatch  = IDispatch
data BSTR       = BSTR
data VARIANT    = VARIANT
data VARIANTARG = VARIANTARG
data SYSTEMTIME = SYSTEMTIME

type OLEobj     = (Ptr IDispatch)
type BSTRobj    = (Ptr BSTR)
type VARIANTobj = (Ptr VARIANT)
type VARGobj    = (Ptr VARIANTARG)
type SYSTime    = (Ptr SYSTEMTIME)

data Obj =
          O  OLEobj
        | CW CWString
        | B  BSTRobj
        | V  VARIANTobj
        | VA VARGobj

type TupleDate    = (Integer, Int, Int)



#define DISPATCH_METHOD         1
#define DISPATCH_PROPERTYGET    2
#define DISPATCH_PROPERTYPUT    4
#define DISPATCH_PROPERTYPUTREF 8

data Vmaterial =
          VLong    CLong 
        | VString  String 
        | VDouble  Double
        | VDate    (Integer, Int, Int)
        | VBool    Bool
        | VList    [Vmaterial] 
        deriving (Show)

data HaskellDt =
          HInteger  Integer
        | HString   String 
        | HBool     Bool
        | HDouble   Double
        | HDateTime DateTime
        deriving (Show,Eq)

data VariantFunc =
          FLong     (VARIANTobj -> IO Integer)
        | FString   (VARIANTobj -> IO String)
        | FDateTime (VARIANTobj -> IO DateTime)
        | FBool     (VARIANTobj -> IO Bool)
        | FDouble   (VARIANTobj -> IO Double)
        | FNull     (VARIANTobj -> IO String)

toVARIANTobj :: Vmaterial -> IO VARIANTobj
toVARIANTobj (VLong   value) = c_longToVariant     value
toVARIANTobj (VString value) = stringToBSTRvariant value
toVARIANTobj (VDouble value) = c_DoubleToVariant   value
toVARIANTobj (VDate   value) = tupleDateToVariant  value
toVARIANTobj (VBool   value) = boolToVariant       value

tupleDateToVariant :: TupleDate ->  IO VARIANTobj
tupleDateToVariant (year, month, day) = do
    sysTime <- c_setSYSTEMTIME (fromIntegral year) (fromIntegral month) (fromIntegral day) 0 0 0
    variant <- c_SystemTimeToVariant sysTime 
    free sysTime
    return variant

toVariantLS :: [Vmaterial] -> [VARIANTobj] -> IO [VARIANTobj]
toVariantLS (x:xs) acc = do
    variant <- toVARIANTobj x
    toVariantLS xs (variant:acc)

toVariantLS []     acc = return (reverse acc)


destroyOBJ :: Obj -> IO()
destroyOBJ (O obj)     = c_ReleaseObject obj
destroyOBJ (CW str)    = free str
destroyOBJ (B  bstr)   = c_freeBSTR bstr
destroyOBJ (V variant) = free variant
destroyOBJ (VA varg)   = free varg

-- String
stringToBSTR :: String -> IO BSTRobj
stringToBSTR string = withCString string c_CStringToBSTR

bSTRToString :: BSTRobj -> IO String
bSTRToString string = do
    cstring <- c_BSTRtoCString string
    string  <- peekCString cstring
    free cstring
    return string

stringToBSTRvariant :: String -> IO VARIANTobj
stringToBSTRvariant string = do
    bstr    <- withCString string c_CStringToBSTR
    variant <- c_BSTRToVariant bstr
    free bstr
    return variant

-- Variant
variantType :: VARIANTobj -> IO String
variantType  variant = variant2Str variant c_getVariantType

variantToString :: VARIANTobj -> IO String
variantToString variant = variant2Str variant c_variantToString

variant2Str :: VARIANTobj -> (VARIANTobj -> IO CString) -> IO String
variant2Str variant func = do
    cstring <- func variant
    string  <- peekCString cstring
    free cstring
    return string

variantToDateTime :: VARIANTobj -> IO DateTime
variantToDateTime variant = do
    sysTime     <- c_VariantToSystemTime variant
    clongDate   <- c_getVsysDate sysTime
    clongTime   <- c_getVsysTime sysTime
    free sysTime
    let numDate = fromIntegral clongDate
    let numTime = fromIntegral clongTime
    return $ fromGregorian (numDate `div` 10000)          -- year
                           (fromInteger  (numDate `div` 100 `rem` 100))  -- month
                           (fromInteger  (numDate `rem` 100))            -- day
                           (numTime `div` 10000)          -- hour 
                           (numTime `div` 100  `rem` 100) -- minute
                           (numTime `rem` 100)            -- second 

variantToDate :: VARIANTobj -> IO DateTime
variantToDate variant = do
    sysTime     <- c_VariantToSystemTime variant
    clongDate   <- c_getVsysDate         sysTime
    free sysTime
    let numDate = fromIntegral clongDate
    return $ fromGregorian (numDate `div` 10000)          -- year
                           (fromInteger  (numDate `div` 100 `rem` 100))  -- month
                           (fromInteger  (numDate `rem` 100))            -- day
                           0 0 0

variantToTime :: VARIANTobj -> IO DateTime
variantToTime variant = do
    sysTime     <- c_VariantToSystemTime variant
    clongTime   <- c_getVsysTime         sysTime
    free sysTime
    let numTime = fromIntegral clongTime
    return $ fromGregorian 0 0 0 (numTime `div` 10000)          -- hour 
                                 (numTime `div` 100  `rem` 100) -- minute
                                 (numTime `rem` 100)            -- second 

variantToBool ::  VARIANTobj -> IO Bool
variantToBool  variant = do
    cInt <- c_Variant2bool variant
    if cInt==0 then return False else return True

boolToVariant :: Bool -> IO VARIANTobj
boolToVariant bool =
    case bool of
        True -> c_bool2Variant ((-1)::CShort)
        _    -> c_bool2Variant ( 0::CShort)


variantToInteger :: VARIANTobj -> IO Integer
variantToInteger variant = do
    clong <- c_VariantToLong variant
    return $ fromIntegral clong
 
-- VARIANTARG
setWxVariantARG :: [VARIANTobj] -> IO VARGobj
setWxVariantARG variants = do
    vARGS <- c_createVARIANTARG vArgSize
    return =<< setWxVariantARG' vARGS 0 variants
        where
          vArgSize :: (Num a) => a
          vArgSize = fromIntegral $ length variants

          setWxVariantARG' :: VARGobj -> CInt -> [VARIANTobj]  -> IO VARGobj
          setWxVariantARG' vARGS _ []     = return vARGS
          setWxVariantARG' vARGS n (x:xs) = do
              retVARGS <- c_setVARIANTARG vARGS n x 
              setWxVariantARG' retVARGS (n+1) xs

-- Automation
createInstance :: String -> IO OLEobj
createInstance name = withCString name c_createInstance

invoke :: OLEobj -> CWString -> [VARIANTobj] -> CUShort -> IO VARIANTobj
invoke obj cwCom vArgs invokeType = do
    vaParam <- setWxVariantARG vArgs
    variant <- c_invoke obj cwCom (fromIntegral $ length vArgs) vaParam invokeType
    mapM_ destroyOBJ [VA vaParam]
    return variant

getProperty :: OLEobj -> CWString -> [VARIANTobj] -> IO VARIANTobj
getProperty obj name  vArgs = invoke obj name vArgs DISPATCH_PROPERTYGET

putProperty :: OLEobj -> CWString -> [Vmaterial] -> IO ()
putProperty  obj name vMaterial = do
    vArgs   <- toVariantLS vMaterial []
    variant <- invoke obj name vArgs DISPATCH_PROPERTYPUT
    free variant

getObject :: OLEobj -> CWString -> [VARIANTobj] -> IO OLEobj
getObject obj name vArgs = do
    variant <- invoke obj name vArgs DISPATCH_PROPERTYGET
    obj     <- c_Variant2Dispatch variant
    free variant
    return obj
    
-- GetAbsolutePathName メソッドをコールし、パス名を含めたファイル名を取得
getFullPathName :: String -> IO VARIANTobj
getFullPathName fileName = do
    obj      <- createInstance  "Scripting.FileSystemObject"
    cwstring <- newCWString "GetAbsolutePathName"
    vFname   <- stringToBSTRvariant fileName
    variant  <- invoke obj cwstring [vFname] DISPATCH_METHOD
    mapM_ destroyOBJ [O obj, V vFname]
    return variant

callMethod :: OLEobj -> CWString -> [VARIANTobj]  -> IO ()
callMethod obj name vArgs = do
    variant <- invoke obj name vArgs DISPATCH_METHOD
    free variant

methodTuple :: (OLEobj, String) -> IO ()
methodTuple (obj, method) = withCWString method (\x-> callMethod obj x [])

-- C の関数を呼ぶための定義
foreign import ccall unsafe "processMessage"     c_processMessage :: IO ()
foreign import ccall unsafe "CStringToBSTR"      c_CStringToBSTR  :: CString -> IO BSTRobj
foreign import ccall unsafe "BSTRtoCString"      c_BSTRtoCString  :: BSTRobj -> IO CString
foreign import ccall unsafe "variantToBSTR"      c_variantToBSTR  :: VARIANTobj -> IO BSTRobj
foreign import ccall unsafe "varantToLong"       c_VariantToLong         :: VARIANTobj -> IO CLong
foreign import ccall unsafe "longToVariant"      c_longToVariant  :: CLong -> IO VARIANTobj
foreign import ccall unsafe "Variant2double"     c_VariantToDouble :: VARIANTobj -> IO Double
foreign import ccall unsafe "Double2Variant"     c_DoubleToVariant :: Double -> IO VARIANTobj

foreign import ccall unsafe "Variant2SystemTime" c_VariantToSystemTime   :: VARIANTobj -> IO SYSTime
foreign import ccall unsafe "SystemTime2Variant" c_SystemTimeToVariant   :: SYSTime -> IO VARIANTobj
foreign import ccall unsafe "setSYSTEMTIME"      c_setSYSTEMTIME 
                          :: CUInt -> CUInt -> CUInt -> CUInt -> CUInt -> CUInt -> IO SYSTime

foreign import ccall unsafe "getVsysTime"        c_getVsysTime    :: SYSTime -> IO CLong
foreign import ccall unsafe "getVsysDate"        c_getVsysDate    :: SYSTime -> IO CLong
foreign import ccall unsafe "BSTRToVariant"    c_BSTRToVariant    :: BSTRobj -> IO VARIANTobj
foreign import ccall unsafe "Variant2bool"     c_Variant2bool     :: VARIANTobj -> IO CShort
foreign import ccall unsafe "bool2Variant"     c_bool2Variant     :: CShort -> IO VARIANTobj

-- VARIANTARG
foreign import ccall unsafe "createVARIANTARG" c_createVARIANTARG :: CInt -> IO VARGobj
foreign import ccall unsafe "setVARIANTARG"    c_setVARIANTARG    :: VARGobj -> CInt -> VARIANTobj -> IO VARGobj

foreign import ccall unsafe "invoke" c_invoke  :: OLEobj -> CWString -> CInt -> VARGobj -> CUShort -> IO VARIANTobj


foreign import ccall unsafe "createInstance"  c_createInstance   :: CString -> IO OLEobj
foreign import ccall unsafe "getVariantType"  c_getVariantType   :: VARIANTobj -> IO CString
foreign import ccall unsafe "VariantTypeCode" c_VariantTypeCode :: VARIANTobj -> IO CInt
foreign import ccall unsafe "variantToString" c_variantToString  :: VARIANTobj -> IO CString

foreign import ccall unsafe "Variant2Dispatch" c_Variant2Dispatch :: VARIANTobj -> IO OLEobj
foreign import ccall unsafe "ReleaseObject"    c_ReleaseObject    :: OLEobj -> IO ()

foreign import stdcall "ole2.h OleInitialize"    c_OleInitialize   :: CInt -> IO ()
foreign import stdcall "ole2.h OleUninitialize"  c_OleUninitialize :: IO ()
foreign import stdcall "oleauto.h SysFreeString" c_freeBSTR        :: BSTRobj -> IO ()
foreign import stdcall "oleauto.h SafeArrayGetElemsize" c_SafeArrayGetElemsize        :: VARIANTobj -> IO CUInt
  • C の部分
#include <stdio.h>
#include <malloc.h>
#include <windows.h>

// 参考 ruby.h
#define ALLOCA_N(type,n) (type*)alloca(sizeof(type)*(n))

void messageBox (char* str, char* t, unsigned long x)
{
 MessageBox ( NULL, str, t, x);
 return;
}

void processMessage(){
    MSG msg;

    if (PeekMessage (&msg,NULL, WM_PAINT, WM_PAINT,PM_NOREMOVE)) {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
    }
}


IDispatch *mallocDispatch(){
    return (struct IDispatch *)malloc( sizeof(struct IDispatch) );
}

// COM は内部でBSTRを使用しています。
BSTR CStringToBSTR(char* cstring ){
    int    cstringlen, out_size;
    BSTR   wstr;
    cstringlen = strlen(cstring);
    out_size   = MultiByteToWideChar(CP_ACP, 0, cstring, cstringlen, NULL, 0);
    wstr       = SysAllocStringLen(NULL, out_size);
    MultiByteToWideChar(CP_ACP, 0, cstring, cstringlen, wstr, out_size);

    return wstr;
}

char* BSTRtoCString(BSTR bstr){
    int    out_size;
    char   *cstring;
    out_size = WideCharToMultiByte(CP_ACP, 0, (OLECHAR*)bstr, -1, NULL, 0, NULL, NULL);
    cstring  = (char*)malloc((out_size+1) * sizeof(char));
    WideCharToMultiByte(CP_ACP, 0, (OLECHAR*)bstr, -1, cstring, out_size, NULL, NULL);
    return cstring;
}

IDispatch *Variant2Dispatch(VARIANT *pVariant){
    IDispatch *pDispatch;

    if (V_ISBYREF(pVariant))
        pDispatch = *V_DISPATCHREF(pVariant);
    else
        pDispatch = V_DISPATCH(pVariant);

    return pDispatch;
}

// 常にインタフェーステーブルへアクセスする。
// Open,Close などのコマンドからテーブルのディスパッチIDを求め実行する。
VARIANT* invoke( PVOID *p, BSTR com, int nArgs, VARIANTARG *param, USHORT wFlags){
    VARIANT   *result;
    IDispatch *pDisp;
    DISPID    dispID;
    HRESULT   hr;
    UINT      puArgErr = 0;
    EXCEPINFO excepinfo;

    // http://msdn.microsoft.com/ja-jp/library/x6828bcx%28v=VS.80%29.aspx
    // Win32OLE 製作過程の雑記 : invoke メソッドの引数
    // http://homepage1.nifty.com/markey/ruby/win32ole/win32ole03.html#invoke-param
    DISPPARAMS  dispParams = { NULL, NULL, 0, 0 };
    dispParams.rgvarg            = param;  // 引数の配列への参照を表します。
    dispParams.rgdispidNamedArgs = NULL;   // 名前付き引数の dispID の配列(未使用)
    dispParams.cArgs             = nArgs;  // 引数の数を表します。 
    dispParams.cNamedArgs        = 0;      // 名前付き引数の数 (未使用)
    // 参考:ruby win32ole.c  ole_invoke2 関数
    if (wFlags & DISPATCH_PROPERTYPUT) {
        dispParams.cNamedArgs = 1;
        dispParams.rgdispidNamedArgs    = ALLOCA_N( DISPID, 1 );
        dispParams.rgdispidNamedArgs[0] = DISPID_PROPERTYPUT;
    }

    memset( &excepinfo, 0, sizeof(EXCEPINFO));
    pDisp = (IDispatch   *)p;

    // コマンド文字列からディスパッチID取得
    hr=pDisp->lpVtbl->GetIDsOfNames((IDispatch  *)pDisp, &IID_NULL, &com, 1, LOCALE_USER_DEFAULT, (DISPID*)&dispID);
    // printf("GetIDsOfNames nArgs:%d %-10S = %04d hr:%08lx\n",  nArgs , com, dispID, hr);

    result = (VARIANT*)malloc( sizeof (*result));
    VariantInit(result);
    hr = pDisp->lpVtbl->Invoke(
             pDisp,                    // 参考: Ruby付属の「OLE View」
             dispID,                   // arg1 - I4 dispidMember        [IN]
             &IID_NULL,                // arg2 - GUID riid              [IN]
             LOCALE_SYSTEM_DEFAULT,    // arg3 - UI4 lcid               [IN]
             wFlags,                   // arg4 - UI2 wFlags             [IN]
             &dispParams,              // arg5 - DISPPARAMS pdispparams [IN]
             result,                   // arg6 - VARIANT pvarResult     [OUT]
             &excepinfo,               // arg7 - EXCEPINFO pexcepinfo   [OUT]
             &puArgErr );              // arg8 - UINT puArgErr          [OUT]
     // printf("Invoke:%-20S dispID:%6d hr:%08x puArgErr:%d\n", com, dispID, hr,puArgErr);
    return result;
}


// ProgID("Excel.Application")からCLSID({00024500-0000-0000-C000000000000046})
// を求め、CoCreateInstance APIを呼びます。
IDispatch* createInstance(char *ComName){
    IDispatch  *pDisp;
    BSTR       name;
    CLSID      clsid;
    HRESULT    hr=0;

    pDisp = mallocDispatch();
    name  = CStringToBSTR( ComName );
    hr    = CLSIDFromProgID(name, &clsid);
    // HRESULTは最上位ビットで OK ,NG を表現します。
    // FAILED は hr が 0 より小さいかどうかチェックするマクロ。
    if(FAILED(hr)) {
        hr = CLSIDFromString(name, &clsid);
    }
    hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, &IID_IDispatch, (void **)&pDisp);
    SysFreeString(name);
    return pDisp;
}
/*
// winbase.h
typedef struct _SYSTEMTIME {
   WORD wYear;
   WORD wMonth; // 1 月は 1 です。
   WORD wDayOfWeek; // 日曜が 0
   WORD wDay;
   WORD wHour;
   WORD wMinute;
   WORD wSecond;
   WORD wMilliseconds;
} SYSTEMTIME;
 */
SYSTEMTIME* Variant2SystemTime(VARIANT *result){
    SYSTEMTIME *st;

    st = (SYSTEMTIME*)malloc(sizeof(*st));
    VariantTimeToSystemTime(V_DATE(result), st);
    return st;
}

VARIANT* SystemTime2Variant(SYSTEMTIME* st){
    DATE    *date;
    VARIANT *variant;

    date    = (DATE*)malloc(sizeof(*date));
    variant = (VARIANT*)malloc(sizeof(*variant));

    SystemTimeToVariantTime(st, date);

    VariantInit(variant);
    V_VT(variant)   = VT_DATE;
    V_DATE(variant) = *date;

    return variant;
}

SYSTEMTIME *setSYSTEMTIME(WORD year, WORD month, WORD day, WORD hour, WORD minute, WORD second){
    SYSTEMTIME *st;
    st = (SYSTEMTIME*)malloc(sizeof(*st));
    st->wYear   = year;
    st->wMonth  = month;
    st->wDay    = day;
    st->wHour   = hour;
    st->wMinute = minute;
    st->wSecond = second;
    return st;
}

long getVsysTime(SYSTEMTIME *st){
    return (long)st->wHour * 10000 + st->wMinute * 100 + st->wSecond;
}
long getVsysDate(SYSTEMTIME *st){
    return (long)st->wYear * 10000 + st->wMonth * 100 + st->wDay;
}

long   varantToLong(VARIANT *result){ return V_I4(result);}
double Variant2double(VARIANT *result){ return V_R8(result);}

VARIANT* Double2Variant(double value){
    VARIANT *variant;

    variant = (VARIANT*)malloc(sizeof(*variant));
    VariantInit(variant);
    V_VT(variant) = VT_R8;
    V_R8(variant) = value;
    return variant;
}

int Variant2bool(VARIANT *result){ 
    return V_BOOL(result);
}

VARIANT* bool2Variant(int value){
    VARIANT *variant;

    variant = (VARIANT*)malloc(sizeof(*variant));
    VariantInit(variant);
    variant->vt = VT_BOOL;
    variant->boolVal = value;
    return variant;
}


/**********
enum VARENUM {
        VT_EMPTY,VT_NULL,VT_I2,VT_I4,VT_R4,VT_R8,
        VT_CY,VT_DATE,VT_BSTR,VT_DISPATCH,
        VT_ERROR,VT_BOOL,VT_VARIANT,VT_UNKNOWN,VT_DECIMAL,
        VT_I1=16,VT_UI1,VT_UI2,VT_UI4,VT_I8,
        VT_UI8,VT_INT,VT_UINT,VT_VOID,VT_HRESULT,VT_PTR,VT_SAFEARRAY,VT_CARRAY,VT_USERDEFINED,
        VT_LPSTR,VT_LPWSTR,
        VT_RECORD  =36,VT_INT_PTR=37,VT_UINT_PTR=38,
        VT_FILETIME=64,VT_BLOB,VT_STREAM,VT_STORAGE,VT_STREAMED_OBJECT,
        VT_STORED_OBJECT,VT_BLOB_OBJECT,VT_CF,VT_CLSID,
        VT_BSTR_BLOB=0xfff,
        VT_VECTOR   =0x1000,
        VT_ARRAY    =0x2000,
        VT_BYREF    =0x4000,
        VT_RESERVED =0x8000,
        VT_ILLEGAL  = 0xffff,
        VT_ILLEGALMASKED=0xfff,
        VT_TYPEMASK =0xfff
};

**********/

int VariantTypeCode(VARIANT *result){ return V_VT(result); }

char *getVariantType(VARIANT *result){
    // printf("V_VT(result):%x\n",V_VT(result));
    switch(V_VT(result)){
        case VT_EMPTY:              return "empty";    break;
        case VT_NULL:               return "null";     break;
        case VT_I2:                 return "short";    break;
        case VT_I4:                 return "long";     break;
        case VT_R4:                 return "float";    break;
        case VT_R8:                 return "double";   break;
        case VT_BOOL:               return "bool";     break;
        case VT_BSTR:               return "string";   break;
        case VT_DATE:               return "datetime"; break;
        case (VT_ARRAY|VT_VARIANT): return "array"; break;
        default  :     return "error";     // SafeArrayGetElemsize(tagSAFEARRAY * psa)プロパティ
    }
}

char* variantToString(VARIANT *result){ BSTRtoCString(V_BSTR(result));}
BSTR variantToBSTR(VARIANT *result){ return V_BSTR(result);}

VARIANT* BSTRToVariant(BSTR bstr){
    VARIANT* dt;
    dt = (VARIANT*)malloc( sizeof (*dt));
    VariantInit(dt);
    V_VT(dt)   =VT_BSTR; // dt->vt = VT_BSTR;
    V_BSTR(dt) = bstr;   // dt->bstrVal = bstr;
    return dt;
}

VARIANT* longToVariant(long n){
    VARIANT* dt;
    dt = (VARIANT*)malloc( sizeof (*dt));
    VariantInit(dt);
    V_VT(dt)   = VT_I4;  // dt->vt = VT_I4
    dt->lVal = n;
    return dt;
}


VARIANTARG* createVARIANTARG(int n){
    VARIANTARG *dt;
    dt = (VARIANT*)malloc( (sizeof (*dt)) * n);
    return dt;
}

VARIANTARG* setVARIANTARG(VARIANTARG* array, int n, VARIANT* dt){
    *(array + n) = *dt;
    return array;
}

// ReleaseObject((void **)pExl);
void ReleaseObject( PVOID *pDisp ){
    ((IDispatch  *)pDisp)->lpVtbl->Release( (void *)pDisp);
}