Помните, что когда вы возвращаете std::string, созданный прямо в методе не из строки в памяти - вы возвращаете указатель на локальную переменную, что является по истине эпической ошибкой всех C++-программистов. Такие строки надо копировать посредством malloc/HeapAlloc/LocalAlloc/new перед возвратом. И, разумеется, не забывать освобождать память.
пятница, 30 ноября 2012 г.
вторник, 27 ноября 2012 г.
Мысль о goto в C++
В С++ нет столь удобной конструкции finally. Как бы печально то ни было, но это так. Есть подход с написанием define-а на finally в таком духе:
#define finally(FUNC) \
catch ( ... ) \
{ \
MLASTERROR;\
FUNC\
throw; \
} \
FUNC
Можно использовать goto. Как бы там Дейкстра его ни критиковал, в таком месте написать "один маааленький goto" (с) xkcd - можно и даже нужно. В метку по нему можно свалить освобождение ресурсов, условный вывод ошибок и все прочее, что должно делаться в С# в блоке finally.
А, картинка кстати вот:
#define finally(FUNC) \
catch ( ... ) \
{ \
MLASTERROR;\
FUNC\
throw; \
} \
FUNC
Можно использовать goto. Как бы там Дейкстра его ни критиковал, в таком месте написать "один маааленький goto" (с) xkcd - можно и даже нужно. В метку по нему можно свалить освобождение ресурсов, условный вывод ошибок и все прочее, что должно делаться в С# в блоке finally.
А, картинка кстати вот:
четверг, 22 ноября 2012 г.
Проблема в wininet
Проблема: во время обращение к HTTPS-серверу через WinInet выясняется, что серверный сертификат отозван (ERROR_INTERNET_SEC_CERT_REV_FAILED) или неверен (ERROR_INTERNET_INVALID_CA) или истек (ERROR_INTERNET_SEC_CERT_DATE_INVALID). Как правильно проигнорировать эти ошибки и продолжить обработку запроса?
Решение: необходимо отправить запрос, посмотреть GetLastError(), после чего поправить флаги соединения и отправить запрос еще раз. Видимо такая архитектура сделана для пущей безопасности, но это самая жуткий протокол взаимодействия подсистем, который я когда-либо видел. Флаги, которые нужно проставить для игнорирования ошибок:
Решение: необходимо отправить запрос, посмотреть GetLastError(), после чего поправить флаги соединения и отправить запрос еще раз. Видимо такая архитектура сделана для пущей безопасности, но это самая жуткий протокол взаимодействия подсистем, который я когда-либо видел. Флаги, которые нужно проставить для игнорирования ошибок:
- ERROR_INTERNET_SEC_CERT_REV_FAILED - SECURITY_FLAG_IGNORE_REVOCATION
- ERROR_INTERNET_INVALID_CA - SECURITY_FLAG_IGNORE_UNKNOWN_CA
- ERROR_INTERNET_SEC_CERT_DATE_INVALID - SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
Код:
BOOL bRepeat = FALSE;
int sendResult = 0;
do{
bRepeat = FALSE;
sendResult = HttpSendRequest(hRequest, lpszHeaders, dwHeadersLength, lpOptional, dwOptionalLength);
if (!sendResult){
int lastErr = GetLastError();
if (lastErr == ERROR_INTERNET_INVALID_CA) {
WARNING("Серверный сертификат недействителен");
DWORD dwFlags;
DWORD dwBuffLen = sizeof(dwFlags);
InternetQueryOption (hRequest, INTERNET_OPTION_SECURITY_FLAGS,(LPVOID)&dwFlags, &dwBuffLen);
dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
InternetSetOption (hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) );
bRepeat = TRUE;
}else if (lastErr==ERROR_INTERNET_SEC_CERT_REV_FAILED){
WARNING("Серверный сертификат отозван");
// аналогично SECURITY_FLAG_IGNORE_REVOCATION
bRepeat = TRUE;
}else if (lastErr==ERROR_INTERNET_SEC_CERT_DATE_INVALID){
WARNING("Серверный сертификат ИСТЕК");
// аналогично SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
bRepeat = TRUE;
}else{
MLASTERROR
ERROR("Не удалось отправить веб-запрос");
}
}
}while(bRepeat);
return sendResult;
Чтобы не умереть от тоски: Сведения: коды ошибок WinInet (с 12001 по 12156), Как обрабатывать недопустимые ошибка WinInet сертификата центра
Проблема в C++ со строками
Проблема: не могу понять в чем разница между BSTR, LPSTR, LPWSTR, LPTSTR и прочими строками в С++.
Решение:
- CHAR - это typedef на char - обычный байт от 0 до 255 или от -128 до +128
- WCHAR - это typedef на wchar_t - "широкий" символ, которого по стандарту должно быть гарантировано достаточно для хранения любой буквы любого алфавита. На практике это не так, ибо в GNU-шных компиляторах вместимость wchar_t - 32 бита (4 байта). В ms-компиляторах - 16 бит, чего уж точно не хватает на все алфавиты, поэтому используются свои извращения. Важно понимать что WCHAR - это не тот же самый Unicode.
- TCHAR - если при сборке задан #define UNICODE, то будет преобразован в WCHAR, если нет - то в CHAR. #define UNICODE по факту вносит путаницу, ибо как было сказано - wchar_t - не является юникодом. При использовании TCHAR - все системные вызовы так же перейдут на широкие символы если будет задан #define UNICODE. Для того, чтобы и c-style функции перешли на широкие символы - необходимо использовать те, что с префиксом _tcs (_tcslen, _tcscat, _stprintf). std-контейнеры не переходят на широкие символы автоматически. Однако это легко решается методами вроде:
#define std::string std::wstring
#endif
- Постфикс STR - признак массива CHAR/TCHAR/WCHAR-ов. Сам по себе не используется
- Префиксы LP, P - признак указателя на массив CHAR/TCHAR/WCHAR-ов, в зависимости от названия (например, LPWSTR - указатель на массив WCHAR-ов, LPTSTR - TCHAR-ов, LPSTR - просто CHAR-ов). LP - "длинный" указатель (Long Pointer), не знаю что значит. P - обычный указатель (Pointer)
- BSTR - по факту всегда является LPWSTR. Используется для общения с COM-интерфейсами и только с ними. Потому как там все только на широких строках. Для операции с ним есть ATL-класс CComBSTR, предоставляющий инструменты конвертации. Так же стоит иметь в виду вызовы ::SysAllocString и ::SysFreeString.
Проблема с MS SQL Server 2008
Проблема: не ставится MS SQL Server 2008 и выше, закрываясь на середине установки с непонятной ошибкой.
Решение: проверьте не открыт ли Google Chrome. По каким-то непонятным причинам установщик MS SQL Server не дружит с этим браузером и в определенный момент они друг друга роняют.
Решение: проверьте не открыт ли Google Chrome. По каким-то непонятным причинам установщик MS SQL Server не дружит с этим браузером и в определенный момент они друг друга роняют.
Проблема с запуском сервисов
Проблема: нужен удобный инструментарий создания, удаления, запуска и остановки windows-сервисов. В идеале из скрипта сборки и деплоймента приложения. Особой пикантности добавляет необходимость делать это на удаленной машине.
Решение: Утилита sc.exe для управления сервисами windows. У нее есть одна особенность: при указании параметров пробел обязательно нужно ставить только после "равно", но не перед. При другом способе расстановки пробелов перед "равно" - утилита почему-то не работает. Для удаленного запуска процедур - в том числе остановке/удаления и прочих операций с windows-сервисами поможет утилита psexec.exe от Марка Руссиновича. Весь озвученный инструментарий успешно применяется в скриптах сборки и разворачивания приложений.
Пример создания сервиса:
sc create $(ServiceName) binPath= "$(FullExePath)" start= auto
Решение: Утилита sc.exe для управления сервисами windows. У нее есть одна особенность: при указании параметров пробел обязательно нужно ставить только после "равно", но не перед. При другом способе расстановки пробелов перед "равно" - утилита почему-то не работает. Для удаленного запуска процедур - в том числе остановке/удаления и прочих операций с windows-сервисами поможет утилита psexec.exe от Марка Руссиновича. Весь озвученный инструментарий успешно применяется в скриптах сборки и разворачивания приложений.
Пример создания сервиса:
sc create $(ServiceName) binPath= "$(FullExePath)" start= auto
Совет
Для тестирования WCF-сервисов есть добротная утилита WcfTestClient.exe, лежит в C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE
Проблема с транзакциями
Проблема: При написании транзакционного кода на C# через using(var transactionScope = new TransactionScope()){...} блок завершается с TransactionAbortedException
Решение: Ставить в конце блока transactionScope.Complete(), если return не должен по логике откатить транзакцию
Решение: Ставить в конце блока transactionScope.Complete(), если return не должен по логике откатить транзакцию
Совет
Отправляешь e-mail из программы? От греха по-дальше - не именуй аттачи русскими буквами. Не любит этого SMTP.
Совет
Чтобы в Windows 7 запустить быстро программу от имени администратора - достаточно в меню Пуск ввести ее имя и нажать Ctrl-Shift-Enter
Риск нежелания сотрудничества
Абстрактно: представитель заказчика по разным причинам не хочет взаимодействовать с командой разработки для предоставления критичных сведений.
Точнее может быть и хочет, но упорно, пардон, проябывается.
Конкретика: проект позразумевает разработку модулей, тесно интегрируемых заказчиком в свою систему. Заказчик обязал тимлидера своей команды связываться с подрядчиками и отвечать на все их вопросы по системе. Но тимлидер почему-то ОЧЕНЬ долго отвечает на письма и категорически отказывается выходить на связь голосом.
Методы борьбы: сдерживание. Эскалирование подобных проблем по мере появления до начальства представителя, которое, как правило, заинтересовано в проекте. Нехай люлей вставит.
среда, 7 ноября 2012 г.
Сюда
Сюда будет писаться коротко и по существу, каждый день. Только мой опыт. Только оригинальный контент.
Подписаться на:
Сообщения (Atom)