Reverse Connection 을 이용한 원격 작업

CuveDev | 2009. 10. 20. 08:55 | 큐브씨

간만의 포스팅이군요.

회사에 있는 PC를 집에서 원격으로 작업하려고 하다가..
방화벽 때문에 좌절한 후.. 방법이 없을까 고민에 고민을 거듭한 끝에..
KLDP에서 Reverse Connection 에 관한 글을 발견했습니다.
유용한 정보 주신 aero님께 감사를..

읽기 귀찮으신 분들을 위해 간단히 설명하자면,
회사 PC를 서버라고 보고 집의 PC를 클라이언트라고 볼 때,
서버에 agent를 설치해서,  Real IP를 가진 집 PC에 지속적으로 http ping을 보낸다고 하면..
해당 연결을 이용해 무리없이 원격 작업이 가능하다는 이야기가 됩니다.

일반적인 생각과는 반대로 Server가 Ping을 보내게 되니..
Reverse Connection 이라는 이름이 붙은 모양인데요,

이도저도 귀찮으신 분은..
GoToMyPC를 유료로 이용하시거나..
LogMeIn Free 또는 데이콤 네트로를 이용하시면 됩니다.
개인적으로는 LogMeIn이 마음에 드네요.

단.. 이런 서비스의 대부분은 중계 서비스를 이용한 방식이므로
양쪽 다 사설 IP라도 접속이 가능하다는 장점이 있지만,
아무래도 속도는 느리겠지요.

이미 많이 알려진 기술입니다만, 이걸 지원하는 원격지원 소프트웨어가 많지 않은 이유는..
아무래도 신기술만 따라가고 너무 알려진 기술을 천시하는 경향 때문이 아닌가 합니다.

중요한 건 역시 기술이 아니라 시장임을.. 잊지 말아야 하겠지요.


프로세스에 로드된 모든 DLL 보기

CuveDev | 2009. 2. 6. 09:04 | 큐브씨

현재 프로세스에 Attach된 DLL을 볼 때 유용합니다.
MSDN에 있는 코드죠. 은근히 유용하게 쓰입니다.


void PrintModules()
{
    HMODULE hMods[1024];
    DWORD cbNeeded;
    unsigned int i;	

    // Get a list of all the modules in this process.
    if(EnumProcessModules(GetCurrentProcess(), hMods,
		sizeof(hMods), &cbNeeded))
    {
        for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ )
        {
            TCHAR szModName[MAX_PATH];
			
            // Get the full path to the module's file.
            if (GetModuleFileNameEx(GetCurrentProcess(),
				hMods[i], szModName,
				sizeof(szModName) / sizeof(TCHAR)))
            {
                // Print the module name and handle value.
                TRACE(_T("\t%s (0x%08X)\n"),
					szModName, hMods[i]);
            }
        }
    }
}


XMLHTTPRequest를 써보자!

CuveDev | 2009. 1. 18. 15:57 | 큐브씨

AJAX 때문에 유명해진 XMLHTTPRequest 입니다 ^^;
사실 저같은 Windows 개발자들은 http 통신을 하는데 수많은 방법이 있지요.
Win32의 HttpOpenRequest 함수나 MFC의 CHttpConection 클래스, 그리고 WinHttp 등..
(일설에 의하면.. XMLHTTPRequest는 WinHttp 를 래핑해서 만들어졌다고 하는군요.)

따라서 사실 굳이 XMLHTTPRequest 를 쓰지 않아도 됩니다.
비동기 통신? 스레드로 돌리면 간단하지요 -_-;

그럼 왜 하필 이런 코드를 올리느냐!

..그냥 왠지 써보고 싶어서 -_-;
Windows 개발자들은 원래 유행에 덜 민감한 편이고 또 그래야 하죠 ;;
괜히 이런 신기술 쪽을 쓰다보면 구버전의 OS에서는 안돌아가고 막 그러니까요.

