GitHub and TortoieseGit: error code 128 git did not exit cleanly

У меня 2 аккаунта на GitHub. И несколько разных репозиториев для разных проектов - некторые должны заливаться на GitHub под первым аккаунтом, другие - под вторым. Я использую Git-клиент TortoiseGit. И столкнулся со странной проблемой - под первым аккаунтом все хорошо работает, а под вторым Push обламывается в формулировкой “error code 128 git did not exit cleanly”. Оказалось, что иногда для второго аккаунта используется ssh-key от первого. Лечится проблема удалением ключей (Remove Key) из Pagent (PuTTY authentication client).

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Вызов консольного приложения из программы на Delphi

Для вызова внешнего приложения из программы на Delphi обычно используется функция WinAPI CreateProcess. Но как получить то, что это приложение выводит в консоль, и, например, отобразить в логе программы?  Варианта два. Первый: пере-направить вывод консольного приложения в файл и потом прочитать этот файл. Однако, создавать временный файл не охота. А кроме того, такой способ не всегда хорошо работает. Есть другой способ - использовать пайпы (pipes). Ниже приведен код процедуры RunDosCommand на Delphi, который использует этот способ. Весь вывод на консоль записывается в AMemo: TMemo.

function RunDosCommand(ACmdString: String; AMemo: TMemo;
AReadPipe, AWritePipe: THandle): Boolean;
const
ReadBuffer = 4200;
var
Security : TSecurityAttributes;
ReadPipe, WritePipe: THandle;
start : TStartUpInfo;
ProcessInfo : TProcessInformation;
Buffer : Pchar;
BytesRead : DWord;
begin
Result := False;
if AMemo <> nil then
begin
AMemo.SelStart := Length(AMemo.Text);
AMemo.SelText := ACmdString + #13#10;
AMemo.SelStart := Length(AMemo.Text);
Application.ProcessMessages;
end;
ReadPipe := AReadPipe;
WritePipe := AWritePipe;
with Security do
begin
nLength := SizeOf(TSecurityAttributes);
bInheritHandle := true;
lpSecurityDescriptor := nil;
end;
if (ReadPipe = 0) and (WritePipe = 0) then
begin
if not CreatePipe(ReadPipe, WritePipe, @Security, 0) then
begin
ReadPipe := 0;
WritePipe := 0;
end;
end;
if (ReadPipe <> 0) or (WritePipe <> 0) then
begin
if AMemo <> nil then
begin
Buffer := AllocMem(ReadBuffer + 1);
end;
FillChar(Start, Sizeof(Start), #0);
start.cb := SizeOf(start) ;
start.hStdOutput := WritePipe;
start.hStdError := WritePipe;
start.hStdInput := ReadPipe;
start.dwFlags := STARTF_USESTDHANDLES +
STARTF_USESHOWWINDOW;
start.wShowWindow := SW_HIDE;
if CreateProcess(nil,
PChar(ACmdString),
@Security,
@Security,
true,
NORMAL_PRIORITY_CLASS,
nil,
nil,
start,
ProcessInfo) then
begin
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
if AMemo <> nil then
begin
BytesRead := 0;
ReadFile(ReadPipe, Buffer[0], ReadBuffer, BytesRead, nil);
Buffer[BytesRead] := #0;
AMemo.SelStart := Length(AMemo.Text);
AMemo.SelText := String(Buffer);
AMemo.SelStart := Length(AMemo.Text);
Application.ProcessMessages;
end;
Result := True;
end else
if AMemo <> nil then
begin
AMemo.SelStart := Length(AMemo.Text);
AMemo.SelText :='Command failed.'#13#10;
AMemo.SelStart := Length(AMemo.Text);
Application.ProcessMessages;
end;
if AMemo <> nil then
begin
FreeMem(Buffer);
end;
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
if (AReadPipe = 0) and (AWritePipe = 0) then
begin
CloseHandle(ReadPipe);
CloseHandle(WritePipe);
end;
end;
end;

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Преобразование _TCHAR в QString

Для преобразования _TCHAR в QString и обратно (QString в _TCHAR) я объявил в tqchar.h пару простых макросов: _TQSTRING_TQCHAR.

#ifndef TQCHAR_H
#define TQCHAR_H

#include <tchar.h>

#ifdef _UNICODE

#define _TQCHAR(x) (wchar_t*) x.utf16()
#define _TQSTRING(x) QString::fromUtf16((x))

#else

#define _TQCHAR(x) x.toLocal8Bit().constData()
#define _TQSTRING(x) QString::fromLocal8Bit((x))

#endif

#endif //TQCHAR_H

Кстати, нашел хороший плагин для Visual Studio чтобы копировать код в HTML с подсветкой синтаксиса: Copy As HTML.

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

ASProtect 1.65 - Delphi XE2 Compatible

Наконец-то появилась версия ASProtect 1.65 в которой заявлена совместимость с Delphi XE и XE2. Я попробовал - созданный в Delphi XE2 и запакованный новым “аспром” exe действительно запускается и работает. Ошибок пока не замечено.

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

VerQueryValue Access Violation

Столкнулся с интересным багом при получении информации о версии из ресурсов (Visual Studio 10, C++).

Access Violation возникает при выполнении VerQueryValue (см. картинку).
Причем, ошибка возникает только если используется Multi-Byte Character Set. А если выставить в свойствах проекта Use Unicode Character Set, то ошибка пропадает. Из этого можно сделать вывод, что баг именно в VerQueryValueA, а в VerQueryValueW его нет.
Что ж, решение проблемы заключается в переписывании функции чтения информации о версии таким образом, чтобы всегда использовалась функция VerQueryValueW. Ниже приведен листинг готовой функции.

UPD 30.01.2014. Спустя полтора года выяснилось, что и с использованием VerQueryValueW возможен Access Violation. Что же, лучше поздно чем никогда. Дело в том, что эта функция рассчитана на то, чтобы быть использованной совместно GetFileVersionInfo. Возможно, VerQueryValue может в каких-то случаях модифицировать ресурс. В общем, фикс заключается в том, что нужно выделить участок памяти (например, с помощью LocalAlloc), скопировать в него информацию о версии из ресурса (CopyMemory), и использовать его при вызове VerQueryValue. Реализация приведена ниже.

void ReadVersion(_TCHAR * szVer, int cbVer, const _TCHAR * szEntry)
{
_tcscpy(szVer, _T("\n"));
HRSRC hVersion = FindResource(
NULL,
MAKEINTRESOURCE(VS_VERSION_INFO),
RT_VERSION);
if (hVersion)
{
DWORD dwSize = SizeofResource(NULL, hVersion);
HGLOBAL hGlobal = LoadResource(NULL, hVersion);
if (hGlobal != NULL)
{
LPVOID versionInfoGlobal = LockResource(hGlobal);
if (versionInfoGlobal != NULL)
{
LPVOID versionInfoLocal = LocalAlloc(LMEM_FIXED, dwSize);
CopyMemory(versionInfoLocal, versionInfoGlobal, dwSize);

DWORD vLen, langD;
BOOL retVal;

LPVOID retbuf = NULL;

#ifdef _UNICODE
const _TCHAR * entry = szEntry;
#else
WCHAR entry[64]; //I assume here that length of szEntry string is less than 64
MultiByteToWideChar(CP_ACP, 0, szEntry, -1, entry, 64);
#endif

WCHAR fileEntry[1024];

_swprintf(fileEntry, L"\\VarFileInfo\\Translation");
retVal = VerQueryValueW(versionInfoLocal, fileEntry, &retbuf, (UINT*)&vLen);
if (retVal && vLen == 4)
{
memcpy(&langD, retbuf, 4);
_swprintf(fileEntry, L"\\StringFileInfo\\%02X%02X%02X%02X\\%s",
(langD & 0xff00)>>8,langD & 0xff,(langD & 0xff000000)>>24,
(langD & 0xff0000)>>16, entry);
}
else
_swprintf(fileEntry, L"\\StringFileInfo\\%04X04B0\\%s",
GetUserDefaultLangID(), entry);

retVal = VerQueryValueW(versionInfoLocal, fileEntry, &retbuf, (UINT*)&vLen);
if (retVal)
{
#ifdef _UNICODE
_sntprintf(szVer, cbVer-1, _T(" %s\n"), (_TCHAR*)retbuf);
szVer[cbVer-1] = '\0';
#else
szVer[0] = '\0';
int n = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)retbuf, -1, szVer+1, cbVer-2, NULL, NULL);
if (n > 0)
{
szVer[0] = ' ';
szVer[n] = '\n';
szVer[n+1] = '\0';
}
#endif
}

LocalFree(versionInfoLocal);
}
FreeResource(hGlobal);
}
}
}

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Qt и Visual Studio 2008

