Module: OLE-Server Synopsis: Additional code for "in-process" servers (i.e. DLL instead of EXE). Author: David N. Gray Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND define open COM-interface ( , ) slot data-cache-iunknown :: = $null-interface; slot server-IViewObject :: false-or() = #f; end ; define method after-initialize ( obj :: ) => (); // Note: not clear whether the second argument below should be // obj.obj-class-ID -- the Microsoft documentation says that it is usually // CLSID_NULL (but doesn't say when it should be otherwise), but the // examples show using the object's class ID. let ( status, cache ) = CreateDataCache(obj.controlling-unknown, $CLSID-NULL, $IID-IUnknown); check-ole-status(status, "CreateDataCache", obj); obj.data-cache-iunknown := pointer-cast(, cache); obj.server-IPersistStorage := make(, server-framework: obj, controlling-unknown: obj); next-method(); obj.server-IViewObject := make(, server-framework: obj, controlling-unknown: obj); end after-initialize; define method terminate (obj :: ) => (); next-method(); IUnknown/Release(obj.data-cache-iunknown); obj.data-cache-iunknown := $null-interface; if ( *initialized-hatch* ) // Need to unregister the Windows class before the DLL is unloaded. // This call will fail if some other object still has an instance of // a hatch window, but that's OK -- we'll do this again when that // object terminates. if ( UnregisterClass($hatch-class-name, application-instance-handle()) ) Output-Debug-String("unregistered hatch\r\n"); // temporary??? *initialized-hatch* := #f; end if; end if; values() end method terminate; define method ole-local-server? (obj :: ) => (local-server? :: ); #f // in-process server (.DLL file, not .EXE file) end; define method IUnknown/QueryInterface( obj :: , rrid :: ) => ( status :: , Object :: ); let ( status, interface ) = next-method(); /* if( $enable-debug-messages ) debug-out("QueryInterface %s => %=\n", as(, rrid), interface); end if; */ if ( (status = $E-NOINTERFACE) & ~ null-pointer?(obj.data-cache-iunknown) ) // for IOleCache[2] IUnknown/QueryInterface(obj.data-cache-iunknown, rrid) else values(status, interface) end if end method; define method query-cache-interface ( obj :: , interface-id :: ) => ( interface :: ); let ( status, interface ) = IUnknown/QueryInterface(obj.data-cache-iunknown, interface-id); if ( FAILED?(status) ) ole-cerror(status, "query cache", obj, as(, interface-id)); else // Because the cache is aggregated on our object, it has incremented its // ref count, which needs to be un-done in order to know when the object // is no longer being used. Use `SubRef' instead of `Release' to // decrement back to 0 without terminating. (This gets used during // initialization before the real reference is established.) SubRef(obj); end if; interface end query-cache-interface; define function make-inproc-factory (rclsid :: , riid :: , dll-local-data, #key class-id, class, server-module-handle, args = #[], #all-keys ) => ( status :: , factory :: ); Output-Debug-String("DllGetClassObject\r\n"); let class-id :: = as(, class-id); if ( ~ IsEqualIID?(rclsid, class-id) ) values ( $CLASS-E-CLASSNOTAVAILABLE, $null-interface ) // wrong class ID else let factory = apply(make, , clsid: class-id, // not rclsid because it is stack-allocated! class: class, server-context: $CLSCTX-INPROC-SERVER, dll-local-data: dll-local-data, server-module-handle: server-module-handle, args); QueryInterface(factory, riid) end if end make-inproc-factory; define function register-or-unregister (unregister? :: , module-handle, #key prog-id, title, class-id, misc-status = 0, full-name, short-name, app-name, verbs, #all-keys ) => ( status :: ); register-ole-server(class-id, prog-id, title, module-handle: module-handle, unregister?: unregister?, full-name: full-name, short-name: short-name, app-name: app-name, misc-status: misc-status, verbs: verbs); $S-OK end register-or-unregister; define function register-inproc-server (module-handle, #rest options) => ( status :: ); Output-Debug-String("DllRegisterServer\r\n"); apply(register-or-unregister, #f, module-handle, options) end; define function unregister-inproc-server (module-handle, #rest options) => ( status :: ); Output-Debug-String("DllUnregisterServer\r\n"); apply(register-or-unregister, #t, module-handle, options) end; define macro initialize-ole-server { initialize-ole-server(?class:expression, ?class-id:expression, ?prog-id:expression, ?title-string:expression, ?options:* ) } => { initialize-in-process-server(register-fn: register-inproc-server, unregister-fn: unregister-inproc-server, get-factory-fn: make-inproc-factory, validate-fn: validate-inproc-server-options, args: vector(class: ?class, class-id: ?class-id, prog-id: ?prog-id, title: ?title-string, ?options) ) } end macro; define function validate-inproc-server-options (#key title :: , prog-id :: , full-name :: false-or(), short-name :: false-or(), app-name :: false-or(), class :: = , args :: = #[], class-id :: type-union(, ) = $IID-NULL, misc-status :: = 0, verbs :: false-or()) => (); // This function doesn't actually do anything, we just want to get an // error if any invalid options are passed. // Note that the default values used here are just for the sake of type // validation; they are not necessarily the same default values that will // be actually used. end;