Ключевое слово в защите информации
КЛЮЧЕВОЕ СЛОВО
в защите информации
Получить ГОСТ TLS-сертификат для домена (SSL-сертификат)
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

3 Страницы123>
Опции
К последнему сообщению К первому непрочитанному
Offline Andrew_Silver  
#1 Оставлено : 15 января 2008 г. 17:56:26(UTC)
Andrew_Silver

Статус: Участник

Группы: Участники
Зарегистрирован: 15.01.2008(UTC)
Сообщений: 18
Мужчина
Откуда: Пермь

Добрый день!

Использовал ли кто-то низкоуровневые функции cades в .net через PInvoke? Может есть у кого опыт?
У нас проблемы уже при импорте библиотеки (cades.dll через DllImport), вываливается ошибка загрузки dll.
Offline Павел Смирнов  
#2 Оставлено : 18 января 2008 г. 16:53:28(UTC)
Павел Смирнов

Статус: Вам и не снилось

Группы: Администраторы
Зарегистрирован: 24.12.2007(UTC)
Сообщений: 831
Откуда: Крипто-Про

Сказал(а) «Спасибо»: 1 раз
Поблагодарили: 48 раз в 44 постах
Для работы с side-by-side ассемблями необходимо использовать API из kernel32.dll:
CreateActCtx/ReleaseActCtx – для создания и освобождения контекста активации,
ActivateActCtx/DeactivateActCtx – для активирования и деактивирования контекста соответственно.

Для работы в .Net удобно использовать следующий класс:

