I wasn't going to bother with a standardised architecture for the DarkFiber services - with them being built-in to the core library anyway it seemed a bit like overkill - but, after working on the api's for the timer.service and the hotkey.service a generic architecture started to develop anyway.
With timers and hotkeys, you
add and
remove them, then you receive notifications of when something happens (the timer elapses; the hotkey gets pressed) - so we already have a standard interface here:
AService.Add(
something)
AService.Remove(
something)
Of course, now we're building a list of things we've added to a service, we can also do searching and enumerating, so we get:
AService.Find(
something)
AService.RewindItems
AService.GetItem(
something)
Of course, for this to be completely generic, the
something has to conform to a standard as well. Initially, I was going to go down the fully object-oriented route by having the
something be something that implements the most basic melon interface there is - MObject - and leave it at that. Instead though, I've taken a slightly more old-fashioned/c-based approach and have created a COM datatype called MSERVICEREQUEST that takes the place of the
something. A nice side of this is that the interface is kept very small and is future-proofed. Here's how it works:
Instead of having Add(), Remove(), Find(), etc. methods in the interface, these have all been consolidated into a single method - DoRequest() - which looks like this:
MService_DoRequest(Request As
MSERVICEREQUEST) As
M_RESULTRequest defines the request itself along with any relevant data required to complete the request. For example, if the request is an add then most likely you'll need to provide a name for the object to be added, the virtual keycode and modifier(s) (if it's a hotkey) or the interval (if it's a timer). All this is contained in MSERVICEREQUEST, which looks like this:
typedef struct MSERVICEREQUEST {
M_SERVICE_COMMANDS Command;
MMessageSink *ReplyPort;
MMessage *Reply;
BSTR Name;
BSTR StrData;
LONG Id;
LONG LngData;
} MSERVICEREQUEST;
With the exception of the
Command value, all other values are optional - they're only relevant to the command itself. The nice thing about this is that the
DoRequest() method can be used to enable two-way communication between a service and a client - for example, if the command was a ficticious 'get_info' one, the client could pass this and the name of the object it wanted information about; the service would then populate the other fields accordingly.
Now we've discussed the basics, one or two examples are probably appropriate. At the time of writing, there are three service currently in existence, all probably around 90% complete. These are the hotkey.service, timer.service and task.service (I've written them using their display names so feel free to read them as 'hotkey service' and not 'hotkey dot service').
The timer.service is the most simple - all it does is allow clients to add timers; it then sends a message back to the client every time the timer interval elapses. To add a timer you need to send an add request and provide a name and an interval for the timer - null (empty) names and timers of less than 1 are not allowed. The pseudo code for this is as follows:
With myrequest
.Command = M_SERVICE_ADD
.Name = "my_timer"
.LngData = 500
End WithIf the_timer_service.DoRequest(myrequest) = M_OK Then
{ request completed okay }
Else
{ something failed }
EndIfNow, this wouldn't work. Why? Well, there are two more fields we must also specify when adding a timer -
Reply and
ReplyPort - which detail a melon message and messagesink respectively. These are required as otherwise the service would have no way of contacting the client when the interval times out. Here's the corrected request structure:
With myrequest
.Command = M_SERVICE_ADD
.Name = "my_timer"
.LngData = 500
.Reply = new_Message(100)
.ReplyPort = Me
End WithThis would now succeed and every 100ms or so the adding class would receive a message with the
What field set to 100.
That's it for this update, next time I'll talk about subscribing to a service and how services are accessed from clients.