그럼에도 불구하고.. 이 넘치는 호기심을 주체하지 못하는지라..
어쨌든 서론은 접어두고, 코드 올라갑니다.
장점이라면.. 도메인과 경로를 분리해서 작업하지 않아도 되고,
비동기 통신을 자체적으로 지원한다는 정도일 것 같네요.
(open 하실 때 세번째 인자를 true 로 주면 된다고 합니다. 해보진 않았지만 ;;)


#import "msxml3.dll"
using namespace MSXML2;

BOOL GetHtml(LPCTSTR tszUrl, LPTSTR tszBuffer,
		 DWORD cbBuffer)
{
	USES_CONVERSION;

	BOOL bRet = FALSE;
	HRESULT hr = S_FALSE;
	IXMLHTTPRequestPtr pIXMLHTTPRequest = NULL;
	_bstr_t bstrHtml = _T("");

	hr = CoInitialize(NULL);
	if (FAILED(hr))
		goto cleanup;
	
	hr = pIXMLHTTPRequest.CreateInstance(
			_T("Msxml2.XMLHTTP.3.0"));
	if (FAILED(hr))
		goto cleanup;

	hr = pIXMLHTTPRequest->open(_T("GET"), tszUrl, false);
	if (FAILED(hr))
		goto cleanup;

	hr = pIXMLHTTPRequest->send();
	if (FAILED(hr))
		goto cleanup;

	bstrHtml = pIXMLHTTPRequest->responseText;
	if (bstrHtml.length() == 0)
		goto cleanup;

	hr = StringCbCopy(tszBuffer, cbBuffer,
			OLE2T(bstrHtml));
	if (FAILED(hr))
		goto cleanup;

	bRet = TRUE;

cleanup:
	pIXMLHTTPRequest.Release();
	CoUninitialize();
	return bRet;
}


Windows 서비스 프로그램 시작하기

CuveDev | 2008. 12. 2. 16:17 | 큐브씨

일반적인 경우 별로 사용할 일은 없지만..
가끔가다 내가 만든 프로그램이 돌아가기 위해 필수적으로 실행중이어야 할 서비스가 실행중이 아닐 경우가 있습니다. 그렇다면 별수없이.. 실행시켜줘야죠 ^^;

Windows에서 서비스들은 SCM(Service Control Manager)이라는 녀석에 의해 관리됩니다. 당연히 이녀석을 제어하는 API도 존재하죠.

다음과 같이 코딩하면 특정 윈도우 서비스를 실행할 수 있습니다.


BOOL MyStartService(LPCTSTR tszServiceName)
{
	BOOL bRet = TRUE;

	// Open SCM
	SC_HANDLE schSCManager = NULL;	
	schSCManager = OpenSCManager(NULL,
		SERVICES_ACTIVE_DATABASE,
		SC_MANAGER_ALL_ACCESS);
	if (schSCManager == NULL)
	{
		bRet = FALSE;
		goto cleanup;
	}

	// Open Service
	SC_HANDLE schService = NULL;
	schService = OpenService(schSCManager, tszServiceName,
		SERVICE_ALL_ACCESS);
	if (schService == NULL)
	{
		bRet = FALSE;
		goto cleanup;
	}

	// Change Service Type
	if (!ChangeServiceConfig(schService,
		SERVICE_NO_CHANGE,
		SERVICE_AUTO_START,
		SERVICE_NO_CHANGE,
		NULL, NULL, NULL, NULL, NULL, NULL, NULL))
	{
		bRet = FALSE;
		goto cleanup;
	}

	// Start Service
	if (!StartService(schService, 0, NULL))
	{
		bRet = FALSE;
		goto cleanup;
	}

cleanup:
	CloseServiceHandle(schService);
	CloseServiceHandle(schSCManager);

	return bRet;
}

실행하려면.. 인자로 서비스명을 지정하시면 되겠네요.


MyStartService(_T("winmgmt"))

WMI는 은근히 재미있는 주제입니다. COM을 사용하는 방법과 WMI에 접근하는 방법을 같이 공부할 수 있거든요.
또한, AD를 사용한다면, 에이전트 없이 원격 클라이언트의 정보를 상당량 수집할 수 있구요.

WMI에 대한 자세한 설명은 MSDN을 참조해주시구요, 전체적인 과정을 살펴보면 대략 다음과 같습니다.

1. CoInitialize로 COM을 초기화한다.
2. CoInitializeSecurity로 보안 레벨을 설정한다.
3. CoCreateInstance로 WMI 의 COM Object 를 생성한다.
4. ConnectServer로 "root\\cimv2" 네임스페이스로 접속한다.
5. ExecQuery 로 WQL 구문을 실행한다.
6. 실행결과 Enumerator 를 분석하고, 제조번호를 파싱한다.

그리하여, 전체 코드는 다음과 같이 되겠습니다.


#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
	int nRet = 0;
	HRESULT hr = S_OK;
	TCHAR tszMsg[1024] = {0, };

	try
	{
		if (_tsetlocale(LC_ALL, _T("")) == NULL)
			throw _T("_tsetlocale");

		hr = CoInitializeEx(0, COINIT_MULTITHREADED);
		if (FAILED(hr))
			throw _T("CoInitialize");

		hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
			RPC_C_AUTHN_LEVEL_DEFAULT,
			RPC_C_IMP_LEVEL_IMPERSONATE,
			NULL, EOAC_NONE, NULL);
		if (FAILED(hr))
			throw _T("CoInitializeSecurity");

		CComPtr<IWbemLocator> wbemLocater = NULL;
		hr = CoCreateInstance(
				CLSID_WbemAdministrativeLocator,
			NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator,
			(LPVOID*)&wbemLocater);
		if (FAILED(hr))
			throw _T("CoCreateInstance");

		CComPtr<IWbemServices> wbemServices = NULL;
		hr = wbemLocater->ConnectServer(L"root\\cimv2",
			NULL, NULL, NULL,
			WBEM_FLAG_CONNECT_USE_MAX_WAIT,
			NULL, NULL, &wbemServices);
		if (FAILED(hr))
			throw _T("ConnectServer");

		CComPtr<IEnumWbemClassObject> wbemEnumerator = NULL;
		hr = wbemServices->ExecQuery(L"WQL",
			L"SELECT * FROM Win32_ComputerSystemProduct",
			WBEM_FLAG_FORWARD_ONLY, NULL, &wbemEnumerator);
		if (FAILED(hr))
			throw _T("ExecQuery");

		ULONG uReturned;
		while (wbemEnumerator)
		{
			CComPtr<IWbemClassObject> wbemObject = NULL;
			hr = wbemEnumerator->Next(
				WBEM_INFINITE, 1, &wbemObject, &uReturned);
			if (FAILED(hr))
				throw _T("Next");

			if (uReturned == 0)
				break;

			_variant_t vtProp;
			hr = wbemObject->Get(L"IdentifyingNumber",
				0, &vtProp, 0 ,0);
			if (FAILED(hr))
				throw _T("Get");

			wprintf(_T("%s\n"), (LPCTSTR)vtProp.bstrVal);
		}
	}
	catch (LPTSTR tszErr)
	{
		DWORD dwErr = GetLastError();
		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0,
			hr, 0, tszMsg, sizeof(tszMsg), NULL);
		if (*tszMsg == 0)
		{
			BSTR bstrErr;
			IErrorInfo* pErrInfo;
			GetErrorInfo(0, &pErrInfo);
			pErrInfo->GetDescription(&bstrErr);
			if (*bstrErr == 0)
			{
				if (*tszMsg == 0)
				{
					FormatMessage(
						FORMAT_MESSAGE_FROM_SYSTEM, 
						0, dwErr, 0,
						tszMsg, sizeof(tszMsg), NULL);
				}
			}
		}
		_tprintf(_T("Error: [%s] %s\n"), tszErr, tszMsg);
		nRet = 1;
	}
	catch (...)
	{
		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0,
			GetLastError(), 0,
			tszMsg, sizeof(tszMsg), NULL);
		_tprintf(_T("Error: [UnExpected], %s\n"), tszMsg);
		nRet = 1;
	}

	CoUninitialize();

	return nRet;
}


