Haskell から COM を呼ぶ ために MultiByteToWideCharを使わないでSysAllocStringで行うためのテストです。
SysAllocString で BSTR を作りますが、SysAllocStringはバイナリの文字列長を含まないワイド文字列の先頭アドレスを返します。読み取るときはCWStringと同等に扱っても問題ありません。
4byte 可変長 2byte ┌───┬──────────┬─┐ │length│ string │\0│ └───┴──────────┴─┘ ↑ SysAllocString はここのアドレスを返します。
-- ghc -Wall --make bstr.hs -loleaut32 -o bstr module Main where import Foreign.C.String (newCWString, CWString, peekCWString) import Foreign.Marshal.Alloc (free) -- 20130821 追記 import Foreign.C.Types (CUInt) import Cinnamon.Ucs (ucs4ToSjis) -- C の関数を呼ぶための定義 -- SysAllocString は BSTR を作りますが、CWStringと同等に扱っても問題ない。 foreign import stdcall "windows.h SysAllocString" cSysAllocString :: CWString -> IO CWString foreign import stdcall "windows.h SysStringLen" cSysStringLen :: CWString -> IO CUInt foreign import stdcall "windows.h SysStringByteLen" cSysStringByteLen :: CWString -> IO CUInt foreign import stdcall "windows.h SysFreeString" cSysFreeString :: CWString -> IO () main :: IO() main = do -- ファイルはUTF8で記述されていますがコンパイルすると ucs4になります。 let ucs4 = "123あいう" putStrLn $ "ucs4->sjis:" ++ (ucs4ToSjis ucs4) -- > ucs4->sjis:123あいう -- Haskell文字列(リスト)からCのワイド文字列に変換し、BSTRを作る。 -- 2013/08/21 指摘により修正 -- bstr <- cSysAllocString =<< newCWString ucs4 cwstr <- newCWString ucs4 bstr <- cSysAllocString cwstr free cwstr -- BSTR APIを使用してみる。 len <- cSysStringLen bstr print ("len:" ,len) -- > ("len:",6) -- バイト数は文字列長の2倍。 byte <- cSysStringByteLen bstr print ("byte:",byte) -- > ("byte:",12) -- BSTR は単純に C のワイド文字列として扱える。 str <- peekCWString bstr -- 最初の文字列とBSTRに変換し、BSTRから生成した文字列は等しい print ("ucs4 == str:", ucs4 == str) -- > ("ucs4 == str:",True) putStrLn $ "str->sjis:" ++ (ucs4ToSjis str) -- > str->sjis:123あいう -- BSTRはメモリを解放する必要がある。 cSysFreeString bstr return()