그냥 주저리... 주저리...
강대욱!!! 코딩 백 번!!!
|
필자의 버릇 혹은 습성 중 하나는 무엇인가를 잊고자 할 때는 다른 무언가를 존나게 열심히 하는 것이다. 뭐 다른 사람들도 대부분 이렇게 하겠지만 말이다...
필자의 경우 고딩 때 여자한테 채이고 담배 2갑을 줄 담배로 조진 후에 네 발로 기어 다니는 등 잠시 방황하다, 열공에 빠져서 성적이 상당히(졸라 많이) 오른 적도 있었으며, 회사에서 CORBA를 못하게 되자 보상 심리로 시작한 COM 이 대박(?)을 터트린 적도 있었다. 재수도 좋지... -_-;
요즘 필자가 코딩에 빠져 살고 있다. 왜 이렇게 코딩을 열심히 하는지는 묻지 않았으면 하는 희망이 있다. 사생활이니깐.... |
 |
코딩이나 열심히 하지 웬 글이냐고?
C++/CLI
다름이 아니고... 요즘에 열심히 코딩을 하는데... 주로 닷넷 프로그래밍이며 Vista 덕분에 ATL(Active Template Library)를 사용하는 ActiveX 코딩도 하고 있다. 그러던 도중 2년여 전에 C++로 작성한 닷넷 코드를 발견하였다. 소위 Managed Extension for C++이라 불렸던 것으로 C++를 이용하여 닷넷 프로그래밍을 하는 것을 말한다. Managed Extension for C++은 Visual Studio 2003 까지의 명칭이며 Visual Studio 2005에서는 C++/CLI 라는 명칭으로 바뀌었다. 이름이야 어찌되었건 C++로 닷넷 코드를 작성할 수 있다는 것인데, 닷넷을 좀 해본 독자라면 직접 코딩 해 본적은 없더라도 이것이 무엇인가는 대충 알고 있을 것이라 생각된다.
그래도... '친절한 필자'가 예제 코드 몇 줄 적어 볼란다.
1 using namespace System;
2
3 void main()
4 {
5 Console::WriteLine("Hello World");
6 }
위 코드는 C++/CLI로 작성한 아주 간단하면서도 매우 유명한 Hello World 프로그램이다. 언뜻 보면 C# 코드 같지만 엄연히 C++ 코드이며, 일반적인 unmanaged code 같지만 곧 죽어도 닷넷 어플리케이션 코드이다. 이 코드를 hello.cpp 라는 파일로 저장하고 다음과 같이 컴파일 하면 hello.exe라는 닷넷 어셈블리가 생성될 것이다. /clr 컴파일 옵션이 사용되었음을 유심히 노려보자. /clr 컴파일 옵션이 코드를 닷넷 프레임워크가 이해할 수 있는 managed 코드로 컴파일 해주는 열쇠이기 때문이다.
cl.exe /clr hello.cpp
C# 관점에서 보면 클래스도 없이 튀어 나온 전역 함수인 main() 함수가 매우 어색해 보일 테지만, 전역 함수(메쏘드)를 허용하지 않는 것은 C#이지 닷넷이 아님을 생각해 보면 이상할 것이 하나도 없는 정.상.적.인 닷넷 코드를 생성해 낸다.
C++/CLI가 이전에 제공되었던 Managed Extension for C++에 비해 좋아진 것은 문법적인 면에서 보다 편리해 졌고 명확해 졌기 때문이며 Visual Studio 2005에서 웹 서비스, WinForm, Windows Service 등에 대한 지원 등이 크게 확충되었다는 것이다.
P/Invoke vs. IJW Interop
뭐... C++/CLI에 대한 강좌를 쓰는 것이 아니므로... 사실 닷넷 프로그램을 작성하는데 C++/CLI는 편리하지는 않은 것 같다. 필자가 닷넷 코드를 작성하는데 C#을 많이 써서가 아니라 C# 이란 언어가 닷넷을 염두 해 두고 설계된 언어이기 때문이다. 사실 닷넷 프레임워크의 클래스 라이브러리 중 대부분이 C#으로 작성되어 있음이 이를 반증해 주곤 한다.
하지만 닷넷 언어들 중 가장 강력한 언어를 고르라면 필자는 주저 없이 C++/CLI를 고를 것이다. 그 이유는 이렇다. 닷넷을 사용하는 수많은 언어들 중에 유일하게 unmanaged 코드와 managed 코드를 섞어 쓸 수 있다는 점과 WIN32 API를 호출하는데 있어서의 자유로움 때문이다. C#이나 VB.NET에서 WIN32 API 깨나 호출해 봤다는 사람들은 P/Invoke 선언을 위해 얼마나 많은 삽질을 해야 하는지 잘 알고 있을 것이다. 비록 PInvoke.NET 과 같은 사이트의 도움을 얻는다 해도 그리 녹녹하지 않은 않을 뿐 더러 WIN32 가 아닌 커스텀 DLL에 존재하는 함수를 호출하려면 대략 난감하기 이를 데 없기 때문이다.
C++/CLI는 unmanaged 코드와 managed 코드가 하나의 어셈블리 내에 평화롭게 공존할 수 있도록 해준다. 다음 예제 코드와 같이 #pragma unmanaged/managed 컴파일 지지자를 이용하여 하나의 소스 코드 내에서 조차 unmanaged 코드(foo 함수)와 managed 코드(main 함수)가 존재할 수 있도록 해준다.
1 using namespace System;
2
3 #pragma unmanaged
4
5 #include "stdio.h"
6
7 void foo()
8 {
9 printf("hello unmanaged...\n");
10 }
11
12 #pragma managed
13
14 int main(array<System::String ^> ^args) // C#으론 int Main(string[] args) 정도 되겠다.
15 {
16 foo();
17 Console::WriteLine(L"Hello World");
18 return 0;
19 }
이것이 무엇에 좋은가는 C++ 코딩을 많이 해보지 않은 독자들은 잘 감이 오지 않을 수도 있다. C++로 Windows 프로그래밍을 해본 경험이 있는 상황에서, 닷넷으로 뭔가 좀 해 보려고 치면 COM과 WIN32 API 벽에 부딪혀 본 독자라면 무릎을 탁 칠 것이다.
이제 좀 더 일반적인 독자들이 재미있어 할 예제를 보여주겠다.
1 #include "windows.h"
2
3 using namespace System;
4
5 int main()
6 {
7 Console::WriteLine(L"Hello World");
8 // WIN32 API 호출
9 ::MessageBoxW(NULL, L"Hello C++/CLI World !", L"Hello World", MB_OK | MB_ICONINFORMATION);
10 return 0;
11 }
위 코드를 다음과 같은 명령으로 컴파일 한다. 물론 Visual Studio의 프로젝트 템플릿의 도움을 받아도 된다. (이건 독자들이 알아서 하길...)
cl.exe /clr t.cpp /D "UNICODE" /D "_UNICODE" user32.lib
위 코드는 훌륭하게 컴파일 되며 WIN32 API인 MessageBoxW 함수(유니코드 버전)를 훌륭하게 호출해 준다. 사실 C++ 컴파일러가 MessageBoxW 함수에 대해 다음과 같이 DllImport 를 자동으로 선언해 주기 때문이다. (아래 선언은 보기 좋게 하기 위해 필자가 일부 선언을 제거했다)
[DllImport("", EntryPoint="", CallingConvention=CallingConvention.StdCall, SetLastError=true)]
public static extern unsafe int MessageBoxW(HWND__*, char modopt(IsConst)*, char modopt(IsConst)*, uint);
이것이 바로 C++/CLI의 It Just Works(IJW) 란 기술로써 단순히 컴파일 옵션에 /clr 옵션만 주면 unmanaged 소스를 managed 코드로 컴파일 해주는 기능이다. IJW의 강력함은 기존의 MFC 프로젝트마저도 /clr 옵션을 주면 managed 코드를 생성해 낼 정도이다. 비록 큰 의미는 없지만 말이다.
다음 화면은 MFC 어플리케이션을 /clr 옵션을 주어 닷넷 어셈블리로 컴파일 한 것이다. 동일하게 수행 된다... ^^