가끔 코딩하다 보면 버퍼에 들어있는 내용 중
4바이트만 뽑아서 int 형 변수로 저장해야 할 일이 생깁니다.
간단한 비트 연산이지만.. 급하면 또 생각이 안나죠 -_-;

int n = (data[0] << 24) + ((data[1] & 0xFF) << 16) + ((data[2] & 0xFF) << 8) + (data[3] & 0xFF);


오른쪽 버튼 메뉴에 프로그램을 추가하는 동작은,
HKEY_CLASSES_ROOT\*\shell 키 아래에서 특정 형식에 맞도록 서브키를 작성하는 것으로 간단히 해결됩니다.
(특정 확장자에 대한 행동을 지정하고 싶으시면 '*' 대신 '.txt' 와 같이 지정하시면 됩니다)

필요한 동작은 다음과 같습니다:
1. HKEY_CLASSES_ROOT\*\shell 키를 만든다.
2. 오른쪽 버튼 메뉴에 나타내고자 하는 이름으로 서브키를 만든다.
3. 작성된 서브키의 아래에 'command' 라는 서브키를 만들고, 기본값의 데이터를
    실행하고자 하는 프로그램으로 지정한다.

따라서, 코드는 다음과 같이 됩니다.

BOOL AddToShellContext(LPCTSTR ptszDisplayName,
			LPCTSTR ptszCommand)
{
	DWORD dwRet = ERROR_SUCCESS;
	BOOL bRet = TRUE;

	HKEY hShellKey, hNameKey, hCmdKey;

	dwRet = RegCreateKeyEx(HKEY_CLASSES_ROOT,
				_T("*\\shell"),	0, NULL,
				REG_OPTION_NON_VOLATILE,
				KEY_WRITE, NULL,
				&hShellKey, NULL);
	if (dwRet != ERROR_SUCCESS)
	{
		bRet = FALSE;
		goto cleanup;
	}

	dwRet = RegCreateKeyEx(hShellKey, 
				ptszDisplayName, 0, NULL, 
				REG_OPTION_NON_VOLATILE,
				KEY_WRITE, NULL,
				&hNameKey, NULL);
	if (dwRet != ERROR_SUCCESS)
	{
		bRet = FALSE;
		goto cleanup;
	}

	dwRet = RegCreateKeyEx(hNameKey,
				_T("command"), 0, NULL,
				REG_OPTION_NON_VOLATILE,
				KEY_WRITE, NULL,
				&hCmdKey, NULL);
	if (dwRet != ERROR_SUCCESS)
	{
		bRet = FALSE;
		goto cleanup;
	}

	dwRet = RegSetValueEx(hCmdKey,
				NULL, 0, REG_SZ,
				(LPBYTE)ptszCommand,
				_tcslen(ptszCommand));
	if (dwRet != ERROR_SUCCESS)
	{
		bRet = FALSE;
		goto cleanup;
	}


cleanup:
	RegCloseKey(hShellKey);
	RegCloseKey(hNameKey);
	RegCloseKey(hCmdKey);

	return bRet;
}
물론, 이렇게 호출하시면 되겠죠? ^^;
AddToShellContext(_T("메모장으로 열기"), _T("notepad %1"));


바탕화면에 인터넷 바로가기 만들기

CuveDev | 2008. 11. 14. 16:16 | 큐브씨
(Last Modified: 2008.11.17 10:22)

사실 '인터넷 바로가기 아이콘'은 그냥 평범한 INI 형식의 텍스트 파일입니다 ^^;
(실행파일 바로가기 아이콘은 조금 다릅니다.. ;;)
따라서 다음과 같이 코딩하는 것만으로 간단히 인터넷 바로가기를 만들 수 있습니다.

