C++ 에서 싱글톤 패턴을 적용하여 인스턴스를 생성할 때, 인스턴스가 리턴되지 않고 무한 대기(내부적으로는 아래 설명에 따라 무한 루프)에 빠지는 문제에 대한 내용입니다. 이 문제는 Visual Studio 2015 (c++ 11 포함 이상)부터 상위 버전에서 발생할 수 있는 문제라고 되어 있네요.
겪은 현상 설명 최근 Visual Studio 2013 버전에서 Visual Studio 2019로 마이그레이션을 진행하면서 겪은 내용을 남겨 놓고자 글을 씁니다. 싱글톤을 리턴할 때 MSDN에서는 스레드 세이프하도록 가드를 넣었다고 설명하고 있습니다. (아래 그림이 첫번째 인스턴스를 생성할 때, 가드를 치는 c runtime 내부 소스입니다.)
/Zc:threadSafeInit(스레드로부터 안전한 로컬 정적 초기화) - MSDN 설명중 일부 발췌 / Zc:threadSafeInit 컴파일러 옵션은 컴파일러에게 스레드로부터 안전한 방식으로 정적 로컬(함수 범위) 변수를 초기화하도록 지시하므로 수동 동기화가 필요하지 않습니다. 초기화만 스레드로부터 안전합니다. 다중 스레드에 의한 정적 지역 변수의 사용 및 수정은 여전히 수동으로 동기화되어야 합니다. 이 옵션은 Visual Studio 2015부터 사용할 수 있습니다. 기본적으로 Visual Studio는 이 옵션을 활성화합니다.
즉, 위의 내용은 싱글톤을 패턴을 포함한 정적 로컬 변수의 초기화시 안정성을 보장하는 옵션에 대한 설명이며 Visual Studio 2015 부터는 기본 옵션으로 포함되어 있습니다.
멀티 스레드 환경에서도 정적 변수 초기화시 별다른 lock 없이 사용할 수 있도록 보장하는 내용이지요. 그게 아니라면 별도로 lock 을 걸어서 안정성을 보장해 주어야 합니다.
최근 테스트된 환경에서 고속으로 돌아가는 개별 스레드에서 싱글톤 객체를 생성할 때, 초기화 코드를 호출하고 해당 코드가 리턴되기 전에 다른 스레드에서 또 객체 생성을 호출하면 위 그림의 while 루프에 빠져 무한 루프를 도는 현상이 확인 되었습니다.
통상적으로 코드 자체에는 문제 없고, 로직이나 이런것에 전혀 문제가 없는 상황에서 발생하는데요 이번에 확인된 부분은 콘솔 터미널 에서는 문제없이 돌아가는 실행파일이, 리모트 터미널 환경에서 hang 걸리는 극악한 환경에서 발생해서 원인을 찾는데 상당히 애를 먹었네요.
아마도 리모트 환경에서는 콘솔에 비하여 바이너리 로딩이나 초기화 등에서 조금더 지연이 발생 하나 봅니다.
해법은 컴파일러 옵션에서 /Zc:threadSafeInit- 옵션을 넣어서 간단하게 해결할 수 있습니다. 즉, 기본옵션인 /Zc:threadSafeInit 를 강제로 해제하는 내용입니다.
이런 경우 해당 객체의 스레드 안정성은 직접 보장해 주시거나, 기존 동작에 문제가 없었다면 옵션만 넣고 빌드하시면 됩니다.
/* 만들어져 있는 MMF를 열 때 사용하는 열기 함수 */ DWORDOpen(constTCHAR*pszMapName,DWORDdwDesiredAccess=FILE_MAP_READ);
/* 파일 핸들이 있거나 시스템 페이지를 이용하여 MMF를 생성하는 함수 */ DWORDCreate(HANDLEhFile,constTCHAR*pszMapName,LONGLONGiFilesize,LPSECURITY_ATTRIBUTESlpAttributes=NULL);
/* 파일을 직접 지정하여 MMF를 생성하는 함수 */ DWORDCreate(constTCHAR*pszMapFilePath,constTCHAR*pszMapName,LONGLONGiFilesize,boolbAudoDelete=true,LPSECURITY_ATTRIBUTESlpAttributes=NULL);
/* a 객체는 C:\\a.map 파일을 열거나 생성하여 1024 크기로 만들고, 이름을 LOCAL_FILE 으로 지정한다.
b 객체는 LOCAL_FILE 이름을 가진 MMF를 읽고/쓰기 모드로 연다. */ if(a.Create(_T("C:\\a.map"),_T("LOCAL_FILE"),1024)==ERROR_SUCCESS) _tcscpy((LPTSTR)a.GetPtr(),_T("까마귀날자 배떨어진다."));
if(b.Open(_T("LOCAL_FILE"),FILE_MAP_READ|FILE_MAP_WRITE)==ERROR_SUCCESS) _tcscpy((LPTSTR)b.GetPtr(),_T("날아도 배 안떨어져요."));
/* a 객체는 시스템 페이지를 이용하여 1024 크기로 만들고, 이름을 SYSTEM_PAGE으로 지정한다.
b 객체는SYSTEM_PAGE이름을 가진 MMF를 읽기 모드로 연다. */ if(a.Create(INVALID_HANDLE_VALUE,_T("SYSTEM_PAGE"),1024)==ERROR_SUCCESS) _tcscpy((LPTSTR)a.GetPtr(),_T("까마귀날자 배떨어진다."));
if(b.Open(_T("SYSTEM_PAGE"),FILE_MAP_READ)==ERROR_SUCCESS) _tcscpy((LPTSTR)b.GetPtr(),_T("날아도 배 안떨어져요.")); <- 에러: 읽기 모드로만 열어서 쓰기 금지
CMMFObject c(a); <- 에러: 복사 생성에 의한 다중 참조 금지
CMMFObjectd = b;<- 에러: 대입 연산에 의한 다중 참조 금지
코드를 다시 정리하면서...
1. MMF는 생성 시점에 바로 메모리에 올라가지 않는다.
실제 생성한 MMF를 액세스 하는 시점에 해당 사용한 영역 만큼만 메모리에 올라간다.
즉, 1 GB 짜리 MMF를 생성해도 시스템 메모리 사용량은 변함이 없고, 거기에 읽고/쓰는 영역이 생기면 그 때 변화한다.
2. 32 Bit OS에서 이론적으로 4GB까지 MMF를 생성할 수 있다고 하는데...
일반적으로 32 bit Windows 에서는 1.5~1.8 GB 까지 생성가능하다.
쫌더 쓰려면? /3GB boot parameter 를 이용하여 2.5~2.7 GB 까지 가능하기는 하지만.. 서버용으로는 쓰기 곤란하다.
즉, 32 Bit OS 에서는 많이 써봐야 1.8 GB 정도로 보면 된다.
3. MMF를 생성하면 메모리가 증가 하겠지만, 오픈 할 때도 증가???
MMF를 생성하면 당연히 할당한 크기만큼 메모리가 증가한다.
그런데 MMF를 열면, 원래 MMF가 지정한 크기만큼 메모리가 다시 증가한다. 또 열면 또 그만큼 증가한다.
아래의 그림을 참고하자. a는 MMF를 생성, b는 MMF를 읽기모드로 오픈한 객체 정보의 비교이다.
데이터에 모두 b 라는 문자열을 가졌지만.. 해당 메모리의 주소가 서로 다르다.
MMF의 오픈이라는 의미는 생성해둔 MMF 주소를 가져다 빌려쓰는게 아닌.. 같은 크기만큼의 메모리를 만들어서
미러형식으로 데이터의 동기화를 이루어 공유하는 것처럼 보이는 방식이다.
4. 파일을 직적 연결하여 MMF를 생성했을 때, 메모리 방식과 직접 파일 액세스 방식은 혼용하면 않된다.
내가 직접 만든 파일로 MMF를 생성할 경우, 2가지 방식으로 액세스 할 수 있는 상태가 된다.
하나는 직접 파일을 read/write 하는 방식과 다른 하나는 메모리를 매핑된 MMF의 주소를 이용함 메모리 방식이다.
각각 혹은 2가지 방식을 동시에 사용할 수 있지만.. 매핑된 메모리에 쓴 내용이 바로 디스크에 반영되지 않기 때문에
2가지 방식으로 데이터를 액세스 하면 같은 위치 영역의 디스크 데이터와 메모리 데이터가 틀어질 수 있다.
5. 그럼 파일이 빠르냐? MMF가 빠르냐?
MMF가 디스크에 스왑되어 있을 경우는 메모리에 올라갈 동안 지연이 일부 생기지만.. 일단 메모리에 올라간 이후라면
MMF가 빠르다.. 랜덤 액세스일 경우는 성능 차이가 많이 벌어진다.
하지만 MMF는 파일 크기를 변경할 수 없기 때문에 쓰기 작업을 이용하여 늘려가면서 쓰는것이 불가능하다.
윈도우의 터미널 서버를 이용하는 경우, 해당 세션별 정보나 프로세스 정보
그러한 목록을 가지고 먼가 작업을 해야 하는경우.. 사용할 수 있는 간단한 래핑클래스 입니다.
class CWTSSession 클래스의 경우는 해당 세션 ID를 알고 있을 경우 세션의 디테일한 정보를 읽어 오는 클래스이죠.
클래스의 내부 멤버를 보시면 어떤 정보를 열거하는지 바로 확인이 가능합니다.
class CWTSBase 위 클래스는 어떤 세션이 존재하는지, 어떤 서버가 존재하는지, 프로세스 목록은 어떤게 있는지
등등을 열거해주는 기능을 제공해줍니다.
[-] Collapse
#include <WtsApi32.h>
class CWTSSession
{
public:
CWTSSession();
CWTSSession(HANDLE hWTS, DWORD SessionID);
~CWTSSession();
세션 아이디를 알고 있을 경우, 해당 세션의 세부정보를 읽어오는 샘플 CWTSSession ws(WTS_CURRENT_SERVER_HANDLE, 세션아이디);
모든 세션에 걸친 프로세스 목록을 읽어오는 샘플 CWTSBase wts;
DWORD count;
PWTS_PROCESS_INFO info;
wts.EnumProcesses(info, &count);
어떤 세션이 존재하는지, 세션 목록 정보를 읽어오는 샘플 CWTSBase wts;
DWORD count;
PWTS_SESSION_INFO info;
wts.EnumProcesses(info, &count);
현재 나의 프로그램이 어떤 세션에 속해있는지를 판단하는 샘플 DWORD PID = GetCurrentProcessId();
DWORD SID = 9999999;
if(ProcessIdToSessionId(PID, &SID))
{
CWTSSession ws(WTS_CURRENT_SERVER_HANDLE, SID);
printf("%s\n", ws.IPAddress);
printf("%s\n", ws.ClientName);
printf("%s\n", ws.DomainName);
printf("%s\n", ws.UserName);
printf("%s\n", ws.WinStation);
}
지정된 세션에 동일한 프로세스가 있을 경우, 해당 세션의 특정 프로세스에 메시지 보내기 오직 하나의 프로그램이 동작하도록 할경우를 제외한 하나의 세션당 하나의 프로그래만 구동되도록 작성해야 할경우.. 사용한 샘플입니다. BOOL CALLBACK CROWBACK_WNDENUMPROC(HWND hWnd, LPARAM lp)
{
DWORD target = lp;
DWORD pid = 9999999;
GetWindowThreadProcessId(hWnd, &pid);
if(pid == target)
{
char caption[512] = {0};
::GetWindowText(hWnd, caption, 512);
if(strstr(caption, "Alert Management"))
{
::PostMessage(hWnd, 0x9999, 0, 0);
return FALSE;
}
}
// 먼저 IinternetSecurityManager 인터페이스를 초기화한다. hr = CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_ALL, IID_IInternetSecurityManager, (void**)&pSecurityMgr); if (hr != S_OK) { CoUninitialize(); return; }
// 다음으로 IinternetZoneManager 인터페이스를 초기화한다. hr = CoCreateInstance(CLSID_InternetZoneManager, NULL, CLSCTX_ALL, IID_IInternetZoneManager, (void**)&pZoneMgr); if (hr != S_OK) { CoUninitialize(); return; } dwEnum = 0;
// Zone Enumerator를 초기화한다. pZoneMgr->CreateZoneEnumerator(&dwEnum, &dwZoneCount, 0);
// 2번 존에 대한 정보를 얻는다. 2번 존이 신뢰할 수 있는 영역 존이다. pZoneMgr->GetZoneAt( dwEnum, nLevel, &dwZone); pZoneMgr->GetZoneAttributes(dwZone, &zoneAttr);
// 지금 예제는 보통의 HTTP 사이트를 등록하는 것이기 때문에 HTTPS만을 등록해야 하는 제약 조건을 없앤다. if (zoneAttr.dwFlags &ZAFLAGS_REQUIRE_VERIFICATION) { // 서버 확인 부분을 뺀다. zoneAttr.dwFlags = (zoneAttr.dwFlags & ~(ZAFLAGS_REQUIRE_VERIFICATION)); }
// 현재 보안 설정이 낮음이 아니면 낮음으로 설정한다. if (zoneAttr.dwTemplateCurrentLevel != 0x10000) { zoneAttr.dwTemplateCurrentLevel = 0x10000; zoneAttr.dwTemplateMinLevel = 0x10000; } pZoneMgr->SetZoneAttributes(dwZone, &zoneAttr);
// 등록 사이트 CString strDomain = "http://www.devpia.com";
// 이 값을 유니코드로 변경한다. BSTR bDomain = strDomain.AllocSysString();
// IInternetSecurity 인터페이스의 SetZoneMapping 함수를 이용해 등록한다. hr = pSecurityMgr->SetZoneMapping(nLevel, bDomain, SZM_CREATE);
if (hr == E_ACCESSDENIED) // 존 설정이 서버 확인을 필요로 하는 곳이면 { ::MessageBox(NULL, "등록하려는 영역이 서버 확인이 필요한 것으로 설정되어 있습니다.", "영역 추가 에러", MB_OK); } else if (hr == ERROR_FILE_EXISTS) // 이미 다른 영역으로 등록된 것이면 { ::MessageBox(NULL, "등록하려는 주소가 이미 다른 영역으로 설정되어 있습니다.", "영역 추가 에러", MB_OK); }
::SysFreeString(bDomain);
if (dwEnum != 0) pZoneMgr->DestroyZoneEnumerator(dwEnum);
C나 C++에서 수식을 연산하다 잘못된 메모리를 읽거나, 저장된 파일을 읽어들이거나 0으로 나누거나 등등의 이유로 실수값에 -1.#INF0 과 같은 값이 지정된 것을 본적이 있을것이다. 혹은 -1.#IND0 과 같은 것도 있다.
.#IND Not a Number <= 숫자가 아닌값(실수체계에서 나올 수 없는 값과 같은 경우) .#INF Infinite Number <= 0으로 나누는 등으로 인하여 무한히 큰값
숫자가 아닌값을 검출할 때는 CRT의 _isnan() 무한대의 숫자를 검출할 때는 CRT의 _finite() 주어진 숫자가 의심스러울 때 혹은 상태를 검출할 때는 _fpclass() 모두 float.h 에 선언되어 있습니다.
제목에서 처럼 0으로 나누어졌을 때는 숫자가 무한히 커져 .#INF00 의 무한값을 나타내고 다른 예로 double a = sqrt(-1) (데브피아 Neo.L 님께서 제시해주신 예시) 과 같은 경우는 실수체계에서 표현할 수 없는 숫자라는 의미로 .#IND00 과 같이 표기됩니다.
이 부분은 IEEE754,854 문서에 표기된 표준 표기에 의한것 (데브피아 stiletto 님께서 표기해줌) 으로 모두 정상적인 표기 방법입니다.
다행한것이 설계를 그렇게 한것인지, 저런 값이 나온다고 하여 연산하다가 죽는 경우는 없다는 점이다. 단, 필요한 요소에서 꼭 확인하여 엉뚱한 결과로 인한 피해를 줄이자.
잘 모르는 내용을 올리다가 망신살도 좀 뻗치고.. 아무튼 이런 기회에 새로운걸 공부해 보네요.
위와 같은 자료가 필요한 이유는, 배포된 프로그램에 시간을 이용해서 확인하는 작업등이 존재할경우 CMOS나, OS시간을 변경시켜버리면 확인하기 힘든 부분이 존재하기 때문이다. 여러가지 방식으로 막을 수는 있겠지만, 인터넷에 연결되어져야 하는 컴퓨터라면 혹은 인터넷이 되어야만 사용할 수 있는 프로그램이라면 위 방법으로 쉽게 해결할 수 있다.
This article was previously published under Q167296
SUMMARY
Under UNIX platforms, file times are maintained in the form of a ANSI C runtime arithmetic type named 'time_t', which represents seconds since midnight January 1, 1970 UTC (coordinated universal time).
Under Win32 platforms, file times are maintained primarily in the form of a 64-bit FILETIME structure, which represents the number of 100-nanosecond intervals since January 1, 1601 UTC (coordinate universal time).
This article shows how to convert UNIX time to other Win32 time formats.
MORE INFORMATION
The following function converts a filetime in the UNIX time_t format to a Win32 FILETIME format. Note that time_t is a 32-bit value and FILETIME is a 64-bit structure, so the Win32 function, Int32x32To64() is used in the following function:
#include <winbase.h>
#include <winnt.h>
#include <time.h>
void UnixTimeToFileTime(time_t t, LPFILETIME pft)
{
// Note that LONGLONG is a 64-bit value
LONGLONG ll;
ll = Int32x32To64(t, 10000000) + 116444736000000000;
pft->dwLowDateTime = (DWORD)ll;
pft->dwHighDateTime = ll >> 32;
}
Once the UNIX time is converted to a FILETIME structure, other Win32 time formats can be easily obtained by using Win32 functions such as FileTimeToSystemTime() and FileTimeToDosDateTime().
권한에 관한 부분은 상당히 방대한 양이라.. 그냥 필요해서 쓰는것만 추려봤습니다. MSDN에 잘 나와았지만 대부분 WCHAR 타입의 인자를 넘겨야해서 그냥 껍데기를 씌운 형태입니다. ^^;
1. 유저 영역 -> 유저 생성, 유저 삭제, 유저 목록, 암호 변경, 유저가 속한 그룹 목록, 유저 정보등..
2. 그룹 영역 -> 그룹 생성, 그룹 삭제, 그룹 목록, 그룹에 유저 추가, 그룹에서 유저 삭제등..
3. 권한 영역 -> 유저의 SID 얻어오기, SID를 문자열로 만들기, 현재 유저의 권한 얻어오기 권한의 활성화/비활성화, 프로세스에 속한 권한 목록 얻어오기, 권한 상승 프로세스 소유자 얻어오기 등등...
// Policy.h: interface for the CPolicy class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_POLICY_H__8DE86AF1_C525_4546_A661_0FC4D12902EF__INCLUDED_)
#define AFX_POLICY_H__8DE86AF1_C525_4546_A661_0FC4D12902EF__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13
#include <ntsecapi.h>
#include <vector>
#include <comutil.h>
#include <lm.h>
#include <sddl.h>
#include <Ntsecapi.h>
namespace CLM // Control Local Machine
{
LPCTSTR GetLogonUser();
LPCTSTR GetHostName();
LPCTSTR GetDomainName();
LPWCH MBtoWC(LPCTSTR src, PWCHAR dst, int dstlen);
LPTSTR WCtoMB(LPCWCH src, LPTSTR dst, int dstlen);
void InitLsaString(PLSA_UNICODE_STRING LsaString, LPWSTR String);
NTSTATUS OpenPolicy(
LPCTSTR ServerName, // machine to open policy
DWORD DesiredAccess, // desired access to policy
PLSA_HANDLE PolicyHandle); // resultant policy handle, must be LsaClose(PolicyHandle)
NTSTATUS OpenPolicy(
PLSA_HANDLE PolicyHandle); // resultant policy handle, must be LsaClose(PolicyHandle)
BOOL GetUserSid(
LPCTSTR servername, // where to lookup account
LPCTSTR username, // account of interest
PSID *Sid); // resultant buffer containing SID, must be HeapFree(GetProcessHeap(), 0, pSid);
BOOL GetCurrentUserSid(
PSID *pSid); // resultant buffer containing SID, must be HeapFree(GetProcessHeap(), 0, pSid);
BOOL GetSIDToString(PSID sid, LPTSTR& strstring);
namespace USER
{
/* Declared in Lmaccess.h; include Lm.h. Use Netapi32.lib.
NetUserAdd Adds a user account and assigns a password and privilege level.
NetUserChangePassword Changes a user's password for a specified network server or domain.
NetUserDel Deletes a user account from the server.
NetUserEnum Lists all user accounts on a server.
NetUserGetGroups Returns a list of global group names to which a user belongs.
NetUserGetInfo Returns information about a particular user account on a server.
NetUserGetLocalGroups Returns a list of local group names to which a user belongs.
NetUserSetGroups Sets global group memberships for a specified user account.
NetUserSetInfo Sets the password and other elements of a user account.
*/
NET_API_STATUS NetUserAdd(
LPCTSTR servername, // NULL is localcomputer
LPCTSTR username, // Not NULL
LPCTSTR password, // allow NULL
LPCTSTR comment, // allow NULL
DWORD privilege, // USER_PRIV_GUEST, USER_PRIV_USER, USER_PRIV_ADMIN
DWORD flags); // 권한에 대한 제한. UF_SCRIPT | UF_DONT_EXPIRE_PASSWD 기타 등등..
NET_API_STATUS NetUserChangePassword(
LPCTSTR domainname, // NULL is localcomputer
LPCTSTR username, // NULL is current logon user
LPCTSTR oldpassword, // current password, Not NULL
LPCTSTR newpassword); // new password, Not NULL
NET_API_STATUS NetUserDel(
LPCTSTR domainname, // NULL is localcomputer
LPCTSTR username); // Not NULL
NET_API_STATUS NetUserEnum(
LPCTSTR domainname, // NULL is localcomputer
LPDWORD dwCount, // user count
LPUSER_INFO_20& userinfos); // user info array, must be free -> NetApiBufferFree(userinfos);
NET_API_STATUS NetUserGetGroups(
LPCTSTR domainname, // NULL is localcomputer
LPCTSTR username, // NULL is current user
std::vector<_bstr_t>& parray); // global group name array
NET_API_STATUS NetUserGetInfo(
LPCTSTR domainname, // NULL is localcomputer
LPCTSTR username, // NULL is current user
LPUSER_INFO_20& userinfos); // user info array, must be free -> NetApiBufferFree(userinfos);
NET_API_STATUS NetUserGetLocalGroups(
LPCTSTR domainname, // NULL is localcomputer
LPCTSTR username, // NULL is current user
std::vector<_bstr_t>& parray); // local group name array
NET_API_STATUS NetUserSetGroups(
LPCTSTR domainname, // NULL is localcomputer
LPCTSTR username, // NULL is current user
LPCTSTR groupname); // Not NULL, global groupname
}
namespace GROUP
{
/* Declared in Lmaccess.h; include Lm.h. Use Netapi32.lib.
NetLocalGroupAdd Creates a local group.
NetLocalGroupAddMembers Adds one or more users or global groups to an existing local group.
NetLocalGroupDel Deletes a local group, removing all existing members from the group.
NetLocalGroupDelMembers Removes one or more members from an existing local group.
NetLocalGroupEnum Returns information about each local group account on a server.
NetLocalGroupGetInfo Returns information about a particular local group account on a server.
NetLocalGroupGetMembers Lists all members of a specified local group.
NetLocalGroupSetInfo Sets general information about a local group.
NetLocalGroupSetMembers Assigns members to a local group.
*/
NET_API_STATUS NetLocalGroupAdd(
LPCTSTR domainname, // NULL is localcomputer
LPCTSTR groupname, // Not NULL, groupname
LPCTSTR comment); // Allow NULL
NET_API_STATUS NetLocalGroupAddMembers(
LPCTSTR domainname, // NULL is localcomputer
LPCTSTR groupname, // Not NULL, groupname
LPCTSTR username); // Not NULL, username
NET_API_STATUS NetLocalGroupDel(
LPCTSTR domainname, // NULL is localcomputer
LPCTSTR groupname); // Not NULL, groupname
NET_API_STATUS NetLocalGroupDelMembers(
LPCTSTR domainname, // NULL is localcomputer
LPCTSTR groupname, // Not NULL, groupname
LPCTSTR username); // Not NULL, username
NET_API_STATUS NetLocalGroupEnum(
LPCTSTR domainname, // NULL is localcomputer
LPDWORD dwCount, // group count
PLOCALGROUP_INFO_1& groupinfo); // group info array, must be free -> NetApiBufferFree(groupinfo);
NET_API_STATUS NetLocalGroupGetInfo(
LPCTSTR domainname, // NULL is localcomputer
LPCTSTR groupname, // Not NULL, groupname
PLOCALGROUP_INFO_1& groupinfo); // group info, must be free -> NetApiBufferFree(groupinfo);
};
namespace PRIVILEGE
{
// 현재 프로세스에 주어진 권한을 인에이블/디스에이블 시킨다.
BOOL SetProcessPrivilege(
LPCTSTR lpszPrivilege, // privilege to grant
BOOL bEnablePrivilege); // add or remove
// 사용자 계정에 특별한 권한을 추가시킨다.
NTSTATUS SetPrivilegeOnAccount(
LSA_HANDLE PolicyHandle, // open policy handle
PSID AccountSid, // SID to grant privilege to
LPWSTR PrivilegeName, // privilege to grant (Unicode)
BOOL bEnable); // enable or disable
// 해당 유저에게 할당된 부가적인 프리빌리지를 얻어온다.
// 일반적인 프리빌리지를 얻어올 경우는, 해당 유저가 띄운 프로세스를 이용하여
// 아래의 GetPrivilegeFromProcessHandle 함수를 이용하여 가져온다.
NTSTATUS EnumPrivilegeOnAccount(
LSA_HANDLE PolicyHandle, // open policy handle
PSID AccountSid, // SID to grant privilege to
std::vector<_bstr_t>& parray); // privileges array
// 프로세스 핸들을 이용하여 해당 유저 이름을 얻어온다.
BOOL GetUserFromProcessHandle(
LPTSTR AccountName, // account of interest
DWORD* cbName, // string buffer length
HANDLE hProcess = NULL); // process id, NULL is owner process
// 프로세스 아이디를 이용하여 해당 유저 이름을 얻어온다.
BOOL GetUserFromProcessID(
LPTSTR AccountName, // account of interest
DWORD* cbName, // string buffer length
DWORD nProcessID = 0xFFFFFFFF); // process id, 0xFFFFFFFF is owner process
// 프로세스 핸들을 이용하여 해당 프로세스에 설정된 프리빌리지 목록을 얻어온다.
BOOL GetPrivilegeFromProcessHandle(
std::vector<_bstr_t>& parray, // privileges array
HANDLE hProcess = NULL); // process id, NULL is owner process
// 프로세스 아이디을 이용하여 해당 프로세스에 설정된 프리빌리지 목록을 얻어온다.
BOOL GetPrivilegeFromProcessID(
std::vector<_bstr_t>& parray, // privileges array
DWORD nProcessID = 0xFFFFFFFF); // process id, 0xFFFFFFFF is owner process
};
};