Чтобы использовать Qt совместно со средой разработки Visual Studio нужно выполнить следующие шаги:

1. Скачать и установить Qt for Visual Studio 2008: http://qt.nokia.com/downloads/windows-cpp-vs2008 (каталог установки не должен содержать пробелов, например это может быть C:\qt\4.8.2)

2. Скачать и установить Visual Studio Add-in: http://qt.nokia.com/downloads/visual-studio-add-in

3. Добавить в Переменную среды Path (Пуск->Панель инструментов->Система->Дополнительно->Переменные среды) путь к bin каталогу Qt (например, C:\\qt\\4.8.2\\bin)
Вот, в принципе, все. В Microsoft Visual Studio IDE должен появиться пункт меню Qt. А в диалоге New Project - типы проектов Qt4 Projects.

Есть только два нюанса:
1. Если хочется избавиться от Qt Runtime (статически прилинковать к exe файлу все необходимые библиотеки), то нужно пересобрать Qt с ключом -static:
  • Запустить Visual Studio 2008 Command Prompt
  • Выполнить команду: cd  C:\qt\4.8.2
  • Выполнить команду: configure -platform win32-msvc2008 -static
  • Выполнить команду: nmake

Сборка Qt у меня заняла несколько часов.

2. По-умолчанию библиотеки Qt компилируются в режиме Multi-threaded DLL - /MD (для Debug -  Multi-threaded Debug DLL - /MDd). Что означает наличие зависимостей от msvcr90.dll и msvcp90.dll. Т.е. при установке программы возможно потребуется устанавливать Microsoft Visual C++ 2008 Redistributable Package. Чтобы избавиться от этой зависимости нужно перед сборкой Qt поправить qmake.conf ( C:\qt\4.8.2 \mkspecs\win32-msvc2008\qmake.conf) следующим образом:

заменить строки

  QMAKE_CFLAGS_RELEASE    = -O2 -MD

  QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += -O2 -MD -Zi

  QMAKE_CFLAGS_DEBUG      = -Zi -MDd

на

  QMAKE_CFLAGS_RELEASE    = -O2 -MT

  QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += -O2 -MT -Zi

  QMAKE_CFLAGS_DEBUG      = -Zi -MTd

===

Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Зависимость от Microsoft Visual C++ Runtime

Чтобы убрать зависимость от Microsoft Visual C++ Runtime нужно в свойствах проекта выбрать Runtime Library: Multi-threaded (/MT). Получающийся при компиляции exe/dll будет несколько больше, зато не нужно будет устанавливать Visual C++ Redistributable Package.

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Проверка серверного сертификата и Enhanced Security

В моем посте про проверку серверного сертификата SSL в Delphi 7 проверка этого самого сертификата выполнялась вызовом функции CryptoAPI CertGetIssuerCertificateFromStore. Я столкнулся на практике со следующим: оказывается, что на эту функцию оказывает влияние Internet Explorer Enhanced Security (по умолчанию оно включено на всех серверах Windows 2003). Т.е. до тех пор, пока хост не будет включен в список доверенных, его сертификат тоже может не проходить проверку. Как-то так (слишком глубоко эту тему не копал). В общем, если на одном компьютере проверка сертификата работает, а на другом - нет (ошибка “Error connecting with SSL”), то следует проверить - включен ли IE Enhanced Security, и если да, то либо отключить его, либо добавить хост в список доверенных.

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Delphi XE 2 & ASProtect

ASProtect, к сожалению, не поддерживает приложения, сделанные в Delphi XE 2. Упакованные им exe-ки не запускаются. В Windows Application Log-е появляется соответствующая запись об ошибке в приложении.

Сообщение о несовместимости Delphi XE 2 и ASProtect на официальном форуме датировано 18 ноября 2011, но до сих пор решения нет. Последний ответ от 26 марта 2012 гласит:

Your problem in the queue for the decision and will be solved in the next release of ASProtect (will be released in the nearest future).

Ждем новую версию… Если в ближайшее время не появится, придется переходить на какой-нибудь другой протектор.

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Coolsoftware on github

Завел аккаунт в github: https://github.com/coolsoftware/

Это не первый мой аккаунт в github, поэтому потребовалось как-то организовывать одновременный доступ к нему из под разных учеток, с разными SSHKey. По этому поводу маленький “трик” - как я все это сделал:

1. При установке Git for Windows создает такой ярлык для Git Bash:

Как мы видим текущий каталог Git Bash - %HOMEDRIVE%%HOMEPATH%. В нем хранятся все настройки git, каталог .ssh с SSHKey и проекты обычно тоже там.

Я создал в %HOMEDRIVE%%HOMEPATH% каталог Coolsoftware и в нем git.cmd следующего содержания:

@set HOME=%HOMEDRIVE%%HOMEPATH%\Coolsoftware

C:\Windows\SysWOW64\cmd.exe /c “”C:\Program Files (x86)\Git\bin\sh.exe” –login -i”

Затем создал ярлык на этот git.cmd и вынес его на рабочий стол, обозвав “Git Bash - Coolsoftware”. Теперь при его запуске текущий каталог пользователя в “баше” %HOMEDRIVE%%HOMEPATH%\Coolsoftware, а при запуске старого ярлыка “Git Bash” текущий каталог %HOMEDRIVE%%HOMEPATH%. Таким образом получилось два “баша”, в каждом - свои настройки.

Далее запустил “Git Bash - Coolsoftware” произвел установку github в соответствии с инструкцией и выложил проект VHashedStringList - оптимизированный список строк, доступ к элементам которого организован с помощью хеш-индекса.

TVHashedStringList - аналог стандартного THashedStringList (фактически, он сделан на его основе, хотя наследуется не от него, а от TStringList).

Существенное отличие от THashedStringList заключается в том, что оптимизированы операции перестроения индекса при изменении списка.

В THashedStringList полное перестроение индекса требуется после любого изменения, что приводит к значительным задержкам в реальных приложениях.

Например, в следующем примере полное перестроение индекса будет произведено 3 раза:

procedure ChangeList(lst: THashedStringList);
begin
lst.Values['key1'] := 'Value1';
lst.Values['key2'] := 'Value2'; //неявный вызов IndexOfName и перестроение индекса
lst.Values['key3'] := 'Value3'; //неявный вызов IndexOfName и перестроение индекса
lst.Values['key4'] := 'Value4'; //неявный вызов IndexOfName и перестроение индекса
end;

В отличие от THashedStringList, TVHashedStringList не требует полного перестроения индекса после добавления нового элемента в конец списка или

при изменении значений элементов списка. Перестроение происходит только после вставки нового элемента в список или удаления из списка.

Поэтому в следующем примере полного перестроения индекса не произойдет ни разу.

procedure ChangeList(lst: TVHashedStringList);
begin
lst.Values['key1'] := 'Value1'; //индекс модифицирован и не требует перестроения
lst.Values['key2'] := 'Value2'; //индекс модифицирован и не требует перестроения
lst.Values['key3'] := 'Value3'; //индекс модифицирован и не требует перестроения
lst.Values['key4'] := 'Value4'; //индекс модифицирован и не требует перестроения
end;

===

Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru