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;
}


12월 데브피아 활동왕 선정!

CuveStyle | 2008. 12. 22. 16:17 | 큐브씨

와, 12월 데브피아 활동왕에 선정되었습니다.
이거 대략 가문의 영광


...그런데




1등이잖아? -_-;;


헉, 가..감사합니다 ;ㅁ; 앞으로도 열심히 할께요!

12월 초에는 별로 활동을 못했는데, 11월말까지 한 활동으로 선정되었나 보네요.
덕분에 왠지 다가오는 2009년, 행복한 한 해가 될 것 같습니다 >ㅁ<

모두들 즐거운 연말되시고, 희망찬 새해 되세요!


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);