[VC]프로그래밍팁
01 14, 2007 02:00
1. 특정 디렉토리 뒤지기
지정한 디렉토리에 있는 모든 파일을 찾아내는 코드를 만들려면 어떻게 해야 합니까 ?
이 때 사용할 수 있는 API가 바로 FindFirstFile과 FindNextFile, FindClose라는 API들입니다. 사용 예제는 다음과 같습니다.
WIN32_FIND_DATA findFileData;
HANDLE hFileHandle;
// szDir에 뒤지고자 하는 디렉토리의 경로명을 준다. 예를 들면 "C:\\TEMP\\*.*"
// 찾아진 파일의 속성은 findFileData의 dwFileAttributes를 살펴본다.
hFileHandle = FindFirstFile(m_szDir, &findFileData);
if (hFileHandle != INVALID_HANDLE_VALUE) // 파일을 찾은 경우
{
// 찾은 파일의 이름은 cFileName 필드로 들어온다.
...
// 다음 파일을 찾는다.
while(FindNextFile(hFileHandle, &findFileData)) {
...
}
FindClose(hFileHandle);
}
2. API를 이용하는 유니코드와 ANSI 문자열간의 변환 방법
API를 이용해서 유니코드와 ANSI 문자열간의 변환은 어떻게 수행합니까 ?
Visual C++에서 유니코드 문자열은 BSTR이란 타입으로 표시됩니다. 또 유니코드와 ANSI 문자열간의 변환을 위해서 윈도우 시스템에는 MultiByteToWideChar와 WideCharToMultiByte라는 API가 존재합니다. MFC에서의 BSTR 타입 변환방법이나 ATL로 하는 BSTR 타입 변환도 참고하시기 바랍니다.
ANSI 문자열에서 유니코드로의 변환 방법
// sTime이란 ANSI 문자열을 bstr이란 이름의 유니코드(BSTR 타입) 변수로 변환
char sTime[] = "유니코드 변환 예제";
BSTR bstr;
// sTime을 유니코드로 변환하기에 앞서 먼저 그 길이를 알아야 한다.
int nLen = MultiByteToWideChar(CP_ACP, 0, sTime, lstrlen(sTime), NULL, NULL);
// 얻어낸 길이만큼 메모리를 할당한다.
bstr = SysAllocStringLen(NULL, nLen);
// 이제 변환을 수행한다.
MultiByteToWideChar(CP_ACP, 0, sTime, lstrlen(sTime), bstr, nLen);
// 필요없어지면 제거한다.
SysFreeString(bstr);
유니코드에서 ANSI 문자열로의 변환 방법
// newVal이란 BSTR 타입에 있는 유니코드 문자열을 sTime이라는 ANSI 문자열로 변환
char *sTime;
int nLen = WideCharToMultiByte(CP_ACP, 0, newVal, -1, sTime, 0, NULL, NULL);
sTime = malloc(nLen+1);
WideCharToMultiByte(CP_ACP, 0, newVal, -1, sTime, 128, NULL, NULL);
// 필요없으면 메모리를 제거한다.
free(sTime);
유니코드 문자열을 UTF-8으로 변환하기
WideCharToMultiByte 함수를 호출할 때 첫 번째 인자로 CP_UTF8을 지정하면 된다. UTF-8은 유니코드의 인코딩 스킴 중의 하나로 쉽게 말하자면 문자열 스트림에서 0을 빼고 표현하는 방법이라고 볼 수 있다.
3. 레지스트리 읽기/쓰기
API를 이용해서 레지스트리에 한 항목을 생성하거나 기존 항목의 값을 읽어들이려면 어떻게 해야합니까 ?
레지스트리 관련 API를 사용하려면 winreg.h라는 헤더 파일을 소스에 포함해야 합니다. 레지스트리에 키를 생성하는 방법과 레지스트리에 존재하는 키의 값을 읽는 방법을 차례로 살펴보겠습니다.
레지스트리 키 생성 예제
// 예를 들어 HKEY_LOCAL_MACHINE밑의 System\CurrentControlSet\Services\GenPort라는 키를
// 생성하고 거기에 DWORD 타입의 값으로 Type을 만들고 문자열 타입의 값으로 Group
// 을 만들어 본다.
#include "winreg.h"
LONG error = 0;
HKEY hKey;
DWORD dwDisp, dwData;
char lpData[] = "Write this down";
// 먼저 만들려는 키가 이미 존재하는 것인지 살혀본다.
error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\GenPort",
0, KEY_ALL_ACCESS, &hKey);
if (error != ERROR_SUCCESS) // 없다면 새로 생성한다.
{
// 키를 생성한다.
error = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Services\\GenPort", 0, "REG_BINARY",
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, &dwDisp);
// 위의 키 밑에 Type이란 DWORD 타입의 값을 만들고 1로 초기화
dwData = 0x1;
error = RegSetValueEx( hKey, "Type", 0, REG_DWORD,&dwData,4);
// 위의 키 밑에 Group이란 문자열 타입의 값을 만들고 lpData의 값으로 초기화
error = RegSetValueEx( hKey, "Group", 0, REG_SZ, lpData, strlen(lpData));
// 키를 닫는다.
RegCloseKey(hKey);
}
기존의 레지스트리 키에서 값 읽기
// HKEY_CURRENT_USER\Software\Netscape\Netscape Navigator\Main 밑의 Install Directory
// 값의 문자열 값을 읽어들인다.
DWORD dwType, cbData;
HKEY hSubKey;
long lRet;
char pszString[255];
// 키를 오픈한다.
if ((lRet = RegOpenKeyEx(HKEY_CURRENT_USER,
"Software\\Netscape\\Netscape Navigator\\Main",
0, KEY_READ | KEY_QUERY_VALUE , &hSubKey)) == ERROR_SUCCESS)
{
cbData = 255; // 문자열 값을 읽어올 데이터의 크기를 준다.
if ((lRet = RegQueryValueEx(hSubKey, "Install Directory",
NULL, &dwType, pszString, &cbData)) == ERROR_SUCCESS)
{
// 제대로 읽힌 경우
}
else
{
// 에러가 발생한 경우
}
RegCloseKey(hSubKey);
}
레지스트리 키 삭제하기 - RegDeleteKey 함수를 사용한다.
4. 윈도우 탐색기로부터의 Drag&Drop을 받으려면
윈도우 탐색기로부터 제가 만든 윈도우로의 drag&drop이 가능하게 하려면 어떻게 해야 합니까 ?
다음 순서를 따라서 프로그래밍하시면 됩니다.
프로그램의 초기화시에 DragAcceptFiles(hWnd, TRUE) 함수를 호출한다. 첫 번째 인자인 hWnd는 드롭의 타겟이 되는 윈도우의 핸들이다.
탐색기로부터 파일이 드롭되는 순간에 WM_DROPFILES 메시지가 날라온다. 이를 처리한다.
case WM_DROPFILES :
{
POINT pt;
// 어느 위치에 드롭되었는지 그 항목을 알아낸다.
if (DragQueryPoint((HDROP)wParam, &pt))
{
UINT i = 0;
// 모두 몇 개의 파일이 드롭되었는지 알아낸다.
// 만일 폴더가 드롭되었다면 폴더의 이름만 넘어온다.
UINT uCount = DragQueryFile((HDROP)wParam, 0xFFFFFFFF, NULL ,0);
for(i = 0;i < uCount;i++)
{
// 드롭된 파일의 이름을 알아온다.
DragQueryFile((HDROP)wParam, i, buffer ,255);
// 드롭된 파일 이름을 출력해본다.
MessageBox(hWnd, buffer, "File Name", MB_OK);
}
}
// drag and drop 작업을 끝낸다.
DragFinish((HDROP)wParam);
break;
}
Drag&drop을 더 사용할 필요가 없어지면 DragAcceptFiles를 호출한다.
DragAcceptFiles(hWnd, FALSE);
5. 시스템의 모든 드라이브 알아내기
현재 시스템에 붙어있는 모든 드라이브(네트웍 드라이브 포함)에 대한 정보를 알아내고 싶습니다.
GetLogicalDriveStrings로 시스템에 마운트되어있는 모든 드라이브 정보를 알아낸다. 두 번째 인자인 buffer로 드라이브 정보가 들어오는데 그 구조는 c:\,d:\과 같은 형식이며 리턴값으로 그 버퍼의 크기가 들어온다.
char buffer[256];
DWORD dwRet;
LPSTR token;
dwRet = GetLogicalDriveStrings(256, buffer);
루프를 돌면서 드라이브별 정보를 알아낸다. 이 때는 GetVolumeInformation 함수를 이용한다.
token = buffer; // token이 지금 처리해야할 드라이브를 가리킨다.
while (dwRet > 0)
{
DWORD FileSystemFlag;
char FileSystemName[64];
strcpy(DriveString, token);
// VolumeName으로 드라이브에 대한 설명 문자열이 넘어온다.
if (GetVolumeInformation(token, VolumeName, 255, NULL, NULL,
&FileSystemFlag, FileSystemName, 63))
{
// 원하는 작업을 수행한다.
}
dwRet -= (strlen(token)+1);
token = token + strlen(token)+1; // 다음 드라이브로 진행한다.
}
6. 드라이브/디렉토리/파일의 이미지 리스트 인덱스 얻기
특정 드라이브/디렉토리/파일이 시스템 이미지 리스트에서 어떤 인덱스를 갖는지 알고 싶습니다.
각 파일이나 드라이브 및 디렉토리에 대한 정보는 Shell 라이브러리에서 제공해주는 SHGetFileInfo 함수를 이용하면 됩니다. 다음의 함수는 첫 번째 인자인 lpFileName으로 주어진 파일에 대한 설명을 두 번째 인자로 받아오고 세 번째 인자로는 시스템 이미지 리스트에서의 인덱스를 얻어옵니다.
void GetFileInfo(LPSTR lpFileName, LPSTR lpDesc, int *nIndex)
{
DWORD dwAttr;
SHFILEINFO sfi;
int hIcon = SHGetFileInfo(lpFileName, dwAttr, &sfi, sizeof(SHFILEINFO),
SHGFI_TYPENAME | SHGFI_SYSICONINDEX);
*nIndex = sfi.iIcon;
strcpy(lpDesc, sfi.szTypeName);
}
7. 리스트 컨트롤에 칼럼 헤더 넣기
리포트뷰 형식의 리스트 컨트롤에 컬럼 헤더를 집어 넣으려면 어떻게 해야합니까 ?
// <문서명, 등록날짜, 상태> : 3개의 헤더를 만든다.
LV_COLUMN col;
col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
col.fmt = LVCFMT_LEFT;
col.cx = 100;
col.pszText = "문서명";
col.cchTextMax = strlen(col.pszText);
ListView_SetColumn(hListView, 0, &col);
col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
col.fmt = LVCFMT_LEFT;
col.cx = 100;
col.pszText = "등록날짜";
col.cchTextMax = strlen(col.pszText);
ListView_InsertColumn(hListView, 0, &col);
col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
col.fmt = LVCFMT_LEFT;
col.cx = 100;
col.pszText = "상태";
col.cchTextMax = strlen(col.pszText);
ListView_InsertColumn(hListView, 1, &col);
8. 리스트뷰에 항목 삽입하기
리스트뷰에 한 항목을 추가하고 싶습니다.
// 이미지 리스트와 부가 정보를 사용하지 않는 리스트뷰 컨트롤이다.
int nIndex;
LV_ITEM item;
// - 첫번째 컬럼 -
item.mask = LVIF_TEXT; // 이미지 리스트를 사용하려면 LVIF_IMAGE를 추가하고
// 부가정보를 지정해야할 일이 있다면 LVIF_PARAM을 추가한다.
item.pszText = lpDocName;
item.cchTextMax = strlen(lpDocName);
item.iItem = 1;
item.iSubItem = 0;
nIndex = ListView_InsertItem(hListView, &item);
// - 두번째 컬럼 -
item.mask = LVIF_TEXT;
item.iItem = nIndex;
item.pszText = lpDate;
item.cchTextMax = strlen(lpDate);
item.iSubItem = 1;
ListView_SetItem(hListView, &item);
// - 세번째 컬럼 -
item.mask = LVIF_TEXT;
item.iItem = nIndex;
item.pszText = "";
item.cchTextMax = strlen(lpDocName);
item.iSubItem = 2;
ListView_SetItem(hListView, &item);
9. 리스트뷰 컨트롤에서의 정렬 구현
리스트뷰 컨트롤에서 칼럼 헤더를 눌렀을 때 정렬이 되도록 하려면 어떻게 해야합니까 ?
일단 리스트뷰 컨트롤의 생성시 윈도우 스타일로 LVS_NOSORTHEADER를 주지 않는다.
리스트뷰로부터 칼럼 헤더가 눌렸을 때 오는 이벤트를 받아들인다.
NM_LISTVIEW *pnmtv = (NM_LISTVIEW FAR *)lParam;
switch(pnmtv->hdr.code)
{
case LVN_COLUMNCLICK :
{
// 어느 항목(pnmtv->iSubItem)이 눌렸는지부터 검사한다.
// g_iSubItem은 어느 항목이 눌렸는지 기록해두는 인덱스이다.
g_iSubItem = pnmtv->iSubItem;
// 정렬함수를 호출한다. CompareFunc가 정렬함수이다.
ListView_SortItems(hListView, (PFNLVCOMPARE)CompareFunc, (LPARAM)this);
break;
}
리스트뷰 항목을 정렬하는데 사용되는 CompareFunc라는 함수를 만든다. 이는 보통 C 함수로 만들거나 클래스를 사용할 경우에는 클래스 내의 static 함수로 만든다. CompareFunc의 코드는 다음과 같다.
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
LV_FINDINFO lvfi;
int iFirstItem, iSecondItem;
lvfi.flags = LVFI_PARAM;
lvfi.lParam = lParam1;
iFirstItem = ListView_FindItem(hListWnd, -1, &lvfi);
lvfi.flags = LVFI_PARAM;
lvfi.lParam = lParam2;
iSecondItem = ListView_FindItem(hListWnd, -1, &lvfi);
char lpFirst[100];
char lpSecond[100];
ListView_GetItemText(hListWnd, iFirstItem, g_iSubItem, lpFisrt, 100);
ListView_GetItemText(hListWnd, iSecondItem, g_iSubItem, lpSecond, 100);
// g_iSubItem 컬럼의 성격에 따라 비교한다. 문자열이라면 아래와 같이 한다.
int iRet = strcmpi(lpFirst, lpSecond);
return iRet;
}
10. 버전 정보 알아내기 코드
파일의 버전을 API를 통해 알아내려면 어떻게 해야합니까 ?
Resource의 한 타입으로 VERSIONINFO라는 것이 존재합니다. 여기에 해당 파일의 버전 정보를 기록하도록 되어있습니다. 이 버전 정보를 읽어오는데 ver.dll이라는 DLL에 들어있는 API들을 사용합니다. 주의할 점은 버전 리소스는 언어별로 설정이 되기 때문에 영어로도 읽어보고 한국어 로도 읽어봐야 한다는 것입니다. 다음 예제를 참고하시기 바랍니다.
// szDrvName이란 파일에 들어있는 버전 정보를 읽어온다.
#include
DWORD dwSize, handle;
LPSTR lpstrVffInfo;
HANDLE hMem;
LPSTR lpVersion; // 이 변수로 파일의 버전 정보가 들어온다.
char szDrvName[80]; // 버전 정보를 알아내고자 하는 파일 이름이 여기에 들어온다.
....
// 버전 정보 블록의 크기를 알아온다.
dwSize = GetFileVersionInfoSize(szDrvName , &handle);
if (dwSize) // 버전 정보 블록이 존재하면
{
// 버전 정보 블록을 포함할 메모리 블록을 할당 받아둔다.
hMem = GlobalAlloc(GMEM_MOVEABLE, dwSize);
lpstrVffInfo = GlobalLock(hMem);
// 버전 정보 블록의 내용을 읽어온다.
GetFileVersionInfo(szDrvName, handle, dwSize, lpstrVffInfo);
// 버전 정보 블록에서 버전 정보를 읽어온다.
VerQueryValue((LPVOID)lpstrVffInfo,
(LPSTR)"\\StringFileInfo\\041204B0\\FileVersion",
(void FAR* FAR*)&lpVersion, (UINT FAR *)&dwSize);
// lpVersion에 들어있는 버전 정보를 사용한다.
....
GlobalUnlock(hMem);
GlobalFree(hMem);
}
위에서 041204B0가 바로 버전 리소스에 사용된 언어가 무엇인지를 나타냅니다. 이는 영어를 나타내며 한국어의 경우에는 040904B0를 사용하면 됩니다. 이 밖에도 version.lib를 링크의 라이브러리 항목에 추가해야 합니다.
11. 시스템 사양 알아내기
현재 시스템에 부착되어 있는 메인 메모리의 양과 CPU와 운영체제의 종류를 알고 싶습니다.
먼저 시스템에 부착되어 있는 메인 메모리의 크기는 GlobalMemoryStatus라는 API를 이용하면 됩니다. 예제 코드는 다음과 같습니다.
//===========================================================
// lMemTotal : 실제 메모리의 전체 크기 (KB 단위)
// lAvailMemTotal : 사용 가능한 실제 메모리의 크기 (KB 단위)
// lVirtualTotal : 가상 메모리의 전체 크기 (KB 단위)
//===========================================================
void GetMemoryStatus(long *lMemTotal, long *lAvailMemTotal, long *lVirtualTotal)
{
double var;
MEMORYSTATUS memoryStatus;
memset (&memoryStatus, sizeof (MEMORYSTATUS), 0);
memoryStatus.dwLength = sizeof (MEMORYSTATUS);
GlobalMemoryStatus (&memoryStatus);
lMemTotal = memoryStatus.dwTotalPhys / 1024;
lAvailMemTotal = memoryStatus.dwAvailPhys / 1024;
lVirtualTotal = memoryStatus.dwTotalVirtual / 1024;
}
다음으로 CPU의 종류를 알아내는 코드는 다음과 같습니다.
//===============================================================
// GetProcessorInfo : 프로세서에 대한 정보를 읽어온다.
// lpCPUSpeed : CPU의 속도. 기록된 시스템에서만 읽어온다.
// lpProcessorType : 프로세서의 종류
// lpNumProcessors : 프로세서의 개수. NT의 경우에만 의미가 있다.
//===============================================================
void GetProcessorInfo(LPSTR lpCPUSpeed, LPSTR lpProcessorType, LPSTR lpNumProcessors)
{
SYSTEM_INFO sysInfo;
LONG result;
HKEY hKey;
DWORD data;
DWORD dataSize;
lpCPUSpeed[0] = 0;
// ---------------------------------------------
// 프로세서의 속도를 얻어낸다.
// ---------------------------------------------
result = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE,
"Hardware\\Description\\System\\CentralProcessor\\0", 0, KEY_QUERY_VALUE, &hKey);
if (result == ERROR_SUCCESS)
{
result = ::RegQueryValueEx (hKey, "~MHz", NULL, NULL,(LPBYTE)&data, &dataSize);
wsprintf(lpCPUSpeed, "%d MHz", data);
}
RegCloseKey (hKey);
// ------------------------------------------
// 하드웨어 정보를 얻어낸다.
// ------------------------------------------
GetSystemInfo (&sysInfo);
// 프로세서 타입부터 검사한다.
if (sysInfo.dwProcessorType == PROCESSOR_INTEL_386)
strcpy(lpProcessorType, "Intel 386");
else if (sysInfo.dwProcessorType == PROCESSOR_INTEL_486)
strcpy(lpProcessorType, "Intel 486");
else if (sysInfo.dwProcessorType == PROCESSOR_INTEL_PENTIUM)
{
if (sysInfo.wProcessorLevel == 6)
strcpy(lpProcessorType, "Intel Pentium (II/Pro)");
else
strcpy(lpProcessorType, "Intel Pentium");
}
else
strcpy(lpProcessorType, "알 수 없는 시스템");
// 프로세서의 갯수를 검사한다.
wsprintf(lpNumProcessors, "%d", sysInfo.dwNumberOfProcessors);
}
다음으로 현재 사용 중인 운영체제의 종류를 알아내는 코드는 다음과 같습니다.
//===============================================================
// GetOSVersion : OS의 버전을 얻어온다.
// --------------------------------------------------------------
// lpstInfo
// lpstBuildNumber
// lpstServicePack
//===============================================================
void GetOSVersion (LPSTR lpstInfo, LPSTR lpstBuildNumber, LPSTR lpstServicePack)
{
int stat = 0;
TCHAR data [64];
DWORD dataSize;
DWORD win95Info;
OSVERSIONINFO versionInfo;
HKEY hKey;
LONG result;
lpstServicePack[0] = 0;
versionInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
// 버전 정보를 얻어낸다.
if (!::GetVersionEx (&versionInfo))
{
strcpy(lpstInfo, "운영체제 정보를 얻을 수 없습니다.");
return;
}
// NT이면 서버인지 웍스테이션인지 검사한다. 이는 레지스트리를 보고 검사한다.
if (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
strcpy(lpstInfo, "Windows NT");
dataSize = sizeof (data);
result = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Control\\ProductOptions", 0, KEY_QUERY_VALUE, &hKey);
if (result != ERROR_SUCCESS)
return;
result = ::RegQueryValueEx (hKey, "ProductType", NULL, NULL, (LPBYTE) data, &dataSize);
RegCloseKey (hKey);
if (result != ERROR_SUCCESS)
return;
if (lstrcmpi (data, "WinNT") == 0)
strcpy(lpstInfo, "Windows NT Workstation");
else if (lstrcmpi (data, "ServerNT") == 0)
strcpy(lpstInfo, "Windows NT Server");
else
strcpy(lpstInfo, "Windows NT Server - Domain Controller");
// NT 버전을 알아낸다.
if (versionInfo.dwMajorVersion == 3 || versionInfo.dwMinorVersion == 51)
strcat(lpstInfo, " 3.51");
else if (versionInfo.dwMajorVersion == 5) // 윈도우 2000의 경우
strcat(lpstInfo, " 5.0");
else
strcat(lpstInfo, " 4.0");
// Build 번호를 알아낸다.
wsprintf(lpstBuildNumber, "%d", versionInfo.dwBuildNumber);
}
else if (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
{
strcpy(lpstInfo, "Windows 95");
if ((versionInfo.dwMajorVersion > 4) || ((versionInfo.dwMajorVersion == 4)
&& (versionInfo.dwMinorVersion > 0)))
{
strcpy(lpstInfo, "Windows 98");
}
// 윈도우 95는 Build 번호가 하위 워드에 들어간다.
win95Info = (DWORD)(LOBYTE(LOWORD(versionInfo.dwBuildNumber)));
wsprintf(lpstBuildNumber, "%d", win95Info);
}
else
wsprintf(lpstInfo, "Windows 3.1");
// 서비스 팩 정보를 얻어낸다.
strcpy(lpstServicePack, versionInfo.szCSDVersion);
}
12. IE의 설치 여부와 버전 확인
현재 시스템에 IE가 설치되었는지 여부와 그 버전을 알려면 어떻게 해야합니까 ?
사실 동작시켜보지 않고서는 IE가 제대로 설치되어있는지 알아내는 방법은 없지만 레지스트리를 통해 IE가 설치되었는지 여부와 버전을 확인할 수 있습니다. 그 함수는 다음과 같습니다.
//===========================================================================
// GetIEVersion : IE의 버전을 얻는다. 정보를 찾을 수 없으면 FALSE를 리턴한다.
//===========================================================================
BOOL GetIEVersion(LPSTR lpVer)
{
LONG result;
HKEY hKey;
DWORD dwType;
char data[65];
DWORD dataSize = 64;
// --------------------
// IE의 버전을 얻는다.
// --------------------
result = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Internet Explorer", 0, KEY_QUERY_VALUE, &hKey);
if (result == ERROR_SUCCESS)
{
result = ::RegQueryValueEx (hKey, "Version", NULL, &dwType, (unsigned char *)data, &dataSize);
strcpy(lpVer, data);
}
else
return FALSE;
RegCloseKey (hKey);
return TRUE;
}
13. IE의 보안 설정 보기
IE에 보면 네 단계의 보안 영역이 있습니다. 그 영역별로 설정되어있는 보안 설정값을 읽으려면 어떻게 해야합니까 ?
IE에는 다음과 같은 네 가지 보안 영역이 존재합니다.
인터넷 영역
로컬 인터넷 영역
신뢰할 수 있는 사이트 영역
제한된 사이트 영역
IE는 보안 영역 설정과 관련하여 Internet Security Manager와 Internet Zone Manager라는 인터페이스가 존재합니다. 이를 이용해 보안 영역의 보안을 설정하고 특정 IP나 도메인 이름을 등록할 수 있습니다. 자세한 사항은 레퍼런스를 찾아보기 바랍니다.
#include "objbase.h"
#include "urlmon.h"
char szTemp1[256];
char szTemp2[256];
HRESULT hr;
IInternetSecurityManager *pSecurityMgr;
IInternetZoneManager *pZoneMgr;
DWORD dwEnum, dwZoneCount;
// --- 변수 선언부
DWORD dwZone;
ZONEATTRIBUTES zoneAttr;
int nLevel = 2;
// COM 라이브러리를 초기화한다.
CoInitialize(NULL);
dwEnum = 0;
pSecurityMgr = NULL;
pZoneMgr = NULL;
// Internet Security 인터페이스 초기화
hr = CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_ALL, //INPROC_SERVER,
IID_IInternetSecurityManager, (void**)&pSecurityMgr);
if (hr != S_OK)
{
return;
}
hr = CoCreateInstance(CLSID_InternetZoneManager, NULL, CLSCTX_ALL, //INPROC_SERVER,
IID_IInternetZoneManager, (void**)&pZoneMgr);
if (hr != S_OK)
{
return;
}
dwEnum = 0;
// 보안 영역 열거자(Zone Enumerator)를 초기화한다.
pZoneMgr->CreateZoneEnumerator(&dwEnum, &dwZoneCount, 0);
for(DWORD i = 1;i < dwZoneCount;i++)
{
pZoneMgr->GetZoneAt(dwEnum, i, &dwZone);
pZoneMgr->GetZoneAttributes(dwZone, &zoneAttr);
// zoneAttr.szDisplayName에 보안 영역의 이름이 들어오는데 유니코드이다. 이를 변환한다.
WideCharToMultiByte(CP_ACP, 0, zoneAttr.szDisplayName, -1, szTemp1, 255, NULL, NULL);
// zoneAttr.dwTemplateCurrentLevel에는 보안 영역의 보안값 설정이 들어온다.
wsprintf(szTemp2, "%x", zoneAttr.dwTemplateCurrentLevel);
}
// 보안 영역 열거자(Zone Enumerator)를 제거한다.
if (dwEnum != 0)
pZoneMgr->DestroyZoneEnumerator(dwEnum);
pSecurityMgr->Release();
pZoneMgr->Release();
// COM 라이브러리를 메모리에서 내린다.
CoUninitialize();
}
14. ActiveX 컨트롤의 등록 방법
Regsvr32 같은 유틸리티를 이용하지 않고 프로그램 내에서 컨트롤을 레지스트리에 등록하려면 어떻게 해야합니까 ?
모든 AcitveX 컨트롤은 자신을 레지스트리에 등록하기위한 목적으로 DllRegisterServer라는 함수를 갖고 있습니다. ActiveX 컨트롤을 메모리로 로드한 다음에 이 함수를 불러주면 원하는 일을 수행할 수 있습니다. 반대로 ActiveX 컨트롤을 레지스트리에서 제거하기 위한 용도로 DllUnRegisterServer라는 함 수도 존재합니다.
// ==============================================================
// RegisterOCX 지정된 ActiveX 컨트롤을 레지스트리에 등록한다.
// --------------------------------------------------------------
// LPSTR pszString 등록하고자 하는 ActiveX 컨트롤의 절대 경로명
// ==============================================================
BOOL WINAPI RegisterOCX(LPSTR pszString)
{
int iReturn = 0;
HRESULT (STDAPICALLTYPE * lpDllEntryPoint)();
HINSTANCE hLib;
// OLE 라이브러리를 초기화한다.
if (FAILED(OleInitialize(NULL)))
{
MessageBox(GetFocus(), "OLE 초기화 실패", "에러", MB_OK);
return FALSE;
}
// 지정된 activeX 컨트롤을 메모리로 로드한다.
hLib = LoadLibrary(pszString);
if (hLib <= NULL)
{
MessageBox(GetFocus(), "파일을 로드하는데 실패했습니다.", "에러", MB_OK);
OleUninitialize();
return FALSE;
}
// "DllRegisterServer" 함수의 위치를 찾는다.
lpDllEntryPoint = (long (__stdcall *)(void))GetProcAddress(hLib, "DllRegisterServer");
// 이 함수를 호출합니다.
if (lpDllEntryPoint)
{
if (FAILED((*lpDllEntryPoint)()))
{
http://www.jadoo.net/bbs/view.php?id=webstudy&page=1&sn1=&divpage=1&sn=off&ss=on&sc=on&select_arrange=headnum&desc=asc&no=61
지정한 디렉토리에 있는 모든 파일을 찾아내는 코드를 만들려면 어떻게 해야 합니까 ?
이 때 사용할 수 있는 API가 바로 FindFirstFile과 FindNextFile, FindClose라는 API들입니다. 사용 예제는 다음과 같습니다.
WIN32_FIND_DATA findFileData;
HANDLE hFileHandle;
// szDir에 뒤지고자 하는 디렉토리의 경로명을 준다. 예를 들면 "C:\\TEMP\\*.*"
// 찾아진 파일의 속성은 findFileData의 dwFileAttributes를 살펴본다.
hFileHandle = FindFirstFile(m_szDir, &findFileData);
if (hFileHandle != INVALID_HANDLE_VALUE) // 파일을 찾은 경우
{
// 찾은 파일의 이름은 cFileName 필드로 들어온다.
...
// 다음 파일을 찾는다.
while(FindNextFile(hFileHandle, &findFileData)) {
...
}
FindClose(hFileHandle);
}
2. API를 이용하는 유니코드와 ANSI 문자열간의 변환 방법
API를 이용해서 유니코드와 ANSI 문자열간의 변환은 어떻게 수행합니까 ?
Visual C++에서 유니코드 문자열은 BSTR이란 타입으로 표시됩니다. 또 유니코드와 ANSI 문자열간의 변환을 위해서 윈도우 시스템에는 MultiByteToWideChar와 WideCharToMultiByte라는 API가 존재합니다. MFC에서의 BSTR 타입 변환방법이나 ATL로 하는 BSTR 타입 변환도 참고하시기 바랍니다.
ANSI 문자열에서 유니코드로의 변환 방법
// sTime이란 ANSI 문자열을 bstr이란 이름의 유니코드(BSTR 타입) 변수로 변환
char sTime[] = "유니코드 변환 예제";
BSTR bstr;
// sTime을 유니코드로 변환하기에 앞서 먼저 그 길이를 알아야 한다.
int nLen = MultiByteToWideChar(CP_ACP, 0, sTime, lstrlen(sTime), NULL, NULL);
// 얻어낸 길이만큼 메모리를 할당한다.
bstr = SysAllocStringLen(NULL, nLen);
// 이제 변환을 수행한다.
MultiByteToWideChar(CP_ACP, 0, sTime, lstrlen(sTime), bstr, nLen);
// 필요없어지면 제거한다.
SysFreeString(bstr);
유니코드에서 ANSI 문자열로의 변환 방법
// newVal이란 BSTR 타입에 있는 유니코드 문자열을 sTime이라는 ANSI 문자열로 변환
char *sTime;
int nLen = WideCharToMultiByte(CP_ACP, 0, newVal, -1, sTime, 0, NULL, NULL);
sTime = malloc(nLen+1);
WideCharToMultiByte(CP_ACP, 0, newVal, -1, sTime, 128, NULL, NULL);
// 필요없으면 메모리를 제거한다.
free(sTime);
유니코드 문자열을 UTF-8으로 변환하기
WideCharToMultiByte 함수를 호출할 때 첫 번째 인자로 CP_UTF8을 지정하면 된다. UTF-8은 유니코드의 인코딩 스킴 중의 하나로 쉽게 말하자면 문자열 스트림에서 0을 빼고 표현하는 방법이라고 볼 수 있다.
3. 레지스트리 읽기/쓰기
API를 이용해서 레지스트리에 한 항목을 생성하거나 기존 항목의 값을 읽어들이려면 어떻게 해야합니까 ?
레지스트리 관련 API를 사용하려면 winreg.h라는 헤더 파일을 소스에 포함해야 합니다. 레지스트리에 키를 생성하는 방법과 레지스트리에 존재하는 키의 값을 읽는 방법을 차례로 살펴보겠습니다.
레지스트리 키 생성 예제
// 예를 들어 HKEY_LOCAL_MACHINE밑의 System\CurrentControlSet\Services\GenPort라는 키를
// 생성하고 거기에 DWORD 타입의 값으로 Type을 만들고 문자열 타입의 값으로 Group
// 을 만들어 본다.
#include "winreg.h"
LONG error = 0;
HKEY hKey;
DWORD dwDisp, dwData;
char lpData[] = "Write this down";
// 먼저 만들려는 키가 이미 존재하는 것인지 살혀본다.
error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\GenPort",
0, KEY_ALL_ACCESS, &hKey);
if (error != ERROR_SUCCESS) // 없다면 새로 생성한다.
{
// 키를 생성한다.
error = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Services\\GenPort", 0, "REG_BINARY",
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, &dwDisp);
// 위의 키 밑에 Type이란 DWORD 타입의 값을 만들고 1로 초기화
dwData = 0x1;
error = RegSetValueEx( hKey, "Type", 0, REG_DWORD,&dwData,4);
// 위의 키 밑에 Group이란 문자열 타입의 값을 만들고 lpData의 값으로 초기화
error = RegSetValueEx( hKey, "Group", 0, REG_SZ, lpData, strlen(lpData));
// 키를 닫는다.
RegCloseKey(hKey);
}
기존의 레지스트리 키에서 값 읽기
// HKEY_CURRENT_USER\Software\Netscape\Netscape Navigator\Main 밑의 Install Directory
// 값의 문자열 값을 읽어들인다.
DWORD dwType, cbData;
HKEY hSubKey;
long lRet;
char pszString[255];
// 키를 오픈한다.
if ((lRet = RegOpenKeyEx(HKEY_CURRENT_USER,
"Software\\Netscape\\Netscape Navigator\\Main",
0, KEY_READ | KEY_QUERY_VALUE , &hSubKey)) == ERROR_SUCCESS)
{
cbData = 255; // 문자열 값을 읽어올 데이터의 크기를 준다.
if ((lRet = RegQueryValueEx(hSubKey, "Install Directory",
NULL, &dwType, pszString, &cbData)) == ERROR_SUCCESS)
{
// 제대로 읽힌 경우
}
else
{
// 에러가 발생한 경우
}
RegCloseKey(hSubKey);
}
레지스트리 키 삭제하기 - RegDeleteKey 함수를 사용한다.
4. 윈도우 탐색기로부터의 Drag&Drop을 받으려면
윈도우 탐색기로부터 제가 만든 윈도우로의 drag&drop이 가능하게 하려면 어떻게 해야 합니까 ?
다음 순서를 따라서 프로그래밍하시면 됩니다.
프로그램의 초기화시에 DragAcceptFiles(hWnd, TRUE) 함수를 호출한다. 첫 번째 인자인 hWnd는 드롭의 타겟이 되는 윈도우의 핸들이다.
탐색기로부터 파일이 드롭되는 순간에 WM_DROPFILES 메시지가 날라온다. 이를 처리한다.
case WM_DROPFILES :
{
POINT pt;
// 어느 위치에 드롭되었는지 그 항목을 알아낸다.
if (DragQueryPoint((HDROP)wParam, &pt))
{
UINT i = 0;
// 모두 몇 개의 파일이 드롭되었는지 알아낸다.
// 만일 폴더가 드롭되었다면 폴더의 이름만 넘어온다.
UINT uCount = DragQueryFile((HDROP)wParam, 0xFFFFFFFF, NULL ,0);
for(i = 0;i < uCount;i++)
{
// 드롭된 파일의 이름을 알아온다.
DragQueryFile((HDROP)wParam, i, buffer ,255);
// 드롭된 파일 이름을 출력해본다.
MessageBox(hWnd, buffer, "File Name", MB_OK);
}
}
// drag and drop 작업을 끝낸다.
DragFinish((HDROP)wParam);
break;
}
Drag&drop을 더 사용할 필요가 없어지면 DragAcceptFiles를 호출한다.
DragAcceptFiles(hWnd, FALSE);
5. 시스템의 모든 드라이브 알아내기
현재 시스템에 붙어있는 모든 드라이브(네트웍 드라이브 포함)에 대한 정보를 알아내고 싶습니다.
GetLogicalDriveStrings로 시스템에 마운트되어있는 모든 드라이브 정보를 알아낸다. 두 번째 인자인 buffer로 드라이브 정보가 들어오는데 그 구조는 c:\,d:\과 같은 형식이며 리턴값으로 그 버퍼의 크기가 들어온다.
char buffer[256];
DWORD dwRet;
LPSTR token;
dwRet = GetLogicalDriveStrings(256, buffer);
루프를 돌면서 드라이브별 정보를 알아낸다. 이 때는 GetVolumeInformation 함수를 이용한다.
token = buffer; // token이 지금 처리해야할 드라이브를 가리킨다.
while (dwRet > 0)
{
DWORD FileSystemFlag;
char FileSystemName[64];
strcpy(DriveString, token);
// VolumeName으로 드라이브에 대한 설명 문자열이 넘어온다.
if (GetVolumeInformation(token, VolumeName, 255, NULL, NULL,
&FileSystemFlag, FileSystemName, 63))
{
// 원하는 작업을 수행한다.
}
dwRet -= (strlen(token)+1);
token = token + strlen(token)+1; // 다음 드라이브로 진행한다.
}
6. 드라이브/디렉토리/파일의 이미지 리스트 인덱스 얻기
특정 드라이브/디렉토리/파일이 시스템 이미지 리스트에서 어떤 인덱스를 갖는지 알고 싶습니다.
각 파일이나 드라이브 및 디렉토리에 대한 정보는 Shell 라이브러리에서 제공해주는 SHGetFileInfo 함수를 이용하면 됩니다. 다음의 함수는 첫 번째 인자인 lpFileName으로 주어진 파일에 대한 설명을 두 번째 인자로 받아오고 세 번째 인자로는 시스템 이미지 리스트에서의 인덱스를 얻어옵니다.
void GetFileInfo(LPSTR lpFileName, LPSTR lpDesc, int *nIndex)
{
DWORD dwAttr;
SHFILEINFO sfi;
int hIcon = SHGetFileInfo(lpFileName, dwAttr, &sfi, sizeof(SHFILEINFO),
SHGFI_TYPENAME | SHGFI_SYSICONINDEX);
*nIndex = sfi.iIcon;
strcpy(lpDesc, sfi.szTypeName);
}
7. 리스트 컨트롤에 칼럼 헤더 넣기
리포트뷰 형식의 리스트 컨트롤에 컬럼 헤더를 집어 넣으려면 어떻게 해야합니까 ?
// <문서명, 등록날짜, 상태> : 3개의 헤더를 만든다.
LV_COLUMN col;
col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
col.fmt = LVCFMT_LEFT;
col.cx = 100;
col.pszText = "문서명";
col.cchTextMax = strlen(col.pszText);
ListView_SetColumn(hListView, 0, &col);
col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
col.fmt = LVCFMT_LEFT;
col.cx = 100;
col.pszText = "등록날짜";
col.cchTextMax = strlen(col.pszText);
ListView_InsertColumn(hListView, 0, &col);
col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
col.fmt = LVCFMT_LEFT;
col.cx = 100;
col.pszText = "상태";
col.cchTextMax = strlen(col.pszText);
ListView_InsertColumn(hListView, 1, &col);
8. 리스트뷰에 항목 삽입하기
리스트뷰에 한 항목을 추가하고 싶습니다.
// 이미지 리스트와 부가 정보를 사용하지 않는 리스트뷰 컨트롤이다.
int nIndex;
LV_ITEM item;
// - 첫번째 컬럼 -
item.mask = LVIF_TEXT; // 이미지 리스트를 사용하려면 LVIF_IMAGE를 추가하고
// 부가정보를 지정해야할 일이 있다면 LVIF_PARAM을 추가한다.
item.pszText = lpDocName;
item.cchTextMax = strlen(lpDocName);
item.iItem = 1;
item.iSubItem = 0;
nIndex = ListView_InsertItem(hListView, &item);
// - 두번째 컬럼 -
item.mask = LVIF_TEXT;
item.iItem = nIndex;
item.pszText = lpDate;
item.cchTextMax = strlen(lpDate);
item.iSubItem = 1;
ListView_SetItem(hListView, &item);
// - 세번째 컬럼 -
item.mask = LVIF_TEXT;
item.iItem = nIndex;
item.pszText = "";
item.cchTextMax = strlen(lpDocName);
item.iSubItem = 2;
ListView_SetItem(hListView, &item);
9. 리스트뷰 컨트롤에서의 정렬 구현
리스트뷰 컨트롤에서 칼럼 헤더를 눌렀을 때 정렬이 되도록 하려면 어떻게 해야합니까 ?
일단 리스트뷰 컨트롤의 생성시 윈도우 스타일로 LVS_NOSORTHEADER를 주지 않는다.
리스트뷰로부터 칼럼 헤더가 눌렸을 때 오는 이벤트를 받아들인다.
NM_LISTVIEW *pnmtv = (NM_LISTVIEW FAR *)lParam;
switch(pnmtv->hdr.code)
{
case LVN_COLUMNCLICK :
{
// 어느 항목(pnmtv->iSubItem)이 눌렸는지부터 검사한다.
// g_iSubItem은 어느 항목이 눌렸는지 기록해두는 인덱스이다.
g_iSubItem = pnmtv->iSubItem;
// 정렬함수를 호출한다. CompareFunc가 정렬함수이다.
ListView_SortItems(hListView, (PFNLVCOMPARE)CompareFunc, (LPARAM)this);
break;
}
리스트뷰 항목을 정렬하는데 사용되는 CompareFunc라는 함수를 만든다. 이는 보통 C 함수로 만들거나 클래스를 사용할 경우에는 클래스 내의 static 함수로 만든다. CompareFunc의 코드는 다음과 같다.
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
LV_FINDINFO lvfi;
int iFirstItem, iSecondItem;
lvfi.flags = LVFI_PARAM;
lvfi.lParam = lParam1;
iFirstItem = ListView_FindItem(hListWnd, -1, &lvfi);
lvfi.flags = LVFI_PARAM;
lvfi.lParam = lParam2;
iSecondItem = ListView_FindItem(hListWnd, -1, &lvfi);
char lpFirst[100];
char lpSecond[100];
ListView_GetItemText(hListWnd, iFirstItem, g_iSubItem, lpFisrt, 100);
ListView_GetItemText(hListWnd, iSecondItem, g_iSubItem, lpSecond, 100);
// g_iSubItem 컬럼의 성격에 따라 비교한다. 문자열이라면 아래와 같이 한다.
int iRet = strcmpi(lpFirst, lpSecond);
return iRet;
}
10. 버전 정보 알아내기 코드
파일의 버전을 API를 통해 알아내려면 어떻게 해야합니까 ?
Resource의 한 타입으로 VERSIONINFO라는 것이 존재합니다. 여기에 해당 파일의 버전 정보를 기록하도록 되어있습니다. 이 버전 정보를 읽어오는데 ver.dll이라는 DLL에 들어있는 API들을 사용합니다. 주의할 점은 버전 리소스는 언어별로 설정이 되기 때문에 영어로도 읽어보고 한국어 로도 읽어봐야 한다는 것입니다. 다음 예제를 참고하시기 바랍니다.
// szDrvName이란 파일에 들어있는 버전 정보를 읽어온다.
#include
DWORD dwSize, handle;
LPSTR lpstrVffInfo;
HANDLE hMem;
LPSTR lpVersion; // 이 변수로 파일의 버전 정보가 들어온다.
char szDrvName[80]; // 버전 정보를 알아내고자 하는 파일 이름이 여기에 들어온다.
....
// 버전 정보 블록의 크기를 알아온다.
dwSize = GetFileVersionInfoSize(szDrvName , &handle);
if (dwSize) // 버전 정보 블록이 존재하면
{
// 버전 정보 블록을 포함할 메모리 블록을 할당 받아둔다.
hMem = GlobalAlloc(GMEM_MOVEABLE, dwSize);
lpstrVffInfo = GlobalLock(hMem);
// 버전 정보 블록의 내용을 읽어온다.
GetFileVersionInfo(szDrvName, handle, dwSize, lpstrVffInfo);
// 버전 정보 블록에서 버전 정보를 읽어온다.
VerQueryValue((LPVOID)lpstrVffInfo,
(LPSTR)"\\StringFileInfo\\041204B0\\FileVersion",
(void FAR* FAR*)&lpVersion, (UINT FAR *)&dwSize);
// lpVersion에 들어있는 버전 정보를 사용한다.
....
GlobalUnlock(hMem);
GlobalFree(hMem);
}
위에서 041204B0가 바로 버전 리소스에 사용된 언어가 무엇인지를 나타냅니다. 이는 영어를 나타내며 한국어의 경우에는 040904B0를 사용하면 됩니다. 이 밖에도 version.lib를 링크의 라이브러리 항목에 추가해야 합니다.
11. 시스템 사양 알아내기
현재 시스템에 부착되어 있는 메인 메모리의 양과 CPU와 운영체제의 종류를 알고 싶습니다.
먼저 시스템에 부착되어 있는 메인 메모리의 크기는 GlobalMemoryStatus라는 API를 이용하면 됩니다. 예제 코드는 다음과 같습니다.
//===========================================================
// lMemTotal : 실제 메모리의 전체 크기 (KB 단위)
// lAvailMemTotal : 사용 가능한 실제 메모리의 크기 (KB 단위)
// lVirtualTotal : 가상 메모리의 전체 크기 (KB 단위)
//===========================================================
void GetMemoryStatus(long *lMemTotal, long *lAvailMemTotal, long *lVirtualTotal)
{
double var;
MEMORYSTATUS memoryStatus;
memset (&memoryStatus, sizeof (MEMORYSTATUS), 0);
memoryStatus.dwLength = sizeof (MEMORYSTATUS);
GlobalMemoryStatus (&memoryStatus);
lMemTotal = memoryStatus.dwTotalPhys / 1024;
lAvailMemTotal = memoryStatus.dwAvailPhys / 1024;
lVirtualTotal = memoryStatus.dwTotalVirtual / 1024;
}
다음으로 CPU의 종류를 알아내는 코드는 다음과 같습니다.
//===============================================================
// GetProcessorInfo : 프로세서에 대한 정보를 읽어온다.
// lpCPUSpeed : CPU의 속도. 기록된 시스템에서만 읽어온다.
// lpProcessorType : 프로세서의 종류
// lpNumProcessors : 프로세서의 개수. NT의 경우에만 의미가 있다.
//===============================================================
void GetProcessorInfo(LPSTR lpCPUSpeed, LPSTR lpProcessorType, LPSTR lpNumProcessors)
{
SYSTEM_INFO sysInfo;
LONG result;
HKEY hKey;
DWORD data;
DWORD dataSize;
lpCPUSpeed[0] = 0;
// ---------------------------------------------
// 프로세서의 속도를 얻어낸다.
// ---------------------------------------------
result = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE,
"Hardware\\Description\\System\\CentralProcessor\\0", 0, KEY_QUERY_VALUE, &hKey);
if (result == ERROR_SUCCESS)
{
result = ::RegQueryValueEx (hKey, "~MHz", NULL, NULL,(LPBYTE)&data, &dataSize);
wsprintf(lpCPUSpeed, "%d MHz", data);
}
RegCloseKey (hKey);
// ------------------------------------------
// 하드웨어 정보를 얻어낸다.
// ------------------------------------------
GetSystemInfo (&sysInfo);
// 프로세서 타입부터 검사한다.
if (sysInfo.dwProcessorType == PROCESSOR_INTEL_386)
strcpy(lpProcessorType, "Intel 386");
else if (sysInfo.dwProcessorType == PROCESSOR_INTEL_486)
strcpy(lpProcessorType, "Intel 486");
else if (sysInfo.dwProcessorType == PROCESSOR_INTEL_PENTIUM)
{
if (sysInfo.wProcessorLevel == 6)
strcpy(lpProcessorType, "Intel Pentium (II/Pro)");
else
strcpy(lpProcessorType, "Intel Pentium");
}
else
strcpy(lpProcessorType, "알 수 없는 시스템");
// 프로세서의 갯수를 검사한다.
wsprintf(lpNumProcessors, "%d", sysInfo.dwNumberOfProcessors);
}
다음으로 현재 사용 중인 운영체제의 종류를 알아내는 코드는 다음과 같습니다.
//===============================================================
// GetOSVersion : OS의 버전을 얻어온다.
// --------------------------------------------------------------
// lpstInfo
// lpstBuildNumber
// lpstServicePack
//===============================================================
void GetOSVersion (LPSTR lpstInfo, LPSTR lpstBuildNumber, LPSTR lpstServicePack)
{
int stat = 0;
TCHAR data [64];
DWORD dataSize;
DWORD win95Info;
OSVERSIONINFO versionInfo;
HKEY hKey;
LONG result;
lpstServicePack[0] = 0;
versionInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
// 버전 정보를 얻어낸다.
if (!::GetVersionEx (&versionInfo))
{
strcpy(lpstInfo, "운영체제 정보를 얻을 수 없습니다.");
return;
}
// NT이면 서버인지 웍스테이션인지 검사한다. 이는 레지스트리를 보고 검사한다.
if (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
strcpy(lpstInfo, "Windows NT");
dataSize = sizeof (data);
result = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Control\\ProductOptions", 0, KEY_QUERY_VALUE, &hKey);
if (result != ERROR_SUCCESS)
return;
result = ::RegQueryValueEx (hKey, "ProductType", NULL, NULL, (LPBYTE) data, &dataSize);
RegCloseKey (hKey);
if (result != ERROR_SUCCESS)
return;
if (lstrcmpi (data, "WinNT") == 0)
strcpy(lpstInfo, "Windows NT Workstation");
else if (lstrcmpi (data, "ServerNT") == 0)
strcpy(lpstInfo, "Windows NT Server");
else
strcpy(lpstInfo, "Windows NT Server - Domain Controller");
// NT 버전을 알아낸다.
if (versionInfo.dwMajorVersion == 3 || versionInfo.dwMinorVersion == 51)
strcat(lpstInfo, " 3.51");
else if (versionInfo.dwMajorVersion == 5) // 윈도우 2000의 경우
strcat(lpstInfo, " 5.0");
else
strcat(lpstInfo, " 4.0");
// Build 번호를 알아낸다.
wsprintf(lpstBuildNumber, "%d", versionInfo.dwBuildNumber);
}
else if (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
{
strcpy(lpstInfo, "Windows 95");
if ((versionInfo.dwMajorVersion > 4) || ((versionInfo.dwMajorVersion == 4)
&& (versionInfo.dwMinorVersion > 0)))
{
strcpy(lpstInfo, "Windows 98");
}
// 윈도우 95는 Build 번호가 하위 워드에 들어간다.
win95Info = (DWORD)(LOBYTE(LOWORD(versionInfo.dwBuildNumber)));
wsprintf(lpstBuildNumber, "%d", win95Info);
}
else
wsprintf(lpstInfo, "Windows 3.1");
// 서비스 팩 정보를 얻어낸다.
strcpy(lpstServicePack, versionInfo.szCSDVersion);
}
12. IE의 설치 여부와 버전 확인
현재 시스템에 IE가 설치되었는지 여부와 그 버전을 알려면 어떻게 해야합니까 ?
사실 동작시켜보지 않고서는 IE가 제대로 설치되어있는지 알아내는 방법은 없지만 레지스트리를 통해 IE가 설치되었는지 여부와 버전을 확인할 수 있습니다. 그 함수는 다음과 같습니다.
//===========================================================================
// GetIEVersion : IE의 버전을 얻는다. 정보를 찾을 수 없으면 FALSE를 리턴한다.
//===========================================================================
BOOL GetIEVersion(LPSTR lpVer)
{
LONG result;
HKEY hKey;
DWORD dwType;
char data[65];
DWORD dataSize = 64;
// --------------------
// IE의 버전을 얻는다.
// --------------------
result = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Internet Explorer", 0, KEY_QUERY_VALUE, &hKey);
if (result == ERROR_SUCCESS)
{
result = ::RegQueryValueEx (hKey, "Version", NULL, &dwType, (unsigned char *)data, &dataSize);
strcpy(lpVer, data);
}
else
return FALSE;
RegCloseKey (hKey);
return TRUE;
}
13. IE의 보안 설정 보기
IE에 보면 네 단계의 보안 영역이 있습니다. 그 영역별로 설정되어있는 보안 설정값을 읽으려면 어떻게 해야합니까 ?
IE에는 다음과 같은 네 가지 보안 영역이 존재합니다.
인터넷 영역
로컬 인터넷 영역
신뢰할 수 있는 사이트 영역
제한된 사이트 영역
IE는 보안 영역 설정과 관련하여 Internet Security Manager와 Internet Zone Manager라는 인터페이스가 존재합니다. 이를 이용해 보안 영역의 보안을 설정하고 특정 IP나 도메인 이름을 등록할 수 있습니다. 자세한 사항은 레퍼런스를 찾아보기 바랍니다.
#include "objbase.h"
#include "urlmon.h"
char szTemp1[256];
char szTemp2[256];
HRESULT hr;
IInternetSecurityManager *pSecurityMgr;
IInternetZoneManager *pZoneMgr;
DWORD dwEnum, dwZoneCount;
// --- 변수 선언부
DWORD dwZone;
ZONEATTRIBUTES zoneAttr;
int nLevel = 2;
// COM 라이브러리를 초기화한다.
CoInitialize(NULL);
dwEnum = 0;
pSecurityMgr = NULL;
pZoneMgr = NULL;
// Internet Security 인터페이스 초기화
hr = CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_ALL, //INPROC_SERVER,
IID_IInternetSecurityManager, (void**)&pSecurityMgr);
if (hr != S_OK)
{
return;
}
hr = CoCreateInstance(CLSID_InternetZoneManager, NULL, CLSCTX_ALL, //INPROC_SERVER,
IID_IInternetZoneManager, (void**)&pZoneMgr);
if (hr != S_OK)
{
return;
}
dwEnum = 0;
// 보안 영역 열거자(Zone Enumerator)를 초기화한다.
pZoneMgr->CreateZoneEnumerator(&dwEnum, &dwZoneCount, 0);
for(DWORD i = 1;i < dwZoneCount;i++)
{
pZoneMgr->GetZoneAt(dwEnum, i, &dwZone);
pZoneMgr->GetZoneAttributes(dwZone, &zoneAttr);
// zoneAttr.szDisplayName에 보안 영역의 이름이 들어오는데 유니코드이다. 이를 변환한다.
WideCharToMultiByte(CP_ACP, 0, zoneAttr.szDisplayName, -1, szTemp1, 255, NULL, NULL);
// zoneAttr.dwTemplateCurrentLevel에는 보안 영역의 보안값 설정이 들어온다.
wsprintf(szTemp2, "%x", zoneAttr.dwTemplateCurrentLevel);
}
// 보안 영역 열거자(Zone Enumerator)를 제거한다.
if (dwEnum != 0)
pZoneMgr->DestroyZoneEnumerator(dwEnum);
pSecurityMgr->Release();
pZoneMgr->Release();
// COM 라이브러리를 메모리에서 내린다.
CoUninitialize();
}
14. ActiveX 컨트롤의 등록 방법
Regsvr32 같은 유틸리티를 이용하지 않고 프로그램 내에서 컨트롤을 레지스트리에 등록하려면 어떻게 해야합니까 ?
모든 AcitveX 컨트롤은 자신을 레지스트리에 등록하기위한 목적으로 DllRegisterServer라는 함수를 갖고 있습니다. ActiveX 컨트롤을 메모리로 로드한 다음에 이 함수를 불러주면 원하는 일을 수행할 수 있습니다. 반대로 ActiveX 컨트롤을 레지스트리에서 제거하기 위한 용도로 DllUnRegisterServer라는 함 수도 존재합니다.
// ==============================================================
// RegisterOCX 지정된 ActiveX 컨트롤을 레지스트리에 등록한다.
// --------------------------------------------------------------
// LPSTR pszString 등록하고자 하는 ActiveX 컨트롤의 절대 경로명
// ==============================================================
BOOL WINAPI RegisterOCX(LPSTR pszString)
{
int iReturn = 0;
HRESULT (STDAPICALLTYPE * lpDllEntryPoint)();
HINSTANCE hLib;
// OLE 라이브러리를 초기화한다.
if (FAILED(OleInitialize(NULL)))
{
MessageBox(GetFocus(), "OLE 초기화 실패", "에러", MB_OK);
return FALSE;
}
// 지정된 activeX 컨트롤을 메모리로 로드한다.
hLib = LoadLibrary(pszString);
if (hLib <= NULL)
{
MessageBox(GetFocus(), "파일을 로드하는데 실패했습니다.", "에러", MB_OK);
OleUninitialize();
return FALSE;
}
// "DllRegisterServer" 함수의 위치를 찾는다.
lpDllEntryPoint = (long (__stdcall *)(void))GetProcAddress(hLib, "DllRegisterServer");
// 이 함수를 호출합니다.
if (lpDllEntryPoint)
{
if (FAILED((*lpDllEntryPoint)()))
{
http://www.jadoo.net/bbs/view.php?id=webstudy&page=1&sn1=&divpage=1&sn=off&ss=on&sc=on&select_arrange=headnum&desc=asc&no=61
Trackback Address:http://limcom.co.kr/blog/trackback/24