packages feed

hdirect-0.21.0: comlib/ConnectionPoint.lhs

%
% (c), 1999 - sof
%

Generic implementation of COM connectable objects / connection points,
server side.

The connection between this framework impl. and the Haskell object
responsible for firing events on the registered sinks, is still
up in the air. The current arrangement is for the object and a
particular connection point to share an IORef holding the current
set of registered sinks. The object will then fire the events
by using the (generated) stubs for that particular event interface.

Probably want to abstract away the details of how sink broadcasting
is done.

\begin{code}
module ConnectionPoint 
		(
		  mkConnectionContainer
		) where

import ComServ
import Connection ( iidIConnectionPointContainer, iidIConnectionPoint,
		    IConnectionPointContainer, IConnectionPoint,
		    iidIEnumConnections, iidIEnumConnectionPoints
		  )
		    
import Com
import HDirect ( writeWord32, Ptr, sizeofPtr )
import EnumInterface ( mkEnumInterface )
import Foreign.Ptr
import Foreign.ForeignPtr
import Foreign.Storable
import IOExts
import Word
import Int
import ComException

type ThisPtr = Ptr (IUnknown ())
\end{code}


\begin{code}
mkConnectionContainer :: [(IID (IUnknown ()), IORef [(Word32, IUnknown ())])]
                      -> IO (IConnectionPointContainer ())
mkConnectionContainer ls = fixIO $ \ ip -> do
  let ils = unsafePerformIO (mapM (mkConnection ip) ls)
  addrOf_eCP <- export_enumCP (enumConnectionPoints (map snd ils))
  addrOf_fCP <- export_fCP    (findConnectionPoint ils)
  vtbl       <- createComVTable [ addrOf_eCP, addrOf_fCP ]
  createComInstance "" () (return ())
                    [mkIface iidIConnectionPointContainer vtbl]
		    iidIConnectionPointContainer
  
mkConnection :: IConnectionPointContainer ()
             -> (IID (IUnknown ()), IORef [(Word32, IUnknown ())])
             -> IO (IID (IUnknown ()), IConnectionPoint ())
mkConnection ip (iid, regd_sinks) = do
  vtbl <- mkConnectionPointVTBL ip iid regd_sinks
  ip   <- createComInstance "" () (return ())
                            [ mkIface iidIConnectionPoint vtbl ]
			    iidIConnectionPoint
  return (iid, ip)

\end{code}

\begin{code}
mkConnectionPointVTBL :: IConnectionPointContainer ()
		      -> IID (IUnknown iid)
                      -> IORef [(Word32, IUnknown ())]
                      -> IO (ComVTable (IConnectionPoint a) objState)
mkConnectionPointVTBL ip iid sinks = do
   addrOf_gi    <- export_gi    (getConnectionInterface iid)
   addrOf_gcpc  <- export_gcpc  (getConnectionPointContainer ip)
   cookie_ref   <- newIORef (0::Word32)
   addrOf_adv   <- export_adv   (advise sinks cookie_ref iid)
   addrOf_unadv <- export_unadv (unadvise sinks)
   addrOf_eC    <- export_eCP   (enumConnections sinks)
   createComVTable
         [ addrOf_gi , addrOf_gcpc , addrOf_adv, addrOf_unadv, addrOf_eC ]

getConnectionInterface :: IID iid
                       -> ThisPtr
		       -> Ptr GUID
		       -> IO HRESULT
getConnectionInterface iid _ piid 
  | piid == nullPtr  = return e_POINTER
  | otherwise        = do
     writeGUID piid (iidToGUID iid)
     return s_OK

foreign export stdcall dynamic
    export_gi :: (ThisPtr -> Ptr GUID -> IO HRESULT) -> IO (Ptr ())

getConnectionPointContainer :: IConnectionPointContainer ()
                            -> ThisPtr
                            -> Ptr (Ptr (IUnknown b))
                            -> IO HRESULT
getConnectionPointContainer ip _ pip = do
   writeIUnknown True{-addRef-} pip ip
   return s_OK

foreign export stdcall dynamic
    export_gcpc :: (ThisPtr -> Ptr (Ptr (IUnknown b)) -> IO HRESULT) -> IO (Ptr ())

advise :: IORef [(Word32,IUnknown ())]
       -> IORef Word32
       -> IID (IUnknown iid)
       -> ThisPtr
       -> PrimIP ()
       -> Ptr Word32
       -> IO HRESULT
advise sinks cookie_ref iid this pUnkSink pdwCookie = do
  ls      <- readIORef sinks
  cookie  <- readIORef cookie_ref
  ip      <- unmarshallIUnknown False pUnkSink
  catch (do
     ip2 <- ip # queryInterface iid
     if nullPtr == pdwCookie then
        return e_POINTER
      else do
        writeIORef cookie_ref (cookie+1)
        writeIORef sinks ((cookie,castIface ip2):ls)
        writeWord32 pdwCookie cookie
        return s_OK
   )(\ _ -> return cONNECT_E_CANNOTCONNECT)

foreign export stdcall dynamic
   export_adv :: (ThisPtr -> PrimIP () -> Ptr Word32 -> IO HRESULT) -> IO (Ptr ())

unadvise :: IORef [(Word32,IUnknown ())]
         -> ThisPtr
         -> Word32
         -> IO HRESULT
unadvise sinks this dwCookie = do
  ls     <- readIORef sinks
  case break ((==dwCookie).fst) ls of
    (ls,[])    -> return cONNECT_E_NOCONNECTION
    (ls, _:rs) -> do
	 -- just drop the interface pointer and let
	 -- the GC release it.
       writeIORef sinks (ls++rs)
       return s_OK

foreign export stdcall dynamic
    export_unadv :: (ThisPtr -> Word32 -> IO HRESULT) -> IO (Ptr ())

enumConnections :: IORef [(Word32,IUnknown ())]
                -> ThisPtr
	        -> Ptr (Ptr (IUnknown a))
                -> IO HRESULT
enumConnections sinks this ppCP
  | ppCP == nullPtr  = return e_POINTER
  | otherwise        = do
    ls   <- readIORef sinks
    vtbl <- mkEnumInterface (map snd ls) (fromIntegral sizeofPtr) (writeIUnknown True)
    ip   <- createComInstance "" () (return ())
	                      [mkIface iidIEnumConnections vtbl]
			      iidIEnumConnections
    writeIUnknown True ppCP ip
    return s_OK

foreign export stdcall dynamic
    export_enumCP :: (ThisPtr -> Ptr (Ptr (IUnknown b)) -> IO HRESULT) -> IO (Ptr ())
\end{code}

\begin{code}
enumConnectionPoints :: [IConnectionPoint ()]
                     -> ThisPtr
                     -> Ptr (Ptr (IUnknown b))
                     -> IO HRESULT
enumConnectionPoints ls this ppEnum 
  | ppEnum == nullPtr  = return e_POINTER
  | otherwise	       = do
     vtbl <- mkEnumInterface ls (fromIntegral sizeofPtr) (writeIUnknown True)
     ip   <- createComInstance "" () (return ())
	                           [mkIface iidIEnumConnectionPoints vtbl]
				   iidIEnumConnectionPoints
     writeIUnknown True ppEnum ip
     return s_OK

foreign export stdcall dynamic
    export_eCP :: (ThisPtr -> Ptr (Ptr (IUnknown b)) -> IO HRESULT) -> IO (Ptr ())

findConnectionPoint :: [(IID (IUnknown ()), IConnectionPoint ())]
		    -> ThisPtr
		    -> Ptr GUID
		    -> Ptr (Ptr (IUnknown ()))
		    -> IO HRESULT
findConnectionPoint ls this riid ppCP 
  | ppCP == nullPtr  = return e_POINTER
  | otherwise        = do
     guid <- unmarshallGUID False riid
     let iid = guidToIID guid
     case (lookup iid ls) of
       Nothing -> do
          poke ppCP nullPtr
          return cONNECT_E_NOCONNECTION
       Just i  -> do
          writeIUnknown True ppCP i
	  return s_OK

foreign export stdcall dynamic
    export_fCP :: (ThisPtr -> Ptr GUID -> Ptr (Ptr (IUnknown b)) -> IO HRESULT) -> IO (Ptr ())

\end{code}

\begin{code}
cONNECT_E_NOCONNECTION :: HRESULT
cONNECT_E_NOCONNECTION = 0x80040200

cONNECT_E_ADVISELIMIT :: HRESULT
cONNECT_E_ADVISELIMIT  = 0x80040201

cONNECT_E_CANNOTCONNECT :: HRESULT
cONNECT_E_CANNOTCONNECT = 0x80040202

cONNECT_E_OVERRIDDEN :: HRESULT
cONNECT_E_OVERRIDDEN = 0x80040203
\end{code}