#ifndef _ATLCRYPT2_INL_INCLUDED
#define _ATLCRYPT2_INL_INCLUDED
 
#pragma warning(push)
#include <atlconv.h>
#pragma warning(pop)

#include <boost/make_shared.hpp>
#include <vector>

namespace ATL2 {

// В файле atldef.h на Windows CE нет макроса ATLENSURE_RETURN_VAL,
// зато есть макрос ATLENSURE_RETURN_HR
#if !defined(ATLENSURE_RETURN_VAL) && defined(ATLENSURE_RETURN_HR)
#define ATLENSURE_RETURN_VAL(expr, val) ATLENSURE_RETURN_HR(expr, val)
#endif

#ifndef SecureZeroMemory
#define SecureZeroMemory(ptr,cnt) do { \
    volatile char *vptr = (volatile char *)(ptr); \
    SIZE_T tmpcnt = (cnt); \
    while (tmpcnt) { *vptr = 0; vptr++; tmpcnt--; } } while(0)
#endif // _WIN32

template<class String>
inline void SecureZeroString(String& Dest)
{
    DWORD dwLen = Dest.GetLength();
    if (dwLen > 0)
    {
	SecureZeroMemory(Dest.GetBuffer(), Dest.GetLength() *
            sizeof(typename String::XCHAR));
    }
}

#ifdef _WIN32
__declspec(selectany) ATL::CCryptProv EmptyProv;
#elif defined(SOLARIS)
__declspec(selectany) ATL::CCryptProv EmptyProv = ATL::CCryptProv();
  #pragma weak "__1cEATL2JEmptyProv_"
#else
__declspec(selectany) ATL::CCryptProv EmptyProv __attribute__ ((weak)) = ATL::CCryptProv();
#endif /* _WIN32, SOLARIS */

inline char byte_to_hex0 (int byteValue)
{
    int b = (byteValue & 0xF0) >> 4;
    return (char)((b <= 9) ? b + '0' : (b - 10) + 'A');
}

inline char byte_to_hex1 (int byteValue)
{
    int b = byteValue & 0x0F;
    return (char)((b <= 9) ? b + '0' : (b - 10) + 'A');
}

inline void
asn1_integer_to_string (ATL::CAtlString& serial,
    CRYPT_INTEGER_BLOB *blob)
{
    BYTE *pb;
    DWORD i;
    
    if (!blob || !blob->pbData || !blob->cbData)
	return;
    serial.Empty ();
    pb = (BYTE*) blob->pbData + blob->cbData - 1;
    for (i = 0; i<blob->cbData; i++) {
	serial += byte_to_hex0(*pb);
	serial += byte_to_hex1(*pb);
	if (i%2 == 1 && i != blob->cbData - 1)
	    serial += " ";
	--pb;
    }
}
#ifdef _WIN32
inline void
systemtime_to_string(ATL::CAtlString& string, SYSTEMTIME systemtime, LCID locale = LOCALE_USER_DEFAULT )
{
    SYSTEMTIME st = systemtime;
    TCHAR szLocalDate[255], szLocalTime[255];
    ZeroMemory(szLocalDate, sizeof(szLocalDate));
    ZeroMemory(szLocalTime, sizeof(szLocalTime));
    ::GetDateFormat( locale, DATE_LONGDATE, &st, NULL,
	szLocalDate, 255);
    ::GetTimeFormat( locale, 0, &st, NULL,
	szLocalTime, 255);
    string.Empty();
    string = szLocalDate;
    string += TEXT(" ");
    string += szLocalTime;
}

inline void
filetime_to_string (ATL::CAtlString& string, FILETIME filetime, LCID locale = LOCALE_USER_DEFAULT )
{
    FILETIME ft = filetime;
    SYSTEMTIME st;
    ::FileTimeToLocalFileTime (&ft, &ft);
    ::FileTimeToSystemTime (&ft, &st);
    systemtime_to_string(string, st, locale);
}
#endif //_WIN32

inline CCertContext::CCertContext() throw()
    : m_pUserCert(NULL)
{
}

inline CCertContext::CCertContext (const CCertContext& ctx) throw()
	: m_pUserCert(NULL)
{
	// При такой проверке мы избавляемся от ассерта в Duplicate
	// Мы вполне можем создавать копии неинициализированных объектов
	if (ctx.m_pUserCert)
	{
		m_pUserCert = ctx.Duplicate();
	}
}

inline CCertContext &CCertContext::operator=(const CCertContext& ctx) throw()
{
    if (this != &ctx)
    {
        Destroy();
        // При такой проверке мы избавляемся от ассерта в Duplicate
        // Мы вполне можем создавать копии неинициализированных объектов
        if (ctx.m_pUserCert)
        {
            m_pUserCert = ctx.Duplicate();
        }
    }
    return *this;
}

inline CCertContext::CCertContext (
    PCCERT_CONTEXT pUserCert, BOOL bTakeOwnership /* = FALSE */) throw()
{
    if (bTakeOwnership)
	m_pUserCert = pUserCert;
    else
	m_pUserCert = ::CertDuplicateCertificateContext(pUserCert);
}

inline CCertContext::~CCertContext() throw()
{
    Destroy ();
}

inline void CCertContext::Attach(
    PCCERT_CONTEXT pUserCert, BOOL bTakeOwnership /* = FALSE */) throw()
{
    ATLASSERT(m_pUserCert == NULL);
    if (m_pUserCert != NULL) return;

    if (bTakeOwnership)
	m_pUserCert = pUserCert;
    else
	m_pUserCert = ::CertDuplicateCertificateContext(pUserCert);
}

inline void CCertContext::Destroy() throw()
{
    if (m_pUserCert == NULL) return;
    BOOL bSuccess = ::CertFreeCertificateContext (m_pUserCert);
    ATLVERIFY(bSuccess);
    m_pUserCert = NULL;
}

inline PCCERT_CONTEXT CCertContext::Detach() throw()
{
    PCCERT_CONTEXT pUserCert;
    pUserCert = m_pUserCert;
    m_pUserCert = NULL;
    return pUserCert;
}

inline PCCERT_CONTEXT CCertContext::Duplicate() const throw()
{
    ATLENSURE_RETURN_VAL(m_pUserCert != NULL, NULL);
    PCCERT_CONTEXT pUserCert = ::CertDuplicateCertificateContext(m_pUserCert);
    return pUserCert;
}

inline HRESULT CCertContext::Uninitialize() throw()
{
    ATLENSURE_RETURN(m_pUserCert != NULL);
    if (!::CertFreeCertificateContext (m_pUserCert))
	return ATL::AtlHresultFromLastError();
    m_pUserCert = NULL;
    return S_OK;
}

inline HRESULT CCertContext::Initialize (
    const BYTE * pbCertEncoded, DWORD cbCertEncoded, DWORD dwCertEncodingType
	/* = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING */) throw()
{
    ATLENSURE_RETURN(m_pUserCert == NULL);
    m_pUserCert = ::CertCreateCertificateContext (
	dwCertEncodingType, pbCertEncoded, cbCertEncoded);
    if (!m_pUserCert)
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCertContext::Initialize(__in const CStringBlob& certEncoded,
    DWORD dwCertEncodingType /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) throw()
{
    return Initialize(reinterpret_cast<const BYTE*>(certEncoded.GetString()),
        static_cast<DWORD>(certEncoded.GetLength()), dwCertEncodingType);
}

inline HRESULT CCertContext::DetachFromStore() throw()
{
    ATLENSURE_RETURN(m_pUserCert != NULL);
    PCCERT_CONTEXT pDetachedCert = ::CertCreateCertificateContext(
	X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, m_pUserCert->pbCertEncoded,
	m_pUserCert->cbCertEncoded);
    if (!pDetachedCert)
    {
	return ATL::AtlHresultFromLastError();
    }
    CCertContext detachedCert(pDetachedCert, TRUE);

    //Копируем свойства сертификата
    DWORD dwPropId = 0;
    for (;;)
    {
	dwPropId = ::CertEnumCertificateContextProperties(m_pUserCert,
	    dwPropId);
	if (!dwPropId)
	{
	    //Кончились свойства
	    break;
	}
	if (IS_CERT_HASH_PROP_ID(dwPropId)
	    || IS_PUBKEY_HASH_PROP_ID(dwPropId)
	    || dwPropId == CERT_KEY_IDENTIFIER_PROP_ID
	    || dwPropId == CERT_DATE_STAMP_PROP_ID
	    //Не на всех платформах есть все свойства
#	    if defined CERT_AIA_URL_RETRIEVED_PROP_ID
	    || dwPropId == CERT_AIA_URL_RETRIEVED_PROP_ID
#	    endif
#	    if defined CERT_AUTHORITY_INFO_ACCESS_PROP_ID
	    || dwPropId == CERT_AUTHORITY_INFO_ACCESS_PROP_ID
#	    endif
#	    if defined CERT_AUTO_ENROLL_RETRY_PROP_ID
	    || dwPropId == CERT_AUTO_ENROLL_RETRY_PROP_ID
#	    endif
#	    if defined CERT_BACKED_UP_PROP_ID
	    || dwPropId == CERT_BACKED_UP_PROP_ID
#	    endif
	    || dwPropId == CERT_EFS_PROP_ID
	    || dwPropId == CERT_FORTEZZA_DATA_PROP_ID
	    || dwPropId == CERT_IE30_RESERVED_PROP_ID
#	    if defined CERT_NEW_KEY_PROP_ID
	    || dwPropId == CERT_NEW_KEY_PROP_ID
#	    endif
#	    if defined CERT_NO_AUTO_EXPIRE_CHECK_PROP_ID
	    || dwPropId == CERT_NO_AUTO_EXPIRE_CHECK_PROP_ID
#	    endif
#	    if defined CERT_OCSP_CACHE_PREFIX_PROP_ID
	    || dwPropId == CERT_OCSP_CACHE_PREFIX_PROP_ID
#	    endif
	    || dwPropId == CERT_PUBKEY_HASH_RESERVED_PROP_ID
#	    if defined CERT_ROOT_PROGRAM_NAME_CONSTRAINTS_PROP_ID
	    || dwPropId == CERT_ROOT_PROGRAM_NAME_CONSTRAINTS_PROP_ID
#	    endif
#	    if defined CERT_SOURCE_LOCATION_PROP_ID
	    || dwPropId == CERT_SOURCE_LOCATION_PROP_ID
#	    endif
#	    if defined CERT_SOURCE_URL_PROP_ID
	    || dwPropId == CERT_SOURCE_URL_PROP_ID
#	    endif
#	    if defined CERT_SUBJECT_DISABLE_CRL_PROP_ID
	    || dwPropId == CERT_SUBJECT_DISABLE_CRL_PROP_ID
#	    endif
#	    if defined CERT_SUBJECT_OCSP_AUTHORITY_INFO_ACCESS_PROP_ID
	    || dwPropId == CERT_SUBJECT_OCSP_AUTHORITY_INFO_ACCESS_PROP_ID
#	    endif
	    )
	{
	    //Некопируемое свойство
	    continue;
	}
	CStringBlob propValue;
	ATL_HR_ERRORCHECK_RETURN(GetProperty(dwPropId, propValue));
	if (dwPropId != CERT_KEY_PROV_INFO_PROP_ID)
	{
	    //Все свойства, кроме CERT_KEY_PROV_INFO_PROP_ID, копируются путём
	    //упаковки в CRYPT_DATA_BLOB.
	    //Есть ещё несколько подозрительных свойств, задающих дескриптор
	    //провайдера и т.п., но скорее всего если их явно не задавать, то
	    //при перечислении они не обнаружатся, а кроме того они не
	    //сохраняются в долговременном хранилище.
	    CRYPT_DATA_BLOB propBlob = {(DWORD)propValue.GetLength(),
		reinterpret_cast<BYTE*>(propValue.GetBuffer())};
	    ATL_HR_ERRORCHECK_RETURN(
		detachedCert.SetProperty(dwPropId, 0, &propBlob));
	}
	else
	{
	    ATL_HR_ERRORCHECK_RETURN(
		detachedCert.SetProperty(dwPropId, 0, propValue));
	}
    }
	
    operator=(detachedCert);
    return S_OK;
}

inline HRESULT CCertContext::GetProperty(
    __in DWORD dwPropId,
    __out_bcount_part_opt(*pcbData, *pcbData) void *pvData,
    __inout DWORD *pcbData) throw()
{
    ATLENSURE_RETURN(m_pUserCert != NULL);
    if (!::CertGetCertificateContextProperty (
	m_pUserCert, dwPropId, pvData, pcbData))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCertContext::GetProperty(
    __in DWORD dwPropId, __out CStringBlob &Result) //throw(...)
{
    DWORD cbData = 0;
    HRESULT hr = GetProperty(dwPropId, NULL, &cbData);
    if (FAILED(hr))
	return hr;
    hr = GetProperty(dwPropId, Result.GetBuffer(cbData), &cbData);
    Result.ReleaseBufferSetLength (cbData);
    return hr;
}

inline HRESULT CCertContext::SetProperty (
    __in DWORD dwPropId, __in DWORD dwFlags,
    __in const void *pvData) throw()
{
    ATLENSURE_RETURN(m_pUserCert != NULL);
    if (!::CertSetCertificateContextProperty (
	m_pUserCert, dwPropId, dwFlags, pvData))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCertContext::GetKeyIdentifier (
    __out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
    __inout DWORD *pcbData) throw()
{
    return GetProperty (CERT_KEY_IDENTIFIER_PROP_ID, pbData, pcbData);
}

inline HRESULT CCertContext::GetKeyIdentifier (__out CStringBlob &Result) //throw(...)
{
    DWORD cbData = 32;
    HRESULT hr = GetKeyIdentifier(
	reinterpret_cast<BYTE *>(Result.GetBuffer (cbData)), &cbData);
    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) {
	// В случае ERROR_MORE_DATA, в cbInfo вернули, скока надо
	hr = GetKeyIdentifier (
	    reinterpret_cast<BYTE *>(Result.GetBuffer (cbData)),
	    &cbData);
    }
    if (FAILED(hr)) {
	return hr;
    }
    Result.ReleaseBufferSetLength (cbData);
    return hr;
}

inline HRESULT CCertContext::SetKeyIdentifier (
    __in const CRYPT_DATA_BLOB *keyId) throw()
{
    return SetProperty (CERT_KEY_IDENTIFIER_PROP_ID, 0, keyId);
}

inline HRESULT CCertContext::GetKeyProvInfo (
    __out_bcount_part(*pcbData, *pcbData) CRYPT_KEY_PROV_INFO *provInfo,
    __inout DWORD *pcbData) throw()
{
    return GetProperty (CERT_KEY_PROV_INFO_PROP_ID, provInfo, pcbData);
}

inline HRESULT CCertContext::GetKeyProvInfo (__out CStringBlob &Result) //throw(...)
{
    DWORD cbData = 512;
    HRESULT hr = GetKeyProvInfo(
	reinterpret_cast<CRYPT_KEY_PROV_INFO *>(Result.GetBuffer (cbData)),
	&cbData);
    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) {
	// В случае ERROR_MORE_DATA, в cbInfo вернули, скока надо
	hr = GetKeyProvInfo (
	    reinterpret_cast<CRYPT_KEY_PROV_INFO *>(Result.GetBuffer (cbData)),
	    &cbData);
    }
    if (FAILED(hr)) {
	return hr;
    }
    Result.ReleaseBufferSetLength (cbData);
    return hr;
}

inline HRESULT CCertContext::SetKeyProvInfo (
    __in const CRYPT_KEY_PROV_INFO *provInfo) throw()
{
    return SetProperty (CERT_KEY_PROV_INFO_PROP_ID, 0, provInfo);
}

inline HRESULT CCertContext::GetPublicKeyInfo(CERT_PUBLIC_KEY_INFO * keyInfo)
{
    if (m_pUserCert && m_pUserCert->pCertInfo){
	*keyInfo  = m_pUserCert->pCertInfo->SubjectPublicKeyInfo;
	return S_OK;
    }
    return S_FALSE;
}

inline HRESULT CCertContext::GetNameString (
    DWORD dwType, DWORD dwFlags, void *pvTypePara, LPTSTR pszNameString,
    DWORD *pdwNameStringLen) throw()
{
    ATLENSURE_RETURN(m_pUserCert != NULL);
    ATLENSURE_RETURN(pdwNameStringLen != NULL);

    *pdwNameStringLen = ::CertGetNameString (m_pUserCert,
	dwType, dwFlags, pvTypePara, pszNameString, *pdwNameStringLen);
    if (*pdwNameStringLen == 0)
	return ATL::AtlHresultFromLastError();
    // Длина = 1, если поле отсутствует. Возвращаем пустую строку.
    // http://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx
    //   if (*pdwNameStringLen == 1)
	//return E_INVALIDARG;
    return S_OK;
}

inline HRESULT CCertContext::GetSubjectNameString (
    DWORD dwType, void *pvTypePara, LPTSTR pszNameString,
    DWORD *pdwNameStringLen) throw()
{
    return GetNameString (dwType, 0, pvTypePara, pszNameString,
	pdwNameStringLen);
}

inline HRESULT CCertContext::GetIssuerNameString (
    DWORD dwType, void *pvTypePara, LPTSTR pszNameString,
    DWORD *pdwNameStringLen) throw()
{
    return GetNameString (dwType, CERT_NAME_ISSUER_FLAG, pvTypePara,
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetSubjectNameStringEmail (
    LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetSubjectNameString(CERT_NAME_EMAIL_TYPE, NULL,
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetSubjectNameStringToStr (
    DWORD dwStrType, LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetSubjectNameString(CERT_NAME_RDN_TYPE, &dwStrType,
	pszNameString, pdwNameStringLen);
}

// TODO: проверить, szAttrOid должен быть быть LPCSTR или LPCTSTR
// TODO: что означает проверить, какой тип ожидает на вход для pvTypePara
// TODO: UNICODE-версия функции ::CertGetNameStringW для 
// TODO: кода CERT_NAME_ATTR_TYPE
inline HRESULT CCertContext::GetSubjectNameStringAttr (
    LPCSTR szAttrOid, LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetSubjectNameString(CERT_NAME_ATTR_TYPE,
	const_cast<LPSTR>(szAttrOid),
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetSubjectNameStringSimple (
    LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetSubjectNameString(CERT_NAME_SIMPLE_DISPLAY_TYPE, NULL,
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetSubjectNameStringFriendlyDisplay (
    LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetSubjectNameString(CERT_NAME_FRIENDLY_DISPLAY_TYPE, NULL,
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetSubjectNameStringDns (
    LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetSubjectNameString(CERT_NAME_DNS_TYPE, NULL,
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetSubjectNameStringUrl (
    LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetSubjectNameString(CERT_NAME_URL_TYPE, NULL,
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetSubjectNameStringUpn (
    LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetSubjectNameString(CERT_NAME_UPN_TYPE, NULL,
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetIssuerNameStringEmail (
    LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetIssuerNameString(CERT_NAME_EMAIL_TYPE, NULL,
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetIssuerNameStringToStr (
    DWORD dwStrType, LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetIssuerNameString(CERT_NAME_RDN_TYPE, &dwStrType,
	pszNameString, pdwNameStringLen);
}

// TODO: проверить, szAttrOid должен быть быть LPCSTR или LPCTSTR
// TODO: что означает проверить, какой тип ожидает на вход для pvTypePara
// TODO: UNICODE-версия функции ::CertGetNameStringW для
// TODO: кода CERT_NAME_ATTR_TYPE
inline HRESULT CCertContext::GetIssuerNameStringAttr (
    LPCSTR szAttrOid, LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetIssuerNameString(CERT_NAME_ATTR_TYPE,
	const_cast<LPSTR>(szAttrOid),
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetIssuerNameStringSimple (
    LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetIssuerNameString(CERT_NAME_SIMPLE_DISPLAY_TYPE, NULL,
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetIssuerNameStringFriendlyDisplay (
    LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetIssuerNameString(CERT_NAME_FRIENDLY_DISPLAY_TYPE, NULL,
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetIssuerNameStringDns (
    LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetIssuerNameString(CERT_NAME_DNS_TYPE, NULL,
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetIssuerNameStringUrl (
    LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetIssuerNameString(CERT_NAME_URL_TYPE, NULL,
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetIssuerNameStringUpn (
    LPTSTR pszNameString, DWORD *pdwNameStringLen) throw()
{
    return GetIssuerNameString(CERT_NAME_UPN_TYPE, NULL,
	pszNameString, pdwNameStringLen);
}

inline HRESULT CCertContext::GetNameString (DWORD dwType, DWORD dwFlags,
    void *pvTypePara, ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetNameString (dwType, dwFlags, pvTypePara, NULL,
	&dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetNameString (dwType, dwFlags, pvTypePara,
	Result.GetBuffer(dwNameStringLen), &dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetSubjectNameString (DWORD dwType,
    void *pvTypePara, ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetSubjectNameString (dwType, pvTypePara, NULL,
	&dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetSubjectNameString (dwType, pvTypePara,
	Result.GetBuffer(dwNameStringLen), &dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetIssuerNameString (DWORD dwType,
    void *pvTypePara, ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetIssuerNameString (dwType, pvTypePara, NULL,
	&dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetIssuerNameString (dwType, pvTypePara,
	Result.GetBuffer(dwNameStringLen), &dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetSubjectNameStringEmail(
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetSubjectNameStringEmail (NULL, &dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetSubjectNameStringEmail (Result.GetBuffer(dwNameStringLen),
	&dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetSubjectNameStringToStr(DWORD dwStrType,
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetSubjectNameStringToStr (dwStrType, NULL,
	&dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetSubjectNameStringToStr (dwStrType,
	Result.GetBuffer(dwNameStringLen), &dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetSubjectNameStringAttr(LPCSTR szAttrOid,
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetSubjectNameStringAttr (szAttrOid, NULL,
	&dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetSubjectNameStringAttr (szAttrOid,
	Result.GetBuffer(dwNameStringLen), &dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetSubjectNameStringSimple(
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetSubjectNameStringSimple (NULL, &dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetSubjectNameStringSimple (Result.GetBuffer(dwNameStringLen),
	&dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetSubjectNameStringFriendlyDisplay(
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetSubjectNameStringFriendlyDisplay (NULL, &dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetSubjectNameStringFriendlyDisplay (
	Result.GetBuffer(dwNameStringLen), &dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetSubjectNameStringDns(
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetSubjectNameStringDns (NULL, &dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetSubjectNameStringDns (Result.GetBuffer(dwNameStringLen),
	&dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetSubjectNameStringUrl(
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetSubjectNameStringUrl (NULL, &dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetSubjectNameStringUrl (Result.GetBuffer(dwNameStringLen),
	&dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetSubjectNameStringUpn(
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetSubjectNameStringUpn (NULL, &dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetSubjectNameStringUpn (Result.GetBuffer(dwNameStringLen),
	&dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetIssuerNameStringEmail(
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetIssuerNameStringEmail (NULL, &dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetIssuerNameStringEmail (Result.GetBuffer(dwNameStringLen),
	&dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetIssuerNameStringToStr(DWORD dwStrType,
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetIssuerNameStringToStr (dwStrType, NULL,
	&dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetIssuerNameStringToStr (dwStrType,
	Result.GetBuffer(dwNameStringLen), &dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetIssuerNameStringAttr(LPCSTR szAttrOid,
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetIssuerNameStringAttr (szAttrOid, NULL,
	&dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetIssuerNameStringAttr (szAttrOid,
	Result.GetBuffer(dwNameStringLen), &dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetIssuerNameStringSimple(
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetIssuerNameStringSimple (NULL, &dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetIssuerNameStringSimple (Result.GetBuffer(dwNameStringLen),
	&dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetIssuerNameStringFriendlyDisplay(
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetIssuerNameStringFriendlyDisplay (NULL, &dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetIssuerNameStringFriendlyDisplay (
	Result.GetBuffer(dwNameStringLen), &dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetIssuerNameStringDns(
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetIssuerNameStringDns (NULL, &dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetIssuerNameStringDns (
	Result.GetBuffer(dwNameStringLen), &dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetIssuerNameStringUrl(
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetIssuerNameStringUrl (NULL, &dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetIssuerNameStringUrl (
	Result.GetBuffer(dwNameStringLen), &dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::GetIssuerNameStringUpn(
    ATL::CAtlString& Result) //throw(...)
{
    DWORD dwNameStringLen = 0;
    HRESULT hr = GetIssuerNameStringUpn (NULL, &dwNameStringLen);
    if (FAILED(hr))
	return hr;
    hr = GetIssuerNameStringUpn (
	Result.GetBuffer(dwNameStringLen), &dwNameStringLen);
    Result.ReleaseBufferSetLength (dwNameStringLen - 1);
    ATLASSERT( (size_t)Result.GetLength() == _tcslen(Result.GetString ()) );
    return hr;
}

inline HRESULT CCertContext::AddToStore (CCertStore& CertStore,
    DWORD dwAddDisposition, PCCERT_CONTEXT *ppStoreContext) const throw()
{
    ATLENSURE_RETURN(m_pUserCert != NULL);

    if (!::CertAddCertificateContextToStore (CertStore.GetHandle (),
	m_pUserCert, dwAddDisposition, ppStoreContext))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCertContext::AddToStoreAlways (
    CCertStore& CertStore, PCCERT_CONTEXT *ppStoreContext) const throw()
{
    return AddToStore (CertStore, CERT_STORE_ADD_ALWAYS, ppStoreContext);
}

inline HRESULT CCertContext::AddToStoreNew (
    CCertStore& CertStore, PCCERT_CONTEXT *ppStoreContext) const throw()
{
    return AddToStore (CertStore, CERT_STORE_ADD_NEW, ppStoreContext);
}

inline HRESULT CCertContext::AddToStoreReplaceExisting (
    CCertStore& CertStore, PCCERT_CONTEXT *ppStoreContext) const throw()
{
    return AddToStore (CertStore, CERT_STORE_ADD_REPLACE_EXISTING,
	ppStoreContext);
}

inline HRESULT CCertContext::AddToStoreReplaceExistingInheritProperties (
    CCertStore& CertStore, PCCERT_CONTEXT *ppStoreContext) const throw()
{
    return AddToStore (CertStore,
	CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, ppStoreContext);
}

inline HRESULT CCertContext::AddToStoreUseExisting (
    CCertStore& CertStore, PCCERT_CONTEXT *ppStoreContext) const throw()
{
    return AddToStore (CertStore, CERT_STORE_ADD_USE_EXISTING, ppStoreContext);
}

inline HRESULT CCertContext::DeleteFromStore () throw()
{
    ATLENSURE_RETURN(m_pUserCert != NULL);
    if (!::CertDeleteCertificateFromStore (m_pUserCert)) {
        m_pUserCert = NULL;
	return ATL::AtlHresultFromLastError();
    }
    m_pUserCert = NULL;
    return S_OK;
}

inline HRESULT CCertContext::InitPrivateKey(DWORD dwFlags,
    HCRYPTPROV *phCryptProv, DWORD *pdwKeySpec, BOOL *pfCallerFreeProv,
    void *pvReserved /* = NULL*/) throw()
{
    ATLENSURE_RETURN(m_pUserCert != NULL);
    if (!::CryptAcquireCertificatePrivateKey (m_pUserCert, dwFlags, pvReserved,
	phCryptProv, pdwKeySpec, pfCallerFreeProv))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCertContext::BuildChain(
    PCCERT_CHAIN_CONTEXT* ppChainContext,
    HCERTSTORE hAdditionalStore /* = NULL */,
    DWORD dwFlags /* = CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT
		     | CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE */,
    LPFILETIME pTime /* = NULL */,
    HCERTCHAINENGINE hChainEngine /* = NULL */,
    PCERT_CHAIN_PARA pChainPara /* = NULL */) throw()
{
    ATLENSURE_RETURN(m_pUserCert != NULL);
    CERT_CHAIN_PARA ChainPara = { sizeof(ChainPara) };
    if(!pChainPara) {
	ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
	ChainPara.RequestedUsage.Usage.cUsageIdentifier = 0;
	ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = NULL;
	pChainPara = &ChainPara;
    }
    if(!::CertGetCertificateChain(hChainEngine,  m_pUserCert,
	pTime, hAdditionalStore, pChainPara, dwFlags, 0, ppChainContext))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCertContext::BuildChain(
    CCertChainContext& cChainContext,
    HCERTSTORE hAdditionalStore /* = NULL */,
    DWORD dwFlags /* = CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT
		     | CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE */,
    LPFILETIME pTime /* = NULL */,
    HCERTCHAINENGINE hChainEngine /* = NULL */,
    PCERT_CHAIN_PARA pChainPara /* = NULL */) throw()
{
    ATLASSERT(cChainContext.GetHandle() == NULL);
    if (cChainContext.GetHandle() != NULL) {
	cChainContext.Destroy();
    }
    PCCERT_CHAIN_CONTEXT pChain;
    HRESULT hr = BuildChain(&pChain, hAdditionalStore, dwFlags, pTime,
	hChainEngine, pChainPara);
    ATLENSURE_RETURN_HR(SUCCEEDED(hr), hr);
    cChainContext.Attach(pChain, TRUE);
    return S_OK;
}

inline void CCertContext::GetValidFromFiletime(FILETIME *notBefore) //throw(...);
{
    ATLASSERT(m_pUserCert != NULL);
    if(m_pUserCert == NULL) return;

    *notBefore = m_pUserCert->pCertInfo->NotBefore;
}

inline void CCertContext::GetValidToFiletime(FILETIME *notAfter) //throw(...);
{
    ATLASSERT(m_pUserCert != NULL);
    if(m_pUserCert == NULL) return;

    *notAfter = m_pUserCert->pCertInfo->NotAfter;
}

#ifdef _WIN32
inline void CCertContext::GetValidFrom(ATL::CAtlString& Result, LCID locale ) //throw(...)
{
    ATLASSERT(m_pUserCert != NULL);
    if(m_pUserCert == NULL) return;

    filetime_to_string (Result, m_pUserCert->pCertInfo->NotBefore, locale);
}

inline void CCertContext::GetValidTo(ATL::CAtlString& Result, LCID locale ) //throw(...)
{
    ATLASSERT(m_pUserCert != NULL);
    if(m_pUserCert == NULL) return;
	
    filetime_to_string (Result, m_pUserCert->pCertInfo->NotAfter, locale);
}

#ifdef WINCRYPTEX
inline BOOL CCertContext::GetPrivateKeyValidityPeriod(ATL::CAtlString& notBefore,
				 ATL::CAtlString& notAfter, LCID locale ) //throw(...);
{
    FILETIME nB, nA;
    if(!GetPrivateKeyValidityPeriodFiletime(&nB, &nA))
	return FALSE;

    filetime_to_string(notBefore, nB, locale);
    filetime_to_string(notAfter, nA, locale);
    return TRUE;
}

inline BOOL CCertContext::GetPrivateKeyValidityPeriodFiletime(FILETIME *notBefore,
						      FILETIME *notAfter) //throw(...)
{
    ATLASSERT(m_pUserCert != NULL);
    if(m_pUserCert == NULL) return false;

    PCERT_EXTENSION  pCertExtInfo = NULL;
    DWORD cBufferSize = 0;
    PCPCERT_PRIVATEKEY_USAGE_PERIOD pPKValidity = NULL;
    pCertExtInfo = CertFindExtension(szOID_PRIVATEKEY_USAGE_PERIOD,
				    m_pUserCert->pCertInfo->cExtension,
				    m_pUserCert->pCertInfo->rgExtension);

    if(pCertExtInfo == NULL)
	return false;

    if(!::CryptDecodeObject(	    X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
				    szCPGUID_PRIVATEKEY_USAGE_PERIOD_Encode, // szOID_PRIVATEKEY_USAGE_PERIOD,
				    pCertExtInfo->Value.pbData,
				    pCertExtInfo->Value.cbData,
				    0,
				    NULL,
				    &cBufferSize))
    {
	return false;
    }

    //--------------------------------------------------------------------
    // Allocate memory for the decoded information

    pPKValidity = (PCPCERT_PRIVATEKEY_USAGE_PERIOD) malloc (cBufferSize);
    if(!pPKValidity)
    {
	return false;
    }

    if(!::CryptDecodeObject(	    X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
				    szCPGUID_PRIVATEKEY_USAGE_PERIOD_Encode, // szOID_PRIVATEKEY_USAGE_PERIOD,
				    pCertExtInfo->Value.pbData,
				    pCertExtInfo->Value.cbData,
				    0,
				    pPKValidity,
				    &cBufferSize))
    {	        
	return false;
    }

    if (pPKValidity->pNotBefore)
	*notBefore = *(pPKValidity->pNotBefore);
    else
	notBefore->dwHighDateTime = notBefore->dwLowDateTime = 0;

    // TODO: выбрать константку для правой границы:
    *notAfter = *(pPKValidity->pNotAfter);
    return true;
}
#endif // WINCRYPTEX

inline void CCertContext::GetSerialNumberString(
    ATL::CAtlString& Result) //throw(...)
{
    ATLASSERT(m_pUserCert != NULL);
    if(m_pUserCert == NULL) return;

    asn1_integer_to_string(Result, &m_pUserCert->pCertInfo->SerialNumber);
}
#endif //_WIN32

#if !defined(_WIN32) || _WIN32_WINNT >= 0x0501
inline HRESULT CCertContext::GetThumbprintString(
    ATL::CAtlString& Result) //throw(...)
{
    ATLENSURE_RETURN(m_pUserCert != NULL);

    ATL2::CStringBlob blob;
    ATL_HR_ERRORCHECK_RETURN(GetProperty(CERT_HASH_PROP_ID, blob));

   ATL_HR_ERRORCHECK_RETURN(CryptBinaryToString(reinterpret_cast<BYTE*>(blob.GetBuffer()),
	blob.GetLength(), CRYPT_STRING_HEX, Result));

    return S_OK;
}
#endif // !defined(_WIN32) || _WIN32_WINNT >= 0x0501

inline CCRLContext::CCRLContext() throw()
    : m_pUserCRL(NULL)
{
}

inline CCRLContext::CCRLContext (const CCRLContext& ctx) throw()
{
    m_pUserCRL = ctx.Duplicate();
}

inline CCRLContext &CCRLContext::operator=(const CCRLContext& ctx) throw()
{
    if (this != &ctx)
    {
        if (m_pUserCRL)
	    Destroy();
        m_pUserCRL = ctx.Duplicate();
    }
    return *this;
};

inline CCRLContext::CCRLContext (
    PCCRL_CONTEXT pUserCRL, BOOL bTakeOwnership /* = FALSE */) throw()
{
    if (bTakeOwnership)
	m_pUserCRL = pUserCRL;
    else
	m_pUserCRL = ::CertDuplicateCRLContext(pUserCRL);
}

inline CCRLContext::~CCRLContext() throw()
{
    Destroy ();
}

inline void CCRLContext::Attach(
    PCCRL_CONTEXT pUserCRL, BOOL bTakeOwnership /* = FALSE */) throw()
{
    ATLASSERT(m_pUserCRL == NULL);
    if (m_pUserCRL != NULL) return;

    if (bTakeOwnership)
	m_pUserCRL = pUserCRL;
    else
	m_pUserCRL = ::CertDuplicateCRLContext(pUserCRL);
}

inline void CCRLContext::Destroy() throw()
{
    if (m_pUserCRL == NULL) return;
    BOOL bSuccess = ::CertFreeCRLContext (m_pUserCRL);
    ATLVERIFY(bSuccess);
    m_pUserCRL = NULL;
}

inline PCCRL_CONTEXT CCRLContext::Detach() throw()
{
    PCCRL_CONTEXT pUserCRL;
    pUserCRL = m_pUserCRL;
    m_pUserCRL = NULL;
    return pUserCRL;
}

inline PCCRL_CONTEXT CCRLContext::Duplicate() const throw()
{
    ATLENSURE_RETURN_VAL(m_pUserCRL != NULL, NULL);
    PCCRL_CONTEXT pUserCRL = ::CertDuplicateCRLContext(m_pUserCRL);
    return pUserCRL;
}

inline HRESULT CCRLContext::Uninitialize() throw()
{
    ATLENSURE_RETURN(m_pUserCRL != NULL);
    if (!::CertFreeCRLContext (m_pUserCRL))
	return ATL::AtlHresultFromLastError();
    m_pUserCRL = NULL;
    return S_OK;
}

inline HRESULT CCRLContext::Initialize (
    const BYTE * pbCRLEncoded, DWORD cbCRLEncoded, DWORD dwCRLEncodingType
    /* = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING */) throw()
{
    ATLENSURE_RETURN(m_pUserCRL == NULL);
    m_pUserCRL = ::CertCreateCRLContext (
	dwCRLEncodingType, pbCRLEncoded, cbCRLEncoded);
    if (!m_pUserCRL)
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCRLContext::DetachFromStore() throw()
{
    ATLENSURE_RETURN(m_pUserCRL != NULL);
    PCCRL_CONTEXT pDetachedCRL = ::CertCreateCRLContext(
	X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, m_pUserCRL->pbCrlEncoded,
	m_pUserCRL->cbCrlEncoded);
    if (!pDetachedCRL)
    {
	return ATL::AtlHresultFromLastError();
    }
    //Никаких свойств CRL не копируем, поскольку нет понимания смысла
    //сего действия.
    operator=(CCRLContext(pDetachedCRL, TRUE));
    return S_OK;
}

inline HRESULT CCRLContext::SetProperty (
    DWORD dwPropId, DWORD dwFlags, const void *pvData) throw()
{
    ATLENSURE_RETURN(m_pUserCRL != NULL);
    if (!::CertSetCRLContextProperty (
	m_pUserCRL, dwPropId, dwFlags, pvData))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCRLContext::GetProperty(
    DWORD dwPropId, void *pvData, DWORD *pcbData) throw()
{
    ATLENSURE_RETURN(m_pUserCRL != NULL);
    if (!::CertGetCRLContextProperty (
	m_pUserCRL, dwPropId, pvData, pcbData))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCRLContext::GetProperty(
    DWORD dwPropId, CStringBlob &Result) //throw(...)
{
    DWORD cbData = 0;
    HRESULT hr = GetProperty(dwPropId, NULL, &cbData);
    if (FAILED(hr))
	return hr;
    hr = GetProperty(dwPropId, Result.GetBuffer(cbData), &cbData);
    Result.ReleaseBufferSetLength (cbData);
    return hr;
}
#ifdef _WIN32
inline HRESULT CCRLContext::GetNameString(
    DWORD dwStrType, LPTSTR pszNameString, DWORD *pdwNameStringLen)
{
    ATLENSURE_RETURN(m_pUserCRL != NULL);
    if (!pszNameString || !*pdwNameStringLen) {
	*pdwNameStringLen = ::CertNameToStr(X509_ASN_ENCODING,
	    &m_pUserCRL->pCrlInfo->Issuer, dwStrType, 0, 0);
	return S_OK;
    }
    ::CertNameToStr(X509_ASN_ENCODING,
	&m_pUserCRL->pCrlInfo->Issuer,
	dwStrType, pszNameString, *pdwNameStringLen);
    return S_OK;
}

inline HRESULT CCRLContext::GetNameString(
    DWORD dwStrType, ATL::CAtlString &NameString)
{
    DWORD cbData = 0;
    HRESULT hr = GetNameString(dwStrType, NULL, &cbData);
    if (FAILED(hr))
	return hr;
    hr = GetNameString(dwStrType, NameString.GetBuffer(cbData), &cbData);
    NameString.ReleaseBufferSetLength(cbData);
    return hr;
}
#endif //_WIN32

inline HRESULT CCRLContext::AddToStore (
    CCertStore& CertStore,
    DWORD dwAddDisposition, PCCRL_CONTEXT *ppStoreContext) throw()
{
    ATLENSURE_RETURN(m_pUserCRL != NULL);
    if (!::CertAddCRLContextToStore (CertStore.GetHandle (),
	m_pUserCRL, dwAddDisposition, ppStoreContext))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCRLContext::AddToStoreAlways (
    CCertStore& CertStore, PCCRL_CONTEXT *ppStoreContext) throw()
{
    return AddToStore (CertStore, CERT_STORE_ADD_ALWAYS, ppStoreContext);
}

inline HRESULT CCRLContext::AddToStoreNew (
    CCertStore& CertStore, PCCRL_CONTEXT *ppStoreContext) throw()
{
    return AddToStore (CertStore, CERT_STORE_ADD_NEW, ppStoreContext);
}

inline HRESULT CCRLContext::AddToStoreReplaceExisting (
    CCertStore& CertStore, PCCRL_CONTEXT *ppStoreContext) throw()
{
    return AddToStore (CertStore, CERT_STORE_ADD_REPLACE_EXISTING,
	ppStoreContext);
}

inline HRESULT CCRLContext::AddToStoreReplaceExistingInheritProperties (
    CCertStore& CertStore, PCCRL_CONTEXT *ppStoreContext) throw()
{
    return AddToStore (CertStore,
	CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, ppStoreContext);
}

inline HRESULT CCRLContext::AddToStoreUseExisting (
    CCertStore& CertStore, PCCRL_CONTEXT *ppStoreContext) throw()
{
    return AddToStore (CertStore, CERT_STORE_ADD_USE_EXISTING, ppStoreContext);
}

inline HRESULT CCRLContext::DeleteFromStore () throw()
{
    ATLENSURE_RETURN(m_pUserCRL != NULL);
    if (!::CertDeleteCRLFromStore (m_pUserCRL)) {
	m_pUserCRL = NULL;
	return ATL::AtlHresultFromLastError();
    }
    m_pUserCRL = NULL;
    return S_OK;
}

inline CCertChainContext::CCertChainContext() throw()
    : m_pCertChain(NULL)
{
}

inline CCertChainContext::CCertChainContext (
    const CCertChainContext& ctx) throw()
{
    m_pCertChain = ctx.Duplicate();
}

inline CCertChainContext& CCertChainContext::operator=(
    const CCertChainContext& ctx) throw() 
{
    if (this != &ctx)
    {
        if (m_pCertChain)
	    Destroy();
        m_pCertChain = ctx.Duplicate();
    }
    return *this;
}

inline CCertChainContext::CCertChainContext (
    PCCERT_CHAIN_CONTEXT pCertChain, BOOL bTakeOwnership /* = FALSE */) throw()
{
    if (bTakeOwnership)
	m_pCertChain = pCertChain;
    else
	m_pCertChain = ::CertDuplicateCertificateChain(pCertChain);
}

inline CCertChainContext::~CCertChainContext() throw()
{
    Destroy ();
}

inline void CCertChainContext::Attach(
    PCCERT_CHAIN_CONTEXT pCertChain, BOOL bTakeOwnership /* = FALSE */) throw()
{
    ATLASSERT( m_pCertChain == NULL );
    if (m_pCertChain != NULL) return;

    if (bTakeOwnership)
	m_pCertChain = pCertChain;
#if defined _WIN32
    else
	m_pCertChain = ::CertDuplicateCertificateChain(pCertChain);
#else
	// XXX dim: на UNIX вызывается только из CCertContext::BuildChain() с bTakeOwnership=TRUE
	ATLASSERT(bTakeOwnership == FALSE);
#endif // _WIN32
}

inline void CCertChainContext::Destroy() throw()
{
    // Реализация этого метода не похожа на аналогичные методы классов
    // CCertContext и CCertStore, потому что функция CertFreeCertificateChain
    // не ошибается.
    if (m_pCertChain == NULL) return;
    ::CertFreeCertificateChain(m_pCertChain);
    m_pCertChain = NULL;
}

inline PCCERT_CHAIN_CONTEXT CCertChainContext::Detach() throw()
{
    PCCERT_CHAIN_CONTEXT pCertChain;
    pCertChain = m_pCertChain;
    m_pCertChain = NULL;
    return pCertChain;
}

inline PCCERT_CHAIN_CONTEXT CCertChainContext::Duplicate() const throw()
{
    ATLENSURE_RETURN_VAL(m_pCertChain != NULL, NULL);
    PCCERT_CHAIN_CONTEXT pCertChain = ::CertDuplicateCertificateChain(
	m_pCertChain);
    return pCertChain;
}

inline HRESULT CCertChainContext::Uninitialize() throw()
{
    // Реализация этого метода не похожа на аналогичные методы классов
    // CCertContext и CCertStore, потому что функция CertFreeCertificateChain
    // не ошибается.
    if (m_pCertChain == NULL) return S_OK;
    ::CertFreeCertificateChain(m_pCertChain);
    m_pCertChain = NULL;
    return S_OK;
}

inline CCertChainEngine::CCertChainEngine() throw()
    : m_hChainEngine(NULL)
{
}

//inline CCertChainEngine::CCertChainEngine (const CCertChainEngine& ctx) throw()
//{
//    m_phChainEngine = ctx.Duplicate();
//}

inline CCertChainEngine::CCertChainEngine (HCERTCHAINENGINE hChainEngine) throw()
{
    m_hChainEngine = hChainEngine;
}

inline HRESULT CCertChainEngine::Create (PCERT_CHAIN_ENGINE_CONFIG pConfig) throw()
{
    ATLENSURE_RETURN(m_hChainEngine == NULL);

    if(!CertCreateCertificateChainEngine(pConfig, &m_hChainEngine))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline CCertChainEngine::~CCertChainEngine() throw()
{
    Destroy ();
}

inline void CCertChainEngine::Attach(HCERTCHAINENGINE hChainEngine) throw()
{
    ATLASSERT(m_hChainEngine == NULL);
    if (m_hChainEngine != NULL) return;

    m_hChainEngine = hChainEngine;
}

inline void CCertChainEngine::Destroy() throw()
{
    if (m_hChainEngine == NULL) return;
    CertFreeCertificateChainEngine(m_hChainEngine);
    m_hChainEngine = NULL;
}

inline HCERTCHAINENGINE CCertChainEngine::Detach() throw()
{
    HCERTCHAINENGINE hChainEngine;
    hChainEngine = m_hChainEngine;
    m_hChainEngine = NULL;
    return hChainEngine;
}

inline HRESULT CCertChainEngine::Uninitialize() throw()
{
    ATLENSURE_RETURN(m_hChainEngine != NULL);
    CertFreeCertificateChainEngine(m_hChainEngine);
    return S_OK;
}


#ifdef CRYPTUI
//* dwFlags
//* 
//* CRYPTUI_HIDE_HIERARCHYPAGE
//* CRYPTUI_HIDE_DETAILPAGE
//* CRYPTUI_DISABLE_EDITPROPERTIES
//* CRYPTUI_ENABLE_EDITPROPERTIES
//* CRYPTUI_DISABLE_ADDTOSTORE
//* CRYPTUI_ENABLE_ADDTOSTORE
//* CRYPTUI_ACCEPT_DECLINE_STYLE
//* CRYPTUI_IGNORE_UNTRUSTED_ROOT
//* CRYPTUI_DONT_OPEN_STORES
//* CRYPTUI_ONLY_OPEN_ROOT_STORE
//* CRYPTUI_WARN_UNTRUSTED_ROOT
//*   -  For use with viewing of certificates on remote
//*      machines only.  If this flag is used rghStores[0]
//*      must be the handle of the root store on the remote machine.

inline HRESULT CCertContextUI::View (
    HWND hwndParent, LPCWSTR szTitle, DWORD dwFlags,
    BOOL *pPropertiesChanged, HCERTSTORE hAdditionalStore) throw()
{
    ATLENSURE_RETURN(m_pUserCert != NULL);

    CRYPTUI_VIEWCERTIFICATE_STRUCTW CertViewInfo = { sizeof (CertViewInfo) };
    // Все остальные поля = 0
    CertViewInfo.hwndParent = hwndParent;
    CertViewInfo.dwFlags = dwFlags;
    CertViewInfo.szTitle = szTitle;
    CertViewInfo.pCertContext = m_pUserCert;    
    
    BOOL PropertiesChanged = 0;
    if (!pPropertiesChanged)
	pPropertiesChanged = &PropertiesChanged;
    if ( hAdditionalStore )
    {
	CertViewInfo.cStores = 1;
	CertViewInfo.rghStores = &hAdditionalStore;
    }
    // TODO: проверить, что будет, если пользователь нажмет Cancel
    if (!::CryptUIDlgViewCertificateW (&CertViewInfo, pPropertiesChanged))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCertContextUI::View (
    HWND hwndParent, LPCSTR szTitle, DWORD dwFlags,
    BOOL *pPropertiesChanged, HCERTSTORE hAdditionalStore) throw()
{
    ATLENSURE_RETURN(m_pUserCert != NULL);

    CRYPTUI_VIEWCERTIFICATE_STRUCTA CertViewInfo = { sizeof (CertViewInfo) };
    // Все остальные поля = 0
    CertViewInfo.hwndParent = hwndParent;
    CertViewInfo.dwFlags = dwFlags;
    CertViewInfo.szTitle = szTitle;
    CertViewInfo.pCertContext = m_pUserCert;

    BOOL PropertiesChanged = 0;
    if (!pPropertiesChanged)
	pPropertiesChanged = &PropertiesChanged;
    if ( hAdditionalStore )
    {
	CertViewInfo.cStores = 1;
	CertViewInfo.rghStores = &hAdditionalStore;
    }

    // TODO: проверить, что будет, если пользователь нажмет Cancel
    if (!::CryptUIDlgViewCertificateA (&CertViewInfo, pPropertiesChanged))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCRLContextUI::View(
    HWND hwndParent, LPCSTR szTitle, DWORD dwFlags) throw()
{
    ATLENSURE_RETURN(m_pUserCRL != NULL);

    CRYPTUI_VIEWCRL_STRUCTA ViewCrl = { sizeof(ViewCrl) };
    ViewCrl.hwndParent = hwndParent;
    ViewCrl.dwFlags = dwFlags;
    ViewCrl.szTitle = szTitle;
    ViewCrl.pCRLContext = GetHandle();

    if(!::CryptUIDlgViewCRLA(&ViewCrl))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCRLContextUI::View(
    HWND hwndParent, LPCWSTR szTitle, DWORD dwFlags) throw()
{
    ATLENSURE_RETURN(m_pUserCRL != NULL);

    CRYPTUI_VIEWCRL_STRUCTW ViewCrl = { sizeof(ViewCrl) };
    ViewCrl.hwndParent = hwndParent;
    ViewCrl.dwFlags = dwFlags;
    ViewCrl.szTitle = szTitle;
    ViewCrl.pCRLContext = GetHandle();

    if(!::CryptUIDlgViewCRLW(&ViewCrl))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCRLContextUI::WizExport(
    HWND hwndParent, LPCWSTR szWizardTitle, DWORD dwFlags) throw()
{
    ATLENSURE_RETURN(m_pUserCRL != NULL);

    CRYPTUI_WIZ_EXPORT_INFO exportCrl = { sizeof(exportCrl) };
    exportCrl.pwszExportFileName = NULL;
    exportCrl.dwSubjectChoice = CRYPTUI_WIZ_EXPORT_CRL_CONTEXT;
    exportCrl.pCRLContext = GetHandle();
    exportCrl.cStores = 0;
    exportCrl.rghStores = NULL;

    if(!::CryptUIWizExport(dwFlags, hwndParent, szWizardTitle,
	&exportCrl, NULL))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

#endif // CRYPTUI


class CCertStoreDeleter
{
protected:
    bool m_fCheckOnClose;
public:
    CCertStoreDeleter() : m_fCheckOnClose(
#ifdef _DEBUG
	true
#else
	false
#endif 
    ) {}

    void operator()(HCERTSTORE *phStore)
    {
	if (*phStore)
	{
	    ATLVERIFY(::CertCloseStore(*phStore,
		m_fCheckOnClose ? CERT_CLOSE_STORE_CHECK_FLAG : 0));
	}
	delete phStore;
    }
};

class CCertStoreNonCheckingDeleter : public CCertStoreDeleter
{
public:
    CCertStoreNonCheckingDeleter()
    {
	m_fCheckOnClose = false;
    }
};

inline CCertStore::CCertStore()
    : m_phStore(new HCERTSTORE(0), CCertStoreDeleter())
{
}

inline CCertStore& CCertStore::operator=(const CCertStore& store)
{
    if (this == &store)
    {
	return *this;
    }
    m_phStore = store.m_phStore;
    m_collectedStores = store.m_collectedStores; //может выкинуть исключение
    return *this;
}

inline CCertStore::~CCertStore() throw()
{
    //Деструктор реализован только для того, чтобы при использовании Collection
    //Store оно закрывалось раньше добавленных в него хранилищ, которые
    //сохранены в m_collectedStores.
    m_phStore.reset();
}

inline HRESULT CCertStore::AttachOwnership(HCERTSTORE hStore) throw()
{
    ATLENSURE_RETURN(!*m_phStore);
    *m_phStore = hStore;
    return S_OK;
}

inline HRESULT CCertStore::AttachWeak(HCERTSTORE hStore) throw()
{
    ATLENSURE_RETURN(!*m_phStore);
    HCERTSTORE hDuplicatedStore = ::CertDuplicateStore(hStore);
    ATLENSURE_RETURN_LASTWIN32HR(hDuplicatedStore);
    try
    {
	m_phStore.reset(new HCERTSTORE(hDuplicatedStore),
	    CCertStoreNonCheckingDeleter());
    }
    catch(std::bad_alloc&)
    {
	return E_OUTOFMEMORY;
    }
    return S_OK;
}

inline void CCertStore::Destroy()
{
    operator=(CCertStore());
}

inline HRESULT CCertStore::Initialize (
    LPCSTR lpszStoreProvider, DWORD dwMsgAndCertEncodingType,
    ATL::CCryptProv& CryptProv, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    const void *pvPara) throw()
{
    ATLENSURE_RETURN(!*m_phStore);

    //Туповатый в deliter
    if (dwFlagsLow & CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG)
    {
	m_phStore.reset(new HCERTSTORE(0),
	    CCertStoreNonCheckingDeleter());
    }
    HCERTSTORE hStore = ::CertOpenStore(lpszStoreProvider,
	dwMsgAndCertEncodingType, CryptProv.GetHandle(),
	dwFlagsLow | dwFlagsHigh, pvPara);
    if (!hStore)
    {
	return ATL::AtlHresultFromLastError();
    }
    *m_phStore = hStore;
    return S_OK;
}

inline HRESULT CCertStore::InitMemoryStore (
    DWORD dwFlagsLow, DWORD dwFlagsHigh, ATL::CCryptProv *pCryptProv) throw()
{
    ATL::CCryptProv CryptProv;
    if (pCryptProv)
	CryptProv = *pCryptProv;
    return Initialize (CERT_STORE_PROV_MEMORY, 0, CryptProv,
	dwFlagsLow, dwFlagsHigh, 0);
}

inline HRESULT CCertStore::InitFileStore (
    HANDLE hStoreFileHandle, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv) throw()
{
    return Initialize (CERT_STORE_PROV_FILE, 0, CryptProv,
	dwFlagsLow, dwFlagsHigh, hStoreFileHandle);
}

inline HRESULT CCertStore::InitFileNameStore (
    LPCWSTR wszStoreFileName, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv, DWORD dwMsgAndCertEncodingType) throw()
{
    return Initialize (CERT_STORE_PROV_FILENAME_W, dwMsgAndCertEncodingType,
	CryptProv, dwFlagsLow, dwFlagsHigh, wszStoreFileName);
}

inline HRESULT CCertStore::InitFileNameStore (
    LPCSTR szStoreFileName, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv, DWORD dwMsgAndCertEncodingType) throw()
{
    return Initialize (CERT_STORE_PROV_FILENAME_A, dwMsgAndCertEncodingType,
	CryptProv, dwFlagsLow, dwFlagsHigh, szStoreFileName);
}

inline HRESULT CCertStore::InitCollectionStore (
    DWORD dwFlagsLow, ATL::CCryptProv& CryptProv) throw()
{
    return Initialize (CERT_STORE_PROV_COLLECTION, 0, CryptProv,
	dwFlagsLow, 0, 0);
}

#ifdef _WIN32
inline HRESULT CCertStore::InitRegStore (
    HKEY hStoreRegKey, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv) throw()
{
    return Initialize (CERT_STORE_PROV_REG, 0, CryptProv,
	dwFlagsLow, dwFlagsHigh, hStoreRegKey);
}
#endif // _WIN32

inline HRESULT CCertStore::InitSystemStore (
    LPCWSTR wszStoreName, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv) throw()
{
    return Initialize (CERT_STORE_PROV_SYSTEM_W, 0, CryptProv,
	dwFlagsLow, dwFlagsHigh, wszStoreName);
}

inline HRESULT CCertStore::InitSystemStore (
    LPCSTR szStoreName, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv) throw()
{
    return Initialize (CERT_STORE_PROV_SYSTEM_A, 0, CryptProv,
	dwFlagsLow, dwFlagsHigh, szStoreName);
}

inline HRESULT CCertStore::InitSystemRegistryStore (
    LPCWSTR wszStoreName, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv) throw()
{
    return Initialize (CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, CryptProv,
	dwFlagsLow, dwFlagsHigh, wszStoreName);
}

inline HRESULT CCertStore::InitSystemRegistryStore (
    LPCSTR szStoreName, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv) throw()
{
    return Initialize (CERT_STORE_PROV_SYSTEM_REGISTRY_A, 0, CryptProv,
	dwFlagsLow, dwFlagsHigh, szStoreName);
}

inline HRESULT CCertStore::InitPhysicalStore (
    LPCWSTR wszStoreName, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv) throw()
{
    return Initialize (CERT_STORE_PROV_PHYSICAL_W, 0, CryptProv,
	dwFlagsLow, dwFlagsHigh, wszStoreName);
}

inline HRESULT CCertStore::InitMsgStore (
    HCRYPTMSG hStoreMsg, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv, DWORD dwMsgAndCertEncodingType) throw()
{
    return Initialize (CERT_STORE_PROV_MSG, dwMsgAndCertEncodingType,
	CryptProv, dwFlagsLow, dwFlagsHigh, hStoreMsg);
}

inline HRESULT CCertStore::InitPkcs7Store (
    CRYPT_DATA_BLOB *pEncodedStore, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv, DWORD dwMsgAndCertEncodingType) throw()
{
    return Initialize (CERT_STORE_PROV_PKCS7, dwMsgAndCertEncodingType,
	CryptProv, dwFlagsLow, dwFlagsHigh, pEncodedStore);
}

inline HRESULT CCertStore::InitSerializedStore (
    CRYPT_DATA_BLOB *pSerializedStore, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv) throw()
{
    return Initialize (CERT_STORE_PROV_SERIALIZED, 0, CryptProv,
	dwFlagsLow, dwFlagsHigh, pSerializedStore);
}

inline HRESULT CCertStore::InitPkcs7Store (
    const CStringBlob& EncodedStore, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv, DWORD dwMsgAndCertEncodingType) throw()
{
    CStringBlob& Store = const_cast<CStringBlob&>(EncodedStore);
    CRYPT_DATA_BLOB CryptBlob;
    CryptBlob.cbData = Store.GetLength ();
    CryptBlob.pbData = reinterpret_cast<BYTE *>(Store.GetBuffer ());
    HRESULT hr = Initialize (CERT_STORE_PROV_PKCS7, dwMsgAndCertEncodingType,
	CryptProv, dwFlagsLow, dwFlagsHigh, &CryptBlob);
    Store.ReleaseBuffer();
    return hr;
}

inline HRESULT CCertStore::InitSerializedStore (
    const CStringBlob& SerializedStore, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv) throw()
{
    CStringBlob& Store = const_cast<CStringBlob&>(SerializedStore);
    CRYPT_DATA_BLOB CryptBlob;
    CryptBlob.cbData = Store.GetLength ();
    CryptBlob.pbData = reinterpret_cast<BYTE *>(Store.GetBuffer ());
    HRESULT hr = Initialize (CERT_STORE_PROV_SERIALIZED, 0, CryptProv,
	dwFlagsLow, dwFlagsHigh, &CryptBlob);
    Store.ReleaseBuffer();
    return hr;
}

inline HRESULT CCertStore::InitLdapStore (
    LPCWSTR wszLdapQuery, DWORD dwFlagsLow, DWORD dwFlagsHigh,
    ATL::CCryptProv& CryptProv) throw()
{
    return Initialize (CERT_STORE_PROV_LDAP, 0, CryptProv,
	dwFlagsLow, dwFlagsHigh, wszLdapQuery);
}


inline HRESULT CCertStore::OpenSystemStore (
    LPCTSTR szSubsystemProtocol, ATL::CCryptProv& CryptProv) throw()
{
    ATLENSURE_RETURN(!*m_phStore);
    HCERTSTORE hStore = ::CertOpenSystemStore(CryptProv.GetHandle(),
	szSubsystemProtocol);
    ATLENSURE_RETURN_LASTWIN32HR(hStore);
    *m_phStore = hStore;
    return S_OK;
}

#ifdef WINCRYPTEX
inline HRESULT CCertStore::OpenContainerExtensionStore(ATL::CCryptProv &cProv) throw()
{
    ATLENSURE_RETURN(!*m_phStore);

    DWORD	dwFlags = CRYPT_FIRST;
    DWORD	cbExt = 0, cbExtMax = 0;
    CStringBlob extension;

    HRESULT hr = cProv.GetParam( PP_ENUM_CONTAINER_EXTENSION, NULL, &cbExtMax, CRYPT_FIRST );
    if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS))
	return S_OK;
    else if (FAILED(hr))
	return hr;	

    InitMemoryStore(CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG);

    while (cbExt = cbExtMax, SUCCEEDED(cProv.GetParam(PP_ENUM_CONTAINER_EXTENSION, reinterpret_cast<BYTE*>(extension.GetBufferSetLength(cbExt)), &cbExt, dwFlags)))
    {	    
	CONTAINER_EXTENSION *pExt = (CONTAINER_EXTENSION *)extension.GetBuffer();
	dwFlags &= ~CRYPT_FIRST;

	char	*szOID = NULL;
	CStringBlob	decoded;

	if (!pExt || !pExt->cbExtension) {
	    continue;
	}
	szOID = (char*)pExt->pbExtension + pExt->cbExtension;
	if (!szOID) {
	    continue;
	}
	//handle only known extension oids
	if (strcmp(szOID, szOID_CryptoPro_private_keys_extension_intermediate_store) &&
	    strcmp(szOID, szOID_CryptoPro_private_keys_extension_signature_trust_store) &&
	    strcmp(szOID, szOID_CryptoPro_private_keys_extension_exchange_trust_store)) {
	    continue;
	}

	DWORD cbDecoded = 0;
	CRYPT_SEQUENCE_OF_ANY *certs;
	DWORD i;

	if (!::CryptDecodeObject(
	    X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID,
	    pExt->pbExtension, pExt->cbExtension, 0, NULL, &cbDecoded))
	{
	    continue;
	}	    

	if (!::CryptDecodeObject( 
	    X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID, 
	    pExt->pbExtension, pExt->cbExtension, 0, decoded.GetBufferSetLength(cbDecoded), &cbDecoded))
	{
	    continue;
	}
	certs = (CRYPT_SEQUENCE_OF_ANY*)decoded.GetBuffer();
	for (i = 0; i < certs->cValue; i++) {
	    ::CertAddEncodedCertificateToStore( 
		*m_phStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, certs->rgValue[i].pbData, certs->rgValue[i].cbData, CERT_STORE_ADD_REPLACE_EXISTING, NULL );
	}
    }	
    return S_OK;
}
#endif

inline HRESULT CCertStore::AddStoreToCollection (
    const CCertStore& SiblingStore, DWORD dwUpdateFlag, DWORD dwPriority) throw()
{
    ATLENSURE_RETURN(*m_phStore);
    ATLENSURE_RETURN_LASTWIN32HR(::CertAddStoreToCollection(*m_phStore,
	SiblingStore.GetHandle(), dwUpdateFlag, dwPriority));
    try
    {
	// CollectionStore должно закрываться раньше добавленных в него
	// хранилищ. Для этого запоминаем добавляемые хранилища, увеличивая
	// их счётчики ссылок.
	m_collectedStores.push_back(SiblingStore);
    }
    catch(std::bad_alloc&)
    {
	return E_OUTOFMEMORY;
    }
    catch(...)
    {
	return E_FAIL;
    }
    return S_OK;
}

inline HRESULT CCertStore::EnumCertificatesInStore(
    PCCERT_CONTEXT pPrevCertContext, PCCERT_CONTEXT *ppCertContext) throw()
{
    ATLENSURE_RETURN(*m_phStore);
    ATLENSURE_RETURN(ppCertContext != NULL);

    *ppCertContext = ::CertEnumCertificatesInStore(
	*m_phStore, pPrevCertContext);
    if (!*ppCertContext)
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCertStore::EnumCertificatesInStore(CCertContext& cert) throw()
{
    PCCERT_CONTEXT pCertContext;
    HRESULT hr = EnumCertificatesInStore(cert.Detach(), &pCertContext);
    if (SUCCEEDED(hr)) {
	cert.Attach(pCertContext, TRUE);
    }

    return hr;
}

inline HRESULT CCertStore::EnumCRLsInStore(
    PCCRL_CONTEXT pPrevCRLContext, PCCRL_CONTEXT *ppCRLContext) throw()
{
    ATLENSURE_RETURN(*m_phStore);
    ATLENSURE_RETURN(ppCRLContext != NULL);

    *ppCRLContext = ::CertEnumCRLsInStore(*m_phStore, pPrevCRLContext);
    if (!*ppCRLContext)
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

#if defined _WIN32
inline HRESULT CCertStore::EnumSystemStore (
    DWORD dwFlags,
    void *pvSystemStoreLocationPara,
    void *pvArg,
    PFN_CERT_ENUM_SYSTEM_STORE pfnEnum) throw()
{
    if (!::CertEnumSystemStore (
	dwFlags, pvSystemStoreLocationPara, pvArg, pfnEnum))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}
#endif // _WIN32

#if defined _WIN32
inline HRESULT CCertStore::EnumPhysicalStore (
    const void *pvSystemStore,
    DWORD dwFlags,
    void *pvArg,
    PFN_CERT_ENUM_PHYSICAL_STORE pfnEnum) throw()
{
    if (!::CertEnumPhysicalStore (
	pvSystemStore, dwFlags, pvArg, pfnEnum))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}
#endif // _WIN32

#if defined _WIN32
inline HRESULT CCertStore::EnumSystemStoreLocation(
    void *pvArg,
    PFN_CERT_ENUM_SYSTEM_STORE_LOCATION pfnEnum,
    DWORD dwFlags) throw()
{
    if (!::CertEnumSystemStoreLocation(dwFlags, pvArg, pfnEnum))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}
#endif // _WIN32

#if defined _WIN32
inline HRESULT CCertStore::GetProperty(
    DWORD dwPropId,
    BYTE * pbData,
    DWORD * pdwDataLen) throw()
{
    ATLENSURE_RETURN(*m_phStore);

    if (!::CertGetStoreProperty (*m_phStore, dwPropId, pbData, pdwDataLen))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}
#endif // _WIN32

#if defined _WIN32
inline HRESULT CCertStore::SetProperty(
    DWORD dwPropId,
    CRYPT_DATA_BLOB * pbData,
    DWORD dwFlags) throw()
{
    ATLENSURE_RETURN(*m_phStore);
    if (!::CertSetStoreProperty (*m_phStore, dwPropId, dwFlags, pbData))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}
#endif /* _WIN32 */

#if defined _WIN32
inline HRESULT CCertStore::GetProperty(DWORD dwPropId,
    CStringBlob &Result) //throw(...)
{
    DWORD dwDataLen = 0;
    HRESULT hr = GetProperty(dwPropId, NULL, &dwDataLen);
    if (FAILED(hr))
	return hr;
    hr = GetProperty(dwPropId,
	reinterpret_cast<BYTE *>(Result.GetBuffer(dwDataLen)), &dwDataLen);
    Result.ReleaseBufferSetLength (dwDataLen);
    return hr;
}
#endif /* _WIN32 */

#if defined _WIN32
inline HRESULT CCertStore::SetProperty(DWORD dwPropId, const CStringBlob &Data,
    DWORD dwFlags) throw()
{
    CStringBlob& Blob = const_cast<CStringBlob&>(Data);
    CRYPT_DATA_BLOB CryptBlob;
    CryptBlob.cbData = Blob.GetLength ();
    CryptBlob.pbData = reinterpret_cast<BYTE *>(Blob.GetBuffer ());
    HRESULT hr = SetProperty (dwPropId, &CryptBlob, dwFlags);
    Blob.ReleaseBuffer();
    return hr;
}
#endif /* _WIN32 */

#if defined _WIN32
inline HRESULT CCertStore::GetLocalizedName(
    LPWSTR wszBuf,
    DWORD *pdwStringLength) throw()
{
    DWORD dwDataLen = 0;
    DWORD *pwdDataLen = &dwDataLen;
    if (!pdwStringLength)
	pwdDataLen = 0;
    else
	dwDataLen = *pdwStringLength * sizeof(WCHAR);

    HRESULT hr = GetProperty (CERT_STORE_LOCALIZED_NAME_PROP_ID,
	reinterpret_cast<BYTE *>(wszBuf), pwdDataLen);

    if (pdwStringLength)
	*pdwStringLength = *pwdDataLen / sizeof(WCHAR);
    return hr;
}
#endif /* _WIN32 */

#if defined _WIN32
inline HRESULT CCertStore::SetLocalizedName(
    LPWSTR wszBuf) throw()
{
    CRYPT_DATA_BLOB blob;
    blob.pbData = reinterpret_cast<BYTE *>(wszBuf);
    blob.cbData = DWORD((wcslen(wszBuf)+1) * sizeof(WCHAR));

    return SetProperty(CERT_STORE_LOCALIZED_NAME_PROP_ID,
	&blob, 0);
}
#endif /* _WIN32 */

#if defined _WIN32
inline HRESULT CCertStore::GetLocalizedName(
    ATL::CAtlStringW& Result) //throw(...)
{
    DWORD dwStringLength = 0;
    HRESULT hr = GetLocalizedName (NULL, &dwStringLength);
    if (FAILED(hr))
	return hr;
    hr = GetLocalizedName (Result.GetBuffer(dwStringLength), &dwStringLength);
    Result.ReleaseBufferSetLength (dwStringLength - 1);
    ATLASSERT((size_t)Result.GetLength() == wcslen(Result.GetString()));
    return hr;
}
#endif /* _WIN32 */

inline HRESULT CCertStore::Commit() throw()
{
    ATLENSURE_RETURN(*m_phStore);
    ATLENSURE_RETURN_LASTWIN32HR(::CertControlStore(*m_phStore,
	0, CERT_STORE_CTRL_COMMIT, NULL));
    return S_OK;
}

inline HRESULT CCertStore::AddCertificateContext (
    PCCERT_CONTEXT pCertContext,
    DWORD dwAddDisposition, PCCERT_CONTEXT *ppStoreContext) throw()
{
    ATLENSURE_RETURN(*m_phStore);
    if (!::CertAddCertificateContextToStore (*m_phStore, pCertContext,
	dwAddDisposition, ppStoreContext))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCertStore::AddCertificateContextAlways (
    PCCERT_CONTEXT pCertContext, PCCERT_CONTEXT *ppStoreContext) throw()
{
    return AddCertificateContext (pCertContext,
	CERT_STORE_ADD_ALWAYS, ppStoreContext);
}

inline HRESULT CCertStore::AddCertificateContextNew (
    PCCERT_CONTEXT pCertContext, PCCERT_CONTEXT *ppStoreContext) throw()
{
    return AddCertificateContext (pCertContext,
	CERT_STORE_ADD_NEW, ppStoreContext);
}

inline HRESULT CCertStore::AddCertificateContextReplaceExisting (
    PCCERT_CONTEXT pCertContext, PCCERT_CONTEXT *ppStoreContext) throw()
{
    return AddCertificateContext (pCertContext,
	CERT_STORE_ADD_REPLACE_EXISTING, ppStoreContext);
}

inline HRESULT CCertStore::AddCertificateContextReplaceExistingInheritProperties (
    PCCERT_CONTEXT pCertContext, PCCERT_CONTEXT *ppStoreContext) throw()
{
    return AddCertificateContext (pCertContext,
	CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, ppStoreContext);
}

inline HRESULT CCertStore::AddCertificateContextUseExisting (
    PCCERT_CONTEXT pCertContext, PCCERT_CONTEXT *ppStoreContext) throw()
{
    return AddCertificateContext (pCertContext,
	CERT_STORE_ADD_USE_EXISTING, ppStoreContext);
}


inline HRESULT CCertStore::AddEncodedCertificate (
    const BYTE *pbCertEncoded, DWORD cbCertEncoded, DWORD dwAddDisposition,
    PCCERT_CONTEXT *ppCertContext, DWORD dwCertEncodingType) throw()
{
    ATLENSURE_RETURN(*m_phStore);
    if (!::CertAddEncodedCertificateToStore(*m_phStore,
	dwCertEncodingType, pbCertEncoded, cbCertEncoded,
	dwAddDisposition, ppCertContext))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCertStore::AddEncodedCertificateAlways (
    const BYTE *pbCertEncoded, DWORD cbCertEncoded,
    PCCERT_CONTEXT *ppCertContext, DWORD dwCertEncodingType) throw()
{
    return AddEncodedCertificate (pbCertEncoded, cbCertEncoded,
	CERT_STORE_ADD_ALWAYS, ppCertContext, dwCertEncodingType);
}

inline HRESULT CCertStore::AddEncodedCertificateNew (
    const BYTE *pbCertEncoded, DWORD cbCertEncoded,
    PCCERT_CONTEXT *ppCertContext, DWORD dwCertEncodingType) throw()
{
    return AddEncodedCertificate (pbCertEncoded, cbCertEncoded,
	CERT_STORE_ADD_NEW, ppCertContext, dwCertEncodingType);
}

inline HRESULT CCertStore::AddEncodedCertificateReplaceExisting (
    const BYTE *pbCertEncoded, DWORD cbCertEncoded,
    PCCERT_CONTEXT *ppCertContext, DWORD dwCertEncodingType) throw()
{
    return AddEncodedCertificate (pbCertEncoded, cbCertEncoded,
	CERT_STORE_ADD_REPLACE_EXISTING, ppCertContext, dwCertEncodingType);
}

inline HRESULT CCertStore::AddEncodedCertificateReplaceExistingInheritProperties (
    const BYTE *pbCertEncoded, DWORD cbCertEncoded,
    PCCERT_CONTEXT *ppCertContext, DWORD dwCertEncodingType) throw()
{
    return AddEncodedCertificate (pbCertEncoded, cbCertEncoded,
	CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES,
	ppCertContext, dwCertEncodingType);
}

inline HRESULT CCertStore::AddEncodedCertificateUseExisting (
    const BYTE *pbCertEncoded, DWORD cbCertEncoded,
    PCCERT_CONTEXT *ppCertContext, DWORD dwCertEncodingType) throw()
{
    return AddEncodedCertificate (pbCertEncoded, cbCertEncoded,
	CERT_STORE_ADD_USE_EXISTING, ppCertContext, dwCertEncodingType);
}

inline HRESULT CCertStore::AddEncodedCertificate (const CStringBlob& CertEncoded,
    DWORD dwAddDisposition, PCCERT_CONTEXT *ppCertContext,
    DWORD dwCertEncodingType) throw()
{
    return AddEncodedCertificate(
	reinterpret_cast<const BYTE *>(CertEncoded.GetString()),
	CertEncoded.GetLength(),
	dwAddDisposition, ppCertContext, dwCertEncodingType);
}

inline HRESULT CCertStore::AddEncodedCertificateAlways (
    const CStringBlob& CertEncoded, PCCERT_CONTEXT *ppCertContext,
    DWORD dwCertEncodingType) throw()
{
    return AddEncodedCertificateAlways (
	reinterpret_cast<const BYTE *>(CertEncoded.GetString()),
	CertEncoded.GetLength(),
	ppCertContext, dwCertEncodingType);
}

inline HRESULT CCertStore::AddEncodedCertificateNew (const CStringBlob& CertEncoded,
    PCCERT_CONTEXT *ppCertContext, DWORD dwCertEncodingType) throw()
{
    CStringBlob& Cert = const_cast<CStringBlob&>(CertEncoded);
    HRESULT hr = AddEncodedCertificateNew (
	reinterpret_cast<BYTE *>(Cert.GetBuffer ()), Cert.GetLength (),
	ppCertContext, dwCertEncodingType);
    Cert.ReleaseBuffer();
    return hr;
}

inline HRESULT CCertStore::AddEncodedCertificateReplaceExisting (
    const CStringBlob& CertEncoded, PCCERT_CONTEXT *ppCertContext,
    DWORD dwCertEncodingType) throw()
{
    CStringBlob& Cert = const_cast<CStringBlob&>(CertEncoded);
    HRESULT hr = AddEncodedCertificateReplaceExisting (
	reinterpret_cast<BYTE *>(Cert.GetBuffer ()), Cert.GetLength (),
	ppCertContext, dwCertEncodingType);
    Cert.ReleaseBuffer();
    return hr;
}

inline HRESULT
CCertStore::AddEncodedCertificateReplaceExistingInheritProperties (
    const CStringBlob& CertEncoded, PCCERT_CONTEXT *ppCertContext,
    DWORD dwCertEncodingType) throw()
{
    CStringBlob& Cert = const_cast<CStringBlob&>(CertEncoded);
    HRESULT hr = AddEncodedCertificateReplaceExistingInheritProperties (
	reinterpret_cast<BYTE *>(Cert.GetBuffer ()), Cert.GetLength (),
	ppCertContext, dwCertEncodingType);
    Cert.ReleaseBuffer();
    return hr;
}

inline HRESULT CCertStore::AddEncodedCertificateUseExisting (
    const CStringBlob& CertEncoded, PCCERT_CONTEXT *ppCertContext,
    DWORD dwCertEncodingType) throw()
{
    CStringBlob& Cert = const_cast<CStringBlob&>(CertEncoded);
    HRESULT hr = AddEncodedCertificateUseExisting (
	reinterpret_cast<BYTE *>(Cert.GetBuffer ()), Cert.GetLength (),
	ppCertContext, dwCertEncodingType);
    Cert.ReleaseBuffer();
    return hr;
}

inline HRESULT CCertStore::AddCRLContext (
    PCCRL_CONTEXT pCrlContext,
    DWORD dwAddDisposition, PCCRL_CONTEXT *ppStoreContext) throw()
{
    ATLENSURE_RETURN(*m_phStore);
    if (!::CertAddCRLContextToStore (*m_phStore, pCrlContext,
	dwAddDisposition, ppStoreContext))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCertStore::AddCRLContextAlways (
    PCCRL_CONTEXT pCrlContext, PCCRL_CONTEXT *ppStoreContext) throw()
{
    return AddCRLContext (pCrlContext,
	CERT_STORE_ADD_ALWAYS, ppStoreContext);
}

inline HRESULT CCertStore::AddCRLContextNew (
    PCCRL_CONTEXT pCrlContext, PCCRL_CONTEXT *ppStoreContext) throw()
{
    return AddCRLContext (pCrlContext,
	CERT_STORE_ADD_NEW, ppStoreContext);
}

inline HRESULT CCertStore::AddCRLContextReplaceExisting (
    PCCRL_CONTEXT pCrlContext, PCCRL_CONTEXT *ppStoreContext) throw()
{
    return AddCRLContext (pCrlContext,
	CERT_STORE_ADD_REPLACE_EXISTING, ppStoreContext);
}

inline HRESULT CCertStore::AddCRLContextReplaceExistingInheritProperties (
    PCCRL_CONTEXT pCrlContext, PCCRL_CONTEXT *ppStoreContext) throw()
{
    return AddCRLContext (pCrlContext,
	CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, ppStoreContext);
}

inline HRESULT CCertStore::AddCRLContextUseExisting (
    PCCRL_CONTEXT pCrlContext, PCCRL_CONTEXT *ppStoreContext) throw()
{
    return AddCRLContext (pCrlContext,
	CERT_STORE_ADD_USE_EXISTING, ppStoreContext);
}

inline HRESULT CCertStore::CountCertsInStore (DWORD& dwCertNumber) throw()
{
    CCertContext ctx;
    HRESULT hr = EnumCertificatesInStore(ctx);
    dwCertNumber = 0;
    while (SUCCEEDED(hr))
    {
	dwCertNumber++;
	hr = EnumCertificatesInStore(ctx);
    }
    return S_OK;
}

inline HRESULT CCertStore::Save (
    DWORD dwSaveAs, DWORD dwSaveTo,
    void* pvSaveToPara, DWORD dwFlags,
    DWORD dwMsgAndCertEncodingType) throw()
{
    ATLENSURE_RETURN(*m_phStore);
    if (!::CertSaveStore(*m_phStore,
	dwMsgAndCertEncodingType, dwSaveAs, dwSaveTo, pvSaveToPara, dwFlags))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCertStore::SaveAsStore (
    DWORD dwSaveTo, void* pvSaveToPara) throw()
{
    return Save(CERT_STORE_SAVE_AS_STORE, dwSaveTo,
	pvSaveToPara, 0);
}

inline HRESULT CCertStore::SaveAsPkcs7 (
    DWORD dwSaveTo,
    void* pvSaveToPara,
    DWORD dwMsgAndCertEncodingType) throw()
{
    return Save(CERT_STORE_SAVE_AS_PKCS7, dwSaveTo,
	pvSaveToPara, 0, dwMsgAndCertEncodingType);
}

inline HRESULT CCertStore::SaveToFile (
    DWORD dwSaveAs, HANDLE hStoreFileHandle,
    DWORD dwMsgAndCertEncodingType) throw()
{
    return Save(dwSaveAs, CERT_STORE_SAVE_TO_FILE,
	hStoreFileHandle, 0, dwMsgAndCertEncodingType);
}

inline HRESULT CCertStore::SaveToMemory (
    DWORD dwSaveAs, CRYPT_DATA_BLOB *pStoreBlob,
    DWORD dwMsgAndCertEncodingType) throw()
{
    return Save(dwSaveAs, CERT_STORE_SAVE_TO_MEMORY,
	pStoreBlob, 0, dwMsgAndCertEncodingType);
}

inline HRESULT CCertStore::SaveToMemory ( DWORD dwSaveAs, CStringBlob& StoreBlob,
    DWORD dwMsgAndCertEncodingType) //throw(...)
{
    CRYPT_DATA_BLOB CryptBlob;
    CryptBlob.cbData = 0;
    CryptBlob.pbData = NULL;
    HRESULT hr = SaveToMemory (dwSaveAs, &CryptBlob, dwMsgAndCertEncodingType);
    if (FAILED(hr))
	return hr;
    CryptBlob.pbData = reinterpret_cast<BYTE *>(
	StoreBlob.GetBuffer (CryptBlob.cbData));
    hr = SaveToMemory (dwSaveAs, &CryptBlob, dwMsgAndCertEncodingType);
    StoreBlob.ReleaseBufferSetLength (CryptBlob.cbData);
    return hr;
}

inline HRESULT CCertStore::SaveToFileName (
    DWORD dwSaveAs, LPCWSTR wszStoreFileName,
    DWORD dwMsgAndCertEncodingType) throw()
{
    return Save(dwSaveAs, CERT_STORE_SAVE_TO_FILENAME_W,
	const_cast<LPWSTR>(wszStoreFileName), 0, dwMsgAndCertEncodingType);
}

inline HRESULT CCertStore::SaveToFileName (
    DWORD dwSaveAs, LPCSTR szStoreFileName,
    DWORD dwMsgAndCertEncodingType) throw()
{
    return Save(dwSaveAs, CERT_STORE_SAVE_TO_FILENAME_A,
	const_cast<LPSTR>(szStoreFileName), 0, dwMsgAndCertEncodingType);
}

inline HRESULT CCertStore::SaveAsStoreToFile (HANDLE hStoreFileHandle) throw()
{
    return SaveAsStore(CERT_STORE_SAVE_TO_FILE, hStoreFileHandle);
}

inline HRESULT CCertStore::SaveAsStoreToMemory (
    CRYPT_DATA_BLOB *pStoreBlob) throw()
{
    return SaveAsStore(CERT_STORE_SAVE_TO_MEMORY, pStoreBlob);
}

inline HRESULT CCertStore::SaveAsStoreToMemory (CStringBlob& StoreBlob) //throw(...)
{
    CRYPT_DATA_BLOB CryptBlob;
    CryptBlob.cbData = 0;
    CryptBlob.pbData = NULL;
    HRESULT hr = SaveAsStoreToMemory (&CryptBlob);
    if (FAILED(hr))
	return hr;
    CryptBlob.pbData = reinterpret_cast<BYTE *>(
	StoreBlob.GetBuffer (CryptBlob.cbData));
    hr = SaveAsStoreToMemory (&CryptBlob);
    StoreBlob.ReleaseBufferSetLength (CryptBlob.cbData);
    return hr;
}

inline HRESULT CCertStore::SaveAsStoreToFileName (
    LPCWSTR wszStoreFileName) throw()
{
    return SaveAsStore(CERT_STORE_SAVE_TO_FILENAME_W,
	const_cast<LPWSTR>(wszStoreFileName));
}

inline HRESULT CCertStore::SaveAsStoreToFileName (
    LPCSTR szStoreFileName) throw()
{
    return SaveAsStore(CERT_STORE_SAVE_TO_FILENAME_A,
	const_cast<LPSTR>(szStoreFileName));
}

inline HRESULT CCertStore::SaveAsPkcs7ToFile (
    HANDLE hStoreFileHandle, DWORD dwMsgAndCertEncodingType) throw()
{
    return SaveAsPkcs7(CERT_STORE_SAVE_TO_FILE, hStoreFileHandle,
	dwMsgAndCertEncodingType);
}

inline HRESULT CCertStore::SaveAsPkcs7ToMemory (
    CRYPT_DATA_BLOB *pStoreBlob, DWORD dwMsgAndCertEncodingType) throw()
{
    return SaveAsPkcs7(CERT_STORE_SAVE_TO_MEMORY, pStoreBlob,
	dwMsgAndCertEncodingType);
}

inline HRESULT CCertStore::SaveAsPkcs7ToMemory (CStringBlob& StoreBlob,
    DWORD dwMsgAndCertEncodingType) throw()
{
    CRYPT_DATA_BLOB CryptBlob;
    CryptBlob.cbData = 0;
    CryptBlob.pbData = NULL;
    HRESULT hr = SaveAsPkcs7ToMemory (&CryptBlob, dwMsgAndCertEncodingType);
    if (FAILED(hr))
	return hr;
    CryptBlob.pbData = reinterpret_cast<BYTE *>(
	StoreBlob.GetBuffer (CryptBlob.cbData));
    hr = SaveAsPkcs7ToMemory (&CryptBlob, dwMsgAndCertEncodingType);
    StoreBlob.ReleaseBufferSetLength (CryptBlob.cbData);
    return hr;
}

inline HRESULT CCertStore::SaveAsPkcs7ToFileName (
    LPCWSTR wszStoreFileName, DWORD dwMsgAndCertEncodingType) throw()
{
    return SaveAsPkcs7(CERT_STORE_SAVE_TO_FILENAME_W,
	const_cast<LPWSTR>(wszStoreFileName), dwMsgAndCertEncodingType);
}

inline HRESULT CCertStore::SaveAsPkcs7ToFileName (
    LPCSTR szStoreFileName, DWORD dwMsgAndCertEncodingType) throw()
{
    return SaveAsPkcs7(CERT_STORE_SAVE_TO_FILENAME_A,
	const_cast<LPSTR>(szStoreFileName), dwMsgAndCertEncodingType);
}

inline HRESULT CCertStore::FindCertificate (
    PCCERT_CONTEXT *ppCertContext, DWORD dwFindType,
    const void* pvFindPara /*= NULL*/, DWORD dwFindFlags /*= 0*/,
    PCCERT_CONTEXT pPrevCertContext /*= NULL*/,
    DWORD dwCertEncodingType
    /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) throw()
{
    ATLENSURE_RETURN(*m_phStore);
    ATLENSURE_RETURN(ppCertContext != NULL);

    *ppCertContext = ::CertFindCertificateInStore(
	*m_phStore, dwCertEncodingType, dwFindFlags, dwFindType,
	pvFindPara, pPrevCertContext);
    if (!*ppCertContext)
    {
	return ATL::AtlHresultFromLastError();
    }
    return S_OK;
}

inline HRESULT CCertStore::FindCertificate(
    CCertContext &ctx, DWORD dwFindType, const void* pvFindPara /*= NULL*/,
    DWORD dwFindFlags /*= 0*/, DWORD dwCertEncodingType
    /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) throw()
{
    ATLENSURE_RETURN(*m_phStore);

    PCCERT_CONTEXT pCert = NULL;
    ATL_HR_ERRORCHECK_RETURN(FindCertificate(&pCert, dwFindType, pvFindPara,
	dwFindFlags, ctx.Detach(), dwCertEncodingType));
    ctx.Attach(pCert, TRUE);
    return S_OK;
}

inline HRESULT CCertStore::FindCertificateExisting(
    PCCERT_CONTEXT pCertToFind, PCCERT_CONTEXT *ppCertContext /*= NULL*/,
    DWORD dwCertEncodingType
    /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) throw()
{
    ATLENSURE_RETURN(*m_phStore);

    CCertContext tmp;
    ATL_HR_ERRORCHECK_RETURN(FindCertificate(tmp, CERT_FIND_EXISTING,
	pCertToFind, 0, dwCertEncodingType));
    if (ppCertContext != NULL)
    {
	*ppCertContext = tmp.Detach();
    }
    return S_OK;
}

inline HRESULT CCertStore::FindCertificateExisting(
    CCertContext& CertToFind, CCertContext *pCert /*= NULL*/,
    DWORD dwCertEncodingType
    /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) throw()
{
    ATLENSURE_RETURN(*m_phStore);

    CCertContext tmp;
    ATL_HR_ERRORCHECK_RETURN(FindCertificate(tmp, CERT_FIND_EXISTING,
	CertToFind.GetHandle(), 0, dwCertEncodingType));
    if (pCert != NULL)
    {
        *pCert = tmp;
    }

    return S_OK;
}

inline HRESULT CCertStore::FindCRL (PCCRL_CONTEXT *ppCrlContext,
    DWORD dwFindType, const void* pvFindPara /*= NULL*/,
    DWORD dwFindFlags /*= 0*/, PCCRL_CONTEXT pPrevCrlContext /*= NULL*/,
    DWORD dwCertEncodingType /*= 0*/) throw()
{
    ATLENSURE_RETURN(*m_phStore);
    ATLENSURE_RETURN(ppCrlContext != NULL);

    *ppCrlContext = ::CertFindCRLInStore(
	*m_phStore, dwCertEncodingType, dwFindFlags, dwFindType,
	pvFindPara, pPrevCrlContext);
    if (!*ppCrlContext)
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCertStore::FindCRL (CCRLContext &ctx, DWORD dwFindType, 
    const void* pvFindPara /*= NULL*/, DWORD dwFindFlags /*= 0*/,
    PCCRL_CONTEXT pPrevCrlContext /*= NULL*/,
    DWORD dwCertEncodingType /*= 0*/) throw()
{
    ATLENSURE_RETURN(*m_phStore);
    ATLASSERT(ctx.GetHandle() == NULL);
    if (ctx.GetHandle() != NULL)
	ctx.Destroy();
    PCCRL_CONTEXT pCrl = NULL;
    HRESULT hr = FindCRL(&pCrl, dwFindType, pvFindPara, dwFindFlags,
	pPrevCrlContext, dwCertEncodingType);
    if (FAILED(hr))
	return hr;
    ctx.Attach(pCrl, TRUE);
    return S_OK;
}

#ifdef CRYPTUI
//dwEnumFlags:
//  CERT_SYSTEM_STORE_CURRENT_USER
//  CERT_SYSTEM_STORE_CURRENT_SERVICE
//  CERT_SYSTEM_STORE_LOCAL_MACHINE
//  CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY
//  CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY
//  CERT_SYSTEM_STORE_SERVICES
//  CERT_SYSTEM_STORE_USERS
//  CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE
//dwFlags:
//  CRYPTUI_ALLOW_PHYSICAL_STORE_VIEW
//  CRYPTUI_RETURN_READ_ONLY_STORE
//  CRYPTUI_DISPLAY_WRITE_ONLY_STORES
//  CRYPTUI_VALIDATE_STORES_AS_WRITABLE
inline HRESULT CCertStoreUI::SelectStore (
    HWND hwndParent, LPCWSTR szTitle, LPCWSTR szDisplayString,
    DWORD dwEnumFlags, DWORD dwFlags) throw()
{
    ATLENSURE_RETURN(!*m_phStore);

    CRYPTUI_SELECTSTORE_STRUCTW pcss = { sizeof (pcss) };
    // Все остальные поля = 0
    STORESFORSELCTION_STRUCT sfs = {}; // Все остальные поля = 0
    STORENUMERATION_STRUCT ses = {}; // Все остальные поля = 0

    pcss.hwndParent = hwndParent;
    pcss.dwFlags = dwFlags;
    pcss.szTitle = szTitle;
    pcss.szDisplayString = szDisplayString;
    pcss.pStoresForSelection = &sfs;

    //pcss.pValidateStoreCallback = NULL;
    //pcss.pvCallbackData = NULL;

    //sfs.cStores = 0;
    //sfs.rghStores = 0;
    sfs.cEnumerationStructs = 1;
    sfs.rgEnumerationStructs = &ses;

    ses.dwFlags = dwEnumFlags;
    //ses.pvSystemStoreLocationPara = 0;

    HCERTSTORE hStore = ::CryptUIDlgSelectStoreW (&pcss);
    if (!hStore)
    {
	// Если пользователь нажал Cancel, возвращается S_OK, переделываем его
	// в код ошибки ERROR_CANCELLED
	HRESULT hr = ATL::AtlHresultFromLastError();
	if (S_OK == hr)
	{
	    hr = ATL::AtlHresultFromWin32 (ERROR_CANCELLED);
	}
	return hr;
    }
    *m_phStore = hStore;
    return S_OK;
}

inline HRESULT CCertStoreUI::SelectStore (
    HWND hwndParent, LPCSTR szTitle, LPCSTR szDisplayString,
    DWORD dwEnumFlags, DWORD dwFlags) throw()
{
    ATLENSURE_RETURN(!*m_phStore);

    CRYPTUI_SELECTSTORE_STRUCTA pcss = { sizeof (pcss) };
    // Все остальные поля = 0
    STORESFORSELCTION_STRUCT sfs = {}; // Все остальные поля = 0
    STORENUMERATION_STRUCT ses = {}; // Все остальные поля = 0

    pcss.hwndParent = hwndParent;
    pcss.dwFlags = dwFlags;
    pcss.szTitle = szTitle;
    pcss.szDisplayString = szDisplayString;
    pcss.pStoresForSelection = &sfs;

    //pcss.pValidateStoreCallback = NULL;
    //pcss.pvCallbackData = NULL;

    //sfs.cStores = 0;
    //sfs.rghStores = 0;
    sfs.cEnumerationStructs = 1;
    sfs.rgEnumerationStructs = &ses;

    ses.dwFlags = dwEnumFlags;
    //ses.pvSystemStoreLocationPara = 0;

    HCERTSTORE hStore = ::CryptUIDlgSelectStoreA (&pcss);
    if (!hStore)
    {
	// Если пользователь нажал Cancel, возвращается S_OK, переделываем его
	// в код ошибки ERROR_CANCELLED
	HRESULT hr = ATL::AtlHresultFromLastError();
	if (S_OK == hr)
	{
	    hr = ATL::AtlHresultFromWin32 (ERROR_CANCELLED);
	}
	return hr;
    }
    *m_phStore = hStore;
    return S_OK;
}

//dwDontUseColumn:
//  CRYPTUI_SELECT_ISSUEDTO_COLUMN
//  CRYPTUI_SELECT_ISSUEDBY_COLUMN
//  CRYPTUI_SELECT_INTENDEDUSE_COLUMN
//  CRYPTUI_SELECT_FRIENDLYNAME_COLUMN
//  CRYPTUI_SELECT_LOCATION_COLUMN
//  CRYPTUI_SELECT_EXPIRATION_COLUMN
inline HRESULT CCertStoreUI::SelectCertificate (
    HWND hwndParent, CCertContext &ctx, LPCWSTR szTitle,
    LPCWSTR szDisplayString, DWORD dwDontUseColumn,
    HCERTSTORE hSelectedCertStore, PFNCFILTERPROC pFilterCallback,
    PFNCCERTDISPLAYPROC pDisplayCallback, void *pvCallbackData) throw()
{
    ATLENSURE_RETURN(*m_phStore);

    CRYPTUI_SELECTCERTIFICATE_STRUCTW pcsc = { sizeof (pcsc) };
    // Все остальные поля = 0
    pcsc.hwndParent = hwndParent;
    if (hSelectedCertStore)
	pcsc.dwFlags |= CRYPTUI_SELECTCERT_MULTISELECT;
    pcsc.dwDontUseColumn = dwDontUseColumn;
    pcsc.szTitle = szTitle;
    pcsc.szDisplayString = szDisplayString;
    pcsc.pFilterCallback = pFilterCallback;
    pcsc.pDisplayCallback = pDisplayCallback;
    pcsc.pvCallbackData = pvCallbackData;
    pcsc.cDisplayStores = 1;
    pcsc.rghDisplayStores = m_phStore.get();
    //pcsc.cStores;            // OPTIONAL
    //pcsc.rghStores;          // OPTIONAL
    //pcsc.cPropSheetPages;    // OPTIONAL
    //pcsc.rgPropSheetPages;   // OPTIONAL
    pcsc.hSelectedCertStore = hSelectedCertStore;

    PCCERT_CONTEXT cc;
    cc = ::CryptUIDlgSelectCertificateW (&pcsc);
    if (cc) {
	ctx.Destroy();
	ctx.Attach (cc, TRUE);
	ATL_HR_ERRORCHECK_RETURN(ctx.DetachFromStore());
	return S_OK;
    }
    HRESULT hr = ATL::AtlHresultFromLastError();
    // Если пользователь нажал Cancel, возвращается S_OK,
    // переделываем его в код ошибки ERROR_CANCELLED
    if (S_OK == hr)
	hr = ATL::AtlHresultFromWin32(ERROR_CANCELLED);
    return hr;
}

inline HRESULT CCertStoreUI::SelectCertificate (
    HWND hwndParent, CCertContext &ctx, LPCSTR szTitle,
    LPCSTR szDisplayString, DWORD dwDontUseColumn,
    HCERTSTORE hSelectedCertStore, PFNCFILTERPROC pFilterCallback,
    PFNCCERTDISPLAYPROC pDisplayCallback, void *pvCallbackData) throw()
{
    ATLENSURE_RETURN(*m_phStore);

    CRYPTUI_SELECTCERTIFICATE_STRUCTA pcsc = { sizeof (pcsc) };
    // Все остальные поля = 0
    pcsc.hwndParent = hwndParent;
    if (hSelectedCertStore)
	pcsc.dwFlags |= CRYPTUI_SELECTCERT_MULTISELECT;
    pcsc.dwDontUseColumn = dwDontUseColumn;
    pcsc.szTitle = szTitle;
    pcsc.szDisplayString = szDisplayString;
    pcsc.pFilterCallback = pFilterCallback;
    pcsc.pDisplayCallback = pDisplayCallback;
    pcsc.pvCallbackData = pvCallbackData;
    pcsc.cDisplayStores = 1;
    pcsc.rghDisplayStores = m_phStore.get();
    //pcsc.cStores;            // OPTIONAL
    //pcsc.rghStores;          // OPTIONAL
    //pcsc.cPropSheetPages;    // OPTIONAL
    //pcsc.rgPropSheetPages;   // OPTIONAL
    pcsc.hSelectedCertStore = hSelectedCertStore;

    PCCERT_CONTEXT cc;
    cc = ::CryptUIDlgSelectCertificateA (&pcsc);
    if (cc) {
	ctx.Attach (cc, TRUE);
	return S_OK;
    }
    HRESULT hr = ATL::AtlHresultFromLastError();
    // Если пользователь нажал Cancel, возвращается S_OK,
    // переделываем его в код ошибки ERROR_CANCELLED
    if (S_OK == hr)
	hr = ATL::AtlHresultFromWin32(ERROR_CANCELLED);
    return hr;
}

inline HRESULT CCertStoreUI::SelectCertificate (
    HWND hwndParent, CCertContext &ctx,
    LPCWSTR szTitle, DWORD dwCertificateDlgFlags,
    LPCWSTR szCertificateDlgTitle, LPCWSTR szDisplayString,
    DWORD dwDontUseColumn, HCERTSTORE hSelectedCertStore,
    PFNCFILTERPROC pFilterCallback) throw()
{
    DWORD_PTR CallBackData[2];
    CallBackData[0] = (DWORD_PTR)szCertificateDlgTitle;
    CallBackData[1] = dwCertificateDlgFlags;

    return SelectCertificate (hwndParent, ctx, szTitle, szDisplayString,
	dwDontUseColumn, hSelectedCertStore, pFilterCallback,
	SelectCertSetViewParamDisplayCallbackW, CallBackData);
}

inline HRESULT CCertStoreUI::SelectCertificate (
    HWND hwndParent, CCertContext &ctx,
    LPCSTR szTitle, DWORD dwCertificateDlgFlags,
    LPCSTR szCertificateDlgTitle, LPCSTR szDisplayString,
    DWORD dwDontUseColumn, HCERTSTORE hSelectedCertStore,
    PFNCFILTERPROC pFilterCallback) throw()
{
    DWORD_PTR CallBackData[2];
    CallBackData[0] = (DWORD_PTR)szCertificateDlgTitle;
    CallBackData[1] = dwCertificateDlgFlags;

    return SelectCertificate (hwndParent, ctx, szTitle, szDisplayString,
	dwDontUseColumn, hSelectedCertStore, pFilterCallback,
	SelectCertSetViewParamDisplayCallbackA, CallBackData);
}

// Callback-функция, предназначена для использования в функции
// SelectCertificate в качестве параметра pDisplayCallback
//
// В качестве pvCallbackData
// ей нужно передать массив из 2-х DWORD-ов, в первом должен лежать
// указатель на заголовок окна, во втором DWORD-е - флаги,
// которые будут переданы в CCertContext::View
inline BOOL WINAPI CCertStoreUI::SelectCertSetViewParamDisplayCallbackW (
    PCCERT_CONTEXT pCertContext, HWND hWndSelCertDlg,
    void *pvCallbackData) throw()
{
    LPCWSTR szTitle = NULL;
    DWORD dwFlags = 0;
    if (pvCallbackData) {
	szTitle = (LPCWSTR)(((DWORD_PTR *)pvCallbackData)[0]);
	dwFlags = (DWORD)(((DWORD_PTR *)pvCallbackData)[1]);
    }
    CCertContextUI ctx;
    ctx.Attach (pCertContext, TRUE);
    ctx.View (hWndSelCertDlg, szTitle, dwFlags);
    return TRUE;
}

// Callback-функция, предназначена для использования в функции
// SelectCertificate в качестве параметра pDisplayCallback
//
// В качестве pvCallbackData
// ей нужно передать массив из 2-х DWORD-ов, в первом должен лежать
// указатель на заголовок окна, во втором DWORD-е - флаги,
// которые будут переданы в CCertContext::View
inline BOOL WINAPI CCertStoreUI::SelectCertSetViewParamDisplayCallbackA (
    PCCERT_CONTEXT pCertContext, HWND hWndSelCertDlg,
    void *pvCallbackData) throw()
{
    LPCSTR szTitle = NULL;
    DWORD dwFlags = 0;
    if (pvCallbackData) {
	szTitle = (LPCSTR)(((DWORD_PTR *)pvCallbackData)[0]);
	dwFlags = (DWORD)(((DWORD_PTR *)pvCallbackData)[1]);
    }
    CCertContextUI ctx;
    ctx.Attach (pCertContext, TRUE);
    ctx.View (hWndSelCertDlg, szTitle, dwFlags);
    return TRUE;
}
#endif // CRYPTUI

inline CCryptProvEx::CCryptProvEx() throw() :
    CCryptProv()
{
}

inline CCryptProvEx::CCryptProvEx(const CCryptProvEx& prov) throw() :
    CCryptProv(prov)
{
}

inline CCryptProvEx::CCryptProvEx(const CCryptProv& prov) throw() :
    CCryptProv(prov)
{
}

inline CCryptProvEx::CCryptProvEx(HCRYPTPROV hProv, BOOL bTakeOwnership) throw() :
    CCryptProv(hProv, bTakeOwnership)
{
}

inline CCryptProvEx& CCryptProvEx::operator=( const CCryptProvEx& prov ) throw()
{
    CCryptProv::operator=(prov);
    return( *this );
}

inline CCryptProvEx& CCryptProvEx::operator=( const CCryptProv& prov ) throw()
{
    CCryptProv::operator=(prov);
    return( *this );
}

inline HRESULT CCryptProvEx::InitializeByCertificate(
    __in const CCertContext& Cert, __in DWORD dwFlags,
    __out_opt DWORD *pdwKeySpec, __reserved void *pvReserved) throw()
{
    DWORD dwSpec = 0;
    DWORD *pdwSpec = (pdwKeySpec) ? pdwKeySpec : &dwSpec;
    BOOL fCallerFreeProvOrNCryptKey = FALSE;
    HCRYPTPROV hProv = 0;
    if (!::CryptAcquireCertificatePrivateKey(Cert.GetHandle(), dwFlags,
	pvReserved, &hProv, pdwSpec, &fCallerFreeProvOrNCryptKey))
	return ATL::AtlHresultFromLastError();
    Attach(hProv, fCallerFreeProvOrNCryptKey);
    return S_OK;
}

inline HRESULT CCryptProvEx::SetClientHwnd(__in_opt HWND hWnd) throw()
{
    if (!::CryptSetProvParam(0, PP_CLIENT_HWND, (BYTE *)&hWnd, 0))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCryptProvEx::SetKeyExchangePin(__in_z_opt LPCSTR szPin) throw()
{
    BYTE *pin = reinterpret_cast<BYTE *>(const_cast<LPSTR>(szPin));
    return SetParam(PP_KEYEXCHANGE_PIN, pin);
}

inline HRESULT CCryptProvEx::SetKeyExchangePin(__in_z_opt LPCWSTR wszPin) throw()
{
    ATL::CAtlStringA Pin(wszPin);
    HRESULT hr = SetKeyExchangePin(Pin.GetString());
    // Временный буфер надо обнулить!!!
    SecureZeroString(Pin);
    return hr;
}

inline HRESULT CCryptProvEx::SetSignaturePin(__in_z_opt LPCSTR szPin) throw()
{
    BYTE *pin = reinterpret_cast<BYTE *>(const_cast<LPSTR>(szPin));
    return SetParam(PP_SIGNATURE_PIN, pin);
}

// Даже на WINCE ПИН-код - это LPSTR
inline HRESULT CCryptProvEx::SetSignaturePin(__in_z_opt LPCWSTR wszPin) throw()
{
    ATL::CAtlStringA Pin(wszPin);
    HRESULT hr = SetSignaturePin(Pin.GetString());
    // Временный буфер надо обнулить!!!
    SecureZeroString(Pin);
    return hr;
}

inline HRESULT CCryptProvEx::SetUseHardwareRng() throw()
{
    return SetParam(PP_USE_HARDWARE_RNG, NULL);
}

inline HRESULT CCryptProvEx::GetKeySetType( DWORD * pdwType ) throw( )
{
    DWORD dwLength = sizeof( DWORD );
    return GetParam( PP_KEYSET_TYPE, (BYTE *)pdwType, &dwLength );
}

inline HRESULT CCryptProvEx::GenRandom(
    __in ULONG nLength,
    __out_ecount(nLength) BYTE *pbBuffer) throw()
{
    return CCryptProv::GenRandom(nLength, pbBuffer);
}

inline HRESULT CCryptProvEx::GenRandom(
    __in ULONG nLength,
    __out CStringBlob& Result) //throw(...)
{
    BYTE *pbBuffer = reinterpret_cast<BYTE *>(Result.GetBuffer(nLength));
    HRESULT hr = CCryptProv::GenRandom(nLength, pbBuffer);
    if (FAILED(hr))
	return hr;
    Result.ReleaseBufferSetLength(nLength);
    return hr;
}

inline HRESULT CCryptProvEx::ExportPublicKeyInfo (
    __out_bcount_part(*pcbInfo, *pcbInfo) CERT_PUBLIC_KEY_INFO *pInfo,
    __inout DWORD *pcbInfo, __in DWORD dwKeySpec,
    __in DWORD dwCertEncodingType) throw()
{
    ATLENSURE_RETURN(m_hProv != 0);
    if (!::CryptExportPublicKeyInfo (m_hProv, dwKeySpec, dwCertEncodingType,
	pInfo, pcbInfo))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCryptProvEx::ExportPublicKeyInfo (
    __out CStringBlob &Result, __in DWORD dwKeySpec,
    __in DWORD dwCertEncodingType) //throw(...)
{
    DWORD cbInfo = 512;
    HRESULT hr = ExportPublicKeyInfo(
	reinterpret_cast<CERT_PUBLIC_KEY_INFO *>(Result.GetBuffer(cbInfo)),
	&cbInfo, dwKeySpec, dwCertEncodingType);
    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) {
	// В случае ERROR_MORE_DATA, в cbInfo вернули, скока надо
	hr = ExportPublicKeyInfo(
	    reinterpret_cast<CERT_PUBLIC_KEY_INFO *>(Result.GetBuffer(cbInfo)),
	    &cbInfo, dwKeySpec, dwCertEncodingType);
    }
    if (FAILED(hr))
	return hr;
    Result.ReleaseBufferSetLength (cbInfo);
    return hr;
}

inline HRESULT CCryptProvEx::SignAndEncodeCertificate(
    __out_bcount_part(*pcbEncoded, *pcbEncoded) BYTE *pbEncoded,
    __inout DWORD *pcbEncoded, __in LPCSTR lpszStructType,
    __in const void *pvStructInfo,
    __in PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm,
    __in DWORD dwKeySpec,
    __in DWORD dwCertEncodingType)
{
    ATLENSURE_RETURN(m_hProv != 0);
    if (!::CryptSignAndEncodeCertificate (m_hProv, dwKeySpec, dwCertEncodingType,
	lpszStructType, pvStructInfo, pSignatureAlgorithm, NULL, pbEncoded, pcbEncoded))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCryptProvEx::SignAndEncodeCertificate(
    __out CStringBlob &Result, __in LPCSTR lpszStructType,
    __in const void *pvStructInfo,
    __in PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm,
    __in DWORD dwKeySpec,
    __in DWORD dwCertEncodingType) //throw(...)
{
    DWORD cbEncoded = 2048;
    HRESULT hr = SignAndEncodeCertificate(
	reinterpret_cast<BYTE*>(Result.GetBuffer(cbEncoded)), &cbEncoded, lpszStructType,
        pvStructInfo, pSignatureAlgorithm, dwKeySpec, dwCertEncodingType);
    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) {
	// В случае ERROR_MORE_DATA, в cbInfo вернули необходимый размер буфера
	hr = SignAndEncodeCertificate(
	    reinterpret_cast<BYTE*>(Result.GetBuffer(cbEncoded)), &cbEncoded, lpszStructType,
            pvStructInfo, pSignatureAlgorithm, dwKeySpec, dwCertEncodingType);
    }
    if (FAILED(hr))
	return hr;
    Result.ReleaseBufferSetLength (cbEncoded);
    return hr;
}

inline HRESULT CCryptProvEx::GetName(
    __out_ecount_part_z(*pdwLength, *pdwLength) LPSTR szBuf,
    __inout DWORD *pdwLength) throw()
{
    ATLENSURE_RETURN(pdwLength != NULL);
    return CCryptProv::GetName(szBuf, pdwLength);
}

inline HRESULT CCryptProvEx::GetName(
    __out ATL::CAtlStringA& Result) //throw(...)
{
    ATL::CAtlStringA sResult;
    DWORD dwLength = 256;
    HRESULT hr = GetName(sResult.GetBuffer(dwLength), &dwLength);
    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) {
	// В случае ERROR_MORE_DATA, в dwLength вернули, скока надо
	hr = GetName(sResult.GetBuffer(dwLength), &dwLength);
    }
    if (FAILED(hr))
	return hr;
    sResult.ReleaseBufferSetLength (dwLength - 1);
    ATLASSERT((size_t)sResult.GetLength() == strlen(sResult.GetString()));
    Result = sResult;
    return hr;
}

inline HRESULT CCryptProvEx::GetName(
    __out ATL::CAtlStringW& Result) //throw(...)
{
    ATL::CAtlStringA ResultA;
    HRESULT hr = GetName(ResultA);
    if (FAILED(hr))
	return hr;
    Result = ResultA;
    return hr;
}

inline HRESULT CCryptProvEx::GetContainer(
    __out_ecount_part_z(*pdwLength, *pdwLength) LPSTR szBuf,
    __inout DWORD *pdwLength) throw()
{
    ATLENSURE_RETURN(pdwLength != NULL);
    return CCryptProv::GetContainer(szBuf, pdwLength);
}

inline HRESULT CCryptProvEx::GetContainer(
    __out ATL::CAtlStringA& Result) //throw(...)
{
    ATL::CAtlStringA sResult;
    DWORD dwLength = 256;
    HRESULT hr = GetContainer(sResult.GetBuffer(dwLength), &dwLength);
    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) {
	// В случае ERROR_MORE_DATA, в dwLength вернули, скока надо
	hr = GetContainer(sResult.GetBuffer(dwLength), &dwLength);
    }
    if (FAILED(hr))
	return hr;
    sResult.ReleaseBufferSetLength (dwLength - 1);
    ATLASSERT((size_t)sResult.GetLength() == strlen(sResult.GetString()));
    Result = sResult;
    return hr;
}

inline HRESULT CCryptProvEx::GetContainer(
    __out ATL::CAtlStringW& Result) //throw(...)
{
    ATL::CAtlStringA ResultA;
    HRESULT hr = GetContainer(ResultA);
    if (FAILED(hr))
	return hr;
    Result = ResultA;
    return hr;
}

inline HRESULT CCryptProvEx::GetUniqueContainer(
    __out_ecount_part_z(*pdwLength,*pdwLength) LPSTR szBuf,
    __inout DWORD *pdwLength) throw()
{
    ATLENSURE_RETURN(pdwLength != NULL);
    return GetParam(PP_UNIQUE_CONTAINER, reinterpret_cast<BYTE *>(szBuf),
	pdwLength);
}
inline HRESULT CCryptProvEx::GetUniqueContainer(
    __out ATL::CAtlStringA &Result) //throw(...)
{
    ATL::CAtlStringA sResult;
    DWORD dwLength = 256;
    HRESULT hr = GetUniqueContainer(sResult.GetBuffer(dwLength), &dwLength);
    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) {
	// В случае ERROR_MORE_DATA, в dwLength вернули, скока надо
	hr = GetUniqueContainer(sResult.GetBuffer(dwLength), &dwLength);
    }
    if (FAILED(hr))
	return hr;
    sResult.ReleaseBufferSetLength (dwLength - 1);
    ATLASSERT((size_t)sResult.GetLength() == strlen(sResult.GetString()));
    Result = sResult;
    return hr;
}
inline HRESULT CCryptProvEx::GetUniqueContainer(
    __out ATL::CAtlStringW &Result) //throw(...)
{
    ATL::CAtlStringA ResultA;
    HRESULT hr = GetUniqueContainer(ResultA);
    if (FAILED(hr))
	return hr;
    Result = ResultA;
    return hr;
}

#ifdef WINCRYPTEX

inline HRESULT CCryptProvEx::GetFQCN(
    __out_ecount_part_z(*pdwLength,*pdwLength) LPSTR szBuf,
    __inout DWORD *pdwLength) throw()
{
    ATLENSURE_RETURN(pdwLength != NULL);
    return GetParam(PP_FQCN, reinterpret_cast<BYTE *>(szBuf),
	pdwLength);
}

inline HRESULT CCryptProvEx::GetFQCN(
    __out ATL::CAtlStringA &Result ) //throw(...)
{
    ATL::CAtlStringA sResult;
    DWORD dwLength = 256;
    HRESULT hr = GetFQCN(sResult.GetBuffer(dwLength), &dwLength);
    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) {
	// В случае ERROR_MORE_DATA, в dwLength вернули, скока надо
	hr = GetFQCN(sResult.GetBuffer(dwLength), &dwLength);
    }
    if (FAILED(hr))
	return hr;
    sResult.ReleaseBufferSetLength (dwLength - 1);
    ATLASSERT((size_t)sResult.GetLength() == strlen(sResult.GetString()));
    Result = sResult;
    return hr;
}
inline HRESULT CCryptProvEx::GetFQCN(
    __out ATL::CAtlStringW &Result) //throw(...)
{
    ATL::CAtlStringA ResultA;
    HRESULT hr = GetFQCN(ResultA);
    if (FAILED(hr))
	return hr;
    Result = ResultA;
    return hr;
}

inline HRESULT CCryptProvEx::GetSelectContainer(
    __out_ecount_part_z(*pdwLength,*pdwLength) LPSTR szBuf,
    __inout DWORD *pdwLength, __in BOOL bFullyQualified) throw()
{
    ATLENSURE_RETURN(pdwLength != NULL);
    DWORD dwFlags = bFullyQualified ? CRYPT_FQCN : 0;
    return GetParam(PP_SELECT_CONTAINER, reinterpret_cast<BYTE *>(szBuf),
	pdwLength, dwFlags);
}

inline HRESULT CCryptProvEx::GetSelectContainer(
    __out ATL::CAtlStringA &Result, __in BOOL bFullyQualified) //throw(...)
{
    ATL::CAtlStringA sResult;
    DWORD dwLength = 256;
    HRESULT hr = GetSelectContainer(sResult.GetBuffer(dwLength), &dwLength,
	bFullyQualified);
    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr)
    {
	// В случае ERROR_MORE_DATA, в dwLength вернули, скока надо
	hr = GetSelectContainer(sResult.GetBuffer(dwLength), &dwLength,
	    bFullyQualified);
    }
    if (FAILED(hr))
    {
	return hr;
    }
    // В CSP 3_0 есть ошибка (CSP-80) - в dwLength не возвращается длина 
    // записанной строки, поэтому здесь считаем её сами до первого нуля.
    // Данный параметр (PP_SELECT_CONTAINER) используется только в нашей 
    // функции CPGetProvParam.
    sResult.ReleaseBufferSetLength ((int // TODO:XXX заплата в виде тупого 
				    // приведения, возможно нужно использовать
				    // (int)strnlen_s(sResult.GetString(), sResult.GetLenght())
				    )strlen(sResult.GetString()));
    Result = sResult;
    return hr;
}
inline HRESULT CCryptProvEx::GetSelectContainer(
    __out ATL::CAtlStringW &Result, __in BOOL bFullyQualified) //throw(...)
{
    ATL::CAtlStringA ResultA;
    HRESULT hr = GetSelectContainer(ResultA, bFullyQualified);
    if (FAILED(hr))
	return hr;
    Result = ResultA;
    return hr;
}


inline HRESULT CCryptProvEx::GetHashOid(
    __out_ecount_part_z(*pdwLength,*pdwLength) LPSTR szBuf,
    __inout DWORD *pdwLength) throw()
{
    return CCryptProv::GetParam(PP_HASHOID,
	reinterpret_cast<BYTE *>(szBuf), pdwLength);
}

inline HRESULT CCryptProvEx::GetHashOid(
    __out ATL::CAtlStringA& Result) //throw(...)
{
    ATL::CAtlStringA sResult;
    DWORD dwLength = 64;
    HRESULT hr = GetHashOid(sResult.GetBuffer(dwLength), &dwLength);
    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) {
	// В случае ERROR_MORE_DATA, в dwLength вернули, скока надо
	hr = GetHashOid(sResult.GetBuffer(dwLength), &dwLength);
    }
    if (FAILED(hr))
	return hr;
    sResult.ReleaseBufferSetLength (dwLength - 1);
    ATLASSERT((size_t)sResult.GetLength() == strlen(sResult.GetString()));
    Result = sResult;
    return hr;
}

inline HRESULT CCryptProvEx::SetHashOid(__in_z LPCSTR szBuf) throw()
{
    return CCryptProv::SetParam(PP_HASHOID,
	reinterpret_cast<BYTE *>(const_cast<LPSTR>(szBuf)));
}


inline HRESULT CCryptProvEx::GetCipherOid(
    __out_ecount_part_z(*pdwLength,*pdwLength) LPSTR szBuf,
    DWORD *pdwLength) throw()
{
    return CCryptProv::GetParam(PP_CIPHEROID,
	reinterpret_cast<BYTE *>(szBuf), pdwLength);
}

inline HRESULT CCryptProvEx::GetCipherOid(
    __out ATL::CAtlStringA& Result) //throw(...)
{
    ATL::CAtlStringA sResult;
    DWORD dwLength = 64;
    HRESULT hr = GetCipherOid(sResult.GetBuffer(dwLength), &dwLength);
    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) {
	// В случае ERROR_MORE_DATA, в dwLength вернули, скока надо
	hr = GetCipherOid(sResult.GetBuffer(dwLength), &dwLength);
    }
    if (FAILED(hr))
	return hr;
    sResult.ReleaseBufferSetLength (dwLength - 1);
    ATLASSERT((size_t)sResult.GetLength() == strlen(sResult.GetString()));
    Result = sResult;
    return hr;
}

inline HRESULT CCryptProvEx::SetCipherOid(__in_z LPCSTR szBuf) throw()
{
    return CCryptProv::SetParam(PP_CIPHEROID,
	reinterpret_cast<BYTE *>(const_cast<LPSTR>(szBuf)));
}

inline HRESULT CCryptProvEx::GetSignatureOid(
    __out_ecount_part_z(*pdwLength,*pdwLength) LPSTR szBuf,
    __inout DWORD *pdwLength) throw()
{
    return CCryptProv::GetParam(PP_SIGNATUREOID,
	reinterpret_cast<BYTE *>(szBuf), pdwLength);
}

inline HRESULT CCryptProvEx::GetSignatureOid(ATL::CAtlStringA& Result) //throw(...)
{
    ATL::CAtlStringA sResult;
    DWORD dwLength = 64;
    HRESULT hr = GetSignatureOid(sResult.GetBuffer(dwLength), &dwLength);
    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) {
	// В случае ERROR_MORE_DATA, в dwLength вернули, скока надо
	hr = GetSignatureOid(sResult.GetBuffer(dwLength), &dwLength);
    }
    if (FAILED(hr))
	return hr;
    sResult.ReleaseBufferSetLength (dwLength - 1);
    ATLASSERT((size_t)sResult.GetLength() == strlen(sResult.GetString()));
    Result = sResult;
    return hr;
}

inline HRESULT CCryptProvEx::SetSignatureOid(__in_z LPCSTR szBuf) throw()
{
    return CCryptProv::SetParam(PP_SIGNATUREOID,
	reinterpret_cast<BYTE *>(const_cast<LPSTR>(szBuf)));
}

inline HRESULT CCryptProvEx::GetDhOid(
    __out_ecount_part_z(*pdwLength,*pdwLength) LPSTR szBuf,
    __inout DWORD *dwLength) throw()
{
    return CCryptProv::GetParam(PP_DHOID,
	reinterpret_cast<BYTE *>(szBuf), dwLength);
}

inline HRESULT CCryptProvEx::GetDhOid(
    __out ATL::CAtlStringA& Result) //throw(...)
{
    ATL::CAtlStringA sResult;
    DWORD dwLength = 64;
    HRESULT hr = GetDhOid(sResult.GetBuffer(dwLength), &dwLength);
    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) {
	// В случае ERROR_MORE_DATA, в dwLength вернули, скока надо
	hr = GetDhOid(sResult.GetBuffer(dwLength), &dwLength);
    }
    if (FAILED(hr))
	return hr;
    sResult.ReleaseBufferSetLength (dwLength - 1);
    ATLASSERT((size_t)sResult.GetLength() == strlen(sResult.GetString()));
    Result = sResult;
    return hr;
}

inline HRESULT CCryptProvEx::SetDhOid(__in_z LPCSTR szBuf) throw()
{
    return CCryptProv::SetParam(PP_DHOID,
	reinterpret_cast<BYTE *>(const_cast<LPSTR>(szBuf)));
}


inline HRESULT CCryptProvEx::SetContainerExtension(
    __in const CONTAINER_EXTENSION *pContExt) throw()
{
    CONTAINER_EXTENSION *pExt = const_cast<CONTAINER_EXTENSION*>(pContExt);
    CERT_EXTENSION certExt;
    certExt.fCritical = pExt->bCritical;
    certExt.Value.cbData = pExt->cbExtension;
    certExt.Value.pbData = pExt->pbExtension;
    certExt.pszObjId = reinterpret_cast<LPSTR>(pExt->pbExtension)
	+ pExt->cbExtension;
    return CCryptProv::SetParam(PP_CONTAINER_EXTENSION, (BYTE *)&certExt);
}

// Внутренний дескриптор провайдера, всегда 32-битный (даже на x64)
// Этот дескриптор может быть использован только для PP_SET_PIN
inline HRESULT CCryptProvEx::GetInternalCryptProv(
    __out DWORD *pdwInternalProv) throw()
{
    DWORD dwLength = sizeof(DWORD);
    return CCryptProv::GetParam(PP_HCRYPTPROV,
	reinterpret_cast<BYTE *>(pdwInternalProv), &dwLength);
}

inline HRESULT CCryptProvEx::InternalLoadKey() throw()
{
    DWORD dwInternalProv = 0;
    // Используем побочный эффект от GetInternalCryptProv, заключающийся
    // в полной прогрузке ключа
    return GetInternalCryptProv(&dwInternalProv);
}

inline HRESULT CCryptProvEx::SetPin(__in const CRYPT_PIN_INFO *pPinInfo) throw()
{
    BYTE *pin = reinterpret_cast<BYTE *>(const_cast<CRYPT_PIN_INFO *>(pPinInfo));
    return SetParam(PP_SET_PIN, pin);
}
#endif // WINCRYPTEX

inline HRESULT CCryptHashEx::Initialize(
    __in ATL::CCryptProv &Prov, __in ALG_ID Algid,
    __in LPCTSTR szText) throw()
{
    ATLENSURE_RETURN(m_hHash == 0);
    if (!::CryptCreateHash(Prov.GetHandle(), Algid, 0, 0, &m_hHash))
	return ATL::AtlHresultFromLastError();
    if (szText != NULL)
	return AddString(szText);
    return S_OK;
}

inline HRESULT CCryptHashEx::Initialize(
    __in ATL::CCryptProv &Prov, __in ALG_ID Algid,
    __in ATL::CCryptKey &key) throw()
{
    ATLENSURE_RETURN(m_hHash == 0);
    if (!::CryptCreateHash(Prov.GetHandle(), Algid, key.GetHandle(), 0, &m_hHash))
        return ATL::AtlHresultFromLastError();
    return S_OK;
}


inline HRESULT CCryptHashEx::GetValue(
    __out_ecount_part_opt(*pdwSize, *pdwSize) BYTE *pBuf,
    __inout DWORD *pdwSize) throw()
{
    return ATL::CCryptHash::GetValue(pBuf, pdwSize);
}
inline HRESULT CCryptHashEx::Sign(
    __out_ecount_part_opt(*pdwSigLen, *pdwSigLen) BYTE *pbSignature,
    __inout DWORD *pdwSigLen, __in DWORD dwFlags, __in DWORD dwKeySpec) throw()
{
    return ATL::CCryptHash::Sign(pbSignature, pdwSigLen, dwFlags, dwKeySpec);
}

inline HRESULT CCryptHashEx::GetValue(__out CStringBlob &Result) //throw(...)
{
    HRESULT hr;
    BYTE *pBuf = NULL;
    DWORD dwSize = 0;
    hr = GetValue (pBuf, &dwSize);
    if (FAILED(hr))
	return hr;
    pBuf = reinterpret_cast<BYTE *>(Result.GetBuffer (dwSize));
    hr = GetValue (pBuf, &dwSize);
    Result.ReleaseBufferSetLength (dwSize);
    return hr;
}

inline HRESULT CCryptHashEx::Sign(__in CStringBlob &Signature,
    __in DWORD dwFlags, __in DWORD dwKeySpec) //throw(...)
{
    HRESULT hr;
    BYTE *pbSignature = NULL;
    DWORD dwSigLen = 0;
    hr = Sign(pbSignature, &dwSigLen, dwFlags, dwKeySpec);
    if (FAILED(hr))
	return hr;
    pbSignature = reinterpret_cast<BYTE *>(Signature.GetBuffer (dwSigLen));
    hr = Sign(pbSignature, &dwSigLen, dwFlags, dwKeySpec);
    Signature.ReleaseBufferSetLength (dwSigLen);
    return hr;
}

inline HRESULT CCryptHashEx::VerifySignature(
    __in CStringBlob &Signature, __in ATL::CCryptKey &PubKey,
    __in DWORD dwFlags) //throw(...)
{
    const BYTE *pbSignature
	= reinterpret_cast<const BYTE *>(Signature.GetString());
    DWORD dwSigLen = Signature.GetLength();
    HRESULT hr = CCryptHash::VerifySignature(pbSignature, dwSigLen,
	PubKey, dwFlags);
    return hr;
}

inline HRESULT CCryptHashEx::AddSessionKey(__in ATL::CCryptKey &SessionKey,
    __in DWORD dwFlags /*= 0*/) //throw(...)
{
    ATLENSURE_RETURN(m_hHash != 0);
    if (!::CryptHashSessionKey(m_hHash, SessionKey.GetHandle(), dwFlags)) {
	return ATL::AtlHresultFromLastError();
    }
    return S_OK;
}


#ifdef WINCRYPTEX
inline HRESULT CCryptGOST3411Hash::Initialize(ATL::CCryptProv &Prov,
    LPCTSTR szText) throw()
{
    ATLENSURE_RETURN(m_hHash == 0);
    if (!::CryptCreateHash(Prov.GetHandle(), CALG_GR3411, 0, 0, &m_hHash))
	return ATL::AtlHresultFromLastError();
    if (szText != NULL)
	return AddString(szText);
    return S_OK;
}
inline HRESULT CCryptGOST3411HashEx::Initialize(ATL::CCryptProv &Prov,
    LPCTSTR szText) throw()
{
    return CCryptHashEx::Initialize(Prov, CALG_GR3411, szText);
}
#endif // WINCRYPTEX

inline CCryptKeyEx::CCryptKeyEx(__in const ATL::CCryptKey& key) throw() :
    m_key(const_cast<ATL::CCryptKey&>(key))
{
}

inline HRESULT CCryptKeyEx::GetCertificate(
    __out_ecount_part_opt(*pdwCertDataLen,*pdwCertDataLen) BYTE *pbCertData,
    __inout DWORD *pdwCertDataLen) throw()
{
    return m_key.GetParam(KP_CERTIFICATE, pbCertData, pdwCertDataLen);
}

inline HRESULT CCryptKeyEx::SetCertificate(__in const BYTE *pbCertData) throw()
{
    return m_key.SetParam(KP_CERTIFICATE, const_cast<BYTE *>(pbCertData));
}

inline HRESULT CCryptKeyEx::GetCertificate(__out CStringBlob& Result) //throw(...)
{
    DWORD dwCertDataLen = 0;
    HRESULT hr = GetCertificate(NULL, &dwCertDataLen);
    if (FAILED(hr)) {
	return hr;
    }
    hr = GetCertificate(
	reinterpret_cast<BYTE *>(Result.GetBuffer(dwCertDataLen)),
	&dwCertDataLen);
    Result.ReleaseBufferSetLength (dwCertDataLen);
    return hr;
}

inline HRESULT CCryptKeyEx::SetCertificate(
    __in const CStringBlob& Result) //throw(...)
{
    return SetCertificate(reinterpret_cast<const BYTE *>(
	Result.GetString()));
}

inline HRESULT CCryptKeyEx::GetX(
	__out_ecount_part_opt(*pdwXLen,*pdwXLen) BYTE *pbX,
	__inout DWORD *pdwXLen) throw()
{
    return m_key.GetParam(KP_X, pbX, pdwXLen);
}

inline HRESULT CCryptKeyEx::SetX(__in const _CRYPTOAPI_BLOB *pBlobX) throw()
{
    return m_key.SetParam(KP_X,
	const_cast<BYTE*>(reinterpret_cast<const BYTE *>(pBlobX)));
}

inline HRESULT CCryptKeyEx::GetX(__out CStringBlob& Result) //throw(...)
{
    DWORD dwXDataLen = 0;
    HRESULT hr = GetX(NULL, &dwXDataLen);
    if (FAILED(hr)) {
	return hr;
    }
    hr = GetX(
	reinterpret_cast<BYTE *>(Result.GetBuffer(dwXDataLen)),
	&dwXDataLen);
    Result.ReleaseBufferSetLength(dwXDataLen);
    return hr;
}

inline HRESULT CCryptKeyEx::SetX(__in const CStringBlob& Result) //throw(...)
{
    return SetX(reinterpret_cast<const _CRYPTOAPI_BLOB *>(
	Result.GetString()));
}

inline HRESULT CCryptKeyEx::GetParam(
    __in DWORD dwParam, __out CStringBlob& Result,
    __in DWORD dwFlags) //throw(...)
{
    DWORD dwDataLen = 0;
    HRESULT hr = m_key.GetParam(dwParam, NULL, &dwDataLen, dwFlags);
    if (FAILED(hr))
	return hr;
    hr = m_key.GetParam(dwParam,
	reinterpret_cast<BYTE *>(Result.GetBuffer(dwDataLen)),
	&dwDataLen, dwFlags);
    Result.ReleaseBufferSetLength (dwDataLen);
    return hr;
}

inline HRESULT CCryptKeyEx::SetParam(__in DWORD dwParam,
    __in const CStringBlob& Value, __in DWORD dwFlags) //throw(...)
{
    return m_key.SetParam(dwParam, const_cast<BYTE *>(
	reinterpret_cast<const BYTE *>(Value.GetString())), dwFlags);
}

inline HRESULT CCryptKeyEx::Decrypt(__in BOOL final,
    __inout_ecount_part(*pdwDataLen,*pdwDataLen) BYTE *pbData,
    __inout DWORD *pdwDataLen,
    __in ATL::CCryptHash &Hash /*= CCryptHash::EmptyHash*/,
    __in DWORD dwFlags /*= 0*/)
    throw()
{
    ATLENSURE_RETURN(m_key.GetHandle() != 0);

    if (!::CryptDecrypt(m_key.GetHandle(), Hash.GetHandle(), final, dwFlags,
	pbData,	pdwDataLen)) {
	    return ATL::AtlHresultFromLastError();
    }
    else return S_OK;
}

inline HRESULT CCryptKeyEx::Decrypt(
    __in_ecount(dwCipherTextLen) const BYTE *pbCipherText,
    __in DWORD dwCipherTextLen,
    __out_ecount_part(*pdwPlainTextLen,*pdwPlainTextLen) BYTE *pbPlainText,
    __inout DWORD *pdwPlainTextLen,
    __in ATL::CCryptHash &Hash /*= CCryptHash::EmptyHash*/,
    __in DWORD dwFlags /*= 0*/)
    throw()
{
    ATLENSURE_RETURN(m_key.GetHandle() != 0);

    if (*pdwPlainTextLen < dwCipherTextLen)
	return ERROR_MORE_DATA;

#if defined _WIN32
    ATL::Checked::memcpy_s(pbPlainText, *pdwPlainTextLen, pbCipherText,
	dwCipherTextLen);
#else
    memcpy(pbPlainText, pbCipherText, dwCipherTextLen);
#endif // _WIN32
    DWORD dwSize = dwCipherTextLen;
    if (!::CryptDecrypt(m_key.GetHandle(), Hash.GetHandle(), TRUE, dwFlags,
	pbPlainText, &dwSize)) {
	    return ATL::AtlHresultFromLastError();
    }

    *pdwPlainTextLen = dwSize;
    return S_OK;
}

#ifdef WINCRYPTEX
inline HRESULT CCryptKeyEx::GetHashOid(__out CStringBlob& Result) //throw(...)
{
    return GetParam(KP_HASHOID, Result, 0);
}

inline HRESULT CCryptKeyEx::SetHashOid(__in const CStringBlob& Value) //throw(...)
{
    return SetParam(KP_HASHOID, Value, 0);
}

inline HRESULT CCryptKeyEx::GetDhOid(__out CStringBlob& Result) //throw(...)
{
    return GetParam(KP_DHOID, Result, 0);
}

inline HRESULT CCryptKeyEx::SetDhOid(__in const CStringBlob& Value) //throw(...)
{
    return SetParam(KP_DHOID, Value, 0);
}

inline HRESULT CCryptKeyEx::GetCipherOid(__out CStringBlob& Result) //throw(...)
{
    return GetParam(KP_CIPHEROID, Result, 0);
}

inline HRESULT CCryptKeyEx::SetCipherOid(__in const CStringBlob& Value) //throw(...)
{
    return SetParam(KP_CIPHEROID, Value, 0);
}

inline HRESULT CCryptKeyEx::GetNotAfter(__out FILETIME * pNotAfter) throw()
{
    DWORD dwSize = sizeof(FILETIME);
    return m_key.GetParam(KP_NOTAFTER, (BYTE*)pNotAfter, &dwSize);
}

#endif //WINCRYPTEX

inline HRESULT CCryptKeyEx::ExportSimpleBlob(__out CStringBlob& Result,
    __in ATL::CCryptKey &ExpKey, __in DWORD dwFlags) //throw(...)
{
    DWORD dwDataLen = 0;
    HRESULT hr = m_key.ExportSimpleBlob(ExpKey, dwFlags, NULL, &dwDataLen);
    if (FAILED(hr))
	return hr;
    hr = m_key.ExportSimpleBlob(ExpKey, dwFlags,
	reinterpret_cast<BYTE *>(Result.GetBuffer(dwDataLen)),
	&dwDataLen);
    Result.ReleaseBufferSetLength (dwDataLen);
    return hr;
}

inline HRESULT CCryptKeyEx::ExportPublicKeyBlob(__out CStringBlob& Result,
    __in ATL::CCryptKey &ExpKey, __in DWORD dwFlags) //throw(...)
{
    DWORD dwDataLen = 0;
    HRESULT hr = m_key.ExportPublicKeyBlob(ExpKey, dwFlags, NULL, &dwDataLen);
    if (FAILED(hr))
	return hr;
    hr = m_key.ExportPublicKeyBlob(ExpKey, dwFlags,
	reinterpret_cast<BYTE *>(Result.GetBuffer(dwDataLen)),
	&dwDataLen);
    Result.ReleaseBufferSetLength (dwDataLen);
    return hr;
}

inline HRESULT CCryptKeyEx::ExportPrivateKeyBlob(__out CStringBlob& Result,
    __in ATL::CCryptKey &ExpKey, __in DWORD dwFlags) //throw(...)
{
    DWORD dwDataLen = 0;
    HRESULT hr = m_key.ExportPrivateKeyBlob(ExpKey, dwFlags, NULL, &dwDataLen);
    if (FAILED(hr))
	return hr;
    hr = m_key.ExportPrivateKeyBlob(ExpKey, dwFlags,
	reinterpret_cast<BYTE *>(Result.GetBuffer(dwDataLen)),
	&dwDataLen);
    Result.ReleaseBufferSetLength (dwDataLen);
    return hr;
}

inline HRESULT CCryptKeyEx::GetKeySpec(__out DWORD *pdwKeySpec) throw()
{
    ATLENSURE_RETURN(pdwKeySpec != NULL);
    ALG_ID AlgId = 0;
    HRESULT hr = m_key.GetAlgId(&AlgId);
    if (FAILED(hr))
	return hr;
    *pdwKeySpec =  GET_ALG_CLASS(AlgId) == ALG_CLASS_SIGNATURE
	    ? AT_SIGNATURE : AT_KEYEXCHANGE;
    return hr;
}

inline HRESULT CCryptKeyEx::GetKeyLength(__out DWORD * pdwKeyLen) throw()
{
	DWORD dwSize = sizeof(DWORD);
	return m_key.GetParam(KP_KEYLEN, (BYTE *)pdwKeyLen, &dwSize);
}

inline HRESULT CCryptImportKeyEx::Initialize(
    __in ATL::CCryptProv& Prov, __in const CStringBlob& Value,
    __in ATL::CCryptKey& PubKey, DWORD dwFlags) //throw(...)
{
    return ATL::CCryptImportKey::Initialize(Prov, const_cast<BYTE *>(
	reinterpret_cast<const BYTE *>(Value.GetString())),
	Value.GetLength(), PubKey, dwFlags);
}

inline HRESULT CCryptImportPublicKeyInfo::Initialize(
    __in ATL::CCryptProv &Prov, __in const PCERT_PUBLIC_KEY_INFO pInfo,
    __in DWORD dwCertEncodingType /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/
    ) throw()
{
    ATLASSERT(m_hKey == 0);

    if (!CryptImportPublicKeyInfo(Prov.GetHandle(), dwCertEncodingType, pInfo,
	&m_hKey)) {
	    return ATL::AtlHresultFromLastError();
    }
    else return S_OK;
}

inline HRESULT CCryptRandomDefferedKey::Initialize(
    __in ATL::CCryptProv &Prov, __in ALG_ID algid,
    __in DWORD dwFlags) throw()
{
    return CCryptRandomKey::Initialize(Prov, algid, dwFlags | CRYPT_PREGEN);
}

inline HRESULT CCryptRandomDefferedKey::Generate() throw()
{
    return CCryptRandomKey::SetX();
}

inline HRESULT CCryptUserKey::Initialize(
    __in ATL::CCryptProv &Prov, __in DWORD dwKeySpec) throw()
{
    ATLENSURE_RETURN(m_hKey == 0);
    if (!::CryptGetUserKey(Prov.GetHandle(), dwKeySpec, &m_hKey))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCryptUserKey::Create(
    __in ATL::CCryptProv &Prov, __in DWORD dwKeySpec,
    __in DWORD dwKeyLen, __in DWORD dwFlags) throw()
{
    ATLENSURE_RETURN(m_hKey == 0);

    if (!CryptGenKey(Prov.GetHandle(), dwKeySpec, dwKeyLen << 16 | dwFlags,
	&m_hKey))
    {
	return ATL::AtlHresultFromLastError();
    }
    return S_OK;
}

inline CCryptKeyNotDuplicatable::CCryptKeyNotDuplicatable(__in HCRYPTKEY hKey)
    throw()
{
    m_hKey = hKey;
}

inline CCryptKeyNotDuplicatable::~CCryptKeyNotDuplicatable() throw()
{
    m_hKey = 0;
}

inline HRESULT CopyKeyParam(
    ATL::CCryptKey& keyDst, DWORD pp, ATL::CCryptKey& keySrc, DWORD dwSetFlags) //throw(...)
{
    CCryptKeyEx src(keySrc);
    CStringBlob data;
    HRESULT hr = src.GetParam(pp, data, 0);
    if (FAILED(hr))
	return hr;

    CCryptKeyEx dst(keyDst);
    hr = dst.SetParam(pp, data, dwSetFlags);
    if (FAILED(hr))
	return hr;
    return hr;
}

inline HRESULT COidInfoEnum::Enum(DWORD dwGroupId)
{
    // TODO: Solaris: Warning (Anachronism): Formal argument
    // pfnEnumOIDInfo of type extern "C" int(*)(const _CRYPT_OID_INFO*,void*)
    // in call to CryptEnumOIDInfo(unsigned, unsigned, void*, 
    // extern "C" int(*)(const _CRYPT_OID_INFO*,void*)) is being passed
    // int(*)(const _CRYPT_OID_INFO*,void*).
    
    if (!::CryptEnumOIDInfo(dwGroupId, 0, this, (PFN_CRYPT_ENUM_OID_INFO)EnumOIDInfoCallback))
	return S_FALSE;
    return S_OK;
}


inline BOOL WINAPI COidInfoEnum::EnumOIDInfoCallback(
    PCCRYPT_OID_INFO pInfo, void *pvArg)
{
    COidInfoEnum *pThis = static_cast<COidInfoEnum *>(pvArg);
    if (pThis->OnOidInfo (pInfo))
	return TRUE;
    else
	return FALSE;
}


inline HRESULT CHashAlgorithmOidEnum::Enum()
{
    return COidInfoEnum::Enum (CRYPT_HASH_ALG_OID_GROUP_ID);
}

inline bool CHashAlgorithmOidEnum::OnOidInfo(PCCRYPT_OID_INFO pInfo)
{
    return OnHashAlg(pInfo->pszOID, pInfo->pwszName, pInfo->Algid);
}


inline HRESULT CEncryptionAlgorithmOidEnum::Enum()
{
    return COidInfoEnum::Enum (CRYPT_ENCRYPT_ALG_OID_GROUP_ID);
}

inline bool CEncryptionAlgorithmOidEnum::OnOidInfo(PCCRYPT_OID_INFO pInfo)
{
    return OnEncryptAlg(pInfo->pszOID, pInfo->pwszName, pInfo->Algid);
}


inline HRESULT CPublicKeyAlgorithmOidEnum::Enum()
{
    return COidInfoEnum::Enum (CRYPT_PUBKEY_ALG_OID_GROUP_ID);
}

inline bool CPublicKeyAlgorithmOidEnum::OnOidInfo(PCCRYPT_OID_INFO pInfo)
{
    DWORD dwFlags = 0;
    if (pInfo->ExtraInfo.pbData) {
	DWORD *ExtraInfo = (DWORD *)pInfo->ExtraInfo.pbData;
	if (pInfo->ExtraInfo.cbData >= sizeof(DWORD))
	    dwFlags = ExtraInfo[0];
    }
    return OnPubKeyAlg(pInfo->pszOID, pInfo->pwszName, pInfo->Algid,
	dwFlags);
}


inline HRESULT CSignatureAlgorithmOidEnum::Enum()
{
    return COidInfoEnum::Enum (CRYPT_SIGN_ALG_OID_GROUP_ID);
}

inline bool CSignatureAlgorithmOidEnum::OnOidInfo(PCCRYPT_OID_INFO pInfo)
{
    ALG_ID PublicKeyAlgid = 0;
    DWORD dwFlags = 0;
    DWORD dwProvType = 0;
    if (pInfo->ExtraInfo.pbData) {
	DWORD *ExtraInfo = (DWORD *)pInfo->ExtraInfo.pbData;
	if (pInfo->ExtraInfo.cbData >= sizeof(DWORD))
	    PublicKeyAlgid = ExtraInfo[0];
	if (pInfo->ExtraInfo.cbData >= 2 * sizeof(DWORD))
	    dwFlags = ExtraInfo[1];
	if (pInfo->ExtraInfo.cbData >= 3 * sizeof(DWORD))
	    dwProvType = ExtraInfo[2];
    }
    return OnSignAlg(pInfo->pszOID, pInfo->pwszName, pInfo->Algid,
	PublicKeyAlgid, dwFlags, dwProvType);
}


inline HRESULT CRdnAttributeOidEnum::Enum()
{
    return COidInfoEnum::Enum (CRYPT_RDN_ATTR_OID_GROUP_ID);
}

inline bool CRdnAttributeOidEnum::OnOidInfo(PCCRYPT_OID_INFO pInfo)
{
    DWORD *pdwAcceptableValueTypes = (DWORD *)pInfo->ExtraInfo.pbData;
    DWORD cAcceptableValueTypes = 0;
    if (pdwAcceptableValueTypes) {
	cAcceptableValueTypes = pInfo->ExtraInfo.cbData / sizeof (DWORD);
	// Последний элемент массива pdwAcceptableValueTypes
	// всегда нулевой, уберем этот ноль из длины массива
	if (cAcceptableValueTypes > 0)
	    --cAcceptableValueTypes;
    }
    if (cAcceptableValueTypes <= 0) {
	cAcceptableValueTypes = 0;
	pdwAcceptableValueTypes = 0;
    }
    // On Unix, in CSP_WinCrypt.h there is no union with dwLength member in CRYPT_OID_INFO
    return OnRdnAttr(pInfo->pszOID, pInfo->pwszName, (DWORD)pInfo->Algid,
	pdwAcceptableValueTypes, cAcceptableValueTypes);
}


inline HRESULT CExtensionOrAttributeOidEnum::Enum()
{
    return COidInfoEnum::Enum (CRYPT_EXT_OR_ATTR_OID_GROUP_ID);
}

inline bool CExtensionOrAttributeOidEnum::OnOidInfo(PCCRYPT_OID_INFO pInfo)
{
    return OnExtOrAttr(pInfo->pszOID, pInfo->pwszName);
}


inline HRESULT CEnhancedKeyUsageOidEnum::Enum()
{
    return COidInfoEnum::Enum (CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
}

inline bool CEnhancedKeyUsageOidEnum::OnOidInfo(PCCRYPT_OID_INFO pInfo)
{
    return OnEnhKeyUsage(pInfo->pszOID, pInfo->pwszName);
}


inline HRESULT CPolicyOidEnum::Enum()
{
    return COidInfoEnum::Enum (CRYPT_POLICY_OID_GROUP_ID);
}

inline bool CPolicyOidEnum::OnOidInfo(PCCRYPT_OID_INFO pInfo)
{
    return OnPolicy(pInfo->pszOID, pInfo->pwszName);
}


inline HRESULT CTemplateOidEnum::Enum()
{
    return COidInfoEnum::Enum (CRYPT_TEMPLATE_OID_GROUP_ID);
}

inline bool CTemplateOidEnum::OnOidInfo(PCCRYPT_OID_INFO pInfo)
{
    return OnTemplate(pInfo->pszOID, pInfo->pwszName);
}

inline HRESULT CFindSignatureAlgPubKeyEnum::FindSignatureAlgorithmOIDInfo(
    PCCRYPT_OID_INFO *ppOidInfo)
{
    if(!ppOidInfo)
	return E_INVALIDARG;
    HRESULT hr = Enum();
    if(FAILED(hr))
	return hr;
    if(!m_pOidInfo)
	return CRYPT_E_NOT_FOUND;
    (*ppOidInfo) = m_pOidInfo;
    return S_OK;
}

inline bool CFindSignatureAlgPubKeyEnum::OnPubKeyAlg(
    LPCSTR pszOID, LPCWSTR /*pwszName*/, ALG_ID Algid, DWORD /*dwFlags*/)
{
    if( m_sPubKeyAlgOID.Compare(pszOID) )
	return true;
    ALG_ID algIds[] = { m_hashAlg, Algid };
    m_pOidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_SIGN_KEY,
	algIds,CRYPT_SIGN_ALG_OID_GROUP_ID);
    if( !m_pOidInfo )
	return true;
    return false;
}

inline HRESULT CFindSignatureAlgorithmEnum::FindSignatureAlgorithmOIDInfo( PCCRYPT_OID_INFO *ppOidInfo)
{
    if(!ppOidInfo)
	return E_INVALIDARG;
    HRESULT hr = Enum();
    if(FAILED(hr))
	return hr;
    if(FAILED(m_hr))
	return m_hr;
    if(!m_pOidInfo)
	return CRYPT_E_NOT_FOUND;
    (*ppOidInfo) = m_pOidInfo;
    return S_OK;
}

inline bool CFindSignatureAlgorithmEnum::OnHashAlg(LPCSTR pszOID, LPCWSTR /*pwszName*/, ALG_ID Algid)
{
    if(m_sHashAlgOID.Compare(pszOID))
	return true;
    CFindSignatureAlgPubKeyEnum pubKeyEnum(Algid,m_sPubKeyAlgOID);
    HRESULT hr = pubKeyEnum.FindSignatureAlgorithmOIDInfo(&m_pOidInfo);
    if( CRYPT_E_NOT_FOUND == hr )
	return true;
    m_hr = hr;
    return false;
}

template <typename T> inline BOOL WINAPI CryptEnumProviderTypesT(
    DWORD dwIndex, DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType,
    T *szTypeName, DWORD *pcbTypeName)
{
    UNUSED(dwIndex);
    UNUSED(pdwReserved);
    UNUSED(dwFlags);
    UNUSED(pdwProvType);
    UNUSED(szTypeName);
    UNUSED(pcbTypeName);
    SetLastError(E_NOTIMPL);
    return FALSE;
}
template <> inline BOOL WINAPI CryptEnumProviderTypesT<char>( DWORD dwIndex,
    DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType, char *szTypeName,
    DWORD *pcbTypeName)
{
    return ::CryptEnumProviderTypesA( dwIndex,
	pdwReserved, dwFlags, pdwProvType, szTypeName, pcbTypeName);
}
// Вместо того, чтобы по-человечески написать так:
// из-за ошибки в Crypto API приходится извращаться следующим образом:
template <> inline BOOL WINAPI CryptEnumProviderTypesT<wchar_t>(
    DWORD dwIndex, DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType,
    wchar_t *szTypeName, DWORD *pcbTypeName)
{
    DWORD cbTypeName = 0;
    if (pcbTypeName)
	cbTypeName = *pcbTypeName / 2;
    BOOL bRes = ::CryptEnumProviderTypesA( dwIndex, pdwReserved, dwFlags,
	pdwProvType, (LPSTR)szTypeName, &cbTypeName );
    if (bRes) {
	USES_ATL_SAFE_ALLOCA;
	LPWSTR TempBuf = (LPWSTR)_ATL_SAFE_ALLOCA(cbTypeName*2,
	    _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
	if (!AtlA2WHelper(TempBuf, (LPCSTR)szTypeName, cbTypeName))
	    return FALSE;
	memcpy(szTypeName, TempBuf, cbTypeName*2);
    }
    if (pcbTypeName)
	*pcbTypeName = cbTypeName * 2;
    return bRes;
}

template <typename T> inline BOOL WINAPI CryptEnumProvidersT( DWORD dwIndex,
    DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType, T *szProvName,
    DWORD *pcbProvName)
{
    UNUSED(dwIndex);
    UNUSED(pdwReserved);
    UNUSED(dwFlags);
    UNUSED(pdwProvType);
    UNUSED(szProvName);
    UNUSED(pcbProvName);
    SetLastError(E_NOTIMPL);
    return FALSE;
}
template <> inline BOOL WINAPI CryptEnumProvidersT<char>( DWORD dwIndex,
    DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType, char *szProvName,
    DWORD *pcbProvName)
{
    return ::CryptEnumProvidersA( dwIndex,
	pdwReserved, dwFlags, pdwProvType, szProvName, pcbProvName);
}
// Вместо того, чтобы по-человечески написать так:
// из-за ошибки в Crypto API приходится извращаться следующим образом:
template <> inline BOOL WINAPI CryptEnumProvidersT<wchar_t>( DWORD dwIndex,
    DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType,
    wchar_t *szProvName, DWORD *pcbProvName)
{
    DWORD cbProvName = 0;
    if (pcbProvName)
	cbProvName = *pcbProvName / 2;
    BOOL bRes = ::CryptEnumProvidersA( dwIndex, pdwReserved, dwFlags,
	pdwProvType, (LPSTR)szProvName, &cbProvName );
    if (bRes) {
	USES_ATL_SAFE_ALLOCA;
	LPWSTR TempBuf = (LPWSTR)_ATL_SAFE_ALLOCA(cbProvName*2,
	    _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
	if (!AtlA2WHelper(TempBuf, (LPCSTR)szProvName, cbProvName))
	    return FALSE;
	memcpy(szProvName, TempBuf, cbProvName*2);
    }
    if (pcbProvName)
	*pcbProvName = cbProvName * 2;
    return bRes;
}

template <typename T> inline HRESULT CryptGetProvParamAnsiStringT(
    const ATL::CCryptProv &prov, DWORD dwParam, T *szData,
    DWORD *pcchDataLen, DWORD dwFlags)
{
    UNUSED(prov);
    UNUSED(dwParam);
    UNUSED(szData);
    UNUSED(pcchDataLen);
    UNUSED(dwFlags);
    return E_NOTIMPL;
}
template <> inline HRESULT CryptGetProvParamAnsiStringT<char>(
    const ATL::CCryptProv &prov, DWORD dwParam, char *szData,
    DWORD *pcchDataLen, DWORD dwFlags)
{
    ATL::CCryptProv &p = const_cast<ATL::CCryptProv &>(prov);
#ifdef CRYPT_UNIQUE
    if(szData && pcchDataLen && (*pcchDataLen)
	&& (dwParam == PP_ENUMCONTAINERS) && (dwFlags&CRYPT_UNIQUE))
	memset(szData,0,(*pcchDataLen)*sizeof(szData[0]));
#endif // CRYPT_UNIQUE
    return p.GetParam(dwParam, (LPBYTE)szData, pcchDataLen, dwFlags);
}
template <> inline HRESULT CryptGetProvParamAnsiStringT<wchar_t>(
    const ATL::CCryptProv &prov, DWORD dwParam, wchar_t *wszData,
    DWORD *pcchDataLen, DWORD dwFlags)
{
    DWORD cbDataLen = 0;
    if (pcchDataLen)
	cbDataLen = *pcchDataLen;
    ATL::CCryptProv &p = const_cast<ATL::CCryptProv &>(prov);
#ifdef CRYPT_UNIQUE
    if(wszData && pcchDataLen && cbDataLen 
	&& (dwParam == PP_ENUMCONTAINERS) && (dwFlags&CRYPT_UNIQUE))
	memset(wszData,0,(*pcchDataLen)*sizeof(wszData[0]));
#endif // CRYPT_UNIQUE
    HRESULT hr = p.GetParam(dwParam, (LPBYTE)wszData, &cbDataLen, dwFlags);
    if (SUCCEEDED(hr) && wszData) {
	USES_ATL_SAFE_ALLOCA;
	LPWSTR TempBuf = (LPWSTR)_ATL_SAFE_ALLOCA(cbDataLen*2,
	    _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
	size_t len = strlen((char *)wszData)+1;
#ifdef CRYPT_UNIQUE
	if((dwParam == PP_ENUMCONTAINERS) && (dwFlags&CRYPT_UNIQUE))
	    len += strlen((char *)wszData+strlen((char *)wszData)+1)+1;
#endif // CRYPT_UNIQUE
	TempBuf[0] = '\0';
	if (!MultiByteToWideChar(CP_ACP,0,(char *)wszData,(int)len,TempBuf,(int)len))
	    return FALSE;
	memcpy(wszData, TempBuf, cbDataLen*2);
    }
    if (pcchDataLen)
	*pcchDataLen = cbDataLen;
    return hr;
}
template <typename T> inline HRESULT CryptGetProvParamUnicodeStringT(
    const ATL::CCryptProv &prov, DWORD dwParam, T *szData,
    DWORD *pcchDataLen, DWORD dwFlags)
{
    UNUSED(prov);
    UNUSED(dwParam);
    UNUSED(szData);
    UNUSED(pcchDataLen);
    UNUSED(dwFlags);
    return E_NOTIMPL;
}
template <> inline HRESULT CryptGetProvParamUnicodeStringT<char>(
    const ATL::CCryptProv &prov, DWORD dwParam, char *szData,
    DWORD *pcchDataLen, DWORD dwFlags)
{
    USES_ATL_SAFE_ALLOCA;
    DWORD cbDataLen = 0;
    if (pcchDataLen)
	cbDataLen = *pcchDataLen * 2;
    LPWSTR TempBuf = 0;
    if(szData)
    {
	TempBuf = (LPWSTR)_ATL_SAFE_ALLOCA(cbDataLen,
	    _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
#ifdef CRYPT_UNIQUE
	if((dwParam == PP_ENUMCONTAINERS) && (dwFlags&CRYPT_UNIQUE))
	    memset(TempBuf,0,cbDataLen);
#endif // CRYPT_UNIQUE
    }
    ATL::CCryptProv &p = const_cast<ATL::CCryptProv &>(prov);
    HRESULT hr = p.GetParam(dwParam, (LPBYTE)TempBuf, &cbDataLen, dwFlags);
    if (SUCCEEDED(hr) && szData) {
	size_t len = wcslen(TempBuf)+1;
#ifdef CRYPT_UNIQUE
	if((dwParam == PP_ENUMCONTAINERS) && (dwFlags&CRYPT_UNIQUE))
	    len += wcslen(TempBuf+wcslen(TempBuf)+1)+1;
#endif // CRYPT_UNIQUE
	szData[0] = '\0';
	if (!WideCharToMultiByte(CP_ACP,0,TempBuf,(int)len,szData,(int)len,0,0))
	    return FALSE;
    }
    if (pcchDataLen)
	*pcchDataLen = cbDataLen / 2;
    return hr;
}
template <> inline HRESULT CryptGetProvParamUnicodeStringT<wchar_t>(
    const ATL::CCryptProv &prov, DWORD dwParam, wchar_t *wszData,
    DWORD *pcchDataLen, DWORD dwFlags)
{
    DWORD cbDataLen = 0;
    if (pcchDataLen)
	cbDataLen = *pcchDataLen * 2;
#ifdef CRYPT_UNIQUE
    if(wszData && pcchDataLen && (*pcchDataLen)
	&& (dwParam == PP_ENUMCONTAINERS) && (dwFlags&CRYPT_UNIQUE))
	memset(wszData,0,(*pcchDataLen)*sizeof(wszData[0]));
#endif // CRYPT_UNIQUE
    ATL::CCryptProv &p = const_cast<ATL::CCryptProv &>(prov);
    HRESULT hr = p.GetParam(dwParam, (LPBYTE)wszData, &cbDataLen, dwFlags);
    if (pcchDataLen)
	*pcchDataLen = cbDataLen / 2;
    return hr;
}
template <typename T> inline HRESULT CryptGetProvParamStringT(
    const ATL::CCryptProv &prov, DWORD dwParam, T *szData,
    DWORD *pcchDataLen, DWORD dwFlags)
{
    return CryptGetProvParamAnsiStringT(prov,dwParam,szData,pcchDataLen,dwFlags);
}

template<typename T>
inline CCryptProvidersEnumT<T>::CCryptProvidersEnumT(ATL::IAtlMemMgr *pMemMgr)
    : m_TempBuf(pMemMgr)
{
}

template<typename T>
inline HRESULT CCryptProvidersEnumT<T>::Enum ()
{
    T *pszProvName = m_TempBuf.Allocate(TempBufferLen);
    DWORD cbProvNameBuff = TempBufferByteLen;

    DWORD dwIndex = 0;
    bool recall = false;
    for (;;) {
	DWORD dwProvType = 0;
	DWORD cbProvName = cbProvNameBuff;
	pszProvName[0] = 0;
	if (!CryptEnumProvidersT (dwIndex, NULL, 0, &dwProvType,
	    pszProvName, &cbProvName)) {
	    DWORD dwError = ::GetLastError();
	    if (ERROR_NO_MORE_ITEMS == dwError)
	    {
		// Конец перечислению
		break;
	    }
	    if (recall || (ERROR_MORE_DATA != dwError))
	    {
		recall = false;
		++dwIndex;
		continue;
	    }
	    // Reallocate Buffer
	    if (cbProvName <= cbProvNameBuff)
		return HRESULT_FROM_WIN32(dwError);
	    cbProvNameBuff = cbProvName * 2;
	    pszProvName = m_TempBuf.Reallocate (cbProvNameBuff / sizeof(T));
	    // Перевызов с большим буфером
	    recall = true;
	    continue;
	}
	if (!OnProv (pszProvName, dwProvType))
	    break;
	recall = false;
	++dwIndex;
    }
    return S_OK;
}

template<typename T>
inline CCryptProviderTypesEnumT<T>::CCryptProviderTypesEnumT(
    ATL::IAtlMemMgr *pMemMgr) : m_TempBuf(pMemMgr)
{
}

template<typename T>
inline HRESULT CCryptProviderTypesEnumT<T>::Enum ()
{
    T *pszTypeName = m_TempBuf.Allocate(TempBufferLen);
    DWORD cbTypeNameBuff = TempBufferByteLen;

    DWORD dwIndex = 0;
    bool recall = false;
    for (;;) {
	DWORD dwProvType = 0;
	DWORD cbTypeName = cbTypeNameBuff;
	pszTypeName[0] = 0;
	if (!CryptEnumProviderTypesT (dwIndex, NULL, 0, &dwProvType,
	    pszTypeName, &cbTypeName)) {
	    DWORD dwError = ::GetLastError();
	    if (ERROR_NO_MORE_ITEMS == dwError)
		// Конец перечислению
		break;
	    if (recall || (ERROR_MORE_DATA != dwError))
		return HRESULT_FROM_WIN32(dwError);
	    // Reallocate Buffer
	    if (cbTypeName <= cbTypeNameBuff)
		return HRESULT_FROM_WIN32(dwError);
	    cbTypeNameBuff = cbTypeName * 2;
	    pszTypeName = m_TempBuf.Reallocate (cbTypeNameBuff / sizeof(T));
	    // Перевызов с большим буфером
	    recall = true;
	    continue;
	}
	_ATLTRY 
	{
	    if (!OnType (dwProvType, pszTypeName))
		break;
	}
	_ATLCATCHALL()
	{	    
	    recall = false;
	    ++dwIndex;
	    continue;
	}
	recall = false;
	++dwIndex;
    }
    return S_OK;
}

template<typename T>
inline CCryptProvContainersEnumT<T>::CCryptProvContainersEnumT(
    ATL::IAtlMemMgr *pMemMgr) : m_TempBuf(pMemMgr)
{
}

template<typename T>
inline HRESULT CCryptProvContainersEnumT<T>::Enum (const ATL::CCryptProv &prov, DWORD dwFlags)
{
    // Можно сразу определить длину максимального элемента, поддерживаемого
    // провайдером.
    DWORD cchContNameBuff = 0;
    HRESULT hr;
    hr = CryptGetProvParamStringT(prov, PP_ENUMCONTAINERS, (T*)0,
	&cchContNameBuff, CRYPT_FIRST|dwFlags);
    // CryptGetProvParamStringT получает и возвращает длину в символах!
    // Allocate/Reallocate требует длину в символах, не байтах!
    T *pszContName = m_TempBuf.Reallocate(cchContNameBuff);
    bool bFirstPass = true;
    for(;;)
    {
	DWORD cchContName = cchContNameBuff;
	pszContName[0] = 0;
	hr = CryptGetProvParamStringT(prov, PP_ENUMCONTAINERS,
	    pszContName, &cchContName, (bFirstPass?CRYPT_FIRST:0)|dwFlags);
	// Все вызовы, кроме первого пойдут с нулем в dwFlags
	if (FAILED(hr))
	{
	    if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr)
	    {
		// Конец перечислению
		break;
	    }
	    return hr;
	}
	if (!OnContainer (pszContName))
	{
	    break;
	}
#ifdef WINCRYPTEX
	if (dwFlags & CRYPT_UNIQUE)
	{
	    int len = ATL::CAtlString(pszContName).GetLength();
	    if (!OnUniqueContainer(pszContName, pszContName + len + 1))
	    {
		break;
	    }
	}
#endif // WINCRYPTEX
	if(bFirstPass)
	{
	    bFirstPass = false;
	}
    }
    return S_OK;
}

template<typename T>
inline HRESULT CCryptProvContainersEnumT<T>::Enum (const ATL::CCryptProv &prov)
{
    return Enum(prov,0);
}

#ifdef WINCRYPTEX
template<typename T>
inline HRESULT CCryptProvContainersEnumT<T>::EnumFqcn(const ATL::CCryptProv &prov)
{
    DWORD dwProvType = 0;
    DWORD dwSize = sizeof(dwProvType);
    ATL::CCryptProv& p = const_cast<ATL::CCryptProv&>(prov);
    HRESULT hr = p.GetParam(PP_PROVTYPE,(BYTE *)&dwProvType,&dwSize);
    if(FAILED(hr))
	return hr;
    if( dwProvType != PROV_GOST_2001_DH &&
	dwProvType != PROV_GOST_2012_256 && 
	dwProvType != PROV_GOST_2012_512 )
	return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
    return Enum(prov,CRYPT_FQCN);
}

template<typename T>
inline HRESULT CCryptProvContainersEnumT<T>::EnumUnique(const ATL::CCryptProv &prov)
{
    DWORD dwProvType = 0;
    DWORD dwSize = sizeof(dwProvType);
    ATL::CCryptProv& p = const_cast<ATL::CCryptProv&>(prov);
    HRESULT hr = p.GetParam(PP_PROVTYPE,(BYTE *)&dwProvType,&dwSize);
    if(FAILED(hr))
	return hr;
    if( dwProvType != PROV_GOST_2001_DH &&
	dwProvType != PROV_GOST_2012_256 && 
	dwProvType != PROV_GOST_2012_512 )
	return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
    return Enum(prov,CRYPT_UNIQUE);
}

template<typename T>
inline bool CCryptProvContainersEnumT<T>::OnUniqueContainer(
    const T * /*szUniqueContName*/, const T * /*szContName*/)
{
    return true;
}
#endif //WINCRYPTEX

#ifdef WINCRYPTEX
inline HRESULT CContainerExtensionEnum::Enum(const ATL::CCryptProv& prov)
{
    ATL::CCryptProv& p = const_cast<ATL::CCryptProv&>(prov);
    DWORD dwLen;
    DWORD dwFlags = CRYPT_FIRST;
    HRESULT hr = p.GetParam(PP_ENUM_CONTAINER_EXTENSION, 0, &dwLen, dwFlags);
    if( HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr )
	return S_OK;
    else if(FAILED(hr))
	return hr;
    CStringBlob buf;
    CONTAINER_EXTENSION& ext = *reinterpret_cast<CONTAINER_EXTENSION*>(
	buf.GetBufferSetLength(dwLen));
    while (hr == S_OK) {
	dwLen = static_cast<DWORD>(buf.GetLength());
	hr = p.GetParam(PP_ENUM_CONTAINER_EXTENSION,
	    reinterpret_cast<BYTE*>(buf.GetBuffer()), &dwLen, dwFlags);
	if( HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr )
	    return S_OK;
	else if( FAILED(hr) )
	    return hr;
	if(!OnContainerExtension(reinterpret_cast<const CONTAINER_EXTENSION *>(
	    buf.GetBuffer()), dwLen))
	    break;
	if(!OnContainerExtension(ext.sOid + ext.cbExtension - 1,
	    ext.pbExtension, ext.cbExtension, ext.bCritical))
	    break;
	dwFlags &= ~CRYPT_FIRST;
    }
    return S_OK;
}

#endif //WINCRYPTEX


#ifdef READPKIOBJECT

inline LPCSTR TrimHeaders( const BYTE *pbSourceData, DWORD cbSourceData,
    int * srcLen)
{
    if( !pbSourceData || !srcLen )
	return 0;

    const int START = 0;
    const int HEADER_PREFIX = 1;
    const int HEADER_TEXT = 2;
    const int HEADER_SUFFIX = 3;
    const int MESSAGE = 4;
    const int FOOTER_PREFIX = 5;

    LPCSTR pSrc = reinterpret_cast<LPCSTR>(pbSourceData);
    if(!cbSourceData || '-' != pbSourceData[0] )
	return pSrc;
    int state = START;
    LPCSTR p = pSrc;
    while(p < pSrc + cbSourceData && state != FOOTER_PREFIX )
    {
	if( '-' == *p )
	{
	    switch(state)
	    {
	    case START: state = HEADER_PREFIX; break;
	    case HEADER_TEXT: state = HEADER_SUFFIX; break;
	    case MESSAGE: state = FOOTER_PREFIX; break;
	    }
	}
	else
	{
	    switch(state)
	    {
	    case HEADER_PREFIX: state = HEADER_TEXT; break;
	    case HEADER_SUFFIX: state = MESSAGE; pSrc = p; break;
	    }
	}
	++p;
    }

    if( state != FOOTER_PREFIX )
	return 0;

    (*srcLen) = static_cast<int>(p - pSrc - 1);
    return pSrc;
}

inline HRESULT CertReadPKIObjectBase64ASCII( const BYTE *pbSourceData, 
    DWORD cbSourceData, BYTE *pbResultData, DWORD *pcbResultData)
{
    if(!pbSourceData || !pcbResultData)
	return E_INVALIDARG;

    int size = 0;
    BYTE *pDest = 0;
    if(pbResultData)
    {
	size = *pcbResultData;
	pDest = pbResultData;
    }
    //удаляем все пробелы из сообщения
    std::vector<BYTE> pbCleanData(cbSourceData);
    DWORD cbCleanData = 0;
    for(unsigned int i = 0; i < cbSourceData; i++)
    {
        if(isspace(pbSourceData[i]))
            continue;
        pbCleanData[cbCleanData] = pbSourceData[i];
        cbCleanData++;
    }

    int srcLen = cbCleanData;
    LPCSTR szData = TrimHeaders(&pbCleanData[0], cbCleanData, &srcLen);
    if( !szData )
        return E_FAIL;
    BOOL res = ATL::Base64Decode(szData, srcLen,
	pDest, &size);
    BOOL moreData = ((pbResultData)?(size>(int(*pcbResultData))):FALSE);
    *pcbResultData = size;
    if( pbResultData && !res )
	return (moreData?(ATL::AtlHresultFromWin32(ERROR_MORE_DATA)):E_FAIL);
    return S_OK;
}

inline HRESULT CertReadPKIObjectBase64UTF16LE( const BYTE *pbSourceData, 
    DWORD cbSourceData, BYTE *pbResultData, DWORD *pcbResultData)
{
    LPCWSTR sourceStr = reinterpret_cast<LPCWSTR>(pbSourceData);
    int cbMultiByte = ::WideCharToMultiByte( CP_ACP, 0, sourceStr,
	cbSourceData/sizeof(wchar_t),0,0,0,0);
    if(cbMultiByte <= 0)
	return ATL::AtlHresultFromLastError();
    ATL::CAtlStringA destStr;
    cbMultiByte = ::WideCharToMultiByte( CP_ACP, 0, sourceStr,
	cbSourceData/sizeof(wchar_t), 
	destStr.GetBufferSetLength(cbMultiByte),
	cbMultiByte,0,0);
    if( cbMultiByte != destStr.GetLength() )
	return ATL::AtlHresultFromLastError();

    const BYTE *pbNewSourceData = reinterpret_cast<BYTE*>(
	destStr.GetBuffer());
    DWORD cbNewSourceData = destStr.GetLength();
    return CertReadPKIObjectBase64ASCII(pbNewSourceData,
	cbNewSourceData,pbResultData,pcbResultData);
}

inline HRESULT CertReadPKIObjectBase64UTF16BE( const BYTE *pbSourceData, 
    DWORD cbSourceData, BYTE *pbResultData, DWORD *pcbResultData)
{
    if( 0 != (cbSourceData%(sizeof(wchar_t))) )
	return E_INVALIDARG;

    ATL::CAtlStringA dest;
    BYTE *pbNewSourceData = reinterpret_cast<BYTE*>(
	dest.GetBufferSetLength(cbSourceData));
    for( DWORD i = 0; i < cbSourceData; i += 2 )
    {
	pbNewSourceData[i] = pbSourceData[i+1];
	pbNewSourceData[i+1] = pbSourceData[i];
    }

    return CertReadPKIObjectBase64UTF16LE(pbNewSourceData,cbSourceData,
	pbResultData,pcbResultData);
}
inline HRESULT CertReadPKIObject( const BYTE *pbSourceData, DWORD cbSourceData,
    BYTE *pbResultData, DWORD *pcbResultData)
{

    if(!pbSourceData || !pcbResultData)
	return E_INVALIDARG;
    // Проверка на UTF8 BOM. Если есть BOM, то это может быть только base64.
    DWORD utf8BomLen = 3;
    if( cbSourceData > utf8BomLen 
	&& 0xEF == pbSourceData[0] && 0xBB == pbSourceData[1]
	&& 0xBF == pbSourceData[2] )
	return CertReadPKIObjectBase64ASCII(pbSourceData + utf8BomLen,
	    cbSourceData-utf8BomLen, pbResultData, pcbResultData);

    // Проверка на UTF16LE BOM. Если есть BOM, то это может быть только base64.
    DWORD utf16leBomLen = 2;
    if( cbSourceData > utf16leBomLen 
	&& 0xFF == pbSourceData[0] && 0xFE == pbSourceData[1] )
	return CertReadPKIObjectBase64UTF16LE(pbSourceData + utf16leBomLen,
	    cbSourceData-utf16leBomLen, pbResultData, pcbResultData);

    // Проверка на UTF16BE BOM. Если есть BOM, то это может быть только base64.
    DWORD utf16beBomLen = 2;
    if( cbSourceData > utf16beBomLen 
	&& 0xFE == pbSourceData[0] && 0xFF == pbSourceData[1] )
	return CertReadPKIObjectBase64UTF16BE(pbSourceData + utf16beBomLen,
	cbSourceData-utf16beBomLen, pbResultData, pcbResultData);

    unsigned int first_char_index = 0;
    if( cbSourceData > 1 )
    {
            //тут символ занимает по 4 байта
            LPCWSTR sourceStr = reinterpret_cast<LPCWSTR>(pbSourceData);
            for(first_char_index = 0; first_char_index < cbSourceData; first_char_index++)
            {
                if(sourceStr[first_char_index] != L' ' && sourceStr[first_char_index] != L'\r' && sourceStr[first_char_index] != L'\n' 
                && sourceStr[first_char_index] != L'\t')
                    break;
            }
            first_char_index*=sizeof(WCHAR);
	// Проверка на UTF16LE
#ifdef WORDS_BIGENDIAN
	bool isM = ( 'M' == pbSourceData[first_char_index + 3] && 0 == pbSourceData[first_char_index + 2] );
	bool isMinus = ( '-' == pbSourceData[first_char_index + 3] && 0 == pbSourceData[first_char_index + 2] );
#else
	bool isM = ( 'M' == pbSourceData[first_char_index] && 0 == pbSourceData[first_char_index + 1] );
	bool isMinus = ( '-' == pbSourceData[first_char_index] && 0 == pbSourceData[first_char_index + 1] );
#endif // WORDS_BIGENDIAN
	if( isM || isMinus )
	    return CertReadPKIObjectBase64UTF16LE(&pbSourceData[0], cbSourceData,
		pbResultData,pcbResultData);
	// Проверка на UTF16BE
#ifdef WORDS_BIGENDIAN
	isM = ( 'M' == pbSourceData[first_char_index + 2] && 0 == pbSourceData[first_char_index + 3] );
	isMinus = ( '-' == pbSourceData[first_char_index + 2] && 0 == pbSourceData[first_char_index + 3] );
#else
	isM = ( 'M' == pbSourceData[first_char_index + 1] && 0 == pbSourceData[first_char_index] );
	isMinus = ( '-' == pbSourceData[first_char_index + 1] && 0 == pbSourceData[first_char_index] );
#endif //WORDS_BIGENDIAN
	if( isM || isMinus )
	    return CertReadPKIObjectBase64UTF16BE(&pbSourceData[0], cbSourceData,
		pbResultData, pcbResultData);
    }
    for(first_char_index = 0; first_char_index < cbSourceData; first_char_index++)
    {
        if(!isspace(pbSourceData[first_char_index]))
            break;
    }
    if( cbSourceData > 0 
	&& ( 'M' == pbSourceData[first_char_index] || '-' == pbSourceData[first_char_index] ) )
	return CertReadPKIObjectBase64ASCII(&pbSourceData[0], cbSourceData,
	    pbResultData, pcbResultData);
    // Остаётся предположить, что это DER
    if(pbResultData)
    {
	if(!pcbResultData)
	    return E_INVALIDARG;
	if( (*pcbResultData) < cbSourceData )
	    return ATL::AtlHresultFromWin32(ERROR_MORE_DATA);
	memcpy(pbResultData, pbSourceData, cbSourceData);
    }
    if(pcbResultData)
	(*pcbResultData) = cbSourceData;
    return S_OK;
}

inline HRESULT CertReadPKIObject( const BYTE *pbSourceData, 
    DWORD cbSourceData, ATL::CAtlStringA& buffer)
{
    DWORD size = 0;
    HRESULT hr = CertReadPKIObject(
	pbSourceData, cbSourceData, 0, &size);
    if(FAILED(hr))
	return hr;
    hr = CertReadPKIObject(pbSourceData, cbSourceData,
	reinterpret_cast<BYTE*>(buffer.GetBufferSetLength(size)), &size);
    return hr;
}

inline HRESULT CertReadPKIObjectFileA( LPCSTR szFilePath, 
    ATL::CAtlStringA& buffer )
{
    USES_CONVERSION;
    ATL::CAtlFile file;
    HRESULT hr = file.Create(ATL::CA2T(szFilePath),
	GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
    if(FAILED(hr))
	return hr;
    ULONGLONG ullSize;
    hr = file.GetSize(ullSize);
    if(FAILED(hr))
	return hr;
    DWORD size = static_cast<DWORD>(ullSize);
    ATL::CAtlStringA sourceBuffer;
    hr = file.Read(sourceBuffer.GetBufferSetLength(size),size);
    if(FAILED(hr))
	return hr;
    return CertReadPKIObject(
	reinterpret_cast<BYTE*>(sourceBuffer.GetBuffer()),
	sourceBuffer.GetLength(),buffer);
}

inline HRESULT CertReadPKIObjectFileW( LPCWSTR wszFilePath, 
    ATL::CAtlStringA& buffer )
{
    USES_CONVERSION;
    ATL::CAtlFile file;
    HRESULT hr = file.Create(ATL::CW2T(wszFilePath),
	GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
    if(FAILED(hr))
	return hr;
    ULONGLONG ullSize;
    hr = file.GetSize(ullSize);
    if(FAILED(hr))
	return hr;
    DWORD size = static_cast<DWORD>(ullSize);
    ATL::CAtlStringA sourceBuffer;
    hr = file.Read(sourceBuffer.GetBufferSetLength(size),size);
    if(FAILED(hr))
	return hr;
    return CertReadPKIObject(
	reinterpret_cast<BYTE*>(sourceBuffer.GetBuffer()),
	sourceBuffer.GetLength(),buffer);
}

inline HRESULT CertReadCertificate( const BYTE *pbSourceData,
    DWORD cbSourceData, PCCERT_CONTEXT *ppCertContext)
{
    ATL::CAtlStringA buffer;
    HRESULT hr = CertReadPKIObject(pbSourceData,cbSourceData,buffer);
    if(FAILED(hr))
	return hr;
    (*ppCertContext) = ::CertCreateCertificateContext(
	X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
	reinterpret_cast<BYTE*>(buffer.GetBuffer()),buffer.GetLength());
    if( !(*ppCertContext) )
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CertReadCertificateFileA( LPCSTR szFilePath,
    PCCERT_CONTEXT *ppCertContext)
{
    ATL::CAtlStringA buffer;
    HRESULT hr = CertReadPKIObjectFileA(szFilePath,buffer);
    if(FAILED(hr))
	return hr;
    (*ppCertContext) = ::CertCreateCertificateContext(
	X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
	reinterpret_cast<BYTE*>(buffer.GetBuffer()),buffer.GetLength());
    if( !(*ppCertContext) )
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CertReadCertificateFileW( LPCWSTR wszFilePath,
    PCCERT_CONTEXT *ppCertContext)
{
    ATL::CAtlStringA buffer;
    HRESULT hr = CertReadPKIObjectFileW(wszFilePath,buffer);
    if(FAILED(hr))
	return hr;
    (*ppCertContext) = ::CertCreateCertificateContext(
	X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
	reinterpret_cast<BYTE*>(buffer.GetBuffer()),buffer.GetLength());
    if( !(*ppCertContext) )
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CertReadCertificateFileA( LPCSTR szFilePath,
    CCertContext& cert)
{
    ATLENSURE_RETURN(!cert.GetHandle());
    PCCERT_CONTEXT pCertContext;
    ATL_HR_ERRORCHECK_RETURN(
	CertReadCertificateFileA(szFilePath, &pCertContext));
    cert.Attach(pCertContext, TRUE);
    return S_OK;
}

inline HRESULT CertReadCertificateFileW( LPCWSTR wszFilePath,
    CCertContext& cert)
{
    ATLENSURE_RETURN(!cert.GetHandle());
    PCCERT_CONTEXT pCertContext;
    ATL_HR_ERRORCHECK_RETURN(
	CertReadCertificateFileW(wszFilePath, &pCertContext));
    cert.Attach(pCertContext, TRUE);
    return S_OK;
}

inline HRESULT CertReadCRL( const BYTE *pbSourceData, DWORD cbSourceData,
    PCCRL_CONTEXT *ppCrlContext)
{
    ATL::CAtlStringA buffer;
    HRESULT hr = CertReadPKIObject(pbSourceData,cbSourceData,buffer);
    if(FAILED(hr))
	return hr;
    (*ppCrlContext) = ::CertCreateCRLContext(
	X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
	reinterpret_cast<BYTE*>(buffer.GetBuffer()),buffer.GetLength());
    if( !(*ppCrlContext) )
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CertReadCRLFileA( LPCSTR szFilePath,
    PCCRL_CONTEXT *ppCrlContext)
{
    ATL::CAtlStringA buffer;
    HRESULT hr = CertReadPKIObjectFileA(szFilePath,buffer);
    if(FAILED(hr))
	return hr;
    (*ppCrlContext) = ::CertCreateCRLContext(
	X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
	reinterpret_cast<BYTE*>(buffer.GetBuffer()),buffer.GetLength());
    if( !(*ppCrlContext) )
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CertReadCRLFileW( LPCWSTR wszFilePath,
    PCCRL_CONTEXT *ppCrlContext)
{
    ATL::CAtlStringA buffer;
    HRESULT hr = CertReadPKIObjectFileW(wszFilePath,buffer);
    if(FAILED(hr))
	return hr;
    (*ppCrlContext) = ::CertCreateCRLContext(
	X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
	reinterpret_cast<BYTE*>(buffer.GetBuffer()),buffer.GetLength());
    if( !(*ppCrlContext) )
	return ATL::AtlHresultFromLastError();
    return S_OK;
}
#endif // READPKIOBJECT

inline CAutoCryptProvWnd::CAutoCryptProvWnd(HWND hWnd) // : m_hWnd(hWnd)
{
    CCryptProvEx::SetClientHwnd(hWnd);
}

inline CAutoCryptProvWnd::~CAutoCryptProvWnd()
{
    CCryptProvEx::SetClientHwnd(0);
}

inline CCryptKeyProvInfo::CCryptKeyProvInfo()
{
    // TODO: AIX: 1540-1102 (W) "empty" might be used before it is set.
    CRYPT_KEY_PROV_INFO empty = {};
    m_KeyProvInfo = empty;
}

inline CCryptKeyProvInfo::~CCryptKeyProvInfo()
{
}

inline CCryptKeyProvInfo::operator const CRYPT_KEY_PROV_INFO*() throw()
{
    return &m_KeyProvInfo;
}

inline const CRYPT_KEY_PROV_INFO* CCryptKeyProvInfo::GetHandle() const throw()
{
    return &m_KeyProvInfo;
}

inline bool CCryptKeyProvInfo::IsEmpty() throw()
{
    CRYPT_KEY_PROV_INFO empty = {};
    return ((m_KeyProvInfo.pwszContainerName == NULL) &&
        (m_KeyProvInfo.pwszProvName == NULL) &&
        (m_KeyProvInfo.dwProvType == empty.dwProvType) &&
        (m_KeyProvInfo.dwFlags == empty.dwFlags) &&
        (m_KeyProvInfo.cProvParam == 0) &&
        (m_KeyProvInfo.rgProvParam == NULL) &&
        (m_KeyProvInfo.dwKeySpec == empty.dwKeySpec));
}

inline void CCryptKeyProvInfo::Uninitialize ()
{
    // TODO: AIX: 1540-1102 (W) "empty" might be used before it is set.
    CRYPT_KEY_PROV_INFO empty = {};
    m_KeyProvInfo = empty;
    m_ContainerName.Empty();
    m_ProvName.Empty();
    m_CryptKeyProvParams.RemoveAll();
    m_Params.RemoveAll();
}

inline HRESULT CCryptKeyProvInfo::Initialize (__in DWORD dwProvType,
    __in LPWSTR pwszProvName, __in LPCWSTR pwszContainerName,
    __in DWORD dwKeySpec, __in_opt DWORD dwFlags)
{
    m_ContainerName = pwszContainerName;
    m_KeyProvInfo.pwszContainerName = m_ContainerName.GetBuffer();
    m_ProvName = pwszProvName;
    m_KeyProvInfo.pwszProvName = m_ProvName.GetBuffer();
    m_KeyProvInfo.dwProvType = dwProvType;
    m_KeyProvInfo.dwFlags = dwFlags;
    m_KeyProvInfo.cProvParam = 0;
    m_KeyProvInfo.rgProvParam = NULL;
    m_KeyProvInfo.dwKeySpec = dwKeySpec;
    return S_OK;
}

inline HRESULT CCryptKeyProvInfo::Initialize (
    __in const CRYPT_KEY_PROV_INFO *pInfo)
{
    HRESULT hr = Initialize (pInfo->dwProvType,
	pInfo->pwszProvName, pInfo->pwszContainerName,
	pInfo->dwKeySpec, pInfo->dwFlags);
    if (FAILED(hr))
	return hr;
    for (DWORD i=0; i<pInfo->cProvParam; ++i) {
	hr = AddKeyProvParam (pInfo->rgProvParam[i].dwParam,
	    pInfo->rgProvParam[i].pbData, pInfo->rgProvParam[i].cbData,
	    pInfo->rgProvParam[i].dwFlags);
	if (FAILED(hr))
	    return hr;
    }
    return hr;
}

// Эта функция по ключу key и провайдеру prov заполняет
// структуру CRYPT_KEY_PROV_INFO (после чего её можно
// передавать в CCryptStore::SetKeyProvInfo
inline HRESULT CCryptKeyProvInfo::Initialize (
    __in const ATL::CCryptProv& prov, __in const ATL::CCryptKey& key,
    __in_opt DWORD dwKeySpec, __in DWORD dwFlags)
{
    CCryptProvEx provEx(prov);

    HRESULT hr;
    hr = provEx.GetUniqueContainer(m_ContainerName);
    if (FAILED(hr))
	return hr;
    m_KeyProvInfo.pwszContainerName = m_ContainerName.GetBuffer();

    hr = provEx.GetName(m_ProvName);
    if (FAILED(hr))
	return hr;
    m_KeyProvInfo.pwszProvName = m_ProvName.GetBuffer();

    hr = provEx.GetProvType(&m_KeyProvInfo.dwProvType);
    if (FAILED(hr))
	return hr;
    if( 0  == dwFlags )//угадаем компьютер или нет
	hr = provEx.GetKeySetType( &m_KeyProvInfo.dwFlags );
    else
	m_KeyProvInfo.dwFlags = dwFlags;

    m_KeyProvInfo.cProvParam = 0;
    m_KeyProvInfo.rgProvParam = NULL;
    if (dwKeySpec != 0)
	m_KeyProvInfo.dwKeySpec = dwKeySpec;
    else {
	CCryptKeyEx keyEx(key);
	hr = keyEx.GetKeySpec(&m_KeyProvInfo.dwKeySpec);
	if (FAILED(hr))
	    return hr;
    }
    return hr;
}

inline HRESULT CCryptKeyProvInfo::AddKeyProvParam(
    __in DWORD dwParam, __in_bcount(cbData) BYTE *pbData,
    __in DWORD cbData, __reserved DWORD dwFlags) //throw(...)
{
    CStringBlob sParam(reinterpret_cast<LPSTR>(pbData), cbData);

    int idxParams = m_Params.GetSize();
    ATLASSERT(idxParams == m_CryptKeyProvParams.GetSize());
    if (!m_Params.Add(sParam))
	return E_OUTOFMEMORY;
    CStringBlob& Param = m_Params[idxParams];

    CRYPT_KEY_PROV_PARAM CryptKeyProvParam;
    CryptKeyProvParam.dwParam = dwParam;
    CryptKeyProvParam.pbData = reinterpret_cast<BYTE *>(Param.GetBuffer());
    CryptKeyProvParam.cbData = Param.GetLength();
    CryptKeyProvParam.dwFlags = dwFlags;

    if (!m_CryptKeyProvParams.Add(CryptKeyProvParam))
	return E_OUTOFMEMORY;

    m_KeyProvInfo.cProvParam++;
    m_KeyProvInfo.rgProvParam = m_CryptKeyProvParams.GetData();
    return S_OK;
}

inline CCryptMsg::CCryptMsg() throw()
    : m_hMsg(NULL)
{
}

inline CCryptMsg::CCryptMsg (const CCryptMsg& msg) throw()
{
    m_hMsg = msg.Duplicate();
}

inline CCryptMsg &CCryptMsg::operator=(const CCryptMsg& msg) throw()
{
    if (this == &msg)
    {
	return *this;
    }
    if (m_hMsg)
	Destroy();
    m_hMsg = msg.Duplicate();
    return *this;
}

inline CCryptMsg::CCryptMsg (
    HCRYPTMSG hMsg, BOOL bTakeOwnership /* = FALSE */) throw()
{
    if (bTakeOwnership)
	m_hMsg = hMsg;
    else
	m_hMsg = ::CryptMsgDuplicate(hMsg);
}

inline CCryptMsg::~CCryptMsg() throw()
{
    Destroy ();
}

inline void CCryptMsg::Attach(
    HCRYPTMSG hMsg, BOOL bTakeOwnership /* = FALSE */) throw()
{
    ATLASSERT(m_hMsg == NULL);
    if (m_hMsg != NULL) return;

    if (bTakeOwnership)
	m_hMsg = hMsg;
    else
	m_hMsg = ::CryptMsgDuplicate(hMsg);
}

inline void CCryptMsg::Destroy() throw()
{
    if (m_hMsg == NULL) return;
    BOOL bSuccess = ::CryptMsgClose(m_hMsg);
    ATLVERIFY(bSuccess);
    m_hMsg = NULL;
}

inline HCRYPTMSG CCryptMsg::Detach() throw()
{
    HCRYPTMSG hMsg;
    hMsg = m_hMsg;
    m_hMsg = NULL;
    return hMsg;
}

inline HCRYPTMSG CCryptMsg::Duplicate() const throw()
{
    HCRYPTMSG hMsg = ::CryptMsgDuplicate(m_hMsg);
    return hMsg;
}

inline HRESULT CCryptMsg::Uninitialize() throw()
{
    ATLENSURE_RETURN(m_hMsg != NULL);
    if (!::CryptMsgClose(m_hMsg))
	return ATL::AtlHresultFromLastError();
    m_hMsg = NULL;
    return S_OK;
}

inline HRESULT CCryptMsg::OpenToEncode(DWORD dwMsgType,
    const void* pvMsgEncodeInfo,
    DWORD dwFlags /* = 0*/,
    PCMSG_STREAM_INFO pStreamInfo /* = NULL */, 
    DWORD dwMsgEncodingType /* = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING */,
    LPSTR pszInnerContentObjID /* = NULL */) throw()
{
    ATLENSURE_RETURN(m_hMsg == NULL);
    m_hMsg = ::CryptMsgOpenToEncode(dwMsgEncodingType, dwFlags, dwMsgType,
	    pvMsgEncodeInfo, pszInnerContentObjID, pStreamInfo);
    if (!m_hMsg)
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCryptMsg::OpenToDecode(DWORD dwMsgType /* = 0*/,
    DWORD dwFlags /* = 0*/,
    PCMSG_STREAM_INFO pStreamInfo /* = NULL */, 
    DWORD dwMsgEncodingType /* = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING */,
    HCRYPTPROV hCryptProv /* = NULL */, 
    PCERT_INFO pRecipientInfo /* = NULL */) throw()
{
    ATLENSURE_RETURN(m_hMsg == NULL);
    m_hMsg = ::CryptMsgOpenToDecode(dwMsgEncodingType, dwFlags, dwMsgType,
	    hCryptProv, pRecipientInfo, pStreamInfo);
    if (!m_hMsg)
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCryptMsg::Update(const BYTE* pbData, DWORD cbData,
    BOOL fFinal /*= TRUE*/) throw()
{
    if (!::CryptMsgUpdate(m_hMsg, pbData, cbData, fFinal))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCryptMsg::Update(const CStringBlob& Data,
    BOOL fFinal /*= TRUE*/) //throw(...)
{
    return Update(reinterpret_cast<const BYTE*>(Data.GetString()),
	Data.GetLength(), fFinal);
}

#ifndef UNIX
inline HRESULT CCryptMsg::Countersign(__in DWORD dwIndex,
    __in DWORD cCountersigners,
    __in_ecount(cCountersigners) PCMSG_SIGNER_ENCODE_INFO rgCountersigners)
    throw()
{
    if (!::CryptMsgCountersign(
	m_hMsg, dwIndex, cCountersigners, rgCountersigners))
    {
	return ATL::AtlHresultFromLastError();
    }
    return S_OK;
}
#endif //UNIX

inline HRESULT CCryptMsg::Control(__in DWORD dwCtrlType,
    __in const void* pvCtrlPara, __in DWORD dwFlags /*= 0*/) throw()
{
    ATLENSURE_RETURN(m_hMsg != NULL);
    if (!::CryptMsgControl(m_hMsg, dwFlags, dwCtrlType, pvCtrlPara)) {
	return ATL::AtlHresultFromLastError();
    }
    return S_OK;
}

inline HRESULT CCryptMsg::AddSigner(
	__in PCMSG_SIGNER_ENCODE_INFO para, __in DWORD dwFlags /*= 0*/) throw()
{
    return Control(CMSG_CTRL_ADD_SIGNER, para, dwFlags);
}

inline HRESULT CCryptMsg::AddSignerUnauthAttr(
	__in PCMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR_PARA para) throw()
{
    return Control(CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR, para);
}

inline HRESULT CCryptMsg::GetParam(__in DWORD dwParamType, __in DWORD dwIndex,
	__out_bcount_part_opt(*pcbData, *pcbData) void *pvData,
	__inout DWORD *pcbData) throw()
{
    ATLENSURE_RETURN(m_hMsg != NULL);
    if (!::CryptMsgGetParam (m_hMsg, dwParamType, dwIndex, pvData, pcbData))
	return ATL::AtlHresultFromLastError();
    return S_OK;
}

inline HRESULT CCryptMsg::GetParam(__in DWORD dwParamType, __in DWORD dwIndex,
    __out CStringBlob& Result) //throw(...)
{
    DWORD cbData = 512;
    HRESULT hr = GetParam(dwParamType, dwIndex,
	reinterpret_cast<BYTE *>(Result.GetBuffer (cbData)), &cbData);
    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) {
	// В случае ERROR_MORE_DATA в cbData вернули требуемый размер
	hr = GetParam (dwParamType, dwIndex,
	    reinterpret_cast<BYTE *>(Result.GetBuffer (cbData)),
	    &cbData);
    }
    if (FAILED(hr)) {
	return hr;
    }
    Result.ReleaseBufferSetLength (cbData);
    return hr;
}

inline HRESULT CCryptMsg::GetDwordParam(__in DWORD dwParamType,
    __in DWORD dwIndex, __out DWORD &dwResult) throw()
{
    DWORD size = sizeof(dwResult);
    return GetParam(dwParamType, dwIndex, &dwResult, &size);
}

inline HRESULT CCryptMsg::GetAttrParam(__in DWORD dwParamType,
    __in DWORD dwIndex,
    __out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
    __inout DWORD *pcbData, __out PCCRYPT_ATTRIBUTES& pcAttrs) throw()
{
    HRESULT hr = GetParam(dwParamType, dwIndex, pbData,
	pcbData);
    if (SUCCEEDED(hr) && pbData) {
	pcAttrs = reinterpret_cast<PCCRYPT_ATTRIBUTES>(pbData);
    }
    return hr;
}

inline HRESULT CCryptMsg::GetAttrParam(__in DWORD dwParamType,
    __in DWORD dwIndex, __out CStringBlob& Result,
    __out PCCRYPT_ATTRIBUTES& pcAttrs) //throw(...)
{
    HRESULT hr = GetParam(dwParamType, dwIndex, Result);
    if (SUCCEEDED(hr)) {
	pcAttrs = reinterpret_cast<PCCRYPT_ATTRIBUTES>(Result.LockBuffer());
    }
    return hr;
}

inline HRESULT CCryptMsg::GetContent(
    __out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
    __inout DWORD *pcbData) throw()
{
    return GetParam(CMSG_CONTENT_PARAM, 0, pbData, pcbData);
}

inline HRESULT CCryptMsg::GetContent(__out CStringBlob& Result) //throw(...)
{
    return GetParam(CMSG_CONTENT_PARAM, 0, Result);
}

inline HRESULT CCryptMsg::GetEncodedMessage(
    __out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
    __inout DWORD *pcbData) throw()
{
    return GetParam(CMSG_ENCODED_MESSAGE, 0, pbData, pcbData);
}

inline HRESULT CCryptMsg::GetEncodedMessage(
    __out CStringBlob& Result) //throw(...)
{
    return GetParam(CMSG_ENCODED_MESSAGE, 0, Result);
}

inline HRESULT CCryptMsg::GetEncodedSigner(__in DWORD dwIndex,
    __out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
    __inout DWORD *pcbData) throw()
{
    return GetParam(CMSG_ENCODED_SIGNER, dwIndex, pbData, pcbData);
}

inline HRESULT CCryptMsg::GetEncodedSigner(__in DWORD dwIndex,
    __out CStringBlob& Result) //throw(...)
{
    return GetParam(CMSG_ENCODED_SIGNER, dwIndex, Result);
}

inline HRESULT CCryptMsg::GetEncryptedDigest(__in DWORD dwIndex,
    __out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
    __inout DWORD *pcbData) throw()
{
    return GetParam(CMSG_ENCRYPTED_DIGEST, dwIndex, pbData, pcbData);
}

inline HRESULT CCryptMsg::GetEncryptedDigest(__in DWORD dwIndex,
    __out CStringBlob& Result) //throw(...)
{
    return GetParam(CMSG_ENCRYPTED_DIGEST, dwIndex, Result);
}

inline HRESULT CCryptMsg::GetCmsSignerInfo(__in DWORD dwIndex,
    __out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
    __inout DWORD *pcbData,
    __out PCCMSG_CMS_SIGNER_INFO& pcSignerInfo) throw()
{
    HRESULT hr = GetParam(CMSG_CMS_SIGNER_INFO_PARAM, dwIndex, pbData,
	pcbData);
    if (SUCCEEDED(hr) && pbData) {
	pcSignerInfo = reinterpret_cast<PCCMSG_CMS_SIGNER_INFO>(pbData);
    }
    return hr;
}

inline HRESULT CCryptMsg::GetCmsSignerInfo(__in DWORD dwIndex,
    __out CStringBlob& Result,
    __out PCCMSG_CMS_SIGNER_INFO& pcSignerInfo) //throw(...)
{
    HRESULT hr = GetParam(CMSG_CMS_SIGNER_INFO_PARAM, dwIndex, Result);
    if (SUCCEEDED(hr)) {
	pcSignerInfo =
	    reinterpret_cast<PCCMSG_CMS_SIGNER_INFO>(Result.LockBuffer());
    }
    return hr;
}


#ifndef _WIN32_WCE
// В wincrypt.h для CE CryptDecodeObject - это макрос, заменяющий вызов
// на CryptDecodeObjectEx, поэтому под CE не вводим дополнительные функции
// CryptDecodeObject. При желании можно сделать и для CE, но пока не
// было необходимости.

inline HRESULT CCryptMsg::GetAndDecodeCmsSignerInfo(__in DWORD dwIndex,
    __out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
    __inout DWORD *pcbData,
    __out PCCMSG_CMS_SIGNER_INFO& pcSignerInfo,
    __in DWORD dwFlags /*= CRYPT_DECODE_SHARE_OID_STRING_FLAG*/,
    __in DWORD dwCertEncodingType
    /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) //throw()
{
    CStringBlob encodedSigner;
    ATL_HR_ERRORCHECK_RETURN(GetEncodedSigner(dwIndex, encodedSigner));
    return ATL2::CryptDecodeCmsSignerInfo(encodedSigner,
	pbData, pcbData, pcSignerInfo, dwFlags, dwCertEncodingType);
}

inline HRESULT CCryptMsg::GetAndDecodeCmsSignerInfo(__in DWORD dwIndex,
    __out CStringBlob& Result,
    __out PCCMSG_CMS_SIGNER_INFO& pcSignerInfo,
    __in DWORD dwFlags /*= CRYPT_DECODE_SHARE_OID_STRING_FLAG*/,
    __in DWORD dwCertEncodingType
    /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) //throw()
{
    CStringBlob encodedSigner;
    ATL_HR_ERRORCHECK_RETURN(GetEncodedSigner(dwIndex, encodedSigner));
    return ATL2::CryptDecodeCmsSignerInfo(encodedSigner,
	Result, pcSignerInfo, dwFlags, dwCertEncodingType);
}

#endif // !_WIN32_WCE

inline HRESULT CCryptMsg::GetAuthAttr(__in DWORD dwIndex,
    __out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
    __inout DWORD *pcbData, __out PCCRYPT_ATTRIBUTES& pcAttrs) throw()
{
    return GetAttrParam(CMSG_SIGNER_AUTH_ATTR_PARAM, dwIndex, pbData,
	pcbData, pcAttrs);
}

inline HRESULT CCryptMsg::GetAuthAttr(__in DWORD dwIndex,
    __out CStringBlob& Result, __out PCCRYPT_ATTRIBUTES& pcAttrs) //throw(...)
{
    return GetAttrParam(CMSG_SIGNER_AUTH_ATTR_PARAM, dwIndex, Result, pcAttrs);
}

inline HRESULT CCryptMsg::GetUnauthAttr(__in DWORD dwIndex,
    __out_bcount_part(*pcbData, *pcbData) BYTE *pbData,
    __inout DWORD *pcbData, __out PCCRYPT_ATTRIBUTES& pcAttrs) throw()
{
    return GetAttrParam(CMSG_SIGNER_UNAUTH_ATTR_PARAM, dwIndex, pbData,
	pcbData, pcAttrs);
}

inline HRESULT CCryptMsg::GetUnauthAttr(__in DWORD dwIndex,
    __out CStringBlob& Result, __out PCCRYPT_ATTRIBUTES& pcAttrs) //throw(...)
{
    return GetAttrParam(CMSG_SIGNER_UNAUTH_ATTR_PARAM, dwIndex, Result,
	pcAttrs);
}

inline HRESULT CCryptMsg::GetSignerCount(__out DWORD& dwCount) throw()
{
    return GetDwordParam(CMSG_SIGNER_COUNT_PARAM, 0, dwCount);
}

inline HRESULT CCryptMsg::GetType(__out DWORD &dwMsgType) throw()
{
    return GetDwordParam(CMSG_TYPE_PARAM, 0, dwMsgType);
}

inline HCRYPTMSG CCryptMsg::GetHandle() const throw()
{
    return m_hMsg;
}

#ifndef _WIN32_WCE
// В wincrypt.h для CE CryptDecodeObject - это макрос, заменяющий вызов
// на CryptDecodeObjectEx, поэтому под CE не вводим дополнительные функции
// CryptDecodeObject. При желании можно сделать и для CE, но пока не
// было необходимости.

inline HRESULT CryptDecodeObject(__in LPCSTR lpszStructType,
    __in_bcount(cbEncoded) const BYTE *pbEncoded, __in DWORD cbEncoded,
    __out CStringBlob& Result,
    __in DWORD dwFlags /*= CRYPT_DECODE_SHARE_OID_STRING_FLAG*/,
    __in DWORD dwCertEncodingType
    /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) //throw(...)
{
    DWORD cbData = 512;
    if (!::CryptDecodeObject(dwCertEncodingType, lpszStructType, pbEncoded,
	cbEncoded, dwFlags, Result.GetBuffer(cbData), &cbData)) {
	    if (::GetLastError() == ERROR_MORE_DATA) {
		// В случае ERROR_MORE_DATA в cbData вернули требуемый размер
		ATLENSURE_RETURN_LASTWIN32HR(::CryptDecodeObject(
		    dwCertEncodingType, lpszStructType, pbEncoded, cbEncoded,
		    dwFlags, Result.GetBuffer(cbData), &cbData));
	    } else {
		return ATL::AtlHresultFromLastError();
	    }
    }
    Result.ReleaseBufferSetLength(cbData);
    return S_OK;
}

inline HRESULT CryptDecodeObject(__in LPCSTR lpszStructType,
    __in const CStringBlob& Data,
    __out_bcount_part_opt(*pcbStructInfo, *pcbStructInfo) void *pvStructInfo,
    __inout DWORD *pcbStructInfo,
    __in DWORD dwFlags /*= CRYPT_DECODE_SHARE_OID_STRING_FLAG*/,
    __in DWORD dwCertEncodingType
    /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) //throw(...)
{
    if (!::CryptDecodeObject(dwCertEncodingType, lpszStructType,
	reinterpret_cast<const BYTE*>(Data.GetString()), Data.GetLength(),
	dwFlags, pvStructInfo, pcbStructInfo)) {
	    return ATL::AtlHresultFromLastError();
    }
    return S_OK;
}

inline HRESULT CryptDecodeObject(__in LPCSTR lpszStructType,
    __in const CStringBlob& Data, __out CStringBlob& Result,
    __in DWORD dwFlags /*= CRYPT_DECODE_SHARE_OID_STRING_FLAG*/,
    __in DWORD dwCertEncodingType
    /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) //throw(...)
{
    return ATL2::CryptDecodeObject(lpszStructType,
	reinterpret_cast<const BYTE*>(Data.GetString()), Data.GetLength(),
	Result, dwFlags, dwCertEncodingType);
}

inline HRESULT CryptDecodeCmsSignerInfo(
    __in_bcount(cbEncoded) const BYTE *pbEncoded, __in DWORD cbEncoded,
    __out CStringBlob& Result, __out PCCMSG_CMS_SIGNER_INFO& pSignerInfo,
    __in DWORD dwFlags /*= CRYPT_DECODE_SHARE_OID_STRING_FLAG*/,
    __in DWORD dwCertEncodingType
    /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) //throw(...)
{
    HRESULT hr = CryptDecodeObject(CMS_SIGNER_INFO,
	pbEncoded, cbEncoded, Result, dwFlags, dwCertEncodingType);
    if (SUCCEEDED(hr)) {
	pSignerInfo =
	    reinterpret_cast<PCCMSG_CMS_SIGNER_INFO>(Result.LockBuffer());
    }
    return hr;
}

inline HRESULT CryptDecodeCmsSignerInfo(__in const CStringBlob& Data,
    __out_bcount_part_opt(*pcbStructInfo, *pcbStructInfo) void *pvStructInfo,
    __inout DWORD *pcbStructInfo,
    __out PCCMSG_CMS_SIGNER_INFO& pSignerInfo,
    __in DWORD dwFlags /*= CRYPT_DECODE_SHARE_OID_STRING_FLAG*/,
    __in DWORD dwCertEncodingType
    /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) //throw(...)
{
    HRESULT hr = CryptDecodeObject(CMS_SIGNER_INFO, Data,
	pvStructInfo, pcbStructInfo, dwFlags, dwCertEncodingType);
    if (SUCCEEDED(hr) && pvStructInfo) {
	pSignerInfo =
	    reinterpret_cast<PCCMSG_CMS_SIGNER_INFO>(pvStructInfo);
    }
    else
	pSignerInfo = NULL;
    return hr;
}

inline HRESULT CryptDecodeCmsSignerInfo(__in const CStringBlob& Data,
    __out CStringBlob& Result, __out PCCMSG_CMS_SIGNER_INFO& pSignerInfo,
    __in DWORD dwFlags /*= CRYPT_DECODE_SHARE_OID_STRING_FLAG*/,
    __in DWORD dwCertEncodingType
    /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) //throw(...)
{
    HRESULT hr = CryptDecodeObject(CMS_SIGNER_INFO,
	Data, Result, dwFlags, dwCertEncodingType);
    if (SUCCEEDED(hr)) {
	pSignerInfo =
	    reinterpret_cast<PCCMSG_CMS_SIGNER_INFO>(Result.LockBuffer());
    }
    return hr;
}

inline HRESULT CryptEncodeObject(__in LPCSTR lpszStructType,
    __in const void* pvStructInfo, __out CStringBlob& Result,
    __in DWORD dwCertEncodingType
    /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) //throw(...)
{
    DWORD cbData = 512;
    if (!::CryptEncodeObject(dwCertEncodingType, lpszStructType, pvStructInfo,
	reinterpret_cast<BYTE*>(Result.GetBuffer(cbData)), &cbData)) {
	    if (::GetLastError() == ERROR_MORE_DATA) {
		// В случае ERROR_MORE_DATA в cbData вернули требуемый размер
		ATLENSURE_RETURN_LASTWIN32HR(::CryptEncodeObject(
		    dwCertEncodingType, lpszStructType, pvStructInfo,
		    reinterpret_cast<BYTE*>(Result.GetBuffer(cbData)), &cbData));
	    } else {
		return ATL::AtlHresultFromLastError();
	    }
    }
    Result.ReleaseBufferSetLength(cbData);
    return S_OK;
}

inline HRESULT CryptEncodeAttribute(__in PCCRYPT_ATTRIBUTE pcAttr,
    __out CStringBlob& Result, __in DWORD dwCertEncodingType
    /*= X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/) //throw(...)
{
    return ATL2::CryptEncodeObject(PKCS_ATTRIBUTE, pcAttr, Result,
	dwCertEncodingType);
}

inline HRESULT CryptBinaryToStringW(const BYTE *pbBinary,
    DWORD cbBinary, DWORD dwFlags, ATL::CAtlStringW& result) //throw(...)
{
    // CryptBinaryToString никому не обещала, что в случае недостаточного
    // буфера вернет требуемый размер. Проверено - не вернет.
    DWORD cbData = 0;
    if (!::CryptBinaryToStringW(pbBinary, cbBinary, dwFlags, 0, &cbData))
    {
        return ATL::AtlHresultFromLastError();
    }

    if (!::CryptBinaryToStringW(pbBinary, cbBinary, dwFlags,
        result.GetBuffer(cbData), &cbData))
    {
        return ATL::AtlHresultFromLastError();
    }

    result.ReleaseBufferSetLength(cbData);
    return S_OK;
}

inline HRESULT CryptBinaryToStringW(CStringBlob& binary,
    DWORD dwFlags, ATL::CAtlStringW& result) //throw(...)
{
    return CryptBinaryToStringW(reinterpret_cast<const BYTE*>(binary.GetString()),
        static_cast<DWORD>(binary.GetLength()), dwFlags, result);
}

inline HRESULT CheckStringIsStrictBase64A(
    LPCSTR pszString, DWORD cchString)
{
    // Правильная строка заканчивается на 0-3 символа "=", после которых 
    // могут быть только пробелы.
    
    // Функции типа strchr() работают только с NULL-терминированными
    // строками, которые нам никто не гарантирует - ищем символы вручную
    int iFirstEqual = -1;
    int cSpecSymb = 0;
    for (DWORD i = 0; i < cchString; ++i)
    {
        if ('=' == pszString[i])
        {
            iFirstEqual = i;
            // Пробельные символы могут встречаться после "="
            for (DWORD j = i; j < cchString; ++j)
            {
                if (L'\r' == pszString[j] ||
                    L'\n' == pszString[j] ||
                    L'\t' == pszString[j] ||
                    L' ' == pszString[j]) {
                    ++cSpecSymb;
                }
            }
            break;
        }
        // Тут же считаем кол-во пробельных символов
        if (L'\r' == pszString[i] ||
            L'\n' == pszString[i] ||
            L'\t' == pszString[i] ||
            L' ' == pszString[i]) {
            ++cSpecSymb;
        }
    }
    // Правильная строка имеет длину, кратную 4.
    if (0 != (cchString - cSpecSymb) % 4)
    {
        return E_INVALIDARG;
    }
    if (-1 == iFirstEqual)
    {
        return S_OK;
    }

    int iLastEqual = -1;
    for (int i = cchString - 1; i >= 0; --i)
    {
        if ('=' == pszString[i])
        {
            iLastEqual = i;
            break;
        }
    }
    if (iLastEqual - iFirstEqual > 2)
    {
        return E_INVALIDARG;
    }

    if (2 == iLastEqual - iFirstEqual)
    {
        if ('=' != pszString[iFirstEqual + 1])
        {
            return E_INVALIDARG;
        }
    }

    if (-1 != iLastEqual)
    {
        for (DWORD i = iLastEqual + 1; i < cchString; ++i)
        {
	    // static_cast добавлен для борьбы с предупреждением C6328.
	    // См. https://msdn.microsoft.com/en-us/library/vstudio/ms245348(v=vs.120).aspx
            if (!isspace(pszString[i]))
            {
                return E_INVALIDARG;
            }
        }
    }
    return S_OK;
}

inline HRESULT CheckStringIsStrictBase64W(
    LPCWSTR pszString, DWORD cchString)
{
    // Правильная строка заканчивается на 0-3 символа "=",
    // после которых могут быть только пробелы.

    // Функции типа wcschr() работают только с NULL-терминированными
    // строками, которые нам никто не гарантирует - ищем символы вручную

    int iFirstEqual = -1;
    int cSpecSymb = 0;
    for (DWORD i = 0; i < cchString; ++i)
    {
        if (L'=' == pszString[i])
        {
            iFirstEqual = i;
            // Пробельные символы могут встречаться после "="
            for (DWORD j = i; j < cchString; ++j)
            {
                if (L'\r' == pszString[j] ||
                    L'\n' == pszString[j] ||
                    L'\t' == pszString[j] ||
                    L' ' == pszString[j]) {
                    ++cSpecSymb;
                }
            }
            break;
        }
        // Тут же считаем кол-во пробельных символов
        if (L'\r' == pszString[i] ||
            L'\n' == pszString[i] ||
            L'\t' == pszString[i] ||
            L' ' == pszString[i]) {
            ++cSpecSymb;
        }
    }
    // Правильная строка имеет длину, кратную 4.
    if (0 != (cchString - cSpecSymb) % 4)
    {
        return E_INVALIDARG;
    }
    if (-1 == iFirstEqual)
    {
        return S_OK;
    }

    int iLastEqual = -1;
    for (int i = cchString - 1; i >= 0; --i)
    {
        if (L'=' == pszString[i])
        {
            iLastEqual = i;
            break;
        }
    }
    if (iLastEqual - iFirstEqual > 2)
    {
        return E_INVALIDARG;
    }

    if (2 == iLastEqual - iFirstEqual)
    {
        if (L'=' != pszString[iFirstEqual + 1])
        {
            return E_INVALIDARG;
        }
    }

    if (-1 != iLastEqual)
    {
        for (DWORD i = iLastEqual + 1; i < cchString; ++i)
        {
            if (!iswspace(pszString[i]))
            {
                return E_INVALIDARG;
            }
        }
    }
    return S_OK;
}

#ifndef CRYPT_STRING_STRICT
#define CRYPT_STRING_STRICT                 0x20000000
#endif

inline HRESULT CryptStringToBinaryW(
    LPCWSTR pszString, DWORD cchString,
    DWORD dwFlags, CStringBlob& result) //throw(...)
{
    // Флаг CRYPT_STRING_STRICT не поддерживается на Windows Server 2008,
    // Windows Vista, Windows Server 2003, Windows XP
    // отрежем флаг и реализуем эту проверку сами 
    if (dwFlags & CRYPT_STRING_STRICT)
    {
        dwFlags &= ~CRYPT_STRING_STRICT;
        // На Unix наша реализация CryptStringToBinary всегда работает 
        // как если бы был включен флаг CRYPT_STRING_STRICT
#ifndef UNIX
        ATL_HR_ERRORCHECK_RETURN(CheckStringIsStrictBase64W(pszString, cchString));
#endif
    }

    DWORD cbData = 0;
    if (!::CryptStringToBinaryW(pszString, cchString, dwFlags, 0, &cbData, NULL, NULL))
    {
        return ATL::AtlHresultFromLastError();
    }

    if (!::CryptStringToBinaryW(pszString, cchString, dwFlags,
        reinterpret_cast<BYTE*>(result.GetBuffer(cbData)), &cbData, NULL, NULL))
    {
        return ATL::AtlHresultFromLastError();
    }

    result.ReleaseBufferSetLength(cbData);
    return S_OK;
}

inline HRESULT CryptStringToBinaryW(const ATL::CAtlStringW& str,
    DWORD dwFlags, CStringBlob& result) //throw(...)
{
    ATL_HR_ERRORCHECK_RETURN(
        CryptStringToBinaryW(str.GetString(), str.GetLength(), dwFlags, result));

    return S_OK;
}

inline HRESULT CryptBinaryToStringA(const BYTE *pbBinary,
    DWORD cbBinary, DWORD dwFlags, CStringBlob& result) //throw(...)
{
    // CryptBinaryToString никому не обещала, что в случае недостаточного
    // буфера вернет требуемый размер. Проверено - не вернет.
    DWORD cbData = 0;
    if (!::CryptBinaryToStringA(pbBinary, cbBinary, dwFlags, 0, &cbData))
    {
        return ATL::AtlHresultFromLastError();
    }

    if (!::CryptBinaryToStringA(pbBinary, cbBinary, dwFlags, 
        result.GetBuffer(cbData), &cbData))
    {
        return ATL::AtlHresultFromLastError();
    }

    result.ReleaseBufferSetLength(cbData);
    return S_OK;
}

inline HRESULT CryptBinaryToStringA(CStringBlob& binary,
    DWORD dwFlags, CStringBlob& result) //throw(...)
{
    return CryptBinaryToStringA(reinterpret_cast<const BYTE*>(binary.GetString()),
        static_cast<DWORD>(binary.GetLength()), dwFlags, result);
}

inline HRESULT CryptStringToBinaryA(
    LPCSTR pszString, DWORD cchString,
    DWORD dwFlags, CStringBlob& result) //throw(...)
{
    // Флаг CRYPT_STRING_STRICT не поддерживается на Windows Server 2008,
    // Windows Vista, Windows Server 2003, Windows XP
    // отрежем флаг и реализуем эту проверку сами 
    if (dwFlags & CRYPT_STRING_STRICT)
    {
        dwFlags &= ~CRYPT_STRING_STRICT;
        // На Unix наша реализация CryptStringToBinary всегда работает 
        // как если бы был включен флаг CRYPT_STRING_STRICT
#ifndef UNIX
        ATL_HR_ERRORCHECK_RETURN(CheckStringIsStrictBase64A(pszString, cchString));
#endif
    }
    DWORD cbData = 0;
    // Здесь не используем макрос ATLENSURE_RETURN_LASTWIN32HR
    // так как он кидает ASSERT. В ряде случаев получить ошибку - ожидаемое поведение.
    if (!::CryptStringToBinaryA(pszString, cchString,
        dwFlags, 0, &cbData, NULL, NULL))
    {
        return ATL::AtlHresultFromLastError();
    }

    if (!::CryptStringToBinaryA(pszString, cchString, dwFlags,
        reinterpret_cast<BYTE*>(result.GetBuffer(cbData)), &cbData, NULL, NULL))
    {
        return ATL::AtlHresultFromLastError();
    }

    result.ReleaseBufferSetLength(cbData);
    return S_OK;
}

inline HRESULT CryptStringToBinaryA(const CStringBlob& str,
    DWORD dwFlags, CStringBlob& result) //throw(...)
{
    ATL_HR_ERRORCHECK_RETURN(CryptStringToBinaryA(str.GetString(), str.GetLength(),
        dwFlags, result));

    return S_OK;
}

inline HRESULT CertStrToNameW(__in const ATL::CAtlStringW& str,
    __in DWORD dwStrType, __out CStringBlob& result,
    __in DWORD dwEncodingType/* = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/)
{
    DWORD cbData = 0;
    if (!::CertStrToNameW(dwEncodingType, str.GetString(),
        dwStrType, NULL, NULL, &cbData,  NULL))
    {
        return ATL::AtlHresultFromLastError();
    }

    if (!::CertStrToNameW(dwEncodingType, str.GetString(), dwStrType, NULL,
        reinterpret_cast<BYTE*>(result.GetBuffer(cbData)), &cbData, NULL))
    {
        return ATL::AtlHresultFromLastError();
    }

    result.ReleaseBufferSetLength(cbData);
    return S_OK;
}

inline HRESULT CertStrToNameA(__in const CStringBlob& str,
    __in DWORD dwStrType, __out CStringBlob& result,
    __in DWORD dwEncodingType /* = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING*/)
{
    DWORD cbData = 0;
    if (!::CertStrToNameA(dwEncodingType, str.GetString(), dwStrType,
        NULL, NULL, &cbData,  NULL))
    {
        return ATL::AtlHresultFromLastError();
    }

    if (!::CertStrToNameA(dwEncodingType, str.GetString(), dwStrType, NULL, 
        reinterpret_cast<BYTE*>(result.GetBuffer(cbData)), &cbData, NULL))
    {
        return ATL::AtlHresultFromLastError();
    }

    result.ReleaseBufferSetLength(cbData);
    return S_OK;
}


#endif // !_WIN32_WCE

#ifdef _WIN32

inline CCryptOIDFuncAddr::CCryptOIDFuncAddr() throw()
    : m_hCryptOIDFuncAddr(NULL), m_pfn(NULL)
{
}

inline CCryptOIDFuncAddr::~CCryptOIDFuncAddr() throw()
{
    Destroy();
}

inline HRESULT CCryptOIDFuncAddr::Initialize(
    __in_z LPCSTR pszFuncName, __in DWORD dwEncodingType,
    __in_z LPCSTR pszOID, __in DWORD dwFlags /*= 0*/)
{
    ATLENSURE_RETURN(m_hCryptOIDFuncAddr == NULL);

    HCRYPTOIDFUNCSET hFuncSet =
	::CryptInitOIDFunctionSet(pszFuncName, 0);
    if (hFuncSet == NULL) {
	return ATL::AtlHresultFromLastError();
    }

    ATLENSURE_RETURN_LASTWIN32HR(::CryptGetOIDFunctionAddress(hFuncSet,
	dwEncodingType, pszOID, dwFlags, &m_pfn, &m_hCryptOIDFuncAddr));

    return S_OK;
}

inline void CCryptOIDFuncAddr::Destroy() throw()
{
    if (m_hCryptOIDFuncAddr == NULL) {
	return;
    }
    BOOL bSuccess = ::CryptFreeOIDFunctionAddress(m_hCryptOIDFuncAddr, 0);
    ATLVERIFY(bSuccess);
    m_hCryptOIDFuncAddr = NULL;
}

#define CRYPT_OID_ENCODE_PUBKEY_AND_PARAMETERS_FUNC \
    "CryptDllEncodePublicKeyAndParameters"

inline COIDFuncEncodePublicKeyAndParameters::
    COIDFuncEncodePublicKeyAndParameters() throw()
    : m_pszOID(NULL)
{
}

inline HRESULT COIDFuncEncodePublicKeyAndParameters::Initialize(
    __in_z LPCSTR pszOID, __in DWORD dwFlags /*= 0*/) throw()
{
    m_pszOID = pszOID;
    return CCryptOIDFuncAddr::Initialize(
	CRYPT_OID_ENCODE_PUBKEY_AND_PARAMETERS_FUNC,
	m_dwCertEncodingType, pszOID, dwFlags);
}

inline BOOL COIDFuncEncodePublicKeyAndParameters::Invoke(
    __in DWORD dwCertEncodingType,
    __in_z LPCSTR lpszStructType,
    __in_bcount(cbStructInfo) const void *pvStructInfo,
    __in DWORD cbStructInfo,
    __in DWORD dwFlags,
    __in_opt void *pvAuxInfo,
    __deref_out_bcount_full_opt(*pcbEncodedPublicKey)
	LPBYTE *ppbEncodedPublicKey,
    __deref_out DWORD *pcbEncodedPublicKey,
    __deref_out_bcount_full_opt(*pcbParameters) LPBYTE *ppbParameters,
    __deref_out DWORD *pcbParameters) throw()
{
    return reinterpret_cast<CryptEncodePublicKeyAndParameters_t*>(m_pfn)(
	dwCertEncodingType, lpszStructType, pvStructInfo, cbStructInfo,
	dwFlags, pvAuxInfo, ppbEncodedPublicKey, pcbEncodedPublicKey,
	ppbParameters, pcbParameters);
}

inline BOOL COIDFuncEncodePublicKeyAndParameters::Invoke(
    __in_bcount(cbStructInfo) const void *pvStructInfo,
    __in DWORD cbStructInfo,
    __out CStringBlob& EncodedPublicKey,
    __out CStringBlob& EncodedParameters,
    __in DWORD dwFlags /*= 0*/,
    __in_opt void *pvAuxInfo /*= NULL*/) //throw(...)
{
    DWORD cbEncodedPublicKey = 0;
    DWORD cbEncodedParameters = 0;
    LPBYTE EncodedPublicKeyBuffer;
    LPBYTE EncodedParametersBuffer;

    ATL_BOOL_ERRORCHECK_RETURN(Invoke(
	m_dwCertEncodingType, 
	m_pszOID, 
	pvStructInfo,
	cbStructInfo, 
	dwFlags, 
	pvAuxInfo,	
	&EncodedPublicKeyBuffer,
	&cbEncodedPublicKey,
	&EncodedParametersBuffer,	
	&cbEncodedParameters));

    try
    {
	memcpy(
	    EncodedPublicKey.GetBufferSetLength(cbEncodedPublicKey), 	
	    EncodedPublicKeyBuffer, 
	    cbEncodedPublicKey);
    }
    catch(...)
    {
	LocalFree(EncodedPublicKeyBuffer);
	LocalFree(EncodedParametersBuffer);
	throw;
    }
    LocalFree(EncodedPublicKeyBuffer);

    try
    {
	memcpy(
	    EncodedParameters.GetBufferSetLength(cbEncodedParameters), 
	    EncodedParametersBuffer, 
	    cbEncodedParameters);
    }
    catch(...)
    {
	LocalFree(EncodedParametersBuffer);
	throw;
    }
    LocalFree(EncodedParametersBuffer);
   
    EncodedPublicKey.ReleaseBufferSetLength(cbEncodedPublicKey);
    EncodedParameters.ReleaseBufferSetLength(cbEncodedParameters);
    return TRUE;
}

#endif //_WIN32

} // namespace ATL2

#endif // !_ATLCRYPT2_INL_INCLUDED