Код:
public class ActivationContextHelper : IDisposable
    {
        #region PrivateData
        // Activation Context API Functions
        [DllImport("Kernel32.dll", SetLastError = true)]
        private extern static IntPtr CreateActCtx(ref ACTCTX actctx);
        [DllImport("Kernel32.dll", SetLastError = true)]
        private extern static bool ActivateActCtx(IntPtr hActCtx, out uint cookie);
        [DllImport("Kernel32.dll", SetLastError = true)]
        private extern static bool DeactivateActCtx(uint dwFlags, uint cookie);
        [DllImport("Kernel32.dll", SetLastError = true)]
        private extern static void ReleaseActCtx(IntPtr hActCtx);
        [DllImport("Kernel32.dll", SetLastError = true)]
        public extern static UInt32 GetCurrentThreadId();

        // Activation context structure
        private struct ACTCTX
        {
            public int cbSize;
            public uint dwFlags;
            public string lpSource;
            public ushort wProcessorArchitecture;
            public ushort wLangId;
            public string lpAssemblyDirectory;
            public string lpResourceName;
            public string lpApplicationName;
        }

        // Helpful constants
        private const int ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004;
        private const int INVALID_HANDLE_VALUE = -1;

        // Variables holding information on the activation context
        private IntPtr m_hActCtx = (IntPtr)0;
        #endregion

        // Some helpful error codes                           // Most likely indicate:
        public const UInt32 ERROR_INVALID_PARAMETER = 87;     // Missing or empty parameters
        public const UInt32 ERROR_ALREADY_INITIALIZED = 1247; // Context already created or active
        public const UInt32 ERROR_INVALID_HANDLE = 6;         // Context not yet created or not active

        /// <summary>
        /// Constructor
        /// </summary>
        public ActivationContextHelper()
        {
            // This is superfluous - just document zero default
            m_hActCtx = (IntPtr)0;
        }

        /// <summary>
        /// Release Windows resources
        /// </summary>
        void IDisposable.Dispose()
        {
            Clear();
        }

        /// <summary>
        /// Force the release of Windows resources
        /// </summary>
        public void Clear()
        {
            lock (this)
            {
                if (0 != m_hActCtx.ToInt32())
                    ReleaseActCtx(m_hActCtx);
                m_hActCtx = (IntPtr)0;
            }
        }

        /// <summary>
        /// Method to explicitly load a manifest and create an activation context
        /// </summary>
        /// <param name="manifestPath">Full path to the manifest file</param>
        /// <param name="rootFolder">'null' or the root assembly probing directory</param>
        /// <returns>'true' - success, 'false' - failure</returns>
        public bool CreateContext(string manifestPath, string rootFolder, out UInt32 dwError)
        {
            // Verify parameters, exit if not acceptable
            if (null == manifestPath || "" == manifestPath)
            {
                dwError = ERROR_INVALID_PARAMETER;
                return false;
            }

            // Build the activation context information structure
            ACTCTX info = new ACTCTX();
            info.cbSize = Marshal.SizeOf(typeof(ACTCTX));
            info.dwFlags = 0;
            info.lpSource = manifestPath;

            if (null != rootFolder && "" != rootFolder)
            {
                info.lpAssemblyDirectory = rootFolder;
                info.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID;
            }

            bool bOK = true;
            dwError = 0;

            // Protect from changes by multiple threads
            lock (this)
            {
                // It is illegal to create a context while one is already created
                if (0 != m_hActCtx.ToInt32())
                {
                    dwError = ERROR_ALREADY_INITIALIZED;
                    bOK = false;
                }
                else
                {
                    // Create the activation context
                    m_hActCtx = CreateActCtx(ref info);

                    if (-1 == m_hActCtx.ToInt32())
                    {
                        dwError = (UInt32)Marshal.GetLastWin32Error();
                        m_hActCtx = (IntPtr)0;
                        bOK = false;
                    }

                }
            }

            return bOK;
        }

        /// <summary>
        /// Activate the previously created activation context
        /// </summary>
        /// <returns>'true' - success, 'false' - failure</returns>
        public UInt32 ActivateContext(out UInt32 dwError)
        {
            UInt32 cookie = 0;
            dwError = 0;

            lock (this)
            {
                // We cannot activate a context if we do not have it. 
                if (0 == m_hActCtx.ToInt32())
                {
                    dwError = ERROR_INVALID_HANDLE;
                }
                else
                {
                    // Activate the context
                    if (!ActivateActCtx(m_hActCtx, out cookie))
                    {
                        cookie = 0;
                        dwError = (UInt32)Marshal.GetLastWin32Error();
                    }
                }
            }

            return cookie;
        }

        /// <summary>
        /// Create the activation context, if one is not created yet then
        /// immediately activate it.
        /// </summary>
        /// <param name="manifestPath"></param>
        /// <param name="rootFolder"></param>
        /// <param name="dwError"></param>
        /// <returns></returns>
        public UInt32 CreateAndActivate(string manifestPath, string rootFolder, out UInt32 dwError)
        {
            UInt32 cookie = 0;

            lock (this)
            {
                // If we already have a context or we successfully create one - activate.
                if (0 != m_hActCtx.ToInt32() || CreateContext(manifestPath, rootFolder, out dwError))
                {
                    cookie = ActivateContext(out dwError);
                }
            }

            return cookie;
        }

        /// <summary>
        /// Deactivate the previously activated activation context
        /// </summary>
        /// <returns>'true' - success, 'false' - failure</returns>
        public bool DeactivateContext(UInt32 cookie, out UInt32 dwError)
        {
            bool bOK = true;
            dwError = 0;

            lock (this)
            {
                // We cannot deactivate a context if we do not have it or it is not activated
                if (0 == m_hActCtx.ToInt32() || 0 == cookie)
                {
                    dwError = ERROR_INVALID_HANDLE;
                    bOK = false;
                }
                else
                {
                    // Deactivate the context, may throw an exception if bad sequence
                    if (!DeactivateActCtx(0, cookie))
                    {
                        dwError = (UInt32)Marshal.GetLastWin32Error();
                        bOK = false;
                    }
                }
            }

            return bOK;
        }

        /// <summary>
        /// Release the previously created activation context. Note that the
        /// context is silently released by 'Clear' and 'Dispose'.
        /// </summary>
        /// <returns>'true' - success, 'false' - failure</returns>
        public bool ReleaseContext(out UInt32 dwError)
        {
            bool bOK = true;
            dwError = 0;

            lock (this)
            {
                if (0 == m_hActCtx.ToInt32())
                {
                    dwError = ERROR_INVALID_HANDLE;
                    bOK = false;
                }
                else
                {
                    ReleaseActCtx(m_hActCtx);
                    m_hActCtx = (IntPtr)0;
                    bOK = true;
                }
            }

            return bOK;
        }
    }

