Now we create the actual server application.
RotNExample-server.
library.dylan, and add to the define library declaration the following line:
use RotNExample-server-stubs;
define module declaration in module.dylan.
To implement the IRotNExample interface, we will create a subclass of <IRotNExample>. Because <IRotNExample> was created with define dispatch-interface, we must use define COM-interface to create the subclass.
Note: The remainder of this section of the example involves adding code to RotNExample-server.dylan. A version of this file with all the code we add in this section is available in the Functional Developer installation folder, under Examples\Documentation\RotNExample\RotNExample-server.dylan. You may want to copy that file into place in your project folder rather than typing code in.
RotNExample-server.dylan.
define COM-interface <IRotN-implementation> (<IRotNExample>)
slot IRotNExample/key ::
type-union(<integer>, <machine-word>) = 13;
end;
main.
We provide here an implementation for the IRotNExample/key slot, which was defined as a virtual slot in the superclass. This slot must accept the <machine-word> type, since any 32-bit integer which does not fit in the range of a Dylan <integer> will be passed as a <machine-word>.
The next task is to define the IRotNExample/encrypt and IRotNExample/decrypt methods. Although it is not obvious from the definition of <IRotNExample>, these methods must take as their first parameter the instance of <IRotN-implementation> they operate on, and return as a first result a COM error code.
RotNExample-server.dylan.
define method IRotNExample/encrypt
(this :: <IRotN-implementation>, pre :: <string>)
=> (result :: <HRESULT>, post :: <string>)
if (instance?(this.IRotNExample/key, <integer>))
let post = make(<string>, size: pre.size);
for (char keyed-by index in pre)
post[index] := rot-char-by-n(char, this.IRotNExample/key);
end for;
values($S-OK, post)
else
values($E-INVALIDARG, "")
end if
end;
define method IRotNExample/decrypt
(this :: <IRotN-implementation>, pre :: <string>)
=> (result :: <HRESULT>, post :: <string>)
if (instance?(this.IRotNExample/key, <integer>))
let post = make(<string>, size: pre.size);
for (char keyed-by index in pre)
post[index] := rot-char-by-n(char, -this.IRotNExample/key);
end for;
values($S-OK, post)
else
values($E-INVALIDARG, "")
end if
end;
Note that this code is careful not to crash when IRotNExample/key is a <machine-word>. $S-OK represents success. $E-INVALIDARG is a generic failure representing some kind of invalid argument value.
The above method uses the rot-char-by-n function, which we must also add.
RotNExample-server.dylan.
define function rot-char-by-n
(char :: <character>, n :: <integer>)
=> (r :: <character>)
let char-as-int = as(<integer>, char);
local method rot-if-in-range
(lower :: <integer>, upper :: <integer>) => ()
if (lower <= char-as-int & char-as-int <= upper)
char-as-int := lower + modulo(char-as-int - lower + n,
upper - lower + 1);
end if;
end method;
rot-if-in-range(as(<integer>, 'a'), as(<integer>, 'z'));
rot-if-in-range(as(<integer>, 'A'), as(<integer>, 'Z'));
as(<character>, char-as-int)
end;
This function rotates alphabetic characters forward n positions, wrapping around if the character passes "Z". When n is 13, this implements the classic Rot13 cipher often used to hide offensive material on USENET.
In order to create our server, we must also create a COM class for it.
RotNExample-server.dylan.
define coclass code from stubs.dylan in the RotNExample-server-stubs project and modify it.
define coclass $RotNExample-type-info name "RotNExample"; uuid $RotNExample-class-id; default interface <IRotN-implementation>; end coclass;
Now we simply have to add a Windows event loop as the main body of the server program.
main method in RotNExample-server.dylan to look like the following.
define method main () => ()
if (OLE-util-register-only?())
register-coclass($RotNExample-type-info,
"FunDev.RotNExample");
else
let factory :: <class-factory>
= make-object-factory($RotNExample-type-info);
with-stack-structure (pmsg :: <PMSG>)
while (GetMessage(pmsg, $NULL-HWND, 0, 0))
TranslateMessage(pmsg);
DispatchMessage(pmsg);
end while;
end with-stack-structure;
revoke-registration(factory);
end if;
end method main;
With this code in place, if the server is invoked from the command line with /RegServer as an argument, OLE-util-register-only? will return #t. The call to register-coclass creates a type library (with extension .TLB) and registers the type library and the server itself in the Windows registry.
Note that the server provides no way to exit. We can make it exit whenever our interface object is destroyed. This is a little simplistic, since it does not correctly handle the case in which two servers are created, but it will suffice for our example.
RotNExample-server.dylan.
define method terminate (this :: <IRotN-implementation>) => () next-method(); PostQuitMessage(0); // Cause main event loop to terminate. end;
The PostQuitMessage call causes the next call to GetMessage (in the main event loop) to return #f, and thus cause the program to exit.
RotNExample-server-stubs.hdp.