기본 콘텐츠로 건너뛰기

문자열(스트링) 전격 분석 2부 2강(끝

출처 울트라매니아! | 울트라맨
원문 http://blog.naver.com/alshrl999/20020402126
백택 ( bektekk@yahoo.co.kr )
홈페이지 : http://bektekk.wo.to
문자열(스트링) 전격 분석 2부 2강(끝)

이제 실제 클래스들을 다룹니다.
사실 원문에는 VARIANT타입과 VC7 CLR 환경에서의 스트링에 관련된 부분까지 포함하고 있으나,
제가 CLR에 대해서는 문외한인 관계로 생략했고, VARIANT부분은 특별히 의미가 있어 보이지 않아 생략했음을 알려드립니다.
본 강좌는 코드 프로젝트에 제가 좋아하는 프로그래머인 Michael Dunn의 강좌 The Complete Guide to C++ Strings, Part I 과 II 를 번역한 글입니다.
최대한 의역을 하려고 노력했지만, 이런쪽의 경험이 부족하다 보니 많이 모자란 강좌가 되겠지만, 많은 도움이 되셨으면 합니다.
기타 문의 사항이나 질문은 쪽지나 메일을 이용해 주셨으면 합니다.
본강좌는 제 홈페이지(위의 링크)를 통해서도 보실수 있습니다.

스트링 랩퍼 클래스들

지금까지 여러가지의 스트링 타입에 대해 설명했고, 이제 스트링 랩퍼 클래스들에 대해 설명하겠다. 각각 어떻게 생성을 하고 어떻게 C스타일 스트링으로 변환시킬수 있는지 설명하겠다.
왜냐하면 C스타일 스트링 포인터는 API함수를 호출하거나 다른 랩퍼클래스들을 생성하는데 필요하기 때문이다.
나는 그외의 함수들 예를 들자면 정렬, 비교 등의 기능들에 대해서는 생략하도록 하겠다.
다시한번 강조하지만, 확실하지 않다면 절대로 강제 형변환을 하지 않도록 하자.

CRT에서 제공하는 클래스들

_bstr_t

_bstr_t 클래스는 아주 잘 만들진 BSTR 랩퍼 클래스다. 그리고 BSTR의 내부구현이나 기타 등등의 문제를 랩핑 했다.
이 클래스는 여러가지 연산자를 재정의 했을 뿐만 아니라, 사용자의 편의를 위해 다양한 생성자를 제공한다.
하지만 BSTR자체에 접근하는 연산자들은 없다는 걸 주의해라.
따라서 [out] 파라미터를 갖는 COM 메서드에 직접적으로 전달할수는 없다.
만약 COM메서드에 직접적으로 전달할수 있는 BSTR* 타입이 필요하면 ATL 클래스인 CComBSTR 이 더 좋은 선택일 것이다.
하지만 물론 _bstr_t도 BSTR을 받는 함수에 전달할 수 있다. 이는 다음의 세가지 이유 때문이다.
  • 첫째 _bstr_t 은 wchar_t*으로의 형변환 연산자를 제공한다.
  • wchar_t*과 BSTR는 컴파일 과정에서는 같은 타입으로 인식된다. 왜냐하면 BSTR은 wchar_t*형으로 정의되 있기 때문이다.
  • 마지막 세번째는 _bstr_t는 내부적으로 문자열의 시작부분을 포인터로 가리키고 있기 때문이다.
    따라서 명시적으로 형변환 연산자가 없어도 잘 작동되곤 한다.
// 생성
_bstr_t bs1 = "char string";
// LPCSTR로 부터 생성
_bstr_t bs2 = L"wide char string";
// LPCWSTR로 부터 생성
_bstr_t bs3 = bs1;
// 다른 _bstr_t로 부터 생성
_variant_t v = "Bob";
_bstr_t bs4 = v;
// 스트링을 포함하는 _variant_t로 부터 생성
// Extracting data
LPCSTR psz1 = bs1;
// 자동으로 MBCS로 형변환
LPCSTR psz2 = (LPCSTR) bs1;
// 위의 경우와 같다.
LPCWSTR pwsz1 = bs1;
// 내부적으로 포함하는 유니코드 스트링 리턴
LPCWSTR pwsz2 = (LPCWSTR) bs1;
// 위의 경우와 같다.
BSTR bstr = bs1.copy();
// 복사
// ...
SysFreeString ( bstr );
//메모리에서 해제해 주어야 한다.
_bstr_t은 또한 char*와 wchar_t* 에 대한 형변환 연산자도 가지고 있다.
하지만 이는 의문스러운 구현이다.
이 연산자들에 의해 리턴되는 포인터는 const 타입이 아니여서 수정될 수도 있지만, 만약 수정된다면 내부적인 BSTR 타입이 깨질것이고, 많은 문제를 유발할 것이다.

STL 클래스들

STL은 basic_string 하나의 스트링 클래스가 있다.
basic_string 은 0으로 끝나는 문자배열 스트링을 관리한다. 어떤 캐릭터 타입이 쓰일지에 대한것은 템플릿 매개변수로 전달이 된다.
일반적으로 이 클래스에서 얻은 포인터를 직접적으로 조작하는 일은 피해야 한다. 대신 이 클래스에서 제공하는 함수나 연산자들을 써서 조작하기를 권장한다.
이 클래스에서 미리정해진 두 가지의 형태의 타입이 존재한다. 하나는 char를 조작하는 string이고 다른 하나는 wchar_t를 조작하는 wstring이다.
사실, TCHAR 형의 스트링 클래스는 정의되 있지 않지만 아래의 코드를 통해 TCHAR타입도 사용할 수가 있다.
// 타입 정의
typedef basic_string<TCHAR> tstring;
// TCHAR형 스트링
// 생성자들
string str = "char string";
// LPCSTR로 부터 생성
wstring wstr = L"wide char string";
// LPCWSTR로 부터 생성
tstring tstr = _T("TCHAR string");
// LPCTSTR로 부터 생성
// Extracting data
LPCSTR psz = str.c_str();
// 읽기 전용 즉 const형 버퍼 리턴
LPCWSTR pwsz = wstr.c_str();
// 읽기 전용 즉 const형 버퍼 리턴 LPCWSTR형
LPCTSTR ptsz = tstr.c_str();
// 읽기 전용
_bstr_t형과는 다르게 basic_string 클래스는 다른 캐릭터 타입들끼리 직접적으로 형변환을 할수 없다. 그러나 c_str()함수에서 전달되는 포인터를 이용해서 다른 클래스를 생성할 수 있다.
하지만 그 다른 클래스에는 그 포인터를 받는 생성자가 존재해야만 할 것이다.
예를 들면 :
// 예제 basic_string 타입을 이용해서 _bstr_t 형 객체 생성
_bstr_t bs1 = str.c_str();
// LPCSTR 로 부터 생성
_bstr_t bs2 = wstr.c_str();
// LPCWSTR 로 부터 생성

ATL 클래스들

CComBSTR

CComBSTR은 ATL의 BSTR랩퍼 클래스다. 몇몇 곳에서 _bstr_t 보다 더 유용하게 쓰인다. 가장 중요한 부분은 CComBSTR은 내부에 존재하는 BSTR을 직접적으로 접근할수 있다는 데 있다.
이 얘기는 즉, COM 메서드에 직접적으로 CComBSTR 객체를 전달할수 있다는 얘기가 된다. 그럼 CComBSTR 객체는 자동으로 BSTR에 관련된 메모리를 관리해 줄것이다.
예를 들어 아래의 인터페이스의 메서드를 호출한다고 가정해보자. :
// 간단한 인터페이스
struct IStuff : public IUnknown
{
    // 생략....
    STDMETHOD(SetText)(BSTR bsText);
    STDMETHOD(GetText)(BSTR* pbsText);
};
CComBSTR은 BSTR 형변환 연산자를 가지고 있다. 따라서 객체를 직접적으로 SetText() 메서드에 전달할 수가 있다. 또한 & 연산자를 제정의 해서 BSTR* 타입을 리턴한다.
그래서 여러분은 BSTR* 타입을 받는 메서드에 부가적인 형변환이 없이도 직접적으로 전달 할수 있게 된다.
CComBSTR bs1;
CComBSTR bs2 = "new text";
pStuff->GetText ( &bs1 );
// 내부 BSTR 의 포인터 전달
pStuff->SetText ( bs2 );
// BSTR 로 암시적 형변환
pStuff->SetText ( (BSTR) bs2 );
// 명시적 형변환 위의 경우와 정확히 같다.
CComBSTR은 _bstr_t 타입과 거의 유사한 생성자 구조를 가지고 있다. 하지만 중요한 점은 MBCS 스트링을 자동적으로 변환해 주는 생성자는 없다는 것이다.
따라서 MBCS 스트링을 사용할 경우에는 여러가지 방법으로 BSTR형으로 변환해 주어야만 한다.
그 중에서 ATL 변환 매크로들을 추천한다.
// 생성
CComBSTR bs1 = "char string";
// LPCSTR 로 부터 생성
CComBSTR bs2 = L"wide char string";
// LPCWSTR 로 부터 생성
CComBSTR bs3 = bs1;
// CComBSTR 로 부터 복제생성
CComBSTR bs4; bs4.LoadString ( IDS_SOME_STR );

// 스트링 테이블 리소스로 부터 생성
// 값 얻어오기
BSTR bstr1 = bs1;
// 내부의 BSTR을 리턴 하지만 조작하지는 말것!!
BSTR bstr2 = (BSTR) bs1;
// 형변환도 OK
BSTR bstr3 = bs1.Copy();
// 같은 내용의 BSTR을 생성후 리턴
BSTR bstr4; bstr4 = bs1.Detach();

// bs1 내부의 BSTR을 더이상 관리하지 않는다.
// ...
SysFreeString ( bstr3 );
SysFreeString ( bstr4 );
위의 예제에서 Detach() 메서드 부분을 주의해서 보자. 이 메서드의 호출후에는 CComBSTR 객체는 더이상 BSTR을 관리하지 않는다.
따라서 마지막부분의 SysFreeString() 함수를 호출해 줄 필요가 있는 것이다.
마지막으로 중요한 문제는 위 클래스가 & 연산자를 정의했다는 부분이다. 즉, 이 얘기는 STL 컬렉션 클래스들에는 CComBSTR타입을 직접적으로 쓸수 없다는 얘기이다.
왜냐하면 STL컬렉션들은 & 연산자는 내부적으로 관리하는 객체 리스트들의 주소값을 리턴해야만 하는데 CComBSTR 클래스에서 & 연산자를 정의함으로써 CComBSTR* 을 리턴하는게 아니라, BSTR*를 리턴 하게 된다.
따라서 이 경우에는 아래의 예처럼 CAdapt 클래스로 처리해 주어야 한다. :
std::list< CAdapt<CComBSTR> > bstr_list; 
CAdapt 클래스는 컬랙션 클래스에 필요한 연산자들을 다시 정의 하고 있다. 따라서 CComBSTR의 리스트를 사용할수 있게 된다.

ATL 형변환 매크로들

ATL의 스트링 형변환 매크로 들은 스트링 사이의 인코딩을 변경하는 매우 편리한 방법을 제공한다. 특히, 함수 호출 시에 매우 편리하다.
매크로의 이름들도 명료하다.
[소스 타입]2[새로운 타입] 혹은 [소스 타입]2C[새로운 타입].
두 번째의 매크로는 C라는 글자를 포함하는데 이는 const형(읽기전용)으로의 변환을 의미한다. 각각의 타입에 대한 정의는 다음과 같다.:
A: MBCS 스트링, char* (A 는 ANSI를 의미)
W: Unicode 스트링, wchar_t* (W는 wide를 의미)
T: TCHAR 스트링, TCHAR*
OLE: OLECHAR 스트링, OLECHAR* (사실, W와 같은 의미)
BSTR: BSTR
그래서, 예를 들면, W2A() 매크로는 유니코드 스트링을 MBCS 스트링으로 변환해 준다. 그리고 T2CW() 매크로는 TCHAR 스트링을 읽기전용 유니코드 스트링으로 변환해 준다.
이 매크로들을 사용하기 위해서는 먼저 atlconv.h 헤더를 포함해 준다.
(위의 매크로들은 ATL프로젝트가 아니여도 사용할 수 있다.
!! 왜냐하면 위의 헤더 파일은 다른 ATL 부분과의 의존성이 없다. 즉 따로 놀기 때문이다.
그리고 ATL의 _Module 객체를 필요로 하지 않는다.)
둘째로, 해더를 포함했으면 위의 매크로들을 사용하기에 앞서 USES_CONVERSION 라는 매크로를 써주어야 한다.
위의 매크로는 형변환을 하기위해 필요한 몇몇 지역변수들을 선언한다. 즉, 형변환을 하기위한 지저분한 일들을 해준다.
변환하고자 할 타입이 BSTR 타입이 아닐때는 변환된 타입은 스택에 저장된다. 따라서 함수범위를 넘어서 작업을 하고 싶으면 여러분은 다른 스트링 클래스에 복사를 해서 저장 하든지 해서 계속적으로 사용할수 있을 것이다.
하지만 BSTR타입의 경우는 ::SysFreeString등의 함수로 해제를 시켜 주어야 한다.
아래는 변환 매크로 들을 사용하는 예제 코드 이다. :
// 다양한 형태를 받는 함수들:
void Foo ( LPCWSTR wstr );
void Bar ( BSTR bstr );
// 스트링을 리턴하는 함수(BSTR*형에 주의):
void Baz ( BSTR* pbstr );
#include <atlconv.h>
main()
{
    using std::string;
    USES_CONVERSION;
    
    // 형변환 매크로를 사용하기위해서
    // 예제 1 : Foo()함수에 MBCS를 전달
    LPCSTR psz1 = "Bob";
    string str1 = "Bob";
    Foo ( A2CW(psz1) );
    Foo ( A2CW(str1.c_str()) );
    
    // 예제 2: Bar()함수에 MBCS 와 유니코드 스트링 전달
    LPCSTR psz2 = "Bob";
    LPCWSTR wsz = L"Bob";
    BSTR bs1;
    CComBSTR bs2;
    bs1 = A2BSTR(psz2);
    
    // BSTR 생성
    bs2.Attach ( W2BSTR(wsz) );
    
    // CComBstr에 할당
    Bar ( bs1 );
    Bar ( bs2 );
    
    SysFreeString ( bs1 );
    // 반드시 해제해 주어야 한다.
    // bs2의 경우는 CComBstr의 소멸자가 알아서 해재해 준다.
    
    // 예제 3: Baz()함수에서 리턴받든 BSTR형의 형변환
    BSTR bs3 = NULL;
    string str2;
    Baz ( &bs3 );
    // Baz()를 통해 BSTR을 얻었다.
    
    str2 = W2CA(bs3);
    // MBCS 스트링으로 형변환
    
    SysFreeString ( bs3 );
    // 메모리 해제
}
보시다 시피, 위의 매크로 들은 매우 간편하다. 이제 몇가지 사실들에만 주의하면 어디서든 편하게 위의 매크로들을 사용할수 있을 것이다.

MFC 클래스들

CString

MFC의 클래스는 TCHAR형 타입을 다룬다. 따라서 정확한 캐릭터 타입은 컴파일 시간에 전처리명령어를 통해 달라진다.
일반적으로 CString 타입은 STL의 string 타입과 유사하다. 왜냐하면 여러분은 CString을 다룰때 std::string 에서 처럼 직접적으로 문자열 조작을 피하고 제공되는 함수들을 써야 하기 때문이다.
한 가지 STL의 스트링 클래스보다 CString클래스가 편리한 점은 CString클래스는 MBCS 스트링과 유니코드 스트링을 모두 수용하는 생성자를 가지고 있다는 사실이다.
그리고 LPCTSTR 형변환 연산자를 제공해서 여러분은 LPCTSTR을 받는 함수에 특별한 형변환 없이 전달 할수 있다.
std::string에서 처럼 c_str()들의 메서드를 호출할 필요가 없다.
// 생성자
CString s1 = "char string";
// LPCSTR 로 부터 생성
CString s2 = L"wide char string";
// LPCWSTR 로 부터 생성
CString s3 ( ' ', 100 );
// 100바이트를 할당하고 스페이스 문자로 채운다.
CString s4 = "New window text";
// LPCTSTR을 받는 함수에 직접 전달할수 있다.
SetWindowText ( hwndSomeWindow, s4 );
// 혹은 명시적 형변환으로 위의 경우와 정확히 같다.
SetWindowText ( hwndSomeWindow, (LPCTSTR) s4 );
또한 리소스의 스트링 테이블을 좀 더 편하게 읽어올수 있다. 생성자에서도 그런일을 해주고 Format() 메서드에서도 그런 일이 가능하다.
// 스트링 테이블의 문자열로 부터 생성
CString s5 ( (LPCTSTR) IDS_SOME_STR );
CString s6, s7;
// LoadString메서드
s6.LoadString ( IDS_SOME_STR );
// printf스타일로 포맷팅
s7.Format ( IDS_SOME_FORMAT, "bob", nSomeStuff, ... );
첫 번째의 생성자는 약간 이상해 보인다. 하지만 이는 정확히 문서화 되있는 문자열 테이블로 부터 문자열을 생성하는 방법이다.
CString에 적용될 수 있는 형변환은 LPCTSTR 타입 뿐이다. LPTSTR (즉 non-const) 로의 형변환은 잘못된 것이다.
많은 사람들이 LPTSTR형으로 변환하고 사용하는 일을 종종 보게 되는데 이는 여러가지 문제를 야기할 것이다.
나중에 버그를 찾기도 굉장히 어렵게 만든다. ^^ 따라서 LPTSTR형의 값을 얻으려면 GetBuffer() 메서드를 써야만 한다.
리스트 콘트롤에 텍스트를 지정한다고 가정하고 다음 예제를 보자.:
CString str = _T("new text");
LVITEM item = {0};
item.mask = LVIF_TEXT;
item.iItem = 1;
item.pszText = (LPTSTR)(LPCTSTR) str;
// 잘못된 사용!
item.pszText = str.GetBuffer(0);
// 올바른 사용
ListView_SetItem ( &item );
str.ReleaseBuffer();
// 위의 버퍼의 관리를 다시 CString에서 하게끔 해제
위의 예에서 pszText 멤버는 LPTSTR 타입이다. 그러므로 GetBuffer() 메서드를 사용해 주어야 한다. GetBuffer()의 매개변수는 여러분이 할당하기를 원하는 버퍼의 최소 길이를 지정해 준다.
만약 1Kb의 버퍼를 원한다면 GetBuffer(1024) 이런 식으로 호출해 주면 될것이다.
0을 전달하는 것은 현재 스트링이 포함한 스트링의 길이와 정확히 같은 길이의 버퍼를 리턴한다.
위의 사선으로 표시한 잘못된 예제는 컴파일 되고 사실 잘 작동되는것 처럼 보인다. 그렇지만 그 코드는 분명 잘못된 코드이다.
일단 위의 코드는 객체지향의 개념에 어긋나는 것이다. 캡슐화 되어있는 데이터를 직접조작하는 습관은 좋지 못하다.
그리고 내부적으로 관리되는 데이터의 구조를 무너뜨릴 가능성이 크다. 따라서 나중에 분명 원하지 않는 결과를 초래 할 것이다.
요즘의 많은 소프트웨어들은 모두 버그를 포함하고 있다. 이는 프로그래머들의 잘못된 습관에서 나오는 경향이 많다.
따라서 항상 올바른 프로그래밍 습관을 만들려고 노력하자. 버그가 0%가 되는 그날까지...
CString 클래스는 또한 BSTR을 만들어 주는 두가지 함수가 있다.
AllocSysString() 와 SetSysString().
SetSysString()이 BSTR* 인자를 받는다는 점만 빼고, 위 두 함수는 정확히 같은 일은 한다.
// BSTR로 변환
CString s5 = "Bob!";
BSTR bs1 = NULL, bs2 = NULL;
bs1 = s5.AllocSysString();
s5.SetSysString ( &bs2 );
// ...
SysFreeString ( bs1 );
SysFreeString ( bs2 );

WTL 클래스들

CString

WTL의 CString 클래스는 MFC의 CString 클래스와 정확히 똑같이 작동한다. 따라서 MFC의 CString부분을 참고하길 바란다.

printf스타일의 포맷팅 함수에서의 스트링 클래스 사용

여러분은 printf() 혹은 비슷한 방식으로 작동하는 함수들을 사용할때 특별히 주의해야 한다. 이런 함수들에는 sprintf() 등이 있고, 또한 TRACE나 ATLTRACE 매크로도 똑같은 방식으로 작동한다.
이러한 함수나 매크로에서는 특별히 타입체킹을 하지 않기 때문에 여러분은 반드시 스트링 객체를 직접 전달하지 않고 C스타일 스트링을 전달 해야만 한다.
예를 들면, _bstr_t 객체가 가지고 있는 스트링을 ATLTRACE()에 전달할때 여러분은 반드시 명시적으로 (LPCSTR) 나 (LPCWSTR)로 형변환을 해 주어야 한다.:
_bstr_t bs = L"Bob!";
ATLTRACE("The string is: %s in line %d\n", (LPCSTR) bs, nLine);
만약 형변환을 깜박했다면 아마 예상치 못한 결과가 출력될것이다.

총 정 리

스트링 클래스들 사이의 형변환을 일반적으로 다음의 방식을 따른다. 일반 소스 스트링을 C스타일 스트링으로 변환한 후 그것을 이용해 새로운 스트링 타입으로 생성하는 것이다.
따라서 아래의 표는 각각의 클래스 들이 C스타일로 형변환 할수 있는지 그리고 C스타일 스트링으로부터 객체를 생성할수 있는지 요약해서 보여주고 있다.
Class string
type
convert
to char*?
convert to
const char*?
convert to
wchar_t*?
convert to
const wchar_t*?
convert
to BSTR?
construct
from char*?
construct
from wchar_t*?
_bstr_t BSTR yes, cast1 yes, cast yes, cast1 yes, cast yes2 yes yes
_variant_t BSTR no no no cast to
_bstr_t3
cast to
_bstr_t3
yes yes
string MBCS no yes, c_str()method no no no yes no
wstring Unicode no no no yes, c_str()method no no yes
CComBSTR BSTR no no no yes, cast
to BSTR
yes, cast yes yes
CComVariant BSTR no no no yes4 yes4 yes yes
CString TCHAR no6 in MBCS
builds, cast
no6 in Unicode
builds, cast
no5 yes yes
COleVariant BSTR no no no yes4 yes4 in MBCS builds in Unicode builds
1 Even though _bstr_t provides conversion operators to non-const pointers, modifying the underlying buffer may cause a GPF if you overrun the buffer, or a leak when the BSTR memory is freed.
2 A _bstr_t holds a BSTR internally in a wchar_t* variable, so you can use the const wchar_t* converter to retrieve the BSTR. This is an implementation detail, so use this with caution, as it may change in the future.
3 This will throw an exception if the data cannot be converted to a BSTR.
4 Use ChangeType() then access the bstrVal member of the VARIANT. In MFC, this will throw an exception if the data cannot be converted.
5 There is no BSTR conversion function, however the AllocSysString() method returns a new BSTR.
6 You can temporarily get a non-const TCHAR pointer using the GetBuffer() method.

댓글

이 블로그의 인기 게시물

80040154 오류로 인해 CLSID가 {xxxx-...}인 구성 요소의 COM 클래스 팩터리를 검색하지 못했습니다.

원문보기 .NET 으로 만든 응용프로그램에서 com 객체를 호출한 경우 Windows7 64bit 에서 제목과 같은 에러가 발생했다. Win32 COM 과 .NET 프로그램간의 호환성 때문에 생긴 문제였다. 원인은 .NET 실행시 JIT 컴파일러에 의해 최적화된 기계어로 변환되기 때문.. Win32 COM은 컴파일시.. Win32 COM에 맞춰 빌드 속성에서 하위버전으로 맞춰 컴파일을 다시하는 방법도 있지만 메인 프로젝트가 .NET이라면 참조되는 모든 프로젝트를 다 바꿔야할 노릇.. 또 다른 방법은 COM+를 이용하여 독립적으로 만드는 것이다. 분리시키는 방법은 아래 주소해서 확인할 수 있다. http://support.microsoft.com/kb/281335 나의 경우는 Win32 COM DLL을 64비트 .NET 프로그램에서 참조하니 COM 객체를 제대로 호출하지 못하였습니다. 그래서 .NET 프로그램의 Target Machine을 x86으로 설정하니 제대로 COM 객체를 호출하였습니다.

[Pyinstaller] 실행 파일 관리자 권한 획득하기

고객사에서 일부 사용자에게서 프로그램 오류가 발생한다며 아래와 같이 에러 캡처를 보내왔습니다. 프로그램에서 로그를 남기기 위해 로그 파일을 생성하는데 권한의 문제로 로그 파일을 생성하지 못해 프로그램 오류가 발생한 것 같습니다. 처음에는 Python 코드에서 관리자 권한을 요청하는 코드를 넣으려고 했는데, 실제로 Stackoverflow를 찾아보면 이런 내용이 나옵니다. 프로그램이 관리자 권한으로 실행되지 않았다면 관리자 권한으로 다시 프로그램을 실행시키는 코드입니다. import os import sys import win32com.shell.shell as shell ASADMIN = 'asadmin' if sys.argv[-1] != ASADMIN: script = os.path.abspath(sys.argv[0]) params = ' '.join([script] + sys.argv[1:] + [ASADMIN]) shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params) sys.exit(0) 하지만 개인적으로 이런 방식은 마음에 들지 않았고 조금 더 찾아보니 Pyinstaller로 exe 파일을 만들 때 옵션을 설정하여 관리자 권한을 요청하도록 할 수 있다고 합니다. --uac-admin을 옵션에 추가하면 프로그램 실행 시 관리자 권한을 요청할 수 있습니다. pyinstaller.exe --uac-admin sample.py 하지만 안타깝게도 이 방식은 원하는 대로 동작하지 않았습니다. 마지막으로 manifest 파일을 이용하여 시도해보았습니다. spec 파일을 이용하여 pyinstaller로 빌드하면 <실행 파일 이름>.manifest 라는 파일이 생성됩니다. 파일에서 아랫부분을 찾아볼 수 있습니다. <security> <re