그래서... 어쩌라고?
이쯤 되면 독자들은 "그래서... C++/CLI 를 써서 닷넷 프로그램을 작성하라고? 미친 거 아녀?" 라고 반문할 수 있다. C++/CLI는 C++과 닷넷을 모두 잘 알아야 하며 C++/CLI에 새로이 추가된 몇몇 이상한 문법에 익숙해져야 한다. 맞다. 일반적인 닷넷 개발자라면 C++/CLI로 어플리케이션을 작성한다는 것은 무리다.
그래서... 어쩌라고?
필자가 처음 프로그래밍이란 것을 배웠을 때 주로 사용한 언어가 BASIC 이였다. 주로 게임을 작성하는 것이었는데 BASIC은 매우 느렸다. 그래서 빠른 처리를 필요로 하는 부분은 어셈블리 언어(닷넷 어셈블리가 아니다)를 사용했고 그 외에는 개발이 용이한 BASIC을 썼었다.
쌩뚱 맞게 옛날 이야기를 하는 이유는... 닷넷 어플리케이션을 개발하다 보면 WIN32 API 나 기타 unmanaged 코드를 아주 빡세게 많이 써야 할 때가 발생하곤 한다. 애매한 매개변수, 콜백 함수에 복잡한 구조체, 등등... 이 때 굳이 C# 이나 VB.NET에 매달려서 삽질할 필요가 없다는 것이다. C++/CLI로 Interop 이 필요한 부분만을 작성하고 이 코드를 닷넷 DLL 어셈블리로 만든다. 이제 화면 UI 등 다른 부분은 개발자들이 친숙한 C#을 쓰건 VB.NET을 써서 개발하는 것이다. 복잡한 Interop 이 필요하다면 C++/CLI 로 만들어 놓은 어셈블리를 "참조"하고 필요한 메쏘드를 호출해서 쓰면 된다는 것이다.
어떤가? 비록 C++를 잘 모르더라도 MSDN을 뒤져보면 WIN32 API를 호출하는 코드 쪼가리를 많이 구할 수 있다. 이 코드를 Copy & Paste 신공으로 소스에 갖다 붓고 적절한 래퍼(wrapper) 닷넷 클래스를 작성한 후 DLL로 컴파일 하기만 하면(/clr 옵션을 줘서), 이제 여러 닷넷 프로젝트에서 사용할 수 있는 훌륭한 라이브러리가 될 것이다. C++을 모른다고 너무 쫄 필요가 없다.
(사실 쉽지는 않다... -_-; 텨~ 텨~ 텨~)
Epilog
이 글을 쓰게 된 동기는 Windows Vista 관련 스터디를 하다가 오래 전에 관련된 코드를 작성했던 기억이 났었다. 뭐 코딩 거리 없나 주변을 어슬렁 거리던 필자는 2년여 전의 소스를 하드에서 뒤졌고 발견한 것이 바로 Managed Extension for C++ 코드였다.
이 코드는 WIN32 API인 CreateRestrictedToken() 함수와 CreateProcessAsUser() 함수를 이용하여 제한된 권한을 갖는 프로세스를 생성하는 코드로서, C#에서 Interop 코드를 작성하기엔 너무 빡센 매개변수를 갖는 이들 함수를 쉽게 호출하기 위해 C++을 택한 것 이였다. 이 오래된 코드를 C++/CLI 로 포팅(porting)하고 이것 저것 테스트하다가 이 글을 쓰고 있다.
필자가 보기에도 필자는 정말 쓸데 없는 짓을 많이 하는 것 같다... 그래도 잼 있으니까... ^^
C++/CLI는 강좌로 다루어도 재미있을 것 같다는 생각이 꽁꼬 깊숙한 곳에서 간질거리는데... 흠...