[ Wmi Delphi Code Creator ] WDCC 사용법을 알아보자
🌈 프로그래밍/C++

[ Wmi Delphi Code Creator ] WDCC 사용법을 알아보자

반응형

 

안녕하세요? 수구리입니다.

이번 포스팅에서는 WDCC라는 유용한 오픈소스를 소개해볼까 합니다.

 

[ WMI란? ]

우선, WMI는 무엇인지에 대해 간략하게 알아보자면.. 

Windows Management Instrumentation이라고 합니다. 이는 윈도우 관리 도구를 뜻하며,

기본적으로 윈도우 2000 이상의 MS 운영 체제에 이미 다 설치가 되어있습니다.

이를 사용하여 윈도우의 리소스에 접근해 여러 가지 설정 및 관리를 할 수 있습니다.

그래서 이런걸로 뭘 할 수가 있느냐?

대표적으로 작업관리자를 열고, 성능 tap에 들어가면 하단에 리소스 모니터라고 있습니다.

이 리소스 모니터에 표시되어지는 모든 정보들을 WMI를 사용해 가져올 수도 있고, 로컬 컴퓨터뿐만 아니라 원격으로도 PC를 제어할 수도 있다고 합니다.

아무튼, 나중에 더 알아보도록 하고.. 방금 말했던 리소스 모니터를 WMI로 구현하고 있던 도중

WMI 하위에 수많은 클래스들이 있고 그 클래스들 안에 속성들도 엄청 많습니다.

그 중, 내가 필요로 하는 클래스, 속성을 찾기가 매우 힘들었죠..

MSDN에 들어가서 어떤게 있는지 하나하나 문서를 찾아보아야 했으며, 속성 값은 어떤 Type인지 등등..

알아내기가 너무 힘들었습니다. 그러다가 github를 둘러보던 도중..

 

https://github.com/RRUZ/wmi-delphi-code-creator

 

GitHub - RRUZ/wmi-delphi-code-creator: Generate Object Pascal (Delphi, FPC), Oxygene, C++ and C# code to access the WMI

Generate Object Pascal (Delphi, FPC), Oxygene, C++ and C# code to access the WMI - GitHub - RRUZ/wmi-delphi-code-creator: Generate Object Pascal (Delphi, FPC), Oxygene, C++ and C# code to access th...

github.com

 

WDCC라는 엄청난 도구를 발견하게 되었습니다.

 

[ Wmi Delphi Code Creator 소개 ]

우선, 이 WDCC는 WMI의 모든 클래스안의 속성들을 탐색할 수 있을뿐더러,

다양한 언어를 지원해줍니다. (아래에서 어떤 언어를 지원해주는지 확인 가능)

이름 그대로 테스트 코드까지 자동! 생성해줍니다.

뿐만 아니라, WDCC위에서 테스트 코드를 실행 및 수정도 가능하며,

현 로컬 컴퓨터에 설치되어있는 IDE까지 찾아내어 해당 프로젝트를 생성하고 열기까지 가능합니다.

만약 자신의 PC에 Visual Studio 2015가 깔려있고, WDCC로 실행하지 않고, 만들어준 코드를 프로젝트로 만드는 것이 가능합니다.

 

 

[ Wmi Delphi Code Creator 설치 ]

https://github.com/RRUZ/wmi-delphi-code-creator/releases/tag/1.9.9.482

 

Release WDCC 1.9.9.482 · RRUZ/wmi-delphi-code-creator

 

github.com

 

설치는 위의 링크에서 Setup 파일을 클릭해서 설치해주면 됩니다.

딱히 설치 과정을 설명할 필요는 없을 것 같아서 PASS

 

 

[ Wmi Delphi Code Creator 사용법 ]

 

우선 설치를 마치고 나서 실행파일(. exe)이 스파르타 투구 모양을 연상케 하는 아이콘이 인상적이었습니다.