초간단 프로그램 락 걸기

프로그램에 락을 걸 일이 생겨났다. 하드웨어 락을 걸면 쉬울텐데 그 정도는 아니고 프로그램의 실행 날짜를 제한 해 달라고 한다. 그래서 파일(license.lic)을 가지고 락을 걸리고 결정을 했다. 요구 사항은 아래와 같다. 1. license.lic 파일이 없으면 프로그램을 실행 할수 없게 한다. 2. 지정한 날짜를 넘어서는 프로그램을 실행 할수 없게 한다. 3. 사용자가 시스템 날짜를 되돌렸을때 인식하여 프로그램을 실행 할수 없게 한다. 음.... 1.번 문제는 사용자가 프로그램을 실행하기 위해서 license.lic 파일을 받아야만 한다. license.lic 파일에는 최근 실행 날짜/종료날짜 이런식으로 적도록 한다.(물론 내용은 암호화 한다.) 최근 실행날짜는 프로그램이 실행때마다 업데이트 하도록 하고 시스템 날짜와 비교하여 시스템 날짜가 최근 실행 날짜보다 이전의 날짜면 시스템 날짜를 되돌렸다고 인식하도록 한다.(3.번 문제 해결) 시스템 날짜와 종료 날짜를 비교하여 시스템 날짜가 종료 날짜를 넘으면 프로그램을 실행 할수 없도록 한다.(2.번 문제 해결)