SimpleIsBest.NET

유경상의 닷넷 블로그

강대욱!!! 코딩 백 번!!!

by 블로그쥔장 | 작성일자: 1/19/2007 2:16:00 AM
이 글은 오래된 전에 작성된 글입니다. 따라서 최신 버전의 기술에 알맞지 않거나 오류를 유발할 수 있습니다. 저자는 이 글에 대한 질문을 받지 않을 것입니다. 하지만 이 글이 리뉴얼 되면 이 글에 대한 질문을 하거나 토론을 할 수도 있습니다.

그냥 주저리... 주저리...

강대욱!!! 코딩 백 번!!!

필자의 버릇 혹은 습성 중 하나는 무엇인가를 잊고자 할 때는 다른 무언가를 존나게 열심히 하는 것이다. 뭐 다른 사람들도 대부분 이렇게 하겠지만 말이다...

필자의 경우 고딩 때 여자한테 채이고 담배 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 옵션을 주어 닷넷 어셈블리로 컴파일 한 것이다. 동일하게 수행 된다... ^^

managed mfc

그래서... 어쩌라고?

이쯤 되면 독자들은 "그래서... 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는 강좌로 다루어도 재미있을 것 같다는 생각이 꽁꼬 깊숙한 곳에서 간질거리는데... 흠...



Comments (read-only)
#re: 강대욱!!! 코딩 백 번!!! / 정성태 / 1/19/2007 8:16:00 AM
가끔 읽었던, 강대욱이 누군지 몰랐었는데.... 개그맨 이름이었군요. ^^;
어쨌든, 어떤 사연으로 몰두하시게 된지는 모르겠지만... ... 언제나 행복 만땅 되시길 바라겠습니다. ^^

(댓글 달때마다 느끼는 거지만... 스팸방지용 인증 번호의 자릿수를 2자리 정도로 줄여주실 생각은 없으신지... 댓글 다시는 분들의 0.1% 생산성을 올리기 위해서. ^^;)
#re: 강대욱!!! 코딩 백 번!!! / ness09 / 1/19/2007 10:27:00 AM
c++ 이라는 단어만 봐도 패쑤~~ 였는데..
주변에서 말씀주시는 것들과 근래에 느끼는 부분들에 대해 다시 한번 생각해 보고 갑니다..

웃는 하루 되십시오~~~~
#re: 강대욱!!! 코딩 백 번!!! / 블로그쥔장 / 1/19/2007 10:45:00 AM
성태씨... '강대욱'은 개그맨 이름이 아니라... 개그 코너의 등장인물 이름이지요...
공부하고 코딩만 열심히 하지마시고 TV 프로도 좀 보세요...
능력만큼이나 유머도 중요한 세상인지라... 음냐...

그리고... 인증 번호는 두 자리는 너무 뽀대가 안나서... (썰렁 하잖아요...)
요청은 기각되었슴다... 커커커
#re: 강대욱!!! 코딩 백 번!!! / 컴맹 / 1/19/2007 11:47:00 AM
오늘와보니 또 새루운 칼럼이 올라와있군요.^^
잘읽었습니다.~
그런데...갈길이 멀군요.--;
#re: 강대욱!!! 코딩 백 번!!! / 이방은 / 1/22/2007 12:53:00 PM
음..저는 강대욱씨가...
쥔장님 회사 동료 혹은..아랫사람인줄 알았다는..ㅡ.ㅡ;;
성태님도 저랑 비슷한 종족이군요 ㅋㅋ
#re: 강대욱!!! 코딩 백 번!!! / 찌유니아빠 / 1/25/2007 10:59:00 AM
저만 물어 본것이 아니군요.. ㅋㅋㅋㅋㅋ
#re: 강대욱!!! 코딩 백 번!!! / 승수쌓기 / 2/21/2007 8:02:00 AM
좋은글 감사합니다.
저도 요즘 비슷한 생각에 빠졌으나...쥔장님과 달리 능력부족에 좬장 거리는 중입니다.
혹시, 말씀하신 CreateRestrictedToken() 함수와 CreateProcessAsUser()
요놈 사용하는 소스좀 올려주실수 없을까요.
후후후....
#re: 강대욱!!! 코딩 백 번!!! / 늦은코더 / 2/26/2007 5:36:00 PM
쩝... 늦은 나이(40)에 코딩에 입문한 까닭에, C# 배우는데도 벅차네요. 머리도 잘 안돌아가고...
하지만 C# 하다보니, Win32 API에는 있는데, .NET에는 아직 없는 것들이 꽤 있다는 것을 알게 되었습니다.
GetSystemTime 함수 같은 것이 대표적인 예죠.
일단 C# 좀 어느 정도 마스터한 다음에 VC++ 과 Win32 API, MFC에 도전해봐야겠습니다.
위의 글을 읽다가 pinvoke.net 이란 곳을 알게되었는데, 방문해보니 정말 유용한 곳이더군요.
#re: 강대욱!!! 코딩 백 번!!! / 늦은코더 / 2/26/2007 11:25:00 PM
위에 리플달고보니, GetSystemTime 함수가 아니라 SetSystemTime 함수가 대표적인 예라고 해야 맞을 것 같네요.
.NET의 DateTime의 Public Property들이 대부분 Get only죠.
#re: 강대욱!!! 코딩 백 번!!! / 김준영 / 3/20/2007 10:09:00 PM
void foo()예제 #progma unmanaged, #programa managed 빼도 컴파일 잘되네요. ^^;
#re: 강대욱!!! 코딩 백 번!!! / 블로그쥔장 / 3/21/2007 9:27:00 AM
김준영님...
#pragma 를 빼면 foo() 함수는 unmanaged 코드가 아닌 managed 코드로 컴파일됩니다.
즉, foo() 함수의 결과는 머신 코드가 아닌 MSIL 코드가 된다는 거죠.
따라서 printf 함수 호출은 P/Invoke 를 통해서 호출이 됩니다.
예제에서 제가 보여주고 싶었던 것은 managed 코드와 unmanaged 코드가 공존할 수 있다는
것입니다. #pragma를 빼면 모든 코드가 managed 코드로 컴파일 되겠죠.