아무튼 실행하고 난 뒤에 좌측 상단에 Tasks라는 텝이 있고, 아래 사진과 같이 되어있습니다.

 

여기서 우리는 WMI Class를 탐색하고, 어떤 속성들이 있는지 알아보는 일을 해볼 거기 때문에

맨 위에 있는 것을 클릭

 

 

그러면 위의 사진과 같이 뜨게 됩니다. 천천히 하나씩 뜯어보도록 하겠습니다.

 

우선, 기본적으로 WMI는 namespace provider 하위에서 동작하기 때문에

Namespaces가 root\cim2에서 시작하는 것을 알 수 있습니다.

그리고 그 바로 밑에 Classess에는 모든 Class들을 볼 수 있습니다.

위의 사진에서는 Win32_PerfFormattedData_PerfProc_Process라는 클래스를 선택한 것이고,

아래에는 해당 클래스가 무슨 역할을 하는지에 대한 description이 나와있습니다.

또, 그 클래스 안의 Properties는 어떤 친구들이 있는지 모두 보이게 됩니다.

Type도 알려주고, 해당 속성이 어떤 속성인지에 대한 설명도 나와있어서 양은 매우 많지만..

한눈에 볼 수 있다는 것에 정말 좋았습니다.

 

다음으로는 현재 PC에서 실행 중인 ProcessID와 Process Name을 위의 사진처럼 Check 하고,

Get WMI Class Instances 버튼 클릭 시 출력되는 예시입니다.

이처럼 SnapShot 형태로 출력되어지는 것을 알 수 있습니다.

 

다름으로는 우측 Code Creater 상단 부분에 대해서 알아보도록 하겠습니다.

바로 위의 사진처럼, 언어를 선택할 수 있는 DropDown 메뉴가 있고,

클릭하게 되면 Delphi, Pascal 등등.. 다양한 언어를 지원하는 모습입니다.

바로 아래에 실행 버튼을 클릭하게 되면, 아까 속성들 중에서 PID와 Name을 체크하게 된다면 오른쪽 Code Creater가 자동으로 속성을 추가해주고 Sample Code를 만들어 준 것을 실행합니다. (Visual 2015 기준으로는 ConsolApp으로 만듦)

컴파일 버튼을 클릭하면 해당 Code를 컴파일합니다.

저장 버튼은 생성된 코드를 저장하는 버튼입니다. 아래처럼 화면이 뜨게 됩니다.

그 옆에 Format Code 버튼은 생성된 코드를 수정, 및 변경 후 포맷을 통일시켜주는 버튼으로

위에서 잠깐 소개할 때, Code Creator 부분에서 직접 수정도 가능하다고 했죠? 그 부분에서 만약 통일되지 않는 부분이 있다면 이 버튼으로 통일성을 줄 수 있습니다. (띄어쓰기, 중괄호, 괄호 등등..)

Open in IDE 버튼은 말 그대로 현 PC에 설치되어있는 IDE을 탐색해서 프로젝트로 만들고, 열어주는 버튼입니다.

누르게 되면 아래와 같이 어떤 IDE이 있는지 보이고 어떤 것으로 선택할 건지 물어봅니다.

Code editor Window 버튼은 단순하게 코드 생성 부분을 전체 화면으로 보는 버튼입니다.

 

[ WDCC에서 생성된 전체 코드 ]

 

아래의 코드는 WDCC에서 생성된 전체 코드로 PID와 Processor Name을 출력하는 코드입니다.

