I am trying to write a wrapper or library to create Windows services. In order to do this, I wanted to create a Service object that lets you set up hooks for onStart, onStop etc. to respond to the service control codes.
But, I have hit a problem. When a service is started, you pass a call back to the control handler like so:
RegisterServiceCtrlHandler(serviceName, serviceCtrlHandler)
The serviceCtrlHandler is defined as follows:
LPHANDLER_FUNCTION* = proc (para1: DWORD): WINBOOL{.stdcall.}
So, everything's grand so far. Then I realised that in order to call the hooks for the control events, the serviceCtrlHandler needs to access the instance of Service that has the relevant hooks in.
Obviously I can't change the parameters of the handler function to pass a Service instance over, so the only obvious way to do it is with a global Service variable, but that's not much good if this is going to be a wrapper/library.
In other languages, this is relatively straight forward, as the handler function can be bound to a type but still have the same signature, so it can simply call self.onStop or whatever. In Nim, because procs can't be tied to types, and I can't pass the type over as a parameter, it seems like the only option is a global Service variable.
What is the Nim way of doing this? I am open to doing this completely differently if necessary, I just want a reasonably elegant solution.
So basically, how can I set up something in Nim that calls a hook attached to an object from a proc that doesn't pass the object as a parameter?
This may seem like a total contradiction and not possible, which is fine. Can any suggest a better way of doing this from a library perspective?
There is no good solution here and it's not Nim's fault. Attaching a proc to a type wouldn't benefit Nim for this nor can it help other languages for technical reasons. Maybe other languages provide a nicer wrapper to do it though.
You should use https://msdn.microsoft.com/en-us/library/windows/desktop/ms685058(v=vs.85).aspx this instead which has support for a user-definable context, which you can use to pass your state to it.
Well that's fantastic - don't know how I missed that function! This should solve my problem, thanks Araq.
As for the basic mechanism, you're right it's not Nim's fault and the information required simply isn't in the environment to create a solution.
Actually it did make me think, how DO other languages do it? I don't see how the type information can even reach the callback since it's fired from an entirely different thread environment.
So I had a look how object pascal does it. In a nutshell, they use a global variable to store application state and when the serviceMain is called, they simply dispatch control to the typed procs stored in this global. Passing the context is far better, IMO, so this should wind up being a bit more adaptable.