Использование:

•	Создать в каталоге с исходными кодами вашего приложения файл YourApp.manifest, содержащий манифест, описанный выше. Пример для cades.dll:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="X86"
    name="CompanyName.ProductName.YourApplication"
    type="win32"
/>
<description></description>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="CryptoPro.PKI.CAdES"
            version="1.0.0.0"
            processorArchitecture="x86"
            publicKeyToken="a6d31b994cfcddc4"
            language="*"
        />
    </dependentAssembly>
</dependency>
</assembly>
•	В коде, где необходимо использовать функции из ассембли, необходимо:
o	Описать все используемые функции и структуры данных, используя .Net Interop
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct CADES_SIGN_MESSAGE_PARA
    {
        /// <summary>
        /// uint dwSize
        /// </summary>
        public uint dwSize;
        /// <summary>
        /// PCRYPT_SIGN_MESSAGE_PARA pSignMessagePara
        /// </summary>
        //public CryptoAPI.CRYPT_SIGN_MESSAGE_PARA pSignMessagePara;
        public IntPtr pSignMessagePara;
        /// <summary>
        /// CADES_SIGN_PARA pCadesSignPara
        /// </summary>
        public IntPtr pCadesSignPara;
    }
[DllImport(“cades.dll”, EntryPoint = "CadesSignMessage", SetLastError = true, 
CallingConvention = CallingConvention.StdCall)]
      private static extern bool CadesSignMessageImpl(
            ref CADES_SIGN_MESSAGE_PARA pSignPara,
            bool fDetachedSignature,
            uint cToBeSigned,
            String[] rgpbToBeSigned,
            uint[] rgcbToBeSigned,
            ref IntPtr ppSignedBlob); 
o	Создать и активировать контекст
         UInt32 dwError = 0;
         ActivationContextHelper actCtxHelper = new ActivationContextHelper();
         UInt32 savedCookie = actCtxHelper.CreateAndActivate(“YourAppPath\YourApp.manifest”, “YourAppPath”, out dwError); 
o	Вызвать нужные функции
o	Деактивировать и освободить контекст
                                         If(!actCtxHelper.DeactivateContext(savedCookie, out dwError)) {
                                                             // …
                                         }
                                         If (!actCtxHelper.ReleaseContext(out dwError)) {
                                                             // …
                                         }

Дополнение: примеры по использованию данного класса в Asp .Net можно посмотреть здесь и скачать здесь от “Maze Computer Communications, Inc.”


Отредактировано пользователем 18 января 2008 г. 16:57:12(UTC)  | Причина: Не указана

Техническую поддержку оказываем тут.
Наша база знаний.
Offline Andrew_Silver  
#3 Оставлено : 28 января 2008 г. 20:14:59(UTC)
Andrew_Silver

Статус: Участник

Группы: Участники
Зарегистрирован: 15.01.2008(UTC)
Сообщений: 18
Мужчина
Откуда: Пермь

Спасибо за информацию, все получилось, успешно сделали обертки для функций.

Опыта работы с pinvoke к сожалению(к счастью? :-)) не богато, посему боремся сейчас с утечками памяти.
Утечки весьма значительные, после 100 итераций вызова указанной ниже функции утекает около 40 мб.
Можете ли подсказать как правильно освобождать ресурсы?