BOOL CreateShortcut(LPCTSTR ptszURL,
			LPCTSTR ptszIconPath,
			LPCTSTR ptszShortcutName)
{
	BOOL Res = TRUE;

	Res &= WritePrivateProfileString(_T("InternetShortcut"),
			_T("URL"), ptszURL, ptszShortcutName);
	Res &= WritePrivateProfileString(_T("InternetShortcut"),
			_T("IconFile"), ptszIconPath, ptszShortcutName);
	Res &= WritePrivateProfileString(_T("InternetShortcut"),
			_T("IconIndex"), _T("1"), ptszShortcutName); 

	return Res;
}


한 바이트의 비트를 역순으로 변경하는 간단한(?) 매크로.

#define reverse(b1) ((b1 * 0x0802LU & 0x22110LU) | (b1 * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;


Windows 개발자가 가야할 길은?

CuveDev | 2008. 3. 16. 05:17 | 큐브씨
개발자?

개발자?

출처 : 김국현의 낭만 IT

저는 Windows 개발자입니다(헉.. 죄송합니다 코더입니다 ㅠ_ㅠ).
Win32 API와 MFC를 주력으로, 가끔 WinForm 기반으로 개발을 진행해야 할 때도 있습니다.

요즘같이 기업 인트라넷 및 업무 시스템들,
심지어 오피스나 일정간리 프로그램마저 웹 인터페이스로 만들어지는 세상에서,
문득 어떤 미래를 바라보고 실력을 쌓아야 할까, 하는 생각에 조금 생각한 바를 남겨봅니다.
일단은 국내 기준으로요.. =_=;

먼저 가장 Windows 개발자 수요가 많은 부문은, 역시 게임 프로그래머겠지요.
한창 온라인게임들이 창궐하던 시대는 지났지만,
아마 여전히 Windows 개발자들을 가장 많이 원하고 있는 분야가 아닐까 합니다.
DirectX 계열 기술들을 추가로 공부해야 할 것이고, 최근에는 닷넷으로도 개발되는 게임도 있는 것 같더군요.
일단 뭐 게임개발자가 된다고 하면, 어디서든 먹고사는데는 문제는 없을 것 같지만..
혼자서 뭔가 만들어낸다던가, 프리랜서가 된다거나 하는 꿈은 접어야 할지도 모르겠네요.

다음으로 생각나는 것은 디바이스 드라이버나 임베디드 개발자인데요,
이쪽 부문 역시 수요에 비해 공급이 상당히 부족하다고 생각됩니다만..
세상의 모든 제품에 컴퓨터가 들어간다는 추세를 생각해 본다면
상당히 해볼만한 분야라고 생각됩니다.
소문을 들어보니 공부는 상당히 좀 해야될 것 같지만요 ^^;
비슷한 쪽으로 모바일 쪽도 있겠습니다만, 이쪽은 잘 모르겠습니다 ㅡㅡ;

세 번째로는, 솔루션개발자가 있겠네요..  제가 지금 몸담고 있는 분야입니다.
백신/안티스파이웨어/메신저/증권거래시스템/금융거래시스템 정도가 언뜻 떠오르는군요.
사실 아시다시피 우리나라에서 성공했다고 할만한 솔루션은 손으로 꼽을 정도라..
베스트 케이스로 삼을 만한 사례가 없다는게 좀 문제겠네요..
하지만 뭐 틈새시장을 노리고 창업을 생각한다면 이쪽도 하나의 방법이라고 생각합니다.
그렇게 시작하셔서, 국내의 Adobe나 MS와 같은 굴지의 기업을 만들어 나가시는것도..

생각나는데로 써 봤습니다만, 저랑 다르게 생각하실 분들이 많은 것 같습니다.
여러분들은 어떤 쪽을 염두에 두고 계신가요?
이 글은 남겨주신 의견에 따라 계속 수정됩니다 =ㅁ= 잘 부탁드려요!