COM を学ぶ(10) : SysAllocString を使って BSTR を作る。

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()