Функция такая:
Код:

    public byte[] VerifyAux(byte[] pSignature)
    {

        IntPtr pdecoded = IntPtr.Zero;
        IntPtr pverinfo = IntPtr.Zero;
        IntPtr pcrpara = IntPtr.Zero;
        IntPtr pdata1 = IntPtr.Zero;
        byte[] message = new byte[0];
        int cbDecoded = pSignature.Length;
        try
        {

            CRYPT_VERIFY_MESSAGE_PARA crpara = new CRYPT_VERIFY_MESSAGE_PARA();
            crpara.cbSize = (uint)Marshal.SizeOf(crpara);
            crpara.dwMsgAndCertEncodingType = (uint)65537;
            crpara.hCryptProv = IntPtr.Zero;
            crpara.pfnGetSignerCertificate = IntPtr.Zero;
            crpara.pvGetArg = IntPtr.Zero;

            CADES_VERIFY_MESSAGE_PARA verpara = new CADES_VERIFY_MESSAGE_PARA();
            verpara.dwSize = (uint)Marshal.SizeOf(verpara);
            pcrpara = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CRYPT_VERIFY_MESSAGE_PARA)));
            Marshal.StructureToPtr(crpara, pcrpara, true);
            verpara.pVerifyMessagePara = pcrpara;
            verpara.pCadesVerifyPara = IntPtr.Zero;

            CRYPTOAPI_BLOB origin = new CRYPTOAPI_BLOB();
            origin.cbData = (uint)cbDecoded;
            pdata1 = Marshal.AllocHGlobal(cbDecoded);
            Marshal.Copy(pSignature, 0, pdata1, cbDecoded);
            origin.pbData = pdata1;

            CRYPTOAPI_BLOB decoded = new CRYPTOAPI_BLOB();
            pdecoded = Marshal.AllocHGlobal(Marshal.SizeOf(decoded));
            Marshal.StructureToPtr(decoded, pdecoded, true);

            CADES_VERIFICATION_INFO verinfo = new CADES_VERIFICATION_INFO();
            pverinfo = Marshal.AllocHGlobal(Marshal.SizeOf(verinfo));
            Marshal.StructureToPtr(verinfo, pverinfo, true);

            if (Crypto.CadesVerifyMessage(ref verpara, 0, origin.pbData, origin.cbData, ref pdecoded, ref pverinfo))
            {
                decoded = (CRYPTOAPI_BLOB)Marshal.PtrToStructure(pdecoded, typeof(CRYPTOAPI_BLOB));
                verinfo = (CADES_VERIFICATION_INFO)Marshal.PtrToStructure(pverinfo, typeof(CADES_VERIFICATION_INFO));
                message = new byte[decoded.cbData];
                Marshal.Copy(decoded.pbData, message, 0, (int)decoded.cbData);
                string smessage = Encoding.Default.GetString(message);
            }
            else
            {
                verinfo = (CADES_VERIFICATION_INFO)Marshal.PtrToStructure(pverinfo, typeof(CADES_VERIFICATION_INFO));
                Context.Items["LastError"] = (int)verinfo.dwStatus;
                throw new Exception("Некорректная подпись!");
            }
        }
        finally
        {
            Crypto.CadesFreeBlob(pdecoded);
            Crypto.CadesFreeVerificationInfo(pverinfo);

            Marshal.DestroyStructure(pverinfo, typeof(CADES_VERIFICATION_INFO));
            Marshal.DestroyStructure(pdecoded, typeof(CRYPTOAPI_BLOB));
            if (pcrpara!=IntPtr.Zero) Marshal.FreeHGlobal(pcrpara);
            if (pdata1 != IntPtr.Zero) Marshal.FreeHGlobal(pdata1);
        }
        return message;

    }

Отредактировано пользователем 28 января 2008 г. 20:16:19(UTC)  | Причина: Не указана

Offline rsv  
#4 Оставлено : 28 января 2008 г. 22:35:13(UTC)
rsv

Статус: Новичок

Группы: Участники
Зарегистрирован: 28.01.2008(UTC)
Сообщений: 3
Откуда: Крипто-Про

