.NET Forum / .NET Framework / Interop / October 2005
Service hangs when accessing ActiveX control
|
|
Thread rating:  |
Lucvdv - 13 Sep 2005 15:57 GMT Is there something that has to be taken special care of when using an ActiveX control in a .Net service application?
I'm creating a VB.Net service app that uses an ActiveX control on a hidden form.
When I start the program as a normal application, it works.
When I try to start it as a service, it hangs when the first method of the AxtiveX is called (the method never returns).
The ActiveX itself was written in VC++6/MFC. At this point (the method that hangs), it does nothing but accept a piece of data and store it in a CMap class.
The .Net service app is installed with SERVICE_WIN32_OWN_PROCESS and SERVICE_INTERACTIVE_PROCESS flags so I should see error popups if there are any, and all unhandled exceptions are caught and written to the event log, but nothing shows up either way.
Trying to attach the debugger to the running service gives "access denied", so that way I can't find it either.
To find the exact line where it hangs, I had to write progress messages to the event log before every line of VB source.
Robert Jordan - 13 Sep 2005 16:12 GMT > Is there something that has to be taken special care of when using an > ActiveX control in a .Net service application? This problem is not related to .NET:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/in teractive_services.asp
Rob
Lucvdv - 13 Sep 2005 16:15 GMT > Is there something that has to be taken special care of when using an > ActiveX control in a .Net service application? Add: I've used the same ActiveX control before in services written in VS6, and there it works, so it has to be .Net-related.
Something with the activex interop dll's that makes them unsuited for services?
Robert Jordan - 13 Sep 2005 17:52 GMT >>Is there something that has to be taken special care of when using an >>ActiveX control in a .Net service application? > > Add: I've used the same ActiveX control before in services written in VS6, > and there it works, so it has to be .Net-related. On the same machine, same OS, same version?
> Something with the activex interop dll's that makes them unsuited for > services? No. The interop dll contains just metadata and some glue code.
Rob
Willy Denoyette [MVP] - 13 Sep 2005 19:31 GMT >> Is there something that has to be taken special care of when using an >> ActiveX control in a .Net service application? [quoted text clipped - 4 lines] > Something with the activex interop dll's that makes them unsuited for > services? First off, ActiveX and Windows Forms should never be used from within Windows Services, they aren't designed for this kind of scenarios. The reason your call blocks is that both your form and your control must live in an STA, now for the AX control this is taken care of by COM itself which will spin-up an STA thread when you create an instance on an MTA thread, but your Windows Form will run on an MTA thread (unless you created the form from an STA thread) and as such can't do the required message pumping in the COM thread (STA). The result is blocking COM calls.
So what you should do is create the from and the control's instance from an (the same) STA thread, but again, this is not something I strongly advise against.
Willy.
Lucvdv - 14 Sep 2005 09:00 GMT > So what you should do is create the from and the control's instance from an > (the same) STA thread, but again, this is not something I strongly advise > against. Thanks. I suppose there's a "not" too many there?
But it doesn't even work: the form is created in the constructor of the service class, which is instantiated from a sub Main with <STAThread> attribute. The form is used only as a container for the control.
Just to see if there's a difference I changed it into <MTAThread>. That causes a ThreadStateException when the service is started, so it's not like the <STAThread> was ignored (which it would be if there was no COM interop).
So I guess I'll have to port the control's code to a managed dll. I've already had to do that with another piece of code in another application, but this time it doesn't look as if it will be simple, and time is pressing :(
Robert Jordan - 14 Sep 2005 09:50 GMT >>So what you should do is create the from and the control's instance from an >>(the same) STA thread, but again, this is not something I strongly advise [quoted text clipped - 11 lines] > like the <STAThread> was ignored (which it would be if there was no COM > interop). Main gets ignored when the assembly is used as a service!
> So I guess I'll have to port the control's code to a managed dll. > I've already had to do that with another piece of code in another > application, but this time it doesn't look as if it will be simple, and > time is pressing :( Willy told you to use another thread for that. For that thread you can setup a STA:
Thread t = new Thread (ThreadProc); t.ApartmentState = ApartmentState.STA; t.Start ();
void ThreadProc() { // create the form with the ax object Application.Run (new SomeForm()); }
Rob
Lucvdv - 14 Sep 2005 13:26 GMT > Main gets ignored when the assembly is used as a service! The Main function is generated by Visual Studio with the code necessary to start the service, I assume it is called from ServiceMain when running as a service.
See http://codebetter.com/blogs/sahil.malik/archive/2004/12/06/35295.aspx, I'm using it similar to the way Sahil does there (actually a mix of the way Sahil explains and what I posted in a comment near the bottom of that page).
If it was ignored, changing its attributes to MTAThread shouldn't make any difference, or at least that's what I would expect.
Lucvdv - 14 Sep 2005 13:35 GMT > > Main gets ignored when the assembly is used as a service! > [quoted text clipped - 9 lines] > If it was ignored, changing its attributes to MTAThread shouldn't make any > difference, or at least that's what I would expect. To eliminate all doubt, I wrote an entry into the event log from inside the Main function. It *is* executed when the assembly is run as a service.
Willy Denoyette [MVP] - 14 Sep 2005 17:14 GMT >> Main gets ignored when the assembly is used as a service! > [quoted text clipped - 11 lines] > If it was ignored, changing its attributes to MTAThread shouldn't make any > difference, or at least that's what I would expect. As I said in another thread, the STAThread/MTAThread attribute is ignored in a Service.
Only thing you can do is a Robert said, create another thread and initialize it as a STA.
Willy.
Willy Denoyette [MVP] - 14 Sep 2005 17:11 GMT >> So what you should do is create the from and the control's instance from >> an >> (the same) STA thread, but again, this is not something I strongly advise >> against. > > Thanks. I suppose there's a "not" too many there? Yep.
> But it doesn't even work: the form is created in the constructor of the > service class, which is instantiated from a sub Main with <STAThread> > attribute. > The form is used only as a container for the control. The STAThread attribute is ignored on Main in a service, Service threads run in an MTA by default, if this is not what you want, you have to create your own STA thread.
> Just to see if there's a difference I changed it into <MTAThread>. > That causes a ThreadStateException when the service is started, so it's > not > like the <STAThread> was ignored (which it would be if there was no COM > interop). Sure there is no difference because ... see above.
> So I guess I'll have to port the control's code to a managed dll. > I've already had to do that with another piece of code in another > application, but this time it doesn't look as if it will be simple, and > time is pressing :( Won't help you, Controls in .NET are UI elements and have the (almost) same requirements as AX controls.
Willy.
Lucvdv - 15 Sep 2005 12:47 GMT > The STAThread attribute is ignored on Main in a service, Service threads run > in an MTA by default, if this is not what you want, you have to create your > own STA thread. But observation indicates the opposite.
Not specifying any attribute or specifying it as STA results in the same behavior. Specifing it as MTA is different, in that it causes a ThreadStateException when the service is started - probably because the COM interface checks if it is running in STA.
I can only conclude from that, that the attribute is not ignored in a service and the default is STAThread.
VS.Net *does* explicitly put <MTAThread()> in the declaration (which I removed because I was using COM), and maybe it does so because it has to be that way for a service, but that's not the same as ignoring it.
> > Just to see if there's a difference I changed it into <MTAThread>. > > That causes a ThreadStateException when the service is started, so it's [quoted text clipped - 11 lines] > Won't help you, Controls in .NET are UI elements and have the (almost) same > requirements as AX controls. I wouldn't make it a control, but a simple class that exposes the same properties, methods and events (it has no UI elements, in that respect it behaves like the MS Winsock control among others).
Stephen Martin - 15 Sep 2005 15:12 GMT While the explanations that you have received so far are incorrect the solutions offered up are essentially correct.
When a service process starts it starts up exactly like any other process so Main is called and the STA/MTA attribute is indeed honoured. I'm not sure how it could be ignored since the process has no idea it is even a service process until ServiceBase.Run is called. What does happen is that the main thread of a service process is suspended by the Service Control Manager when ServiceBase.Run is called and all further interaction (OnStart, OnPause, etc.) is done on different threads. In your case you have created your form correctly in an STA on the main thread but you are later calling into this apartment from a different thread. The COM marshaller attempts to marshal this call to the correct apartment but that involves sending a message to a window owned by the original thread. Unfortunately, the main thread is suspended and can never read the message and so your call hangs.
The correct procedure is, as has been suggested, to create an STA thread in your OnStart method and then create the form and control in the ThreadStart procedure of that thread. You must then call either form.ShowDialog or Application.Run(form) to start the message pump. In your OnStop you can then Close the form and allow the thread to exit.
There is no inherent reason not to use windows and message pumps in a service. It is fairly rare to need to do so but it can be very useful especially in cases such as using legacy components or for inter-thread communications.
>> The STAThread attribute is ignored on Main in a service, Service threads >> run [quoted text clipped - 38 lines] > properties, methods and events (it has no UI elements, in that respect it > behaves like the MS Winsock control among others). Lucvdv - 16 Sep 2005 08:54 GMT > When a service process starts it starts up exactly like any other process so > Main is called and the STA/MTA attribute is indeed honoured. I'm not sure [quoted text clipped - 8 lines] > window owned by the original thread. Unfortunately, the main thread is > suspended and can never read the message and so your call hangs. Thanks, it's perfectly clear now.
[I posted this yesterday too, but the message didn't make it to the server?]
Mike Samuels - 17 Oct 2005 18:29 GMT Man, I was hoping that this information of yours would solve my problem! It seems so close to the solution. But ... it didn't. Could you, or anyone else, would read about my problem, and see if you have anything to suggest? I wrote a program which, among other things, creates an Excel spreadsheet. It is eventually supposed to run as a service; but in order to facilitate debugging, I layered my classes so that I could just as easily run it as a Windows App. After a sufficient amount of testing, I made it a Windows service, and that's when I started experiencing difficulties. Here's what happens: intermittantly, the first statement that does something on the WorkSheet object throws an exception. I hate intermittant bugs. They're very difficult to track down. But, having programmed in a Windows environment since Windows 3.0, I at least had a theory; namely: it has been a fact over all these years, and in whichevery development environment you use, that when you try to perform some kind of GUI function from within a background thread, it intermittantly fails. Microsoft's .net development environment has addressed this issue by publishing a BeginInvoke method off of System.Windows.Forms.Control ... so that if you do any GUI updating via this mechanism, then it's safe, and you won't experience any failures. I further theorized that when I am creating an Excel spreadsheet, that even though I'm not making it visible, nevertheless, it's as if it were a visible window, and so updating it is subject to the same "intermittent failures". So, I decided to utilize this BeginInvoke mechanism. This was easy to do when my program is running as a Windows App. I just utilize the Form object's BeginInvoke. But I hadn't been successful trying to create a Form object from within a Windows service, so I couldn't implement this stragegy ... this is, until I read these threads. Now, thanks to the information I received in these threads -- particularly this one that I'm responding to -- I am able to create a Form in a Windows service. And so, as I said, my hopes soared in that I would now be able to utilize that Form's BeginInvoke so that I could safely create the Excel spreadsheet. Unfortunately, it didn't work. Perhaps my "updating GUI from within a background thread" theory is incorrect. One last fact: Once I implemented the BeginInvoke strategy into the code, I have never since received an error as long as the program is running as a Windows App. But ... I'm not totally sure that I ever experienced a problem when the program was running as a Windows App ... even before I implemented the BeginInvoke strategy, and even though all of the Excel updating takes place in background threads.
 Signature Mike Samuels Programmer
> While the explanations that you have received so far are incorrect the > solutions offered up are essentially correct. [quoted text clipped - 65 lines] > > properties, methods and events (it has no UI elements, in that respect it > > behaves like the MS Winsock control among others). Lucvdv - 19 Oct 2005 12:46 GMT > Man, I was hoping that this information of yours would solve my problem! It > seems so close to the solution. But ... it didn't. [quoted text clipped - 35 lines] > the BeginInvoke strategy, and even though all of the Excel updating takes > place in background threads. One thing to keep in mind is that the docs have always said that a window may only be accessed by the thread that created it. All other threads and processes can send windows messages to its message pump, but they should never interact with it directly.
That's where Invoke comes in: it makes sure that everything you do is done by the thread that created the window.
As for your problem, just the thought of running an Office application as a service gives me the willies ;)
Sometimes they pop up dialog boxes and even start Windows Installer if they detect some change to the system they /think/ is related to them. Sometimes it's enough that something is done under a user account the application doesn't expect (such as local system...)
If your service is running with 'normal' settings (i.e. local system account and no access to the desktop), you won't see any of those messages, and your service will mysteriously misbehave.
Look for "Application Popup" type entries in event log: if there are any, there's a good chance that they'll contain messages from Excel.
I don't know if it will help, but there are two things you can try: - run the service under a user account instead of local system. - run it with access to the desktop (so any dialogs are shown).
Finally, and this should solve your problem, but it will reduce your Excel sheets to minimum capabilities: I have a class (written in VB.Net) that creates XL version 2.1 spreadsheet files without having the need for Excel (based on a description of the BIFF format that MS once published, way back when being open about file formats was still considered a good idea).
I can mail it if you want to, but it's very limited in its capabilities: - only single-sheet xls files - writes xls, doesn't read or modify them - only string, 16-bit integer and double precision datatypes supported, but you can do dates and times by saving them as .ToOADate and specifying a date/time format.
It was ported from VB6 code, but the port hasn't been fully completed and tested yet. The VB6 version was itself based on code I found on the web, and that wasn't bug-free to start with.
BTW, it may be even simpler to make your service create .CSV files (Excel supports them, except that it creates/expects incompatible CSV structure if your regional settings aren't "US English").
Willy Denoyette [MVP] - 16 Sep 2005 16:55 GMT Stephen is right, the STA/MTA attribute is honoured on Main in a C# program, but it's NOT true for a VB.NET program (as it's the case here if I'm not mistaken) where the thread running Main is always entering an STA, sorry for being incomplete. The OnStart and other service control functions run on an MTA thread and this cannot be changed, anyway you should never run service code in these functions, you should start another thread initialize it to enter the correct apartment and create the form and the COM object.
Note also that you should never run windows forms, nor should you create instances of ActiveX controls in a windows service, it's inherently unsafe and it won't work any longer in Vista, be warned!
Willy.
>> The STAThread attribute is ignored on Main in a service, Service threads >> run [quoted text clipped - 38 lines] > properties, methods and events (it has no UI elements, in that respect it > behaves like the MS Winsock control among others).
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|