//-----------------------------------------------------------------------------------------------------
//     This code was generated by the Wmi Delphi Code Creator (WDCC) Version 1.9.9.482
//     http://code.google.com/p/wmi-delphi-code-creator/
//     Blog http://theroadtodelphi.wordpress.com/wmi-delphi-code-creator/
//     Author Rodrigo Ruz V. (RRUZ) Copyright (C) 2011-2015 
//----------------------------------------------------------------------------------------------------- 
//
//     LIABILITY DISCLAIMER
//     THIS GENERATED CODE IS DISTRIBUTED "AS IS". NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED.
//     YOU USE IT AT YOUR OWN RISK. THE AUTHOR NOT WILL BE LIABLE FOR DATA LOSS,
//     DAMAGES AND LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING OR MISUSING THIS CODE.
//
//----------------------------------------------------------------------------------------------------
#include "stdafx.h"
#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>
# pragma comment(lib, "wbemuuid.lib")

//CREDENTIAL structure
//http://msdn.microsoft.com/en-us/library/windows/desktop/aa374788%28v=vs.85%29.aspx
#define CRED_MAX_USERNAME_LENGTH            513
#define CRED_MAX_CREDENTIAL_BLOB_SIZE       512
#define CREDUI_MAX_USERNAME_LENGTH CRED_MAX_USERNAME_LENGTH
#define CREDUI_MAX_PASSWORD_LENGTH (CRED_MAX_CREDENTIAL_BLOB_SIZE / 2)

// Process 성능 개체는 응용 프로그램 및 시스템 프로세스 실행을 모니터링하는 카운터로 구성됩니다. 한 프로세스에 있는 모든 스레드는 같은 
// 주소 공간을 공유하고 같은 데이터에 액세스합니다.

