Haskell から wxWidgets(C++) 経由で Excel を操作する。

このプログラムは"日本語.xls"のファイルを読み書きするものです。

  • 文字列は newCWString でワイド文字列に変換します。
  • さらに C++wxString に変換しています。
  • 今回サポートしているVariant型は文字列、LongのVariant型です。
{-# LANGUAGE ForeignFunctionInterface #-}

module Main where

import Foreign.Ptr           (Ptr)
import Foreign.C.String      (CWString, CString ,newCWString, peekCWString, newCString)
import Foreign.C.Types       (CInt, CLong, CClock, CTime, CFloat ,CDouble)
import Foreign.Marshal.Alloc (free)
import Cinnamon.Ucs          (ucs4ToSjis, sjisToUcs4, ucs4ToUtf8, utf8ToUcs4)
import Debug.Trace

data WxAutomation = WxAutomation
data WxVariant    = WxVariant
data WxString     = WxString

type VariantArray  = Ptr WxVariant
type Variant       = Ptr WxVariant
type AutomationObj = Ptr WxAutomation
type WxStringObj   = Ptr WxString

-- type bool, wxChar, double, long, string, string list, time, date, 
-- void pointer, list of strings, and list of variants.
data VariantData =
          VLong    CLong 
        | VString  String 
        | VClock   CClock
        | VTime    CTime 
        | VFloat   CFloat
        | VDouble  CDouble   
        | VwithName   (VariantData, String)
        | VariantList [VariantData] 
        deriving (Show)

main :: IO ()
main = do
    c_OleInitialize 0
    fPathName   <- getFullPathName  "日本語.xls"
    exl         <- createInstance   "Excel.Application"
    putStrLn    =<< variantToString =<< wxGetProperty exl "Version" -- => 9.0
    workbooks   <- wxGetObject exl   "Workbooks"
    callMethodArgs  workbooks "Open" [VwithName (VString fPathName, "Filename")]
    activeWbook <- wxGetObject exl         "ActiveWorkbook"
    workSheets  <- wxGetObject activeWbook "Worksheets"

    sheet       <- getObjectArgs workSheets "Item"  [VLong 2]          -- 2 番目の sheet 
    cell        <- getObjectArgs  sheet     "Cells" [VLong 1, VLong 2] -- 1 行、2列目の cell
    print       =<< automationIsValid cell   -- => True
    putProperty cell "Value" (VString "謹賀新年")
    -- read
    value       <- wxGetProperty cell "Value"
    putStrLn    =<< showVariant "sheet 2 : cel 1 2 :" value
    putStrLn    =<< wxVariantGetType value   -- => string
    outJP       <- variantToString   value
    putStrLn    (ucs4ToSjis outJP)           -- => 謹賀新年
    -- 空白cell
    cell        <- getObjectArgs  sheet     "Cells" [VLong 1, VLong 3] -- 1 行、3列目の cell
    putStrLn    =<< wxVariantGetType =<< wxGetProperty cell "Value"  -- => null

    mapM_ (\(x,y) -> wxCallMethodNonArg x y)
           [(activeWbook, "Save"),(workbooks , "Close"), (exl, "Quit")]
    mapM_ c_destroyWxAutomation [activeWbook, workbooks, exl]
    c_OleUninitialize

-- Variant 型には名前がついていて省略されると "" が設定される。
toVariantDT :: VariantData -> IO Variant
toVariantDT (VLong              value)        = return =<< toVariantLong     (VwithName (VLong   value,  ""))
toVariantDT (VwithName (VLong   value, name)) = return =<< toVariantLong     (VwithName (VLong   value,  name))
toVariantDT (VString            value)        = return =<< toVariantWxString (VwithName (VString value, ""))
toVariantDT (VwithName (VString value, name)) = return =<< toVariantWxString (VwithName (VString value, name))

toVariantLong :: VariantData -> IO Variant
toVariantLong (VwithName (VLong value, name)) = do
    wxName  <- stringToWxString name
    variant <- c_createLongVariant value wxName
    c_destroyWxString wxName
    return variant

toVariantWxString :: VariantData -> IO Variant
toVariantWxString (VwithName (VString value, name)) = do
    wxValue <- stringToWxString value
    wxName  <- stringToWxString name
    variant <-  c_createWxStringVariant wxValue wxName
    mapM_ c_destroyWxString [wxValue,wxName]
    return variant

setWxVariantArray :: [Variant] -> IO VariantArray
setWxVariantArray variant = do
    vArray <- c_createWxVariantArray vArraySize
    return =<< setWxVariantArray' vArray 0 variant
        where
          vArraySize = fromIntegral $ length variant

setWxVariantArray' :: VariantArray -> CInt -> [Variant]  -> IO VariantArray
setWxVariantArray' vArray _ []     = return vArray
setWxVariantArray' vArray n (x:xs) = do
    retVarray <- c_setWxVariantArray vArray n x 
    setWxVariantArray' retVarray (n+1) xs

showVariant :: String -> Variant -> IO String
showVariant memo variant = do
    vType <- wxVariantGetType variant
    case vType of
        "string" -> do name  <- wxVariantGetName variant
                       value <- variantToString  variant
                       return $ render "string" name value memo
        "long"   -> do name  <- wxVariantGetName variant
                       value <- c_wxVariantGetLong variant
                       return $ render "long" name (show value) memo
        _        -> do return "other"
    where
        render vType name value memo = 
               concat ["Variant :<",vType,"> name:<", name, "> value:<",(ucs4ToSjis value),"> memo:<",memo,">"]

vDataToVarray :: [VariantData] -> IO VariantArray
vDataToVarray vData = do
    vArray <- c_createWxVariantArray vDataSize
    return =<< vDataToVarray' vArray 0 vData
        where
          vDataSize = fromIntegral $ length vData

vDataToVarray' :: VariantArray -> CInt -> [VariantData] -> IO VariantArray
vDataToVarray' vArray _ []     = return vArray
vDataToVarray' vArray n (x:xs) = do
    variant   <- toVariantDT x
    newVarray <- c_setWxVariantArray vArray n variant
    vDataToVarray' newVarray (n+1) xs

-- Variant
wxVariantIsNull :: Variant -> IO Bool
wxVariantIsNull variant = do
    ret    <- c_wxVariantIsNull variant
    return $ fromCInt ret

wxVariantGetName :: Variant -> IO String
wxVariantGetName variant = wxVariantAsk variant c_wxVariantGetName

wxVariantGetType :: Variant -> IO String
wxVariantGetType variant = wxVariantAsk variant c_wxVariantGetType

-- Variant 型から情報を取得する関数を引数とする。
wxVariantAsk  :: Variant -> (WxStringObj -> IO WxStringObj) -> IO String
wxVariantAsk variant func = do
    wxStr    <- func variant
    string   <- wxStringToString wxStr
    c_destroyWxString wxStr
    return string

getFullPathName :: String -> IO String
getFullPathName fileName = do
    obj     <- createInstance   "Scripting.FileSystemObject"
    variant <- wxCallMethod obj "GetAbsolutePathName" fileName
    wxStr   <- c_wxVariantGetString variant
    string  <- wxStringToString  wxStr
    c_destroyWxAutomation obj
    c_destroyWxVariant variant
    c_destroyWxString wxStr
    return string

-- String
variantToString :: Variant -> IO String
variantToString variant = peekCWString =<< variantToCWString variant

variantToCWString :: Variant -> IO CWString
variantToCWString variant = do
    wxStr    <- c_wxVariantGetString variant
    cwstring <- c_wxStringToCWString wxStr
    c_destroyWxString wxStr
    return cwstring

wxStringToString :: WxStringObj -> IO String
wxStringToString wxString = peekCWString =<< c_wxStringToCWString wxString

stringToWxString :: String -> IO WxStringObj
stringToWxString string = do
    cwstring    <- newCWString string
    outWxString <- c_createWxString cwstring
    free cwstring
    return outWxString

-- -----------------
createInstance :: String -> IO AutomationObj
createInstance objName = do
    wxObjName <- stringToWxString objName
    fileObj   <- c_wxCreateInstance wxObjName
    c_destroyWxString wxObjName
    return fileObj

callMethodArgs :: AutomationObj -> String ->  [VariantData] -> IO ()
callMethodArgs  obj method vData = do
    varray    <- vDataToVarray vData
    wxMethod  <- stringToWxString method
    c_wxCallMethodArgs obj wxMethod vCount varray
    c_destroyWxString wxMethod
        where  vCount = fromIntegral $ length vData

wxCallMethod :: AutomationObj -> String -> String -> IO Variant
wxCallMethod auto methodName arg = do
    wxMethodName <- stringToWxString methodName
    wxArg        <- stringToWxString arg
    variant      <- c_wxCallMethod  auto wxMethodName wxArg
    mapM_ c_destroyWxString [wxMethodName, wxArg]
    return variant

wxCallMethodNonArg :: AutomationObj -> String -> IO Variant
wxCallMethodNonArg auto methodName = do
    wxMethodName <- stringToWxString methodName
    variant      <- c_wxCallMethodNonArg auto wxMethodName
    c_destroyWxString wxMethodName
    return variant

wxGetProperty :: AutomationObj -> String -> IO Variant
wxGetProperty auto propertyName = do
    wxPropertyName <- stringToWxString propertyName
    variant        <- c_wxGetProperty  auto wxPropertyName
    c_destroyWxString wxPropertyName
    return variant

putProperty :: AutomationObj -> String -> VariantData -> IO Bool
putProperty auto propertyName vdata = wxPutProperty auto propertyName =<< toVariantDT vdata


wxPutProperty :: AutomationObj -> String -> Variant -> IO Bool
wxPutProperty auto propertyName variant = do
    wxPropertyName <- stringToWxString propertyName
    ret            <- c_wxPutProperty auto wxPropertyName variant
    c_destroyWxString wxPropertyName
    return $ fromCInt ret

wxGetObject :: AutomationObj -> String -> IO AutomationObj
wxGetObject auto objectName = do
    wxObjectName  <- stringToWxString objectName
    automation    <- c_wxGetObject  auto wxObjectName
    c_destroyWxString wxObjectName
    return automation

getObjectArgs :: AutomationObj -> String -> [VariantData] -> IO AutomationObj
getObjectArgs auto objectName vData = 
    wxGetObjectArgs auto objectName vCount =<< vDataToVarray vData
        where  vCount = fromIntegral $ length vData

wxGetObjectArgs :: AutomationObj -> String -> CInt -> Variant -> IO AutomationObj
wxGetObjectArgs auto objectName numOfArgs args = do
    wxObjectName  <- stringToWxString objectName
    automation    <- c_wxGetObjectArgs  auto wxObjectName numOfArgs args
    c_destroyWxString wxObjectName
    return automation

automationIsValid :: AutomationObj -> IO Bool
automationIsValid auto = do
    ret <- c_wxAutomationPtrIsNotNULL auto
    return $ fromCInt ret

fromCInt :: CInt -> Bool
fromCInt x = case fromIntegral x of
                  0 -> False
                  _ -> True

-- wxString ------------------
foreign import ccall   "createWxString"      c_createWxString      :: CWString -> IO WxStringObj
foreign import ccall   "destroyWxString"     c_destroyWxString     :: WxStringObj -> IO()
foreign import ccall   "wxStringToCWString"  c_wxStringToCWString  :: WxStringObj -> IO CWString

-- ---------- wxAutomationObject -----------------
foreign import ccall   "wxCreateInstance"    c_wxCreateInstance    :: WxStringObj -> IO AutomationObj
foreign import ccall   "destroyWxAutomation" c_destroyWxAutomation :: AutomationObj -> IO ()
foreign import ccall   "wxAutomationPtrIsNotNULL" c_wxAutomationPtrIsNotNULL    :: AutomationObj -> IO CInt
foreign import ccall   "wxCallMethodNonArg" c_wxCallMethodNonArg  :: AutomationObj -> WxStringObj ->IO Variant
foreign import ccall   "wxCallMethod" c_wxCallMethod :: AutomationObj -> WxStringObj -> WxStringObj -> IO Variant
foreign import ccall   "wxCallMethodArgs" c_wxCallMethodArgs :: AutomationObj -> WxStringObj -> CInt -> Variant->IO()
foreign import ccall   "wxGetProperty"    c_wxGetProperty    :: AutomationObj -> WxStringObj -> IO Variant
foreign import ccall   "wxPutProperty"    c_wxPutProperty    :: AutomationObj -> WxStringObj -> Variant -> IO CInt
foreign import ccall   "wxGetObject"      c_wxGetObject      :: AutomationObj -> WxStringObj -> IO AutomationObj
foreign import ccall   "wxGetObjectArgs"  c_wxGetObjectArgs  :: AutomationObj -> WxStringObj -> CInt -> Variant -> IO AutomationObj
---------------- Variant ------------------
foreign import ccall   "destroyWxVariant"      c_destroyWxVariant      :: Variant -> IO()
foreign import ccall   "createLongVariant"     c_createLongVariant     :: CLong   -> WxStringObj -> IO Variant
foreign import ccall   "createWxStringVariant" c_createWxStringVariant ::  WxStringObj -> WxStringObj -> IO Variant
foreign import ccall   "wxVariantIsNull"       c_wxVariantIsNull       :: Variant -> IO CInt
foreign import ccall   "wxVariantGetName"      c_wxVariantGetName      :: Variant -> IO WxStringObj
foreign import ccall   "wxVariantGetType"      c_wxVariantGetType      :: Variant -> IO WxStringObj
foreign import ccall   "wxVariantGetLong"      c_wxVariantGetLong      :: Variant -> IO CLong
foreign import ccall   "wxVariantGetString"    c_wxVariantGetString    :: Variant -> IO WxStringObj

---------------- VariantArray ------------------
foreign import ccall   "createWxVariantArray"  c_createWxVariantArray  :: CInt -> IO VariantArray
foreign import ccall   "destroyWxVariantArray" c_destroyWxVariantArray :: VariantArray -> IO ()
foreign import ccall   "setWxVariantArray"     c_setWxVariantArray
                          :: VariantArray -> CInt -> Variant -> IO VariantArray
foreign import ccall   "getWxVariantArray"     c_getWxVariantArray     :: VariantArray -> CInt -> IO Variant

foreign import stdcall "ole2.h OleInitialize"   c_OleInitialize    :: CInt -> IO ()
foreign import stdcall "ole2.h OleUninitialize" c_OleUninitialize  :: IO ()
#include <stdio.h>
#include <stdlib.h>
#include <locale.h >
#include <iostream>
#include <wx/msw/ole/automtn.h>
#include <wx/variant.h>
#include <wx/wx.h>
#include <wx/string.h>

extern "C" {


typedef wxAutomationObject WxAutomation;
typedef wxVariant          WxVariant;
typedef const wxString     WxString;

// ---------------- wxString ----------------
inline wxString _U(const char String[] = "")
{
    return wxString(String, wxConvUTF8);
}

WxString* createWxString(wchar_t* wstring){
    return new wxString(wstring);
}

void destroyWxString(WxString* str){ delete str;}

wchar_t* wxStringToCWString(WxString* str){
    wchar_t *wstring  = (wchar_t*)malloc((str->length() +1) * sizeof(wchar_t));
    wcscpy( wstring, (wchar_t*)str->wchar_str()); 
    return wstring;
}

// ---------- wxAutomationObject -----------------
WxAutomation* wxCreateInstance( WxString* instanceName){
    wxAutomationObject *p = new wxAutomationObject();
    p->CreateInstance(*instanceName);
    return p;
}

void destroyWxAutomation(WxAutomation* p){ delete p;}

// Checks if the object is in a valid state.
// Returns true if the object was successfully initialized or false if it has no valid IDispatch pointer.
int wxAutomationPtrIsNotNULL(WxAutomation* p){
    if (NULL==p->GetDispatchPtr()) return 0;
    else                           return 1;
}

WxVariant* wxCallMethodNonArg(WxAutomation* p, WxString* methodName){
    WxVariant *ret = new WxVariant();
    *ret = p->CallMethod(*methodName);
    return ret;
}

WxVariant* wxCallMethod(WxAutomation* p,  WxString* methodName, WxString* arg){
    WxVariant *ret = new WxVariant();
    *ret = p->CallMethod(*methodName, *arg);
    return ret;
}

void wxCallMethodArgs(WxAutomation* p, wxString* methodName, int n, wxVariant* args){
    p->CallMethod(*methodName , n, args);
}

WxVariant* wxGetProperty(WxAutomation* p, WxString* propertyName){
    WxVariant *ret = new WxVariant();
    *ret = p->GetProperty(*propertyName);
    return ret;
}

int wxPutProperty(WxAutomation* p, WxString* propertyName, WxVariant* variant){
    bool ret = p->PutProperty(*propertyName, *variant);
    if (ret) return 1;
    else     return 0;
}


WxAutomation* wxGetObject(WxAutomation* p, WxString* objectName){
    wxAutomationObject *obj = new wxAutomationObject();
    p->GetObject( *obj, *objectName);
    return obj;
}

WxAutomation* wxGetObjectArgs(WxAutomation* p, WxString* objectName, int n, WxVariant* args){
    wxAutomationObject *ret = new wxAutomationObject();
    p->GetObject( *ret, *objectName, n, args);
    return ret;
}

// ---------------- Variant ------------------
void destroyWxVariant(WxVariant* var){ delete var;}

WxVariant* createLongVariant(long value, WxString* name){
    return new WxVariant(value, *name);
}

WxVariant* createWxStringVariant(WxString* value, WxString* name){
    return new WxVariant(*value, *name);
}

int wxVariantIsNull(WxVariant* var){
    bool ret = var->IsNull();
    if (ret) return 1;
    else     return 0;
}

wxString* wxVariantGetName( WxVariant* var){
    wxString *ret = new wxString();
    *ret = var->GetName();
    return ret;
}

wxString* wxVariantGetType( WxVariant* var){
    wxString *ret = new wxString();
    *ret = var->GetType();
    return ret;
}

long wxVariantGetLong(WxVariant* var){ return var->GetLong();}

wxString* wxVariantGetString( WxVariant* var){
    wxString *ret = new wxString();
    *ret = var->GetString();
    return ret;
}

// ---------------- VariantArray ------------------
wxVariant* createWxVariantArray(int n){ return new wxVariant[n];}
void destroyWxVariantArray(WxVariant* array){ delete[] array;}

wxVariant* setWxVariantArray(wxVariant* ret, int n, WxVariant* data){ 
    *(ret + n) = *data;
    return ret;
}

wxVariant* getWxVariantArray( WxVariant* array, int n){ return (array+n);}

}
g++ win32ole.cpp -c `wx-config --cxxflags` -o win32ole.o

ghc --make Main.hs win32ole.o \
-DHAVE_W32API_H -D__WXMSW__ -D_UNICODE \
-I/c/msys/1.0/home/userName/wxMSW-2.8.11/lib/gcc_dll/mswu \
-I/c/msys/1.0/home/userName/wxMSW-2.8.11/include \
-DWXUSINGDLL \
-L/c/msys/1.0/home/userName/wxMSW-2.8.11/lib/gcc_dll \
-L/c/MinGW/lib \
-lstdc++  \
-lwxtiff -lwxjpeg -lwxpng -lwxzlib -lwxregexu \
-lwxexpat -lkernel32 -luser32 -lgdi32 -lcomdlg32 -lwxregexu \
-lwinspool -lwinmm -lshell32 -lcomctl32 -lole32 -loleaut32 \
-luuid -lodbc32 -lodbccp32 -lrpcrt4 -ladvapi32 -lwsock32 -lwxmsw28u -o excel.exe