/*!
 * \file $RCSfile$
 * \version $Revision: 174977 $
 * \date $Date:: 2018-05-17 18:57:35 +0300#$
 * \author $Author: ladunoff $
 *
 * \brief Вспомогательные классы для getopt 
 */

#ifndef _CMDLINE_H_INCLUDED
#define _CMDLINE_H_INCLUDED

#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <iomanip>
#include <iostream>

#ifdef UNIX
static inline
#else /* UNIX */
static __inline
#endif /* UNIX */
char *local_strdup(const char *str) {
    size_t siz = 0;
    char *copy = NULL;
    siz = (strlen(str) + 1) * sizeof(char);
    copy = (char*)malloc(siz);
    if (copy != NULL) {
	memcpy(copy, str, siz);
    }
    return copy;
}

class ICmdlineParser
{
public:
    virtual ~ICmdlineParser()
    {
    }
    virtual int parse (int argc, char **argv) = 0;
    virtual void print_help(void) = 0;
    virtual void print_version(void) = 0;

    virtual void parser_init () = 0;
    virtual void parser_free () = 0;
};

class ICommandHandler: public ICmdlineParser
{
public:
    virtual ~ICommandHandler()
    {
    }
    virtual int run() = 0;
};

class CArgvHolder
{
public:
    CArgvHolder() : m_argc(0), m_argv(0), m_bOwn(false)
    {
    }
    CArgvHolder(int ac, char** av, bool bTakeOwnerShip = false)
	:m_argc(ac), m_argv(av), m_bOwn(bTakeOwnerShip)
    {
    }
    ~CArgvHolder()
    {
	if (m_bOwn && m_argv) {
	    for (int i=0; i<m_argc; ++i)
		::free(m_argv[i]); // Можно удалять нулевой указатель
	    ::free(m_argv);
	}
    }
    void Attach(int ac, char** av, bool bTakeOwnerShip = false)
    {
	ATLASSERT(m_argc == 0 && m_argv == 0);
	ATLASSERT(ac > 0 && av != 0);
	m_argc = ac;
	m_argv = av;
	m_bOwn = bTakeOwnerShip;
    }
    void Clean()
    {
	if (m_bOwn && m_argv) {
	    for (int i=0; i<m_argc; ++i)
		::free(m_argv[i]); // Можно удалять нулевой указатель
	    ::free(m_argv);
	    m_argc = 0;
	    m_argv = 0;
	    m_bOwn = false;
	}
    }
    void Copy(int ac, const char * const * av)
    {
	ATLASSERT(m_argc == 0 && m_argv == 0);
	ATLASSERT(ac > 0 && av != 0);
	m_argc = ac;
	m_argv = (char **)(malloc (m_argc * sizeof(char *)));
	for (int i=0; i < m_argc; i++)
	    m_argv[i] = local_strdup (av[i]);
	m_bOwn = true;
    }
    void Add(int ac, const char * const * av)
    {
	ATLASSERT(m_bOwn == true);
	ATLASSERT(ac > 0 && av != 0);
	ATLASSERT(m_argc > 0 && m_argv != 0);
	m_argv = (char **)(realloc (m_argv, (m_argc + ac) * sizeof(char *)));
	for (int i=m_argc; i < m_argc + ac; i++)
	    m_argv[i] = local_strdup (av[i-m_argc]);
	m_argc += ac;
    }
    int argc()
    {
	return m_argc;
    }
    char **argv()
    {
	return m_argv;
    }
protected:
    int m_argc;
    char **m_argv;
    bool m_bOwn;
};

#ifdef _WIN32
template <class Base = ICmdlineParser,
    const char *t_program_name = NULL,
    class T_gengetopt_args_info = gengetopt_args_info,
    int (*t_parser)(int argc, char **argv,
	T_gengetopt_args_info *args_info) = cmdline_parser,
    void (*t_print_help)(void) = cmdline_parser_print_help,
    void (*t_print_version)(void) = cmdline_parser_print_version,
    void (*t_parser_init)(T_gengetopt_args_info *args_info) = cmdline_parser_init,
    void (*t_parser_free)(T_gengetopt_args_info *args_info) = cmdline_parser_free>
#else
template <class Base,
#ifdef SOLARIS
    char *t_program_name,
#else
    const char *t_program_name,
#endif 
    class T_gengetopt_args_info,
    int (*t_parser)(int argc, char **argv,
	T_gengetopt_args_info *args_info),
    void (*t_print_help)(void),
    void (*t_print_version)(void),
    void (*t_parser_init)(T_gengetopt_args_info *args_info),
    void (*t_parser_free)(T_gengetopt_args_info *args_info)>
#endif // _WIN32
class cmdline_parser_T: public Base
{
public:
    cmdline_parser_T(const char *display_name = t_program_name) : m_init(false),
	m_display_name(display_name)
    {
    }
    virtual ~cmdline_parser_T()
    {
	if (m_init)
	    parser_free ();
    }
    virtual int parse (int argc, char **argv)
    {
	CArgvHolder m_arg;
	// Чтобы сообщения об ошибках программы были без полного пути EXE файла
	// - иначе слишком длинные строки вылезают за край экрана - некрасиво
	if (m_display_name && argc > 0) {
	    m_arg.Copy (1, &m_display_name);
	    if (argc > 1)
		m_arg.Add (argc - 1, &argv[1]);
	} else
	    m_arg.Attach(argc, (char **)argv);
	m_init = true;
	return t_parser (m_arg.argc(), m_arg.argv(), &args_info);
    }

    virtual void print_help(void)
    {
	t_print_help();
    }
    virtual void print_version(void)
    {
	t_print_version();
    }

    virtual void parser_init ()
    {
	t_parser_init (&args_info);
    }
    virtual void parser_free ()
    {
	t_parser_free (&args_info);
    }
    virtual const char* program_name()
    {
	return t_program_name;
    }
    void set_display_name(const char *display_name)
    {
	m_display_name = display_name;
    }

    T_gengetopt_args_info args_info;

protected:
    bool m_init;
    const char *m_display_name;
};

#ifdef _WIN32
#define DECLARE_PARSER(T, suffix, program_name)		\
    __declspec(selectany) char cmdline_parser_##suffix##_program_name[]	\
	= program_name;							\
    typedef cmdline_parser_T<T, cmdline_parser_##suffix##_program_name,	\
	gengetopt_args_info_##suffix, cmdline_parser_##suffix,		\
	cmdline_parser_##suffix##_print_help,				\
	cmdline_parser_##suffix##_print_version,			\
	cmdline_parser_##suffix##_init, cmdline_parser_##suffix##_free	\
    > cmdline_parser_##suffix##_T
#else
#if defined __GNUC__
#   define ATTRIBUTE_WEAK __attribute__((weak))
#else
#ifdef SOLARIS
#pragma weak "cmdline_parser_extractcerts_program_name"
#pragma weak "cmdline_parser_makereq_program_name"
#pragma weak "cmdline_parser_reqinfo_program_name"
#pragma weak "cmdline_parser_license_program_name"
#pragma weak "cmdline_parser_stampinfo_program_name"
#pragma weak "cmdline_parser_sendreq_program_name"
#pragma weak "cmdline_parser_makestamp_program_name"
#pragma weak "cmdline_parser_respinfo_program_name"
#pragma weak "cmdline_parser_makeresp_program_name"
#endif //SOLARIS
#   define ATTRIBUTE_WEAK
#endif	/* __GNUC__ */
#define DECLARE_PARSER(T, suffix, program_name)		                \
    char cmdline_parser_##suffix##_program_name[] ATTRIBUTE_WEAK	\
	= program_name;							\
    typedef cmdline_parser_T<T, cmdline_parser_##suffix##_program_name,	\
	gengetopt_args_info_##suffix, cmdline_parser_##suffix,		\
	cmdline_parser_##suffix##_print_help,				\
	cmdline_parser_##suffix##_print_version,			\
	cmdline_parser_##suffix##_init, cmdline_parser_##suffix##_free	\
    > cmdline_parser_##suffix##_T
#endif // _WIN32

#define DECLARE_CMDLINEPARSER(suffix, program_name)			\
    DECLARE_PARSER(ICmdlineParser, suffix, program_name)
#define DECLARE_COMMANDHANLER(suffix, program_name)			\
    DECLARE_PARSER(ICommandHandler, suffix, program_name);		\
    class CommandHandler_##suffix:					\
	public cmdline_parser_##suffix##_T				\
    {									\
    public:								\
	typedef cmdline_parser_##suffix##_T ParserBase;			\
	CommandHandler_##suffix(const char *display_name		\
	    = cmdline_parser_##suffix##_program_name )			\
	    : ParserBase(display_name)					\
	{								\
	}								\
	virtual int parse (int argc, char **argv);		\
	virtual int run();						\
    }

#ifdef _WIN32
class CTimeMetering
{
public:
    ULARGE_INTEGER kernelTime;
    ULARGE_INTEGER userTime;
    clock_t clockTime;

    CTimeMetering() : clockTime(0)
    {
	kernelTime.QuadPart = 0;
	userTime.QuadPart = 0;
    }

    void start()
    {
	FILETIME creationTime;
	FILETIME exitTime;
	clockTime = clock();
	GetProcessTimes (GetCurrentProcess(), &creationTime, &exitTime,
	    (LPFILETIME)&kernelTime, (LPFILETIME)&userTime);
    }

    void finish()
    {
	FILETIME creationTime;
	FILETIME exitTime;
	ULARGE_INTEGER curKernelTime, curUserTime;
	clock_t curClockTime = clock();
	GetProcessTimes (GetCurrentProcess(), &creationTime, &exitTime,
	    (LPFILETIME)&curKernelTime, (LPFILETIME)&curUserTime);
	kernelTime.QuadPart = curKernelTime.QuadPart - kernelTime.QuadPart;
	userTime.QuadPart = curUserTime.QuadPart - userTime.QuadPart;
	clockTime = curClockTime - clockTime;
    }

    void report_total()
    {
	double total_sys = (__int64)kernelTime.QuadPart / 10000000.0;
	double total_usr = (__int64)userTime.QuadPart / 10000000.0;
	double total_utc = clockTime/(double)CLOCKS_PER_SEC;
	std::cerr << std::setprecision (3) << std::left << "Total:  "
	    << " SYS: " << std::setw(6) << total_sys
	    << " USR: " << std::setw(6) << total_usr
	    << " UTC: " << std::setw(6) << total_utc << std::endl;
    }

    void report_average(int count)
    {
	double average_sys = (__int64)kernelTime.QuadPart / (10000000.0 * count);
	double average_usr = (__int64)userTime.QuadPart / (10000000.0 * count);
	double average_utc = clockTime/((double)CLOCKS_PER_SEC * count);
	std::cerr << std::setprecision (3) << std::left << "Average:"
	    << " SYS: " << std::setw(6) << average_sys
	    << " USR: " << std::setw(6) << average_usr
	    << " UTC: " << std::setw(6) << average_utc << std::endl;
    }
};
#endif /*_WIN32*/

template <class TRequest,
    int HOST_AUTH_ANONYMOUS_IDX		= 0,
    int HOST_AUTH_BASIC_IDX		= 1,
    int HOST_AUTH_NTLM_IDX		= 2,
    int HOST_AUTH_DIGEST_IDX		= 3,
    int HOST_AUTH_NEGOTIATE_IDX		= 4,

    int PROXY_AUTH_ANONYMOUS_IDX	= 0,
    int PROXY_AUTH_BASIC_IDX		= 1,
    int PROXY_AUTH_NTLM_IDX		= 2,
    int PROXY_AUTH_DIGEST_IDX		= 3,
    int PROXY_AUTH_NEGOTIATE_IDX	= 4
>
class CCmdlineAuthTypeArgumentSupport
{
    const char **m_auth_values;	/* Possible values for auth.  */
    const char **m_auth_proxy_values;	/* Possible values for auth-proxy.  */
public:
    CCmdlineAuthTypeArgumentSupport(
	const char **auth_values, const char **auth_proxy_values):
	m_auth_values(auth_values), m_auth_proxy_values(auth_proxy_values)
    {
	ATLASSERT(auth_values != 0);
	ATLASSERT(auth_proxy_values != 0);

	ATLASSERT(m_auth_values[HOST_AUTH_ANONYMOUS_IDX] != 0);
	ATLASSERT(m_auth_values[HOST_AUTH_BASIC_IDX] != 0);
	ATLASSERT(m_auth_values[HOST_AUTH_NTLM_IDX] != 0);
	ATLASSERT(m_auth_values[HOST_AUTH_DIGEST_IDX] != 0);
	ATLASSERT(m_auth_values[HOST_AUTH_NEGOTIATE_IDX] != 0);

	ATLASSERT(m_auth_proxy_values[PROXY_AUTH_ANONYMOUS_IDX] != 0);
	ATLASSERT(m_auth_proxy_values[PROXY_AUTH_BASIC_IDX] != 0);
	ATLASSERT(m_auth_proxy_values[PROXY_AUTH_NTLM_IDX] != 0);
	ATLASSERT(m_auth_proxy_values[PROXY_AUTH_DIGEST_IDX] != 0);
	ATLASSERT(m_auth_proxy_values[PROXY_AUTH_NEGOTIATE_IDX] != 0);
    }
    typename TRequest::enumAuthTypes
	convert_host_auth_type (const char *auth_arg)
    {
	if (!auth_arg)
	    AtlThrow(E_INVALIDARG);
	size_t len = strlen (auth_arg);
	if (0 == strncmp(auth_arg, m_auth_values[HOST_AUTH_ANONYMOUS_IDX], len))
	    return TRequest::atAnonymous;
	if (0 == strncmp(auth_arg, m_auth_values[HOST_AUTH_BASIC_IDX], len))
	    return TRequest::atBasic;
	if (0 == strncmp(auth_arg, m_auth_values[HOST_AUTH_NTLM_IDX], len))
	    return TRequest::atNTLM;
	if (0 == strncmp(auth_arg, m_auth_values[HOST_AUTH_DIGEST_IDX], len))
	    return TRequest::atDigest;
	if (0 == strncmp(auth_arg, m_auth_values[HOST_AUTH_NEGOTIATE_IDX], len))
	    return TRequest::atNegotiate;
	AtlThrow(E_INVALIDARG);
#ifndef _WIN32
	return TRequest::atNegotiate; //make compiler happy
#endif
    }

    typename TRequest::enumAuthTypes
	convert_proxy_auth_type (const char *auth_arg)
    {
	if (!auth_arg)
	    AtlThrow(E_INVALIDARG);
	size_t len = strlen (auth_arg);
	if (0 == strncmp(auth_arg,
	    m_auth_proxy_values[PROXY_AUTH_ANONYMOUS_IDX], len))
	    return TRequest::atAnonymous;
	if (0 == strncmp(auth_arg,
	    m_auth_proxy_values[PROXY_AUTH_BASIC_IDX], len))
	    return TRequest::atBasic;
	if (0 == strncmp(auth_arg,
	    m_auth_proxy_values[PROXY_AUTH_NTLM_IDX], len))
	    return TRequest::atNTLM;
	if (0 == strncmp(auth_arg,
	    m_auth_proxy_values[PROXY_AUTH_DIGEST_IDX], len))
	    return TRequest::atDigest;
	if (0 == strncmp(auth_arg,
	    m_auth_proxy_values[PROXY_AUTH_NEGOTIATE_IDX], len))
	    return TRequest::atNegotiate;
	AtlThrow(E_INVALIDARG);
#ifndef _WIN32
	    return TRequest::atNegotiate; //make compiler happy
#endif
    }
};

template <class TRequest,
    int INC_NONE_IDX = 0,
    int INC_ONLY_CERT_IDX = 1,
    int INC_CHAIN_EXCEPT_ROOT_IDX = 2,
    int INC_WHOLE_CHAIN_IDX = 3
>
class CCmdlineCertToIncludeArgumentSupport
{
    const char **m_add_cert_values;	/* Possible values for add-cert.  */
public:
    CCmdlineCertToIncludeArgumentSupport(const char **add_cert_values) :
	m_add_cert_values(add_cert_values)
    {
	ATLASSERT(add_cert_values != 0);
	ATLASSERT(m_add_cert_values[INC_NONE_IDX] != 0);
	ATLASSERT(m_add_cert_values[INC_ONLY_CERT_IDX] != 0);
	ATLASSERT(m_add_cert_values[INC_CHAIN_EXCEPT_ROOT_IDX] != 0);
	ATLASSERT(m_add_cert_values[INC_WHOLE_CHAIN_IDX] != 0);
    }
    typename TRequest::enumCertToInclude
	convert_add_cert_arg (const char *add_cert_arg)
    {
	if (!add_cert_arg)
	    AtlThrow(E_INVALIDARG);
	size_t len = strlen (add_cert_arg);
	if (0 == strncmp(add_cert_arg,
	    m_add_cert_values[INC_NONE_IDX], len))
	    return TRequest::incNone;
	if (0 == strncmp(add_cert_arg,
	    m_add_cert_values[INC_ONLY_CERT_IDX], len))
	    return TRequest::incCertOnly;
	if (0 == strncmp(add_cert_arg,
	    m_add_cert_values[INC_CHAIN_EXCEPT_ROOT_IDX], len))
	    return TRequest::incChainExceptRoot;
	if (0 == strncmp(add_cert_arg,
	    m_add_cert_values[INC_WHOLE_CHAIN_IDX], len))
	    return TRequest::incChainWhole;
	AtlThrow(E_INVALIDARG);
#ifndef _WIN32
	return TRequest::incChainWhole; //to make compile happy about reaching end of non void func
#endif
    }
};

template <
    int NONCE_FLAG_NO_IDX = 0,
    int NONCE_FLAG_YES_IDX = 1
>
class CCmdlineNonceArgumentSupport
{
    const char **m_nonce_values;	/* Possible values for nonce.  */
public:
    CCmdlineNonceArgumentSupport(const char **nonce_values)
	: m_nonce_values(nonce_values)
    {
	ATLASSERT(nonce_values != 0);
	ATLASSERT(m_nonce_values[NONCE_FLAG_NO_IDX] != 0);
	ATLASSERT(m_nonce_values[NONCE_FLAG_YES_IDX] != 0);
    }
    bool convert_nonce_arg (const char *nonce_arg)
    {
	if (!nonce_arg)
	    AtlThrow(E_INVALIDARG);
	size_t len = strlen (nonce_arg);
	if (0 == strncmp(nonce_arg,
	    m_nonce_values[NONCE_FLAG_NO_IDX], len))
	    return false;
	if (0 == strncmp(nonce_arg,
	    m_nonce_values[NONCE_FLAG_YES_IDX], len))
	    return true;
	AtlThrow(E_INVALIDARG);
#if defined UNIX
	return false; /* XXX dim: опять проблемы с exception на UNIX */
#endif	/* UNIX */
    }
};

template <
    int ACCEPTABLE_RESPONCE_FLAG_NO_IDX = 0,
    int ACCEPTABLE_RESPONCE_FLAG_YES_IDX = 1
>
class CCmdlineAcceptableResponceArgumentSupport
{
    const char **m_acceptable_responce_values;	/* Possible values for acceptable-responce.  */
public:
    CCmdlineAcceptableResponceArgumentSupport(const char **acceptable_responce_values)
	: m_acceptable_responce_values(acceptable_responce_values)
    {
	ATLASSERT(acceptable_responce_values != 0);
	ATLASSERT(m_acceptable_responce_values[ACCEPTABLE_RESPONCE_FLAG_NO_IDX] != 0);
	ATLASSERT(m_acceptable_responce_values[ACCEPTABLE_RESPONCE_FLAG_YES_IDX] != 0);
    }
    bool convert_acceptable_responce_arg (const char *acceptable_responce_arg)
    {
	if (!acceptable_responce_arg)
	    AtlThrow(E_INVALIDARG);
	size_t len = strlen (acceptable_responce_arg);
	if (0 == strncmp(acceptable_responce_arg,
	    m_acceptable_responce_values[ACCEPTABLE_RESPONCE_FLAG_NO_IDX], len))
	    return false;
	if (0 == strncmp(acceptable_responce_arg,
	    m_acceptable_responce_values[ACCEPTABLE_RESPONCE_FLAG_YES_IDX], len))
	    return true;
	AtlThrow(E_INVALIDARG);
#ifndef _WIN32
	return false; //to make compile happy about reaching end of non void func
#endif
    }
};

#ifdef _WIN32
inline HRESULT GetMaskedText(CComBSTR & outText){

    HRESULT hr = S_OK;
    SecureZeroMemory(outText.m_str, sizeof(wchar_t)*outText.Length());

    if (0 == outText.Length()) 
	return E_INVALIDARG;

    int maxpinlen = outText.Length() - 1;
    int pinlen = 0;
    wchar_t ch = _getwch();

    // Считываем символы до тех пор, пока не введут:
    // Enter = 13
    // Ctrl+C = 3
    // Ctrl+D = 4
    // Ctrl+Z = 26
    while( (ch != 13) && (ch != 3) && (ch != 4) && (ch != 26)) 
    {	
	//Даже если введут Backspace, он тоже отобразится как символ
	//putwchar(L'*');

	outText.m_str[pinlen] = ch;    

	ch = _getwch();

	// Будем следить за ним, а не хочет ли он нам буфер переполнить
	pinlen++;
	if (pinlen >= maxpinlen)    {
	    std::cerr << std::endl << "PIN-code has a too long length";
	    ch = 3; // Эмулируем нажатие Ctrl-C
	}
    }
    outText.m_str[pinlen] = L'\0';
    std::wcout << std::endl;

    // Обычно Ctrl-C(Z) нажимают, когда хотят отменить команду.
    // Ctrl-D - вроде означает окончание ввода.
    if (ch == 3 || ch == 26) {
	// Далее, чтобы не заботиться о том, что в разных местах 
	// придется чистить за собой память, просто будем считать,
	// что введен пустой пин-код.

	SecureZeroMemory(outText.m_str, sizeof(wchar_t)*outText.Length());
	pinlen = 0;
	hr = E_FAIL;
    }
    // Пин-код введен, изменим длину, на требуемую
    if (pinlen > 0) {
	outText.m_str[-2] = OLECHAR(pinlen*2);
    }

    return hr;
}

template <class T>
class CCmdlineStartService
{
public:
    CCmdlineStartService():
      m_pin(256, L""), m_bIsPinSet(false)
    {
	CoInitialize(NULL);
    }

    HRESULT start(char * url_arg, int ask_pin){
	HRESULT hr = S_OK;
	USES_CONVERSION;    

	T tControlSvc;
	ServiceState::ServiceStateEnum srvState = ServiceState::Unbound;

	// Ни в коем случае не уменьшать число итераций или паузу между ними!!!
	// Если данный код пускать из Планировщика Заданий во время старта ОС,
	// IIS может запросто не успеть загрузиться и отработать запуск
	// службы, что неоднократно наблюдалось.
	for (int i = 0; i < 60; ++i) {
	    // Установим соединение со службой.
	    hr = tControlSvc.Init(A2W(url_arg));
	    if (SUCCEEDED(hr)) {
		// Делаем попытку получить статус.
		hr = tControlSvc.QueryServiceStatus(srvState);
		if (SUCCEEDED(hr))
		    break;
	    }
	    ::Sleep(5000);
	}
	if (FAILED(hr))
	    return hr;

	// Разбор статуса службы
	switch(srvState) {
	    case ServiceState::Unbound:
	    case ServiceState::StopPending:
	    case ServiceState::ContinuePending:
	    case ServiceState::PausePending:
		return AtlHresultFromWin32(ERROR_INVALID_STATE);
	    case ServiceState::StartPending:
	    case ServiceState::Running:
		break;
	    case ServiceState::Paused:
		hr = tControlSvc.ControlService(ServiceControl::Continue);
		break;
	    case ServiceState::Stopped:
		hr = S_OK;
		if (ask_pin) {
		    std::wcout << L"Input PIN-code:";
		    hr = GetMaskedText(m_pin);
		    if SUCCEEDED(hr) {
			hr = tControlSvc.StartService(m_pin);
		    }
                }
                else {
                    // Ни в коем случае не уменьшать число итераций или паузу между ними!!!
                    // Если данный код пускать из Планировщика Заданий во время старта ОС,
                    // служба смарт-карт или служба хранения ключей КриптоПро CSP может
                    // запросто не успеть загрузиться, что неоднократно наблюдалось.
                    for (int i = 0; i < 60; ++i) {
                        if (m_bIsPinSet)
                            hr = tControlSvc.StartService(m_pin);
                        else
                            hr = tControlSvc.StartService(NULL);
                        if (SUCCEEDED(hr)) {
                            break;
                        }
                        ::Sleep(5000);
                    }
                }
		break;
	    default:
		return AtlHresultFromWin32(ERROR_INVALID_STATE);
	}
	if (SUCCEEDED(hr)) 
	    hr = S_OK;

	return hr;
    }
    
    void set_pin (const char * const pin_arg) {
	m_pin = pin_arg;
	m_bIsPinSet = true;
    }

    virtual ~CCmdlineStartService(){
	// Длина m_pin для очистки таким образом указана верно,
	// так как ровно столько символов мы и ввели в качестве pin-кода.
	SecureZeroMemory(m_pin, sizeof(wchar_t)*m_pin.Length());
	CoUninitialize();
    }
private:
    CComBSTR m_pin;
    bool m_bIsPinSet;
};
#endif // _WIN32


#endif // _CMDLINE_H_INCLUDED
