понедельник, 3 декабря 2012 г.

пятница, 30 ноября 2012 г.

Совет

Помните, что когда вы возвращаете std::string, созданный прямо в методе не из строки в памяти - вы возвращаете указатель на локальную переменную, что является по истине эпической ошибкой всех C++-программистов. Такие строки надо копировать посредством malloc/HeapAlloc/LocalAlloc/new перед возвратом. И, разумеется, не забывать освобождать память.

вторник, 27 ноября 2012 г.

Мысль о goto в C++

В С++ нет столь удобной конструкции finally. Как бы печально то ни было, но это так. Есть подход с написанием define-а на 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(), после чего поправить флаги соединения и отправить запрос еще раз. Видимо такая архитектура сделана для пущей безопасности, но это самая жуткий протокол взаимодействия подсистем, который я когда-либо видел. Флаги, которые нужно проставить для игнорирования ошибок:

  • 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;




Проблема в 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-контейнеры не переходят на широкие символы автоматически. Однако это легко решается методами вроде: 
#ifdef UNICODE
#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.
Раздел MSDN, который поможет не умереть с тоски: Unicode and Character Set Functions (Windows)

Проблема с MS SQL Server 2008

Проблема: не ставится MS SQL Server 2008 и выше, закрываясь на середине установки с непонятной ошибкой.

Решение: проверьте не открыт ли Google Chrome. По каким-то непонятным причинам установщик MS SQL Server не дружит с этим браузером и в определенный момент они друг друга роняют. 

Проблема с запуском сервисов

Проблема: нужен удобный инструментарий создания, удаления, запуска и остановки windows-сервисов. В идеале из скрипта сборки и деплоймента приложения. Особой пикантности добавляет необходимость делать это на удаленной машине.

РешениеУтилита 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 не должен по логике откатить транзакцию

Совет

Отправляешь e-mail из программы? От греха по-дальше - не именуй аттачи русскими буквами. Не любит этого SMTP.

Совет

Чтобы в Windows 7 запустить быстро программу от имени администратора - достаточно в меню Пуск ввести ее имя и нажать Ctrl-Shift-Enter

Риск нежелания сотрудничества


Абстрактно: представитель заказчика по разным причинам не хочет взаимодействовать с командой разработки для предоставления критичных сведений.
Точнее может быть и хочет, но упорно, пардон, проябывается.

Конкретика: проект позразумевает разработку модулей, тесно интегрируемых заказчиком в свою систему. Заказчик обязал тимлидера своей команды связываться с подрядчиками и отвечать на все их вопросы по системе. Но тимлидер почему-то ОЧЕНЬ долго отвечает на письма и категорически отказывается выходить на связь голосом.

Методы борьбы: сдерживание. Эскалирование подобных проблем по мере появления до начальства представителя, которое, как правило, заинтересовано в проекте. Нехай люлей вставит.

среда, 7 ноября 2012 г.

Сюда

Сюда будет писаться коротко и по существу, каждый день. Только мой опыт. Только оригинальный контент.