Visual C++로 데스크톱용 제품을 만들 때에는 주로 MFC 기반으로 작업을 하게 됩니다. 이 때는 모듈화를 할 때 MFC ActiveX를 주로 사용합니다. 이것은 Visual C++ 6.0 시절부터 개발해왔던 습관 때문이라고 볼 수 있겠습니다. MFC 응용프로그램에서든, Internet Explorer에서든 모듈을 사용하는 면에서든 COM 모듈을 쓰는 것과 차이가 없고 만들기는 더 편하니까요.
그런데 서비스는 UI가 제공되지 않는 세션(Session 0)에서 실행됩니다. 따라서 ActiveX 같이 윈도우가 없이는 생성조차 되지 않는 모듈은 사용할 수 없습니다. 서비스가 실행되는 세션에서는 데스크톱 윈도우도 없기 때문에 데스크톱 DC를 이용해서 이미지를 조작한다거나 하는 것도 불가능할 정도이거든요.
하지만 이미 만들어져 있는 수많은 ActiveX 모듈을 생각하면 그냥 그렇구나하며 넘어갈 수는 없죠. 일단 검색을 좀 해보니 윈폼을 쓰면 될 거라는 얘기가 있습니다. 이 정보 하나를 가지고 1차 시도를 해봅니다.
예제로 사용할 ActiveX 모듈은 GDI+를 이용해서 이미지를 JPEG 형식으로 저장해주는 간단한 모듈입니다. TheActiveOne이라는 예제 모듈에는 SaveAsJpg 라는 메서드가 하나 있는데, 아래와 같이 이미지를 저장하는 단순한 메서드입니다. from은 원본 이미지 파일 경로이고, to는 새로 생성될 jpg 이미지 파일의 경로입니다.
InitializeGdiPlus();
Image image(from);
CLSID jpegClsid;
GetEncoderClsid(_T("image/jpeg"), &jpegClsid);
image.Save(to, &jpegClsid, NULL);
FinalizeGdiPlus();
이제 서비스 프로젝트를 하나 만듭니다. 이름은 TheAxService라고 지었습니다. 서비스를 만드는 방법은 이전의 포스트를 참고하시기 바랍니다. 윈폼을 쓰면 될 것이라 했으니, TheActiveOneForm이라는 윈폼을 하나만들고 TheActiveOne 모듈을 넣습니다. 그리고 서비스 클래스 쪽에서 이미지 저장 기능을 사용할 수 있도록 메서드를 하나 추가 했습니다.
public long SaveAsJPG(string srcImgPath, string outJpgPath)
{
return this.axTheActiveOne1.SaveAsJpg(srcImgPath, outJpgPath);
}
서비스 진입점에서 윈폼을 만듭니다. 그리고 UI는 Single Thread Apartment에서 생성되어야 할 것이므로 서비스 진입점에 STAThread Attribute를 붙여줬습니다. 그 결과 Program.cs 의 Main 메서드 내용은 아래와 같이 바뀌었습니다.
[STAThread]
static void Main()
{
//System.Diagnostics.Debugger.Launch();
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new TheAxService(new TheActiveOneForm())
};
ServiceBase.Run(ServicesToRun);
}
TheAxService 클래스의 생성자에 폼을 생성해서 넘겨주고 있는 것을 위의 코드에서 볼 수 있습니다.
자, 그러면 핵심인 TheAxService 클래스의 코드를 보겠습니다.
public partial class TheAxService : ServiceBase
{
TheActiveOneForm axForm;
public TheAxService(TheActiveOneForm form)
{
InitializeComponent();
this.axForm = form;
}
protected override void OnStart(string[] args)
{
this.axForm.Load += new EventHandler(axForm_Load);
this.axForm.Show();
}
void axForm_Load(object sender, EventArgs e)
{
this.axForm.SaveAsJPG(@"C:\Users\wafe\Pictures\office live 2.PNG", @"C:\Users\wafe\Pictures\office live 2.jpg");
}
}
OnStart 메서드에서 폼을 Show 하고 있고, Load 이벤트 핸들러에서 이미지 파일을 저장하는 기능을 호출하고 있습니다.
이제 서비스를 빌드해서 설치하고 실행시키면!
네, 잘 안됩니다. Windows 7에서 개발하고 있는 제 경우에는 윈도우 관리자가 이상해져서 Windows Server 2008 처럼 클래식 테마로 바뀌는 등 난리도 아닙니다. -_-;;
지금까지 만든 코드에는 제대로 동작하지 않을만한 문제가 있습니다. 디버거를 통해서 확인해보면, Main 메서드가 호출되는 메인 쓰레드와 OnStart 메서드가 호출되는 쓰레드는 서로 다른 쓰레드입니다. ActiveX를 생성하지 않은 쓰레드에서 ActiveX의 메서드를 호출하는 것은 문제가 있죠.
그렇다고 OnStart 메서드에서 ActiveX를 포함하는 윈폼을 생성하도록 하는 것은 불가능합니다. STA 쓰레드가 되도록 제어할 수 없는 쓰레드에서 OnStart 메서드가 호출되기 때문입니다.
그렇다면 별도의 쓰레드를 생성해서 윈폼을 만들고 이미지 저장을 하도록 하면 어떨까 하는 생각을 하게 되었습니다. 그 내용은 다음 포스트에서 다뤄보도록 하겠습니다.