#pragma argsused
int main(int argc, char* argv[])
{
	wchar_t pszName[CREDUI_MAX_USERNAME_LENGTH+1] = L"user";
	wchar_t pszPwd[CREDUI_MAX_PASSWORD_LENGTH+1]  = L"password";
	BSTR strNetworkResource;
	//To use a WMI remote connection set localconn to false and configure the values of the pszName, pszPwd and the name of the remote machine in strNetworkResource
	bool localconn = true;	
	strNetworkResource = localconn ?  L"\\\\.\\root\\CIMV2" : L"\\\\remote--machine\\root\\CIMV2";

	COAUTHIDENTITY *userAcct =  NULL ;
	COAUTHIDENTITY authIdent;

	// Initialize COM. ------------------------------------------

	HRESULT hres;
	hres =  CoInitializeEx(0, COINIT_MULTITHREADED);
	if (FAILED(hres))
	{
        cout << "Failed to initialize COM library. Error code = 0x"	<< hex << hres << endl;
        cout << _com_error(hres).ErrorMessage() << endl;
        cout << "press enter to exit" << endl;
        cin.get();		
        return 1;                  // Program has failed.
	}

	// Set general COM security levels --------------------------

	if (localconn)
		hres =  CoInitializeSecurity(
			NULL,
			-1,                          // COM authentication
			NULL,                        // Authentication services
			NULL,                        // Reserved
			RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication
			RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
			NULL,                        // Authentication info
			EOAC_NONE,                   // Additional capabilities
			NULL                         // Reserved
			);
	else
		hres =  CoInitializeSecurity(
			NULL,
			-1,                          // COM authentication
			NULL,                        // Authentication services
			NULL,                        // Reserved
			RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication
			RPC_C_IMP_LEVEL_IDENTIFY,    // Default Impersonation
			NULL,                        // Authentication info
			EOAC_NONE,                   // Additional capabilities
			NULL                         // Reserved
			);
			
	if (FAILED(hres))
	{
        cout << "Failed to initialize security. Error code = 0x" << hex << hres << endl;
        cout << _com_error(hres).ErrorMessage() << endl;
        CoUninitialize();
		cout << "press enter to exit" << endl;
	    cin.get();		
        return 1;                    // Program has failed.
	}

	// Obtain the initial locator to WMI -------------------------

	IWbemLocator *pLoc = NULL;
	hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc);

	if (FAILED(hres))
	{
        cout << "Failed to create IWbemLocator object."	<< " Err code = 0x" << hex << hres << endl;
        cout << _com_error(hres).ErrorMessage() << endl;
        CoUninitialize();	    
        cout << "press enter to exit" << endl;
		cin.get();		
        return 1;                 // Program has failed.
	}

	// Connect to WMI through the IWbemLocator::ConnectServer method

	IWbemServices *pSvc = NULL;

	if (localconn)	
		hres = pLoc->ConnectServer(
			 _bstr_t(strNetworkResource),      // Object path of WMI namespace
			 NULL,                    // User name. NULL = current user
			 NULL,                    // User password. NULL = current
			 0,                       // Locale. NULL indicates current
			 NULL,                    // Security flags.
			 0,                       // Authority (e.g. Kerberos)
			 0,                       // Context object
			 &pSvc                    // pointer to IWbemServices proxy
			 );
	else
		hres = pLoc->ConnectServer(
			_bstr_t(strNetworkResource),  // Object path of WMI namespace
			_bstr_t(pszName),             // User name
			_bstr_t(pszPwd),              // User password
			NULL,                // Locale
			NULL,                // Security flags
			NULL,				 // Authority
			NULL,                // Context object
			&pSvc                // IWbemServices proxy
			);

	if (FAILED(hres))
	{
        cout << "Could not connect. Error code = 0x" << hex << hres << endl;	
        cout << _com_error(hres).ErrorMessage() << endl;
        pLoc->Release();
        CoUninitialize();
	    cout << "press enter to exit" << endl;
	    cin.get();			
        return 1;                // Program has failed.
	}

	cout << "Connected to root\\CIMV2 WMI namespace" << endl;

    // Set security levels on the proxy -------------------------
	if (localconn)
		hres = CoSetProxyBlanket(
		   pSvc,                        // Indicates the proxy to set
		   RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
		   RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
		   NULL,                        // Server principal name
		   RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx
		   RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
		   NULL,                        // client identity
		   EOAC_NONE                    // proxy capabilities
		);
	else
	{
		// Create COAUTHIDENTITY that can be used for setting security on proxy
		memset(&authIdent, 0, sizeof(COAUTHIDENTITY));
		authIdent.PasswordLength = wcslen (pszPwd);
		authIdent.Password = (USHORT*)pszPwd;
		authIdent.User = (USHORT*)pszName;
		authIdent.UserLength = wcslen(pszName);
		authIdent.Domain = 0;
		authIdent.DomainLength = 0;
		authIdent.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
		userAcct = &authIdent;

		hres = CoSetProxyBlanket(
		   pSvc,                           // Indicates the proxy to set
		   RPC_C_AUTHN_DEFAULT,            // RPC_C_AUTHN_xxx
		   RPC_C_AUTHZ_DEFAULT,            // RPC_C_AUTHZ_xxx
		   COLE_DEFAULT_PRINCIPAL,         // Server principal name
		   RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  // RPC_C_AUTHN_LEVEL_xxx
		   RPC_C_IMP_LEVEL_IMPERSONATE,    // RPC_C_IMP_LEVEL_xxx
		   userAcct,                       // client identity
		   EOAC_NONE                       // proxy capabilities
		);
	}

	if (FAILED(hres))
	{
        cout << "Could not set proxy blanket. Error code = 0x" << hex << hres << endl;
		cout << _com_error(hres).ErrorMessage() << endl;
		pSvc->Release();
		pLoc->Release();
		CoUninitialize();
		cout << "press enter to exit" << endl;
		cin.get();		
		return 1;               // Program has failed.
	}

	// Use the IWbemServices pointer to make requests of WMI ----

	IEnumWbemClassObject* pEnumerator = NULL;
	hres = pSvc->ExecQuery(	L"WQL",	L"SELECT * FROM Win32_PerfFormattedData_PerfProc_Process",
	WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);

	if (FAILED(hres))
	{
		cout << "ExecQuery failed" << " Error code = 0x"	<< hex << hres << endl;
		cout << _com_error(hres).ErrorMessage() << endl;
		pSvc->Release();
		pLoc->Release();
		CoUninitialize();
		cout << "press enter to exit" << endl;
		cin.get();		
		return 1;               // Program has failed.
	}
    
	// Secure the enumerator proxy
	if (!localconn)
	{
		
		hres = CoSetProxyBlanket(
			pEnumerator,                    // Indicates the proxy to set
			RPC_C_AUTHN_DEFAULT,            // RPC_C_AUTHN_xxx
			RPC_C_AUTHZ_DEFAULT,            // RPC_C_AUTHZ_xxx
			COLE_DEFAULT_PRINCIPAL,         // Server principal name
			RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  // RPC_C_AUTHN_LEVEL_xxx
			RPC_C_IMP_LEVEL_IMPERSONATE,    // RPC_C_IMP_LEVEL_xxx
			userAcct,                       // client identity
			EOAC_NONE                       // proxy capabilities
			);

		if (FAILED(hres))
		{
			cout << "Could not set proxy blanket on enumerator. Error code = 0x" << hex << hres << endl;
			cout << _com_error(hres).ErrorMessage() << endl;
			pEnumerator->Release();
			pSvc->Release();
			pLoc->Release();
			CoUninitialize();
			cout << "press enter to exit" << endl;
			cin.get();				
			return 1;               // Program has failed.
		}
	}

	// Get the data from the WQL sentence
	IWbemClassObject *pclsObj = NULL;
	ULONG uReturn = 0;

	while (pEnumerator)
	{
		HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);

		if(0 == uReturn || FAILED(hr))
		  break;

		VARIANT vtProp;

                hr = pclsObj->Get(L"IDProcess", 0, &vtProp, 0, 0);// Uint32
                if (!FAILED(hr))
                {
                  if ((vtProp.vt==VT_NULL) || (vtProp.vt==VT_EMPTY))
                    wcout << "IDProcess : " << ((vtProp.vt==VT_NULL) ? "NULL" : "EMPTY") << endl;
                  else
                  if ((vtProp.vt & VT_ARRAY))
                    wcout << "IDProcess : " << "Array types not supported (yet)" << endl;
                  else
                    wcout << "IDProcess : " << vtProp.uintVal << endl;
                }
                VariantClear(&vtProp);

                hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);// String
                if (!FAILED(hr))
                {
                  if ((vtProp.vt==VT_NULL) || (vtProp.vt==VT_EMPTY))
                    wcout << "Name : " << ((vtProp.vt==VT_NULL) ? "NULL" : "EMPTY") << endl;
                  else
                  if ((vtProp.vt & VT_ARRAY))
                    wcout << "Name : " << "Array types not supported (yet)" << endl;
                  else
                    wcout << "Name : " << vtProp.bstrVal << endl;
                }
                VariantClear(&vtProp);

		
		pclsObj->Release();
		pclsObj=NULL;
	}

	// Cleanup

	pSvc->Release();
	pLoc->Release();
	pEnumerator->Release();
	if (pclsObj!=NULL)
	 pclsObj->Release();

	CoUninitialize();
	cout << "press enter to exit" << endl;
	cin.get();
	return 0;   // Program successfully completed.
}

 

[ 실행 결과 ]

아래의 사진에서 왼쪽 위의 재생 버튼을 클릭하면 

 

local에 설치된 ide 환경이 나오고 선택하면 바로 실행된다.

 

이상으로 WDCC라는 오픈소스를 알아보았습니다.

사용해본 결과, MSDN보다 도움이 더 많이 되었던 것 같습니다.

클래스 이름과 속성 이름을 보고 대충 어느 데이터를 가지고 있는지 쉽게 알 수 있고,

무엇보다도 내가 필요한 값이 맞는지 Instance를 제공해주는 기능이 되게 편리했다고 생각합니다.

이상으로 마치겠습니다. 감사합니다.

반응형