FFI を学ぶ(2) : Cの関数とデータをやりとりしてみる。

  • ffi.c : C側のメモリは Haskell で確保してのを受け取って使うか、malloc で確保して開放する関数を Haskell から呼べば良い、たぶん。
  • 勿論、C で開放できるならその方が楽です。
#include <stdio.h>
#include <windows.h>
#include <malloc.h>
#include <objbase.h>

/* CLSID構造体へのポインタを返す */
CLSID* emptyCLSIDstruct (void) {
    static CLSID   clsid;
    static CLSID   *p;
    
    p=&clsid;

    return p;
}

/* CLSID構造体のポインタを受け取ってサイズを返す */
int getCLSIDstructSize (CLSID *clsid) {
    static int size;
    size = sizeof(*clsid);
    return size;
}

/* 文字列を合成して返す */
char *changeString (char *fromList, char *clsid) {
    static char buf[256];
    sprintf(buf,"%s --> sizeof(CLSID) = %s", fromList, clsid);
    return buf;
}

ごちゃごちゃやっていますが、やっている内容に意味はなく C と Haskell との間でデータを渡しあえるかどうかテストしているだけです。

  • FFItest.hs
{-# LANGUAGE ForeignFunctionInterface #-}

--  ghc --make FFItest.hs  ffi.c -main-is FFItest -lole32 -loleaut32 -luuid  -o FFItest

module FFItest where

import Foreign.Ptr
import Foreign.C.String
import Foreign.C.Types
import Foreign.Marshal.Alloc

data CLSID = CLSID


main = do
    -- C の関数でCLSID構造体を作って、そのポインタを取得。
    let pclsid    = c_emptyCLSIDstruct

    -- 受け取ったポインタのアドレスを渡してサイズを取得。
    let size      = c_getCLSIDstructSize (castPtr pclsid)

    -- リスト文字列からC文字列を作成。
    cstrFFI       <- newCString "Foreign Function Interface"

    -- CLSID構造体サイズからリスト文字列を作り、そこからC文字列を作成。
    cstrSize      <- newCString (show size)

    -- 文字列を連結するCの関数に cstrFFI、cstrSize のふたつの文字列を渡し、
    -- 加工された文字列を受け取る。
    let cstrFromC = c_changeString cstrFFI cstrSize

    -- C文字列からリスト文字列を作成。
    lstr          <-  peekCString cstrFromC

    -- リスト文字列を表示
    putStrLn lstr

    -- C文字列のメモリを開放
    Foreign.Marshal.Alloc.free cstrFFI
    Foreign.Marshal.Alloc.free cstrSize


foreign import ccall   "emptyCLSIDstruct"    c_emptyCLSIDstruct     :: (Ptr CLSID)
foreign import ccall   "getCLSIDstructSize"  c_getCLSIDstructSize   :: (Ptr (Ptr CLSID))  -> CInt
foreign import ccall   "changeString"        c_changeString         :: CString -> CString -> CString

{-
  The Haskell 98 Foreign Function Interface 1.0
   An Addendum to the Haskell 98 Report
     3.1  Calling Conventions
   http://www.cse.unsw.edu.au/~chak/haskell/ffi/ffi/ffise3.html#x6-110003.1
   ccall     C compiler on a system
   cplusplus C++ compiler on a system
   dotnet    .net platform
   jvm       Java Virtual Machine
   stdcall   Win32 API (matches Pascal conventions)
-}
> ghc --make FFItest.hs  ffi.c -main-is FFItest -lole32 -loleaut32 -luuid  -o FFItest
Linking FFItest.exe ...

> ./FFItest
Foreign Function Interface --> sizeof(CLSID) = 16