Во-первых, можно посоветовать избавиться от лишнего выделения и копирования памяти для pSignature. Объявляем метод как unsafe и используем fixed:
Код:

    public unsafe byte[] VerifyAux(byte[] pSignature)
    {
        IntPtr pdata1 = IntPtr.Zero;
        int cbDecoded = pSignature.Length;
        try
        {
            // Эта структура вообще  не нужна
            //CRYPTOAPI_BLOB origin = new CRYPTOAPI_BLOB();
            //origin.cbData = (uint)cbDecoded;
            //pdata1 = Marshal.AllocHGlobal(cbDecoded);
            //Marshal.Copy(pSignature, 0, pdata1, cbDecoded);
            //origin.pbData = pdata1;

fixed (byte* sig = pSignature) {
pdata1 = new IntPtr(sig);
if (Crypto.CadesVerifyMessage(ref verpara, 0, pdata1, (uint)cbDecoded, ref pdecoded, ref pverinfo))
{...} else { ... }
}
}
finally{
....
// Это тоже уже не нужно
//if (pdata1 != IntPtr.Zero) Marshal.FreeHGlobal(pdata1);
}


Во-вторых
Код:

        finally
        {
            // Освобождают всю дополнительно выделенную память внутри структуры
            Crypto.CadesFreeBlob(pdecoded);
            Crypto.CadesFreeVerificationInfo(pverinfo);
            // эти вызовы занимаются почти тем же, только неправильно
            //Marshal.DestroyStructure(pverinfo, typeof(CADES_VERIFICATION_INFO));
            //Marshal.DestroyStructure(pdecoded, typeof(CRYPTOAPI_BLOB));
            // а от освобождения уже выделенной под эти объекты памяти никто не освобождал:
            Marshal.FreeHGlobal(pverinfo);
            Marshal.FreeHGlobal(pdecoded);  
            Marshal.FreeHGlobal(pcrpara);
            // конструкция if (pdata != IntPtr.Zero) Marshal.FreeHGlobal(pdata); избыточна
           // читаем Remarks к FreeHGlobal: If the hglobal parameter is IntPtr.Zero, the method does nothing.
        }
Offline Andrew_Silver  
#5 Оставлено : 29 января 2008 г. 18:02:38(UTC)
Andrew_Silver

Статус: Участник

Группы: Участники
Зарегистрирован: 15.01.2008(UTC)
Сообщений: 18
Мужчина
Откуда: Пермь

Переделал вот на такой вариант (ниже), все работает, тем не менее утечки остались.
Можете посоветовать куда думать дальше, что еще может быть?

Код:

    public unsafe byte[] VerifyAux(byte[] pSignature)
    {
        byte[] message = new byte[0];
        IntPtr pdecoded = IntPtr.Zero;
        IntPtr pverinfo = IntPtr.Zero;
        IntPtr pcrpara = IntPtr.Zero;
        IntPtr pdata1 = IntPtr.Zero;
        int cbDecoded = pSignature.Length;

        try
        {
            CRYPT_VERIFY_MESSAGE_PARA crpara = new CRYPT_VERIFY_MESSAGE_PARA();
            crpara.cbSize = (uint)Marshal.SizeOf(crpara);
            crpara.dwMsgAndCertEncodingType = (uint)65537;
            crpara.hCryptProv = IntPtr.Zero;
            crpara.pfnGetSignerCertificate = IntPtr.Zero;
            crpara.pvGetArg = IntPtr.Zero;

            CADES_VERIFY_MESSAGE_PARA verpara = new CADES_VERIFY_MESSAGE_PARA();
            verpara.dwSize = (uint)Marshal.SizeOf(verpara);
            pcrpara = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CRYPT_VERIFY_MESSAGE_PARA)));
            Marshal.StructureToPtr(crpara, pcrpara, true);
            verpara.pVerifyMessagePara = pcrpara;
            verpara.pCadesVerifyPara = IntPtr.Zero;

            CRYPTOAPI_BLOB decoded = new CRYPTOAPI_BLOB();
            pdecoded = Marshal.AllocHGlobal(Marshal.SizeOf(decoded));
            Marshal.StructureToPtr(decoded, pdecoded, true);

            CADES_VERIFICATION_INFO verinfo = new CADES_VERIFICATION_INFO();
            pverinfo = Marshal.AllocHGlobal(Marshal.SizeOf(verinfo));
            Marshal.StructureToPtr(verinfo, pverinfo, true);

            fixed (byte* sig = pSignature)
            {
                pdata1 = new IntPtr(sig);
                if (Crypto.CadesVerifyMessage(ref verpara, 0, pdata1, (uint)cbDecoded, ref pdecoded, ref pverinfo))
                {
                    decoded = (CRYPTOAPI_BLOB)Marshal.PtrToStructure(pdecoded, typeof(CRYPTOAPI_BLOB));
                    verinfo = (CADES_VERIFICATION_INFO)Marshal.PtrToStructure(pverinfo, typeof(CADES_VERIFICATION_INFO));
                    message = new byte[decoded.cbData-1];
                    Marshal.Copy(decoded.pbData, message, 0, (int)decoded.cbData-1);
                    string smessage = Encoding.Default.GetString(message);
                }
                else
                {
                    verinfo = (CADES_VERIFICATION_INFO)Marshal.PtrToStructure(pverinfo, typeof(CADES_VERIFICATION_INFO));
                    Context.Items["LastError"] = (int)verinfo.dwStatus;
                    throw new Exception("Некорректная подпись!");
                }
            }
        }
        finally
        {
            Crypto.CadesFreeBlob(pdecoded);
            Crypto.CadesFreeVerificationInfo(pverinfo);

            //здесь вылетает с ошибкой "неверный дескриптор" если раскомментить
            //Marshal.FreeHGlobal(pverinfo);
            //Marshal.FreeHGlobal(pdecoded); 

            Marshal.FreeHGlobal(pcrpara);
        
        }

        return message;
    }
