Good question! The answer is yes. Before we tell you how, we will first explain some of the background to the answer.
Background
Most developers using ActiveX controls from Visual C++, do so using MFC applications, which provide a number of facilities to make the task easier.
However, Windows Service EXE projects created using the Visual C++ AppWizard are ATL applications. The AppWizard does not offer the option of including MFC support for this type of application, so (as you may have discovered) it is not so obvious how to use our ActiveX controls from within one of these projects.
The main problem is not with The Enabler ActiveX controls themselves, but with the AppWizard and the Windows Service code it produces. Luckily Microsoft have provided some clues in the following article in their Knowledge base:
173974 KB: How To Add MFC Support to an ATL Project
Although the advice in this article does not work for an ATL Service EXE, most of the advice is still relevant. We have developed this further to assist you in creating an ATL Service that should allow you to use any ActiveX control.
There are other ways to use ActiveX controls from within an ATL Service, but we believe the most logical approach is to create an MFC dialog. An MFC dialog provides the best container for an ActiveX control, and using the Class Wizard you can write code just as you would for any normal dialog-based MFC C++ application.
NOTE: Unless your Windows Service is setup to allow interaction with the Windows Desktop, any dialog(s) you create from within the Service will not be visible (open the Service Manager and check the service properties). In most cases you are choosing to write your application as a Service because no user interaction is required, but you may also want to display a status window directly from the Service.
Creating an ATL Windows Service project
To create a new ATL Service project using Visual C++:
Go to the File menu and select New...->Projects->ATL COM AppWizard
Enter the project name, and click Ok.
Select NT Service and click Ok.
Creating an MFC Dialog within an ATL Windows Service project
Add the following lines to StdAfx.h prior to including Atlbase.h:
Change project settings to use MFC. From the Project Settings dialog box, click the General tab, and make sure the Microsoft Foundation Classes drop-down list is set to Use MFC as a Static Library.
For Unicode builds, make sure the entry point is set to wWinMainCRTStartup in the Output category of the Link field in the Project Settings dialog box. For additional information, please see the following article in the Microsoft Knowledge base:
125750 PRB: Error LNK2001: '_WinMain@16': Unresolved External Symbol
If your Service Application publishes any COM interface of it's own, you may need to add the following line of code to the beginning of every member function of the COM interface:
AFX_MANAGE_STATE( AfxGetAppModuleState() );
For more information on AFX_MANAGE_STATE, consult the VC++ online documentation.
Before you can add a form to your project, you need a ClassWizard file. If you already have a ClassWizard file go to the next step. To create an empty Class Wizard (CLW) file:
Go to the View menu and click ClassWizard.
A pop-up dialog will prompt you to select the files to add, just click Ok.
The ClassWizard will now be shown, and you can click Ok to close it.
Go to the Insert menu and click New Form... to add a form to your project. Enter a name for the new form, and the wizard will generate the form class code.
The sample code below assumes the form class is called CEnablerDlg. You may need to add the following line to the header file for your new Form Class:
#include "resource.h"
To create a thread for our form, add the following code to your main Service .CPP file prior to the definition of _tWinMain:
class CFormThread : public CWinThread { public: virtual BOOL InitInstance(); virtual int ExitInstance(); DECLARE_DYNCREATE( CFormThread ); }; IMPLEMENT_DYNCREATE(CFormThread, CWinThread) BOOL CFormThread::InitInstance() { // now call the InitInstance() in our parent class CWinThread::InitInstance(); // initialise COM for use by this thread HRESULT hr = CoInitialize( NULL ); // we have to initialise the Instance and Resource handles afxCurrentInstanceHandle = GetModuleHandle( NULL ); AfxSetResourceHandle( GetModuleHandle( NULL ) ); // Initialize OLE libraries. if ( !AfxOleInit() ) { AfxMessageBox(_T("OLE Initialization Failed!")); return FALSE; } // we want to be a container for OLE controls AfxEnableControlContainer(); // at last we can create the MFC window object and show it LPCTSTR szClass = AfxRegisterWndClass( NULL ); // YOUR FORM CLASS NAME m_pMainWnd = new CEnablerDlg; CDialog* tmpDialog = (CDialog*)m_pMainWnd; // YOUR FORM RESOURCE ID tmpDialog->Create( IDD_ENABLERDLG_DIALOG ); tmpDialog->ShowWindow( true ); return TRUE; } int CFormThread::ExitInstance() { return 0; }To load your MFC form at Service startup, locate _Module.Start(); in _tWinMain and add the lines in bold below::
// create our MFC thread object, and start it CFormThread CFormThread; CWinThread *form = AfxBeginThread( RUNTIME_CLASS( CFormThread ), 0, 0, 0, NULL ); // now start the main service control thread _Module.Start(); // before the service thread exits, we must first close the MFC form thread form->PostThreadMessage( WM_QUIT, 0, 0 ); // to ensure a tidy exit you must wait for the MFC thread to exit DWORD rc = WaitForSingleObject( form->m_hThread, INFINITE ); // TODO: write your own code to check the API return codeYou should now be able to compile and register your ATL Service. When the service is started, the form should be displayed (requires the service to be allowed to interact with the desktop).
#include <afxwin.h> // MFC core and standard components #include <afxext.h> // MFC extensions #include <afxdisp.h> // MFC Automation extensions
Adding The Enabler ActiveX controls to the project
Once you have completed all the steps in the previous section it is now a simple task to add The Enabler ActiveX controls to the project, and to your MFC dialog.
Go to the Project menu and click Add to project->Components and Controls...
Double click Registered ActiveX Controls.
Select the required controls (e.g. EnbSessionX, EnbPumpX) one at a time, and click Insert.
You will be prompted to confirm the wrapper Classes for the Control, click Ok.
When you have added all the required controls, click Close.
When you open your form in the VC++ dialog editor, the Controls palette should now show the ActiveX controls you have added into the project, allowing you to add these to your form.
You can now use the ClassWizard to associate controls to variables, and to add method and event implementations.