пятница, 31 августа 2012 г.

Вызов консольного приложения из программы на 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

воскресенье, 26 августа 2012 г.

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

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

  1. #ifndef TQCHAR_H
  2. #define TQCHAR_H
  3.  
  4. #include <tchar.h>
  5.  
  6. #ifdef _UNICODE
  7.  
  8. #define _TQCHAR(x)        (wchar_t*) x.utf16()
  9. #define _TQSTRING(x)    QString::fromUtf16((x))
  10.  
  11. #else
  12.  
  13. #define _TQCHAR(x)        x.toLocal8Bit().constData()
  14. #define _TQSTRING(x)    QString::fromLocal8Bit((x))
  15.  
  16. #endif
  17.  
  18. #endif //TQCHAR_H

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

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

четверг, 23 августа 2012 г.

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. Реализация приведена ниже.

  1. void ReadVersion(_TCHAR * szVer, int cbVer, const _TCHAR * szEntry)
  2. {
  3.     _tcscpy(szVer, _T("\n"));
  4.     HRSRC hVersion = FindResource(
  5.         NULL,
  6.         MAKEINTRESOURCE(VS_VERSION_INFO),
  7.         RT_VERSION);
  8.     if (hVersion)  
  9.     {
  10.         DWORD dwSize = SizeofResource(NULL, hVersion);
  11.         HGLOBAL hGlobal = LoadResource(NULL, hVersion);
  12.         if (hGlobal != NULL)  
  13.         {
  14.             LPVOID versionInfoGlobal  = LockResource(hGlobal);
  15.             if (versionInfoGlobal != NULL)
  16.             {
  17.                 LPVOID versionInfoLocal = LocalAlloc(LMEM_FIXED, dwSize);
  18.                 CopyMemory(versionInfoLocal, versionInfoGlobal, dwSize);
  19.  
  20.                 DWORD vLen, langD;
  21.                 BOOL retVal;
  22.  
  23.                 LPVOID retbuf = NULL;
  24.  
  25. #ifdef _UNICODE
  26.                 const _TCHAR * entry = szEntry;
  27. #else                    
  28.                 WCHAR entry[64]; //I assume here that length of szEntry string is less than 64
  29.                 MultiByteToWideChar(CP_ACP, 0, szEntry, -1, entry, 64);
  30. #endif
  31.  
  32.                 WCHAR fileEntry[1024];
  33.  
  34.                 _swprintf(fileEntry, L"\\VarFileInfo\\Translation");
  35.                 retVal = VerQueryValueW(versionInfoLocal, fileEntry, &retbuf, (UINT*)&vLen);
  36.                 if (retVal && vLen == 4)
  37.                 {
  38.                     memcpy(&langD, retbuf, 4);            
  39.                     _swprintf(fileEntry, L"\\StringFileInfo\\%02X%02X%02X%02X\\%s",
  40.                             (langD & 0xff00)>>8,langD & 0xff,(langD & 0xff000000)>>24,
  41.                             (langD & 0xff0000)>>16, entry);            
  42.                 }
  43.                 else
  44.                     _swprintf(fileEntry, L"\\StringFileInfo\\%04X04B0\\%s",
  45.                     GetUserDefaultLangID(), entry);
  46.  
  47.                 retVal = VerQueryValueW(versionInfoLocal, fileEntry, &retbuf, (UINT*)&vLen);
  48.                 if (retVal)
  49.                 {
  50. #ifdef _UNICODE
  51.                     _sntprintf(szVer, cbVer-1, _T(" %s\n"), (_TCHAR*)retbuf);
  52.                     szVer[cbVer-1] = '\0';
  53. #else
  54.                     szVer[0] = '\0';
  55.                     int n = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)retbuf, -1, szVer+1, cbVer-2, NULL, NULL);
  56.                     if (n > 0)
  57.                     {
  58.                         szVer[0] = ' ';
  59.                         szVer[n] = '\n';
  60.                         szVer[n+1] = '\0';
  61.                     }
  62. #endif
  63.                 }
  64.  
  65.                 LocalFree(versionInfoLocal);
  66.             }
  67.             FreeResource(hGlobal);
  68.         }
  69.     }
  70. }

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