Offline Павел Смирнов  
#6 Оставлено : 29 января 2008 г. 20:01:49(UTC)
Павел Смирнов

Статус: Вам и не снилось

Группы: Администраторы
Зарегистрирован: 24.12.2007(UTC)
Сообщений: 831
Откуда: Крипто-Про

Сказал(а) «Спасибо»: 1 раз
Поблагодарили: 48 раз в 44 постах
А Вы уверены, что это именно утечка? Вы звали перед замером памяти GC.Collect()? А если позвать его дважды?
Техническую поддержку оказываем тут.
Наша база знаний.
Offline Andrew_Silver  
#7 Оставлено : 29 января 2008 г. 20:42:40(UTC)
Andrew_Silver

Статус: Участник

Группы: Участники
Зарегистрирован: 15.01.2008(UTC)
Сообщений: 18
Мужчина
Откуда: Пермь

GC.Collect() не помогает, дважды тоже.
ну собственно что это если не утечка? Eh?
Замер памяти чисто визуальный - смотрим в диспетчере задач объем памяти занимаемой процессом.
Есть еще аналогичная функция подписи, так вот там таких проблем нет (при том что там вызывается больше функций), почему-то только при проверке

Отредактировано пользователем 29 января 2008 г. 20:43:39(UTC)  | Причина: Не указана

Offline Павел Смирнов  
#8 Оставлено : 29 января 2008 г. 20:53:44(UTC)
Павел Смирнов

Статус: Вам и не снилось

Группы: Администраторы
Зарегистрирован: 24.12.2007(UTC)
Сообщений: 831
Откуда: Крипто-Про

Сказал(а) «Спасибо»: 1 раз
Поблагодарили: 48 раз в 44 постах
Пришлите проект целиком, чтобы мы могли у себя его запустить, на адрес техподдержки.
Техническую поддержку оказываем тут.
Наша база знаний.
Offline Andrew_Silver  
#9 Оставлено : 1 апреля 2008 г. 18:08:41(UTC)
Andrew_Silver

Статус: Участник

Группы: Участники
Зарегистрирован: 15.01.2008(UTC)
Сообщений: 18
Мужчина
Откуда: Пермь

Пришлось отложить проблему на длительное время. Сейчас она снова актуальна.
Предложение отправить проект в техподдержку еще в силе?
Offline Павел Смирнов  
#10 Оставлено : 1 апреля 2008 г. 20:57:43(UTC)
Павел Смирнов

Статус: Вам и не снилось

Группы: Администраторы
Зарегистрирован: 24.12.2007(UTC)
Сообщений: 831
Откуда: Крипто-Про

Сказал(а) «Спасибо»: 1 раз
Поблагодарили: 48 раз в 44 постах
Да. Ждём кода.
Техническую поддержку оказываем тут.
Наша база знаний.
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
3 Страницы123>
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.