HaskellScript previous home next  
lambda ObjectScript Documentation
HaskellScript
  Documentation
  Download
  Questions
  History
  Acknowledgements

HaskellAgent
HaskellDirect
HaskellObject
  Example
  Documentation
ScriptServer
  Interface

Daan Leijen "Nov 22 1999": under construction

Registration

The ScriptReg utility (see the example page) automatically registers (and unregisters) script components. Here is a list of registry entries, where xxxx is the CLSID of the component and progid the ProgID of the component:

Key Subkey Required Default Value
HKEY_CLASSES_ROOT\CLSID\{xxxx} \InprocServer32 yes Locates the ObjectScript library. For example "d:\hscript\objectscript.dll"
\ScriptServer yes Contains the ProgID of the script server. For Haskell scripts, this is normally HugsScript
\ScriptSource yes Locates the source of the script that implements the proxy code, for example "d:\demos\proxycalculator.hs"
\ClassName yes Contains the name of the component, for example Calculator
\ProgID no Contains the ProgID of the component, for example Haskell.Calculator.
\Typelib no Contains the LIBID of the associated type library. Only required when there is a type library and the library resides in a different directory as the source.
HKEY_CLASSES_ROOT\progid \CLSID no Contains the CLSID of the component. ProgID's of Haskell components are normally formed by prefixing the class name with Haskell., for example Haskell.Calculator

The Protocol

ObjectScript uses the standard ScriptServer interfaces to load the proxy source and to call the methods. All methods are directly called on the IScript interface using GetIDsOfNames and Invoke. If there is a Typelib entry in the registry, ObjectScript will load the type information. If there is no entry, ObjectScript will look if there is a type library with the same name as the proxy module (ie. proxyCalculator.tlb. in the same directory, if so, it will register and load the library.

At component creation time, ObjectScript calls proxyClassNameConstruct, where ClassName is the name of the class as specified in the ClassName registry entry. The method should return an IScriptItem interface which represents the state of the component. This interface is passed as an extra (last) argument to every other method called for this component. If the constructor is undefined, ObjectScript assumes that this is a stateless component and will not add a state parameter as a last argument to every method call.

When a component is destroyed, ObjectScript calls proxyClassNameDestruct (passing the state as argument if this is a statefull component). No error is raised when this method is undefined.

When QueryInterface is called component, and ObjectScript can not find the interface in a type library, it will call proxyClassNameQueryInterface with the requested IID as argument (and possibly the state as a last argument). The IID will be passed as a BSTR (string). If the method is defined, it should return TRUE if the class supports the interface and FALSE if not. If the method is undefined, it is assumed that the requested interface is not supported.

For every other method call and propery access, ObjectScript will call proxyName where Name is the name of the method or property. ObjectScript will first lookup the name in the script and check compliance with the type information if available. For a statefull object, the state is passed as an extra argument.

Proxies in Haskell

When defining a proxy module you'll need to import the ScriptEvents module which defines the library functions needed. All proxy methods will have type Export. Values with this type can be called from outside.


public_0_0 :: state -> IO () -> Export
...
public_1_1 :: Variant a, Variant b => (a -> state -> IO b) -> Export
...
public_n_m :: Variant a1..n, Variant a1..m =>
                        (a1 -> ... -> an -> state -> IO (a1, ..., am) 
                           -> Export
The public_n_m function is used to define a method proxy. It takes an IO function, with n arguments and its state and m results, as argument. The state argument is polymorphic but should always be the type of state returned by the constructor function. (Since this is a hole in the type system, proxies should be generated from IDL descriptions to be type safe). Example (from the Calculator):
proxyDigit :: Export
proxyDigit = public_1_0 (digit :: Int -> Calculator -> IO ())

export_0_0 :: IO () -> Export
...
export_1_1 :: Variant a, Variant b => (a -> IO b) -> Export
...
export_n_m :: Variant a1..n, Variant a1..m =>
                (a1 -> ... -> an -> IO (a1, ..., am) 
                  -> Export
The export_n_m function is used to define a method proxy for a stateless component. The only difference with the public function is that it doesn't pass a state argument. export_n_m takes an IO function, with n arguments and m results, as argument. Example (from ScriptReg):
proxyRegReadValue :: Export
proxyRegReadValue = export_2_1 (readValue :: String -> String -> IO String)

constructor :: IO state -> Export

The constructor function is used to define a constructor proxy. state should be the type of the component state. If a constructor is defined, all the methods should use the public_n_m functions. If it is not defined, this will be a stateless component and the methods should use export_n_m For example:
proxyCalculatorConstruct :: Export
proxyCalculatorConstruct  
        = constructor (calculatorConstruct :: IO Calculator)

destructor :: state -> IO () -> Export

The destructor function is used to define a destructor proxy for statefull components (use export_0_0 for stateless destructors). state should be the type of the component state. For example:
proxyCalculatorDestruct :: Export
proxyCalculatorDestruct
        = destructor (calculatorDestruct :: Calculator -> IO ())

queryinterface :: (String -> state -> IO Bool) -> Export

The queryinterface function is used to define QueryInterface proxy for statefull objects (use export_1_1 for stateless components). state should be the type of the component state. The argument function takes the IID as a string argument. If the component supports this interface, it should return True. Normally, the component is dynamic or uses a type library. In both cases this method proxy doesn't have to be defined. For example:
proxyCalculatorQueryInterface :: Export
proxyCalculatorQueryInterface
        = queryInterface calculatorQueryInterface
 
previous home next