I've got a couple of apps that run permanently (they monitor and log various
things) and which are deployed in the "normal" way but have no actual need for a UI. I was thinking about rewriting them to run as a console app so they could (I think) then be run as a service. Has anyone been down this route recently (on XP) and could I get some feedback on how feasible/easy it will be. If it makes any difference the apps will need to use Sockets and the IStream classes (for web access). TIA -- Ian Use the Reply-To address to contact me (limited validity). Mail sent to the From address is ignored. |
Ian Bartholomew wrote:
> I've got a couple of apps that run permanently (they monitor and log various > things) and which are deployed in the "normal" way but have no actual need > for a UI. > > I was thinking about rewriting them to run as a console app so they could (I > think) then be run as a service. > > Has anyone been down this route recently (on XP) and could I get some > feedback on how feasible/easy it will be. If it makes any difference the > apps will need to use Sockets and the IStream classes (for web access). > > TIA > > -- > Ian > > Use the Reply-To address to contact me (limited validity). > Mail sent to the From address is ignored. Hi Ian, I'm working on an app right now that I was planning to run as a service. While I can't vouch 100% for this info until I actually do it, here's this gist of what I've seen. Under XP (and I believe W2k) as service is allowed to have a GUI if the "Interactive" flag is specified. It seems to me that an Dolphin executable could work as a "real" NT service. Here is a nice straightforward explaination: http://www.commsoft.com/services.html Me, I was planning on taking the easy way out and use XYZService: http://www.codeproject.com/system/xyntservice.asp However, since I know you're going to write a cool NTServiceSessionManager class I'll probably just wait for that ;) Take care, John |
In reply to this post by Ian Bartholomew-21
Hello Ian.
I've just made a quick (and ugly) service which opens a Shell and serves objects using rSt, and it seems to be able to do both things (after starting the service, I was able to see the shell and create instances of OrderedCollection from a client image in the same machine). So perhaps it's not even necesary to create a console application. It runs under winXP and Dolphin 5.1.4. It uses Bill's ServiceControl package, the srvany.exe program and a custom ServiceSessionManager I've made, and it seems pretty easy to convert a regular application into a service with all of these things. The ServiceSessionManager is a helper to allow the deployed executable to be installed and uninstalled as a service and to keep the service alive when no views are opened. The only problem I had a couple of months ago with a service in winXP was when the service had to access remote files. It was a permissions problem, and we had to write our own srvany.exe like application which creates the application process impersonating a windows user with enough permissions to open the files we needed. It was as ugly as it sounds, and it's not related to Dolphin in particular, but to all services. Another issue could be the service termination. My guess is that srvany.exe just kills the application process to stop the service (I could get away with it so far). So, in my opinion, using Bill's package and svrany.exe, it shouldn't be too much work to make an existing application become a service. best regards martin "Ian Bartholomew" <[hidden email]> escribió en el mensaje news:[hidden email]... > I've got a couple of apps that run permanently (they monitor and log > various things) and which are deployed in the "normal" way but have no > actual need for a UI. > > I was thinking about rewriting them to run as a console app so they could > (I think) then be run as a service. > > Has anyone been down this route recently (on XP) and could I get some > feedback on how feasible/easy it will be. If it makes any difference the > apps will need to use Sockets and the IStream classes (for web access). > > TIA > > -- > Ian > > Use the Reply-To address to contact me (limited validity). > Mail sent to the From address is ignored. > |
In reply to this post by John Rubier
John Rubier wrote:
> Ian Bartholomew wrote: > > I've got a couple of apps that run permanently (they monitor and log various > > things) and which are deployed in the "normal" way but have no actual need > > for a UI. > > > > I was thinking about rewriting them to run as a console app so they could (I > > think) then be run as a service. > > > > Has anyone been down this route recently (on XP) and could I get some > > feedback on how feasible/easy it will be. If it makes any difference the > > apps will need to use Sockets and the IStream classes (for web access). > > > > TIA > > > > -- > > Ian > > > > Use the Reply-To address to contact me (limited validity). > > Mail sent to the From address is ignored. > > Hi Ian, > I'm working on an app right now that I was planning to run as a > service. While I can't vouch 100% for this info until I actually do it, > here's this gist of what I've seen. > > Under XP (and I believe W2k) as service is allowed to have a GUI if the > "Interactive" flag is specified. > It seems to me that an Dolphin executable could work as a "real" NT > service. Here is a nice straightforward explaination: > http://www.commsoft.com/services.html > > Me, I was planning on taking the easy way out and use XYZService: > http://www.codeproject.com/system/xyntservice.asp > > However, since I know you're going to write a cool > NTServiceSessionManager class I'll probably just wait for that ;) > > Take care, > > John Oh, Bill Schwan has a package in his goodies called ServiceControl that may do everything we need. It works with SrvAny.exe. |
In reply to this post by John Rubier
Thanks to John and Martin for the useful replies (and apologies to Bill for
forgetting about his ServiceControl package). I shall have a read and a play and see what happens :=) -- Ian Use the Reply-To address to contact me (limited validity). Mail sent to the From address is ignored. |
In reply to this post by John Rubier
> It seems to me that an Dolphin executable could work as a "real" NT
> service. Here is a nice straightforward explaination: > http://www.commsoft.com/services.html I don't think it's possible, at least in D5, unless there is a way to indefinitely block an ExternalCallback without blocking the main process and without losing the ability to receive more external callbacks. I couldn't find a way to do this. best regards martin |
In reply to this post by John Rubier
Ian,
> Thanks to John and Martin for the useful replies (and apologies to Bill for > forgetting about his ServiceControl package). That makes two of us ;) I hope it helps. Please feel free to use it as a base for something better if the mood strikes. > I shall have a read and a play and see what happens :=) I obviously reached the conclusion that srvany was the only option for D5, which is not to say that is indeed the case. I recall more of my experiments with Squeak as a service. Either way, be sure you can log out and back in before claiming success, especially if you allow/require the service to be directly interactive. A service is probably best run as a console app with a separate GUI for control. Apache does a very nice job of it, but that's probably not much help. As mentioned elsewhere in this thread, sockets and COM could be problematic. In D6, you have overlapped sockets available, and they might work in this setting. Have a good one, Bill -- Wilhelm K. Schwab, Ph.D. [hidden email] |
In reply to this post by John Rubier
Just a brief update in case anyone is interested.
I converted two of my apps to work as Console apps without too much trouble although, as documented in another thread, some aspects were a bit confusing. I decided on, initially at least, taking the path of least resistance and downloaded the XYZService mentioned by John. It worked very well on my development machine, took about 5 mins to set up and get both console apps running as services. It took a bit longer when I transferred things to the machine that they were intended to run on. It turns out that either XYZService, or Windows itself, didn't like the apps residing in a shared folder (All Users/Documents) and silently ignored the request to run them. Once that was sorted (I just moved the apps to a normal folder but left their log files in the shared one) everything works fine (touch wood). I'll have a look at a more Dolphinish way of doing it now.. -- Ian Use the Reply-To address to contact me (limited validity). Mail sent to the From address is ignored. |
In reply to this post by Ian Bartholomew-21
Ok, it appears that D6 *can* work as a native NT service.
Here is a very primitive and ugly POC. The key appears to be marking the AdvApi setServiceStatus call as overlapped *and* forking the method that calls it. Don't know if this is possible in D5. It *does* survive a login/logout as Bill suggested as a test. I do not have GUI as from what I can tell, one is better off having the GUI a separate app and using named pipes to connect to the service. Apparently an "interactive" service can only show on the first session. Problematic with Terminal Services and the related Fast User Switching. Use Debug view to view the output. Following is the text of the package. It can also be downloaded (for the time being) at: http://www.bigrube.com/NTService.pac I created a service under XP with: sc create "aaaTestService" binPath= c:\aaaTemp\NTService.exe" Apologies in advance if your hard drive gets wiped using this or you get a migraine or stroke from viewing the code ;) Take care, John *********Cut here | package | package := Package name: 'NTService'. package paxVersion: 1; basicComment: ''. package imageStripperBytes: (ByteArray fromBase64String: 'IVNUQiAzIEYPDQAEAAAASW1hZ2VTdHJpcHBlcgAAAABSAAAACQAAAE5UU2VydmljZVIAAAAYAAAA QzpcYWFhVGVtcFxOVFNlcnZpY2UuZXhlmgAAAFIAAAAJAAAATlRTZXJ2aWNlUgAAABcAAABOVFNl cnZpY2VTZXNzaW9uTWFuYWdlcu+/JQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAA'). package classNames add: #NTServiceSessionManager; add: #SERVICE_STATUS; add: #SERVICE_TABLE_ENTRY; yourself. package methodNames add: #AdvApiLibrary -> #registerServiceCtrlHandler:lpHandlerProc:; add: #AdvApiLibrary -> #setServiceStatus:lpHandlerProc:; add: #AdvApiLibrary -> #startServiceCtrlDispatcher:; yourself. package binaryGlobalNames: (Set new yourself). package globalAliases: (Set new yourself). package setPrerequisites: (IdentitySet new add: '..\Object Arts\Dolphin\Base\Dolphin'; add: '..\Object Arts\Dolphin\MVP\Base\Dolphin MVP Base'; add: '..\Object Arts\Dolphin\Lagoon\Lagoon Image Stripper'; yourself). package! "Class Definitions"! ExternalStructure subclass: #SERVICE_STATUS instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' classInstanceVariableNames: ''! ExternalStructure subclass: #SERVICE_TABLE_ENTRY instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' classInstanceVariableNames: ''! RuntimeSessionManager subclass: #NTServiceSessionManager instanceVariableNames: 'tickCount tickCountMutex status serviceMainCallback serviceHandlerCallback serviceStopMutex serviceStatusHandle' classVariableNames: '' poolDictionaries: '' classInstanceVariableNames: ''! "Global Aliases"! "Loose Methods"! !AdvApiLibrary methodsFor! registerServiceCtrlHandler: aString lpHandlerProc: aProcPointer <stdcall: dword RegisterServiceCtrlHandlerA lpvoid lpvoid> ^self invalidCall! setServiceStatus: aServiceHandle lpHandlerProc: aServiceStatus <stdcall: bool SetServiceStatus dword lpvoid> ^self invalidCall! startServiceCtrlDispatcher: aServiceTable <overlap stdcall: bool StartServiceCtrlDispatcherA lpvoid> ^self invalidCall! ! !AdvApiLibrary categoriesFor: #registerServiceCtrlHandler:lpHandlerProc:!must not strip!public!win32 functions-events! ! !AdvApiLibrary categoriesFor: #setServiceStatus:lpHandlerProc:!must not strip!public!win32 functions-events! ! !AdvApiLibrary categoriesFor: #startServiceCtrlDispatcher:!must not strip!public!win32 functions-events! ! "End of package definition"! "Source Globals"! "Classes"! SERVICE_STATUS guid: (GUID fromString: '{2BC2B412-26D7-481E-A5DF-BD933DD96014}')! SERVICE_STATUS comment: ''! !SERVICE_STATUS categoriesForClass!External-Data-Structured! ! !SERVICE_STATUS class methodsFor! defineFields "Define the fields of the SERVICE_STATUS structure. SERVICE_STATUS compileDefinition typedef struct _SERVICE_STATUS { DWORD dwServiceType; DWORD dwCurrentState; DWORD dwControlsAccepted; DWORD dwWin32ExitCode; DWORD dwServiceSpecificExitCode; DWORD dwCheckPoint; DWORD dwWaitHint; } SERVICE_STATUS, *LPSERVICE_STATUS; " self defineField: #dwServiceType type: DWORDField new; defineField: #dwCurrentState type: DWORDField new; defineField: #dwControlsAccepted type: DWORDField new; defineField: #dwWin32ExitCode type: DWORDField new; defineField: #dwServiceSpecificExitCode type: DWORDField new; defineField: #dwCheckPoint type: DWORDField new; defineField: #dwWaitHint type: DWORDField new! ! !SERVICE_STATUS class categoriesFor: #defineFields!**auto generated**!initializing!public! ! SERVICE_TABLE_ENTRY guid: (GUID fromString: '{A65B05B4-0CEC-4E24-83CF-413C89052787}')! SERVICE_TABLE_ENTRY comment: ''! !SERVICE_TABLE_ENTRY categoriesForClass!External-Data-Structured! ! !SERVICE_TABLE_ENTRY methodsFor! lpServiceName "Answer the receiver's lpServiceName field as a Smalltalk object." ^String fromAddress: (bytes sdwordAtOffset: 0)! lpServiceName: anObject "Set the receiver's lpServiceName field to the value of anObject." bytes dwordAtOffset: 0 put: anObject yourAddress! lpServiceProc "Answer the receiver's lpServiceProc field as a Smalltalk object." ^(bytes dwordAtOffset: 4) asExternalAddress! lpServiceProc: anObject "Set the receiver's lpServiceProc field to the value of anObject." bytes dwordAtOffset: 4 put: anObject! ! !SERVICE_TABLE_ENTRY categoriesFor: #lpServiceName!**compiled accessors**!public! ! !SERVICE_TABLE_ENTRY categoriesFor: #lpServiceName:!**compiled accessors**!public! ! !SERVICE_TABLE_ENTRY categoriesFor: #lpServiceProc!**compiled accessors**!public! ! !SERVICE_TABLE_ENTRY categoriesFor: #lpServiceProc:!**compiled accessors**!public! ! !SERVICE_TABLE_ENTRY class methodsFor! defineFields "Define the fields of the SERVICE_TABLE_ENTRY structure. SERVICE_TABLE_ENTRY compileDefinition typedef struct _SERVICE_TABLE_ENTRY { LPTSTR lpServiceName; LPSERVICE_MAIN_FUNCTION lpServiceProc; } SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY; " self defineField: #lpServiceName type: (PointerField type: String); defineField: #lpServiceProc type: LPVOIDField new. ! ! !SERVICE_TABLE_ENTRY class categoriesFor: #defineFields!**auto generated**!initializing!public! ! NTServiceSessionManager guid: (GUID fromString: '{D7D0A391-CA82-47C6-AD7F-AD459669BC0F}')! NTServiceSessionManager comment: ''! !NTServiceSessionManager categoriesForClass!Unclassified! ! !NTServiceSessionManager methodsFor! keepAlive "SessionManager current trace: 'keepAlive called by inputstate'." ^self! main tickCount := 0. tickCountMutex := Mutex new. [self registerService] forkAt: Processor userBackgroundPriority. SessionManager current trace: 'Post registerService fork'. [ [self tick. Processor sleep: 1000] repeat] forkAt: Processor userBackgroundPriority "ServiceShell showOn: tickCount."! registerService | st rc ste | st := StructureArray length:2 elementClass: SERVICE_TABLE_ENTRY. ste := SERVICE_TABLE_ENTRY new. ste lpServiceName: ('' asParameter ). serviceMainCallback := ExternalCallback receiver: self selector: #serviceMain:argv: descriptor: ##(ExternalDescriptor argumentTypes: 'dword lpvoid') . ste lpServiceProc: (serviceMainCallback asParameter yourAddress ). st at:1 put: ste . SessionManager current trace: 'Calling startServiceCtrlDispatcher'. rc := AdvApiLibrary default startServiceCtrlDispatcher: st. SessionManager current trace: 'Return code:',(rc displayString). SessionManager current trace: 'Error:',((KernelLibrary default getLastError ) displayString).! serviceHandler: controlCode | status rc | SessionManager current trace: 'serviceHandler Callback' , controlCode displayString. controlCode = 1 ifTrue: [status := SERVICE_STATUS new. status dwServiceType: 16. "SERVICE_WIN32_OWN_PROCESS" status dwCurrentState: 3. "SERVICE_STOP_PENDING" status dwControlsAccepted: 7. "SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP" status dwWin32ExitCode: 0. status dwServiceSpecificExitCode: 0. status dwCheckPoint: 0. status dwWaitHint: 0. rc := AdvApiLibrary default setServiceStatus: serviceStatusHandle lpHandlerProc: status. SessionManager current trace: 'serviceHandler called setServiceStatus:' , rc displayString. rc ifFalse: [SessionManager current trace: 'setServiceStatus error:' , KernelLibrary default getLastError displayString]. self stopService] ifFalse: [ (controlCode = 2) ifTrue: [status := SERVICE_STATUS new. status dwServiceType: 16. "SERVICE_WIN32_OWN_PROCESS" status dwCurrentState: 3. "SERVICE_STOP_PENDING" status dwControlsAccepted: 7. "SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP" status dwWin32ExitCode: 0. status dwServiceSpecificExitCode: 0. status dwCheckPoint: 0. status dwWaitHint: 0. rc := AdvApiLibrary default setServiceStatus: serviceStatusHandle lpHandlerProc: status. SessionManager current trace: 'serviceHandler called setServiceStatus:' , rc displayString. rc ifFalse: [SessionManager current trace: 'setServiceStatus error:' , KernelLibrary default getLastError displayString]] ]. ^nil! serviceMain: argc argv: ptrArgv | rc status | SessionManager current trace: 'serviceMain Callback'. "Register handler" serviceHandlerCallback := ExternalCallback receiver: self selector: #serviceHandler: descriptor: ##(ExternalDescriptor argumentTypes: 'dword'). serviceStopMutex := Semaphore new. serviceStatusHandle := AdvApiLibrary default registerServiceCtrlHandler: '' asParameter lpHandlerProc: serviceHandlerCallback asParameter yourAddress. SessionManager current trace: 'serviceStatus handle' , serviceStatusHandle displayString. status := SERVICE_STATUS new. status dwServiceType: 16. "SERVICE_WIN32_OWN_PROCESS" status dwCurrentState: 4. "SERVICE_RUNNING" status dwControlsAccepted: 5. "SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP" status dwWin32ExitCode: 0. status dwServiceSpecificExitCode: 0. status dwCheckPoint: 0. status dwWaitHint: 0. rc := AdvApiLibrary default setServiceStatus: serviceStatusHandle lpHandlerProc: (status ). SessionManager current trace: 'serviceMain called setServiceStatus:' , rc displayString. rc ifFalse: [SessionManager current trace: 'setServiceStatus error:' , KernelLibrary default getLastError displayString]. "block until service stopped" "serviceStopMutex wait." ^0! stopService | status rc | status := SERVICE_STATUS new. status dwServiceType: 16. "SERVICE_WIN32_OWN_PROCESS" status dwCurrentState: 1. "SERVICE_STOPPED" status dwControlsAccepted: 7. "SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP" status dwWin32ExitCode: 0. status dwServiceSpecificExitCode: 0. status dwCheckPoint: 0. status dwWaitHint: 0. rc := AdvApiLibrary default setServiceStatus: serviceStatusHandle lpHandlerProc: status. SessionManager current trace: 'serviceHandler called setServiceStatus:' , rc displayString. rc ifFalse: [SessionManager current trace: 'setServiceStatus error:' , KernelLibrary default getLastError displayString]. self exit.! tick SessionManager current trace: 'tick'. tickCountMutex critical: [tickCount := tickCount + 1]! ! !NTServiceSessionManager categoriesFor: #keepAlive!public! ! !NTServiceSessionManager categoriesFor: #main!public! ! !NTServiceSessionManager categoriesFor: #registerService!public! ! !NTServiceSessionManager categoriesFor: #serviceHandler:!public! ! !NTServiceSessionManager categoriesFor: #serviceMain:argv:!public! ! !NTServiceSessionManager categoriesFor: #stopService!public! ! !NTServiceSessionManager categoriesFor: #tick!public! ! "Binary Globals"! |
In reply to this post by Ian Bartholomew-21
Ian Bartholomew wrote:
> Has anyone been down this route recently (on XP) and could I get some > feedback on how feasible/easy it will be. If it makes any difference the > apps will need to use Sockets and the IStream classes (for web access). Just a small warning about running a Dolphin app, with an event loop, as a service. There's a potential security problem with privilege-elevation using a "shatter" attack. That's where the attacker sends Windows messages to the input queue of another application running with elevated/different privileges, causing it to run code of the attacker's choice. Google will find more information. It's an elegant and easy technique which /might/ be applicable to the sort of scenario we have been talking about in this thread. I doubt whether it's an issue for Ian, but if anyone is thinking about creating services to run in more hostile environments, then it's something to consider. -- chris |
In reply to this post by John Rubier
Interesting !
A question: would you, John, and Bill mind if a take code and ideas from both of your packages and merge them with some of my stuff and try to make a fully featured service framework with all of it ? Or, if you prefer, I can send my stuff to you. best regards martin "John" <[hidden email]> escribió en el mensaje news:%h6lg.7266$[hidden email]... > Ok, it appears that D6 *can* work as a native NT service. > Here is a very primitive and ugly POC. > The key appears to be marking the AdvApi setServiceStatus call as > overlapped *and* forking the method that calls it. Don't know if this is > possible in D5. > It *does* survive a login/logout as Bill suggested as a test. > I do not have GUI as from what I can tell, one is better off having the > GUI a separate app and using named pipes to connect to the service. > Apparently an "interactive" service can only show on the first session. > Problematic with Terminal Services and the related Fast User Switching. > Use Debug view to view the output. > > Following is the text of the package. It can also be downloaded (for the > time being) at: http://www.bigrube.com/NTService.pac > > I created a service under XP with: > sc create "aaaTestService" binPath= c:\aaaTemp\NTService.exe" > > Apologies in advance if your hard drive gets wiped using this or you get a > migraine or stroke from viewing the code ;) > > Take care, > > John > > *********Cut here > | package | > package := Package name: 'NTService'. > package paxVersion: 1; > basicComment: ''. > > package imageStripperBytes: (ByteArray fromBase64String: > 'IVNUQiAzIEYPDQAEAAAASW1hZ2VTdHJpcHBlcgAAAABSAAAACQAAAE5UU2VydmljZVIAAAAYAAAA > QzpcYWFhVGVtcFxOVFNlcnZpY2UuZXhlmgAAAFIAAAAJAAAATlRTZXJ2aWNlUgAAABcAAABOVFNl > cnZpY2VTZXNzaW9uTWFuYWdlcu+/JQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA > AAAAAAAA'). > > package classNames > add: #NTServiceSessionManager; > add: #SERVICE_STATUS; > add: #SERVICE_TABLE_ENTRY; > yourself. > > package methodNames > add: #AdvApiLibrary -> #registerServiceCtrlHandler:lpHandlerProc:; > add: #AdvApiLibrary -> #setServiceStatus:lpHandlerProc:; > add: #AdvApiLibrary -> #startServiceCtrlDispatcher:; > yourself. > > package binaryGlobalNames: (Set new > yourself). > > package globalAliases: (Set new > yourself). > > package setPrerequisites: (IdentitySet new > add: '..\Object Arts\Dolphin\Base\Dolphin'; > add: '..\Object Arts\Dolphin\MVP\Base\Dolphin MVP Base'; > add: '..\Object Arts\Dolphin\Lagoon\Lagoon Image Stripper'; > yourself). > > package! > > "Class Definitions"! > > ExternalStructure subclass: #SERVICE_STATUS > instanceVariableNames: '' > classVariableNames: '' > poolDictionaries: '' > classInstanceVariableNames: ''! > ExternalStructure subclass: #SERVICE_TABLE_ENTRY > instanceVariableNames: '' > classVariableNames: '' > poolDictionaries: '' > classInstanceVariableNames: ''! > RuntimeSessionManager subclass: #NTServiceSessionManager > instanceVariableNames: 'tickCount tickCountMutex status > serviceMainCallback serviceHandlerCallback serviceStopMutex > serviceStatusHandle' > classVariableNames: '' > poolDictionaries: '' > classInstanceVariableNames: ''! > > "Global Aliases"! > > > "Loose Methods"! > > !AdvApiLibrary methodsFor! > > registerServiceCtrlHandler: aString lpHandlerProc: aProcPointer > <stdcall: dword RegisterServiceCtrlHandlerA lpvoid lpvoid> > ^self invalidCall! > > setServiceStatus: aServiceHandle lpHandlerProc: aServiceStatus > <stdcall: bool SetServiceStatus dword lpvoid> > ^self invalidCall! > > startServiceCtrlDispatcher: aServiceTable > <overlap stdcall: bool StartServiceCtrlDispatcherA lpvoid> > ^self invalidCall! ! > !AdvApiLibrary categoriesFor: > #registerServiceCtrlHandler:lpHandlerProc:!must not strip!public!win32 > functions-events! ! > !AdvApiLibrary categoriesFor: #setServiceStatus:lpHandlerProc:!must not > strip!public!win32 functions-events! ! > !AdvApiLibrary categoriesFor: #startServiceCtrlDispatcher:!must not > strip!public!win32 functions-events! ! > > "End of package definition"! > > "Source Globals"! > > "Classes"! > > SERVICE_STATUS guid: (GUID fromString: > '{2BC2B412-26D7-481E-A5DF-BD933DD96014}')! > SERVICE_STATUS comment: ''! > !SERVICE_STATUS categoriesForClass!External-Data-Structured! ! > !SERVICE_STATUS class methodsFor! > > defineFields > "Define the fields of the SERVICE_STATUS structure. > SERVICE_STATUS compileDefinition > > typedef struct _SERVICE_STATUS { > DWORD dwServiceType; > DWORD dwCurrentState; > DWORD dwControlsAccepted; > DWORD dwWin32ExitCode; > DWORD dwServiceSpecificExitCode; > DWORD dwCheckPoint; > DWORD dwWaitHint; > } SERVICE_STATUS, *LPSERVICE_STATUS; > > " > > self > defineField: #dwServiceType type: DWORDField new; > defineField: #dwCurrentState type: DWORDField new; > defineField: #dwControlsAccepted type: DWORDField new; > defineField: #dwWin32ExitCode type: DWORDField new; > defineField: #dwServiceSpecificExitCode type: DWORDField new; > defineField: #dwCheckPoint type: DWORDField new; > defineField: #dwWaitHint type: DWORDField new! ! > !SERVICE_STATUS class categoriesFor: #defineFields!**auto > generated**!initializing!public! ! > > SERVICE_TABLE_ENTRY guid: (GUID fromString: > '{A65B05B4-0CEC-4E24-83CF-413C89052787}')! > SERVICE_TABLE_ENTRY comment: ''! > !SERVICE_TABLE_ENTRY categoriesForClass!External-Data-Structured! ! > !SERVICE_TABLE_ENTRY methodsFor! > > lpServiceName > "Answer the receiver's lpServiceName field as a Smalltalk object." > > ^String fromAddress: (bytes sdwordAtOffset: 0)! > > lpServiceName: anObject > "Set the receiver's lpServiceName field to the value of anObject." > > bytes dwordAtOffset: 0 put: anObject yourAddress! > > lpServiceProc > "Answer the receiver's lpServiceProc field as a Smalltalk object." > > ^(bytes dwordAtOffset: 4) asExternalAddress! > > lpServiceProc: anObject > "Set the receiver's lpServiceProc field to the value of anObject." > > bytes dwordAtOffset: 4 put: anObject! ! > !SERVICE_TABLE_ENTRY categoriesFor: #lpServiceName!**compiled > accessors**!public! ! > !SERVICE_TABLE_ENTRY categoriesFor: #lpServiceName:!**compiled > accessors**!public! ! > !SERVICE_TABLE_ENTRY categoriesFor: #lpServiceProc!**compiled > accessors**!public! ! > !SERVICE_TABLE_ENTRY categoriesFor: #lpServiceProc:!**compiled > accessors**!public! ! > > !SERVICE_TABLE_ENTRY class methodsFor! > > defineFields > "Define the fields of the SERVICE_TABLE_ENTRY structure. > SERVICE_TABLE_ENTRY compileDefinition > > typedef struct _SERVICE_TABLE_ENTRY { > LPTSTR lpServiceName; > LPSERVICE_MAIN_FUNCTION lpServiceProc; > } SERVICE_TABLE_ENTRY, > *LPSERVICE_TABLE_ENTRY; > > " > > self > defineField: #lpServiceName type: (PointerField type: String); > defineField: #lpServiceProc type: LPVOIDField new. > ! ! > !SERVICE_TABLE_ENTRY class categoriesFor: #defineFields!**auto > generated**!initializing!public! ! > > NTServiceSessionManager guid: (GUID fromString: > '{D7D0A391-CA82-47C6-AD7F-AD459669BC0F}')! > NTServiceSessionManager comment: ''! > !NTServiceSessionManager categoriesForClass!Unclassified! ! > !NTServiceSessionManager methodsFor! > > keepAlive > "SessionManager current trace: 'keepAlive called by inputstate'." > ^self! > > main > > > tickCount := 0. > tickCountMutex := Mutex new. > [self registerService] forkAt: Processor userBackgroundPriority. > SessionManager current trace: 'Post registerService fork'. > [ > [self tick. > Processor sleep: 1000] repeat] forkAt: Processor userBackgroundPriority > "ServiceShell showOn: tickCount."! > > registerService > | st rc ste | > st := StructureArray length:2 elementClass: SERVICE_TABLE_ENTRY. > ste := SERVICE_TABLE_ENTRY new. > ste lpServiceName: ('' asParameter ). > > serviceMainCallback := ExternalCallback receiver: self selector: > #serviceMain:argv: descriptor: ##(ExternalDescriptor argumentTypes: 'dword > lpvoid') . > ste lpServiceProc: (serviceMainCallback asParameter yourAddress ). > st at:1 put: ste . > SessionManager current trace: 'Calling startServiceCtrlDispatcher'. > rc := AdvApiLibrary default startServiceCtrlDispatcher: st. > SessionManager current trace: 'Return code:',(rc displayString). > SessionManager current trace: 'Error:',((KernelLibrary default > getLastError ) displayString).! > > serviceHandler: controlCode > | status rc | > SessionManager current trace: 'serviceHandler Callback' , controlCode > displayString. > controlCode = 1 > ifTrue: > [status := SERVICE_STATUS new. > status dwServiceType: 16. "SERVICE_WIN32_OWN_PROCESS" > status dwCurrentState: 3. "SERVICE_STOP_PENDING" > status dwControlsAccepted: 7. "SERVICE_ACCEPT_PAUSE_CONTINUE | > SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP" > status dwWin32ExitCode: 0. > status dwServiceSpecificExitCode: 0. > status dwCheckPoint: 0. > status dwWaitHint: 0. > rc := AdvApiLibrary default setServiceStatus: serviceStatusHandle > lpHandlerProc: status. > SessionManager current trace: 'serviceHandler called setServiceStatus:' , > rc displayString. > rc > ifFalse: > [SessionManager current > trace: 'setServiceStatus error:' , KernelLibrary default getLastError > displayString]. > self stopService] ifFalse: [ (controlCode = 2) ifTrue: [status := > SERVICE_STATUS new. > status dwServiceType: 16. "SERVICE_WIN32_OWN_PROCESS" > status dwCurrentState: 3. "SERVICE_STOP_PENDING" > status dwControlsAccepted: 7. "SERVICE_ACCEPT_PAUSE_CONTINUE | > SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP" > status dwWin32ExitCode: 0. > status dwServiceSpecificExitCode: 0. > status dwCheckPoint: 0. > status dwWaitHint: 0. > rc := AdvApiLibrary default setServiceStatus: serviceStatusHandle > lpHandlerProc: status. > SessionManager current trace: 'serviceHandler called setServiceStatus:' , > rc displayString. > rc > ifFalse: > [SessionManager current > trace: 'setServiceStatus error:' , KernelLibrary default getLastError > displayString]] ]. > ^nil! > > serviceMain: argc argv: ptrArgv > | rc status | > SessionManager current trace: 'serviceMain Callback'. > "Register handler" > serviceHandlerCallback := ExternalCallback > receiver: self > selector: #serviceHandler: > descriptor: ##(ExternalDescriptor argumentTypes: 'dword'). > serviceStopMutex := Semaphore new. > serviceStatusHandle := AdvApiLibrary default registerServiceCtrlHandler: > '' asParameter > lpHandlerProc: serviceHandlerCallback asParameter yourAddress. > SessionManager current trace: 'serviceStatus handle' , serviceStatusHandle > displayString. > status := SERVICE_STATUS new. > status dwServiceType: 16. "SERVICE_WIN32_OWN_PROCESS" > status dwCurrentState: 4. "SERVICE_RUNNING" > status dwControlsAccepted: 5. "SERVICE_ACCEPT_SHUTDOWN | > SERVICE_ACCEPT_STOP" > status dwWin32ExitCode: 0. > status dwServiceSpecificExitCode: 0. > status dwCheckPoint: 0. > status dwWaitHint: 0. > rc := AdvApiLibrary default setServiceStatus: serviceStatusHandle > lpHandlerProc: (status ). > SessionManager current trace: 'serviceMain called setServiceStatus:' , rc > displayString. > rc > ifFalse: > [SessionManager current > trace: 'setServiceStatus error:' , KernelLibrary default getLastError > displayString]. > "block until service stopped" > "serviceStopMutex wait." > ^0! > > stopService > | status rc | > status := SERVICE_STATUS new. > status dwServiceType: 16. "SERVICE_WIN32_OWN_PROCESS" > status dwCurrentState: 1. "SERVICE_STOPPED" > status dwControlsAccepted: 7. "SERVICE_ACCEPT_PAUSE_CONTINUE | > SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP" > status dwWin32ExitCode: 0. > status dwServiceSpecificExitCode: 0. > status dwCheckPoint: 0. > status dwWaitHint: 0. > rc := AdvApiLibrary default setServiceStatus: serviceStatusHandle > lpHandlerProc: status. > SessionManager current trace: 'serviceHandler called setServiceStatus:' , > rc displayString. > rc > ifFalse: > [SessionManager current > trace: 'setServiceStatus error:' , KernelLibrary default getLastError > displayString]. > self exit.! > > tick > SessionManager current trace: 'tick'. > tickCountMutex critical: [tickCount := tickCount + 1]! ! > !NTServiceSessionManager categoriesFor: #keepAlive!public! ! > !NTServiceSessionManager categoriesFor: #main!public! ! > !NTServiceSessionManager categoriesFor: #registerService!public! ! > !NTServiceSessionManager categoriesFor: #serviceHandler:!public! ! > !NTServiceSessionManager categoriesFor: #serviceMain:argv:!public! ! > !NTServiceSessionManager categoriesFor: #stopService!public! ! > !NTServiceSessionManager categoriesFor: #tick!public! ! > > "Binary Globals"! |
Martin Rubi wrote:
> Interesting ! > > A question: would you, John, and Bill mind if a take code and ideas from > both of your packages and merge them with some of my stuff and try to make a > fully featured service framework with all of it ? Or, if you prefer, I can > send my stuff to you. > > best regards > martin > Martin, Feel free to use the code. I'm glad I was able to provide something useful instead of mooching for once! :) Take care, John |
John Rubier wrote:
> Martin Rubi wrote: >> Interesting ! >> >> A question: would you, John, and Bill mind if a take code and ideas from >> both of your packages and merge them with some of my stuff and try to make a >> fully featured service framework with all of it ? Or, if you prefer, I can >> send my stuff to you. Go for it! Have a good one, Bill -- Wilhelm K. Schwab, Ph.D. [hidden email] |
Ok, I've done my first attemp to put the whole thing together.
I've tested it with a trivial (and I mean trivial) service in win2k and XP, and it _seems_ to work. However, I would not say it does until I run a real service with it. The package can be found here: http://mrubi.users.dolphinmap.net/Martin Rubi goodies.zip under de "nt service" folder. Thanks again to Bill and John for letting me borrow their work. Please send any comment (since I may use this for real, specially the comments saying why it would be a bad idea to do so) you may have. best regards martin |
Free forum by Nabble | Edit this page |