clr-host-0.1.0.0: src/Clr/Host/DotNet/Common.hs
module Clr.Host.DotNet.Common where
import Clr.Host.DotNet.Guid
import Control.Exception (bracket)
import Data.Int
import Data.Word
import Foreign.Marshal
import Foreign.Ptr
import Foreign.Storable
import Text.Printf
-- | 'InterfacePtr' is a pointer to an arbitrary COM interface (which is a pointer to
-- a vtable of function pointers for the interface methods).
type InterfacePtr = Ptr (Ptr (FunPtr ()))
-- | 'withInterface i f' is like 'bracket' but for COM interface pointers, it calls
-- 'release' once the computation is finished.
withInterface :: (IO InterfacePtr) -> (InterfacePtr -> IO a) -> IO a
withInterface i = bracket i (\x -> if x == nullPtr then return 0 else release x)
-- | 'getInterfaceFunction' @i makeFun obj@ is an action that returns the @i@th function
-- of the COM interface referred to by @obj@. The function is returned as a Haskell
-- function by passing it through @makeFun@.
getInterfaceFunction :: Int -> (FunPtr a -> b) -> InterfacePtr -> IO b
getInterfaceFunction index makeFun this = do
-- Obtain a pointer to the appropriate element in the vtable for this interface
funPtr <- peek this >>= (flip peekElemOff) index
-- Cast the function pointer to the expected type, and import it as a Haskell function
return $ makeFun $ castFunPtr funPtr
foreign import stdcall "CoInitializeEx" coInitializeEx :: Ptr () -> Int32 -> IO HResult
foreign import stdcall "CoUninitialize" coUninitialize :: IO ()
coInit_MultiThreaded = 0 :: Int32
coInit_ApartmentThreaded = 2 :: Int32
--
-- HResult Support
--
type HResult = Word32
checkHR :: String -> HResult -> IO HResult
checkHR msg 0 = return 0
checkHR msg r = error $ printf "%s failed (0x%8x)" msg r
--
-- IUnknown
--
-- | 'queryInterface' calls the QueryInterface method of the given COM interface.
queryInterface this iid = do
f <- getInterfaceFunction 0 {- QueryInterface -} makeQueryInterface this
with (nullPtr :: InterfacePtr) $ \interfacePtr -> do
with iid $ \refIID -> f this refIID interfacePtr >>= checkHR "IUnknown.QueryInterface"
peek interfacePtr
type QueryInterface = InterfacePtr -> Ptr IID -> Ptr InterfacePtr -> IO HResult
foreign import stdcall "dynamic" makeQueryInterface :: FunPtr QueryInterface -> QueryInterface
-- | 'release' calls the Release method of the given COM interface.
release this = do
f <- getInterfaceFunction 2 {- Release -} makeRelease this
f this
type Release = InterfacePtr -> IO Word32
foreign import stdcall "dynamic" makeRelease :: FunPtr Release -> Release