OberonCore
https://forum.oberoncore.ru/

Перенаправление stdout в лог
https://forum.oberoncore.ru/viewtopic.php?f=2&t=1266
Страница 1 из 2

Автор:  QWERTYProgrammer [ Воскресенье, 30 Ноябрь, 2008 16:15 ]
Заголовок сообщения:  Перенаправление stdout в лог

Еще один вопрос по-поводу использования dll:
если в Блэкбоксе вызывается функция из dll, которая пишет какие-нибудь сообщения в stdout, можно ли как-то перенаправить вывод в StdLog?

Автор:  Илья Ермаков [ Воскресенье, 30 Ноябрь, 2008 16:41 ]
Заголовок сообщения:  Re: трансляция структуры из h файла

Вообще говоря, DLL наследует handle файла stdout от всего процесса.
Значит, это хэндл получить можно. Можно и перехватить как-то (до начала работы с ДЛЛ открыть этот файл вместо консоли на куда-то ещё).

Автор:  Илья Ермаков [ Воскресенье, 30 Ноябрь, 2008 16:42 ]
Заголовок сообщения:  Re: трансляция структуры из h файла

Копать в сторону WinApi-функций CreateFile и AllocConsole.

Автор:  Евгений Темиргалеев [ Понедельник, 01 Декабрь, 2008 12:34 ]
Заголовок сообщения:  Re: трансляция структуры из h файла

Мне кажется, копать надо в сторону pipe и SetStdHandle. Идея такая:
- сделать трубу, хендл записи назначить на STD_OUTPUT_HANDLE.
- dll пишет в трубу, в ББ из неё периодически читается и выводится в лог.

Автор:  Евгений Темиргалеев [ Понедельник, 01 Декабрь, 2008 12:39 ]
Заголовок сообщения:  Re: трансляция структуры из h файла

Неименованные трубы, как я понял, поддерживают только блокирующие операции, что со стороны ББ не допустимо.
Попробовал сделать с именованными, но застрял в трубе при открытии её на запись. Кто подскажет?
Код:
MODULE TestStdToLog;

  IMPORT
    WinApi, Dialog, Strings;

  CONST
    INVALID_HANDLE_VALUE = -1;
    pipeName = "\\.\pipe\stdoutToBBLog";
   
  VAR
    read, write: WinApi.HANDLE;
    ovrl: WinApi.OVERLAPPED;
   
  PROCEDURE ShowWinErr (IN act: ARRAY OF CHAR);
    VAR  msg: ARRAY 512 OF CHAR; err, res: INTEGER;
  BEGIN
    err := WinApi.GetLastError();
    res := WinApi.FormatMessageW(
      WinApi.FORMAT_MESSAGE_FROM_SYSTEM,
      0, err, 0, msg, LEN(msg), NIL
      );
    IF res = 0 THEN  (* Описание ошибки не получено *)
      Strings.IntToString(err, msg); msg := msg + " (WinAPI-код ошибки)"
    END;
    Dialog.ShowParamMsg("Ошибка при ^0: ^1", act, msg, "")
  END ShowWinErr;
 
  PROCEDURE Uninstall;
    VAR  res: INTEGER;
  BEGIN
    IF write # INVALID_HANDLE_VALUE THEN
      res := WinApi.CloseHandle(write);
      write := INVALID_HANDLE_VALUE;
      IF res = 0 THEN ShowWinErr("CloseHandle(write)") END
    END;
    IF read # INVALID_HANDLE_VALUE THEN
      res := WinApi.CloseHandle(read);
      read := INVALID_HANDLE_VALUE;
      IF res = 0 THEN ShowWinErr("CloseHandle(read)") END
    END;
    (* An instance of a named pipe is always deleted when the last handle to the instance of the named pipe is closed. *)
    IF ovrl.hEvent # INVALID_HANDLE_VALUE THEN
      res := WinApi.CloseHandle(ovrl.hEvent);
      ovrl.hEvent := INVALID_HANDLE_VALUE;
      IF res = 0 THEN ShowWinErr("CloseHandle(ovrl.hEvent)") END
    END
  END Uninstall;

  PROCEDURE CreatePipe;
    VAR  res: INTEGER;
  BEGIN
    res := WinApi.CreateNamedPipe(
      pipeName,
      ORD({WinApi.PIPE_ACCESS_INBOUND} + WinApi.FILE_FLAG_OVERLAPPED),
      WinApi.PIPE_TYPE_BYTE + WinApi.PIPE_READMODE_BYTE + WinApi.PIPE_WAIT,
      1,
      1024,  (* ? *)
      1024,  (* ? *)
      1,  (* ? *)
      NIL);
    IF res # INVALID_HANDLE_VALUE THEN
      read := res
    ELSE
      ShowWinErr("CreateNamedPipe")
    END
  END CreatePipe;

  PROCEDURE ConnectPipe;
    VAR  res: INTEGER; ovrl: WinApi.OVERLAPPED;
  BEGIN
    (* If hNamedPipe was opened with FILE_FLAG_OVERLAPPED, the lpOverlapped parameter must not be NULL. It must point to a valid OVERLAPPED structure. If hNamedPipe was opened with FILE_FLAG_OVERLAPPED and lpOverlapped is NULL, the function can incorrectly report that the connect operation is complete.

If hNamedPipe was created with FILE_FLAG_OVERLAPPED and lpOverlapped is not NULL, the OVERLAPPED structure pointed to by lpOverlapped must contain a handle to a manual-reset event object (which the server can create by using the CreateEvent function). *)
    ovrl.hEvent := WinApi.CreateEvent(NIL, WinApi.TRUE, WinApi.FALSE, NIL);
    IF ovrl.hEvent # 0 THEN
      res := WinApi.ConnectNamedPipe(read, ovrl);
      IF res = 0 THEN ShowWinErr("ConnectNamedPipe") END
    ELSE
      ShowWinErr("CreateEvent")
    END
  END ConnectPipe;

  PROCEDURE Install;
    VAR  res: INTEGER;
  BEGIN
    read := INVALID_HANDLE_VALUE; write := INVALID_HANDLE_VALUE;
    CreatePipe;
    IF read # INVALID_HANDLE_VALUE THEN
      ConnectPipe;
     
      res := WinApi.CreateFile(
        pipeName,
        WinApi.GENERIC_WRITE,
        {},
        NIL,
        WinApi.OPEN_EXISTING,  (* ? *)
        WinApi.FILE_ATTRIBUTE_NORMAL,  (* ? *)
        0
        );
     
      IF res = INVALID_HANDLE_VALUE THEN
        ShowWinErr("CreateFile")
      ELSE  (* res # INVALID_HANDLE_VALUE *)
        write := res
      END
    END
  END Install;
 
BEGIN
  Install
CLOSE
  Uninstall
END TestStdToLog.

^Q "Kernel.LoadMod('TestStdToLog')"

Автор:  Trurl [ Понедельник, 01 Декабрь, 2008 15:42 ]
Заголовок сообщения:  Re: трансляция структуры из h файла

Евгений Темиргалеев писал(а):
Неименованные трубы, как я понял, поддерживают только блокирующие операции, что со стороны ББ не допустимо.

Нам ведь только почитать, так что допустимо. PeekNamedPipe, вопреки названию, работает и с анонимными каналами.

Автор:  QWERTYProgrammer [ Понедельник, 01 Декабрь, 2008 16:49 ]
Заголовок сообщения:  Re: трансляция структуры из h файла

Действительно, msdn говорит http://msdn.microsoft.com/en-us/library/aa365779(VS.85).aspx, что

PeekNamedPipe Function

Copies data from a named or anonymous pipe into a buffer without removing it from the pipe. It also returns information about data in the pipe.

Надо бы попробовать.

Автор:  Евгений Темиргалеев [ Понедельник, 01 Декабрь, 2008 23:20 ]
Заголовок сообщения:  Re: трансляция структуры из h файла

Trurl, спасибо, получилось. QWERTYProgrammer, осталось посмотреть в какой stdout будет писать dll :)
Код:
MODULE TestStdToLog;

   IMPORT
      WinApi, Dialog, Strings, Log, Services, SYSTEM;

   CONST
      INVALID_HANDLE_VALUE = -1;
   
   TYPE
      Action = POINTER TO RECORD (Services.Action) END;
      
   VAR
      a: Action;
      read, write: WinApi.HANDLE;
   
   PROCEDURE ShowWinErr (IN act: ARRAY OF CHAR);
      VAR   msg: ARRAY 512 OF CHAR; err, res: INTEGER;
   BEGIN
      err := WinApi.GetLastError();
      res := WinApi.FormatMessageW(
         WinApi.FORMAT_MESSAGE_FROM_SYSTEM,
         0, err, 0, msg, LEN(msg), NIL
         );
      IF res = 0 THEN   (* Описание ошибки не получено *)
         Strings.IntToString(err, msg); msg := msg + " (WinAPI-код ошибки)"
      END;
      Dialog.ShowParamMsg("Ошибка при ^0: ^1", act, msg, "")
   END ShowWinErr;

   PROCEDURE WriteChar (c: SHORTCHAR);
   BEGIN
      CASE c OF
      | 09X:   Log.Tab
      | 0AX, 0DX:   Log.Ln
      ELSE
         Log.Char(c)
      END
   END WriteChar;

   PROCEDURE (a: Action) Do;
      VAR   res, avail, n: INTEGER; c: SHORTCHAR;
   BEGIN
      res := WinApi.PeekNamedPipe(read, 0, 0, NIL, avail, NIL);
      (*IF (res = 0) THEN ShowWinErr("PeekNamedPipe") END;*)
      IF (res # 0) & (avail > 0) THEN
         (*Log.Int(avail); Log.Ln;*)
         res := WinApi.ReadFile(read, SYSTEM.ADR(c), 1, n, NIL);
         (*IF (res = 0) THEN ShowWinErr("ReadFile") END;*)
         WHILE (res # 0) & (n = 1) & (avail > 0) DO
            WriteChar(c); DEC(avail);
            IF avail > 0 THEN
               res := WinApi.ReadFile(read, SYSTEM.ADR(c), 1, n, NIL);
               (*IF (res = 0) THEN ShowWinErr("ReadFile") END;*)
            END
         END
      END;
      Services.DoLater(a, Services.Ticks() + Services.resolution)
   END Do;
   
   PROCEDURE Install;
      VAR   res: INTEGER;
   BEGIN
      read := INVALID_HANDLE_VALUE; write := INVALID_HANDLE_VALUE;
      res := WinApi.CreatePipe(read, write, NIL, 0);
      IF res # 0 THEN
         res := WinApi.SetStdHandle(WinApi.STD_OUTPUT_HANDLE, write);
         IF res # 0 THEN
            NEW(a);
            Services.DoLater(a, Services.Ticks() + Services.resolution)
         ELSE
            ShowWinErr("SetStdHandle")
         END
      ELSE
         ShowWinErr("CreatePipe")
      END
   END Install;
   
   PROCEDURE Uninstall;
      VAR   res: INTEGER;
   BEGIN
      IF write # INVALID_HANDLE_VALUE THEN
         res := WinApi.CloseHandle(write); write := INVALID_HANDLE_VALUE;
         IF res = 0 THEN ShowWinErr("CloseHandle(write)") END
      END;
      IF read # INVALID_HANDLE_VALUE THEN
         res := WinApi.CloseHandle(read); read := INVALID_HANDLE_VALUE;
         IF res = 0 THEN ShowWinErr("CloseHandle(read)") END
      END
   END Uninstall;

   PROCEDURE Do*;
      VAR   str: ARRAY 1024 OF SHORTCHAR; res, wr: INTEGER;
   BEGIN
      str := "Тест" + 09X + "Test" + 09X + "***" + 0AX;
      res := WinApi.WriteFile(write, SYSTEM.ADR(str), LEN(str$), wr, NIL);
      IF wr # LEN(str$) THEN ShowWinErr("WriteFile") END
   END Do;

BEGIN
   Install
CLOSE
   Uninstall
END TestStdToLog.

^Q TestStdToLog.Do

P.S. Сонным работать не айс... Получилось не сразу :lol:
- сначала "не печаталось", из-за Services.DoLater(a, Services.Ticks() + Services.resolution * 1000)
- потом печатался мусор из-за res := WinApi.WriteFile(write, SYSTEM.ADR(str), LEN(str), wr, NIL);
- и повторно "не печаталось" из-за скопированного в начале ... Services.resolution * 1000

Автор:  Trurl [ Вторник, 02 Декабрь, 2008 10:33 ]
Заголовок сообщения:  Re: Перенаправление stdout в лог

надо ещё добится того, чтобы SetStdHandle выполнилось раньше, чем dll-ка сделает GetStdHandle.

Автор:  QWERTYProgrammer [ Среда, 03 Декабрь, 2008 01:06 ]
Заголовок сообщения:  Re: трансляция структуры из h файла

Евгений Темиргалеев писал(а):
Trurl, спасибо, получилось. QWERTYProgrammer, осталось посмотреть в какой stdout будет писать dll :)


Что то на ночь глядя не соображу, что нужно сделать кроме импорта TestStdToLog, чтобы перехватить перехватить печать в stdout при вызове в ББ функции, определенной в dll? :roll:

Автор:  Евгений Темиргалеев [ Среда, 03 Декабрь, 2008 12:09 ]
Заголовок сообщения:  Re: Перенаправление stdout в лог

Задумывалось, что:
- для включения перенаправления нужно загрузить модуль Kernel.LoadMod("TestStdToLog")
- для выключения - выгрузить Kernel.UnloadMod("TestStdToLog").

Trurl писал(а):
надо ещё добится того, чтобы SetStdHandle выполнилось раньше, чем dll-ка сделает GetStdHandle.
Для этого, мне кажется, достаточно грузить модуль до модуля с dll-м интерфейсом.

Автор:  Евгений Темиргалеев [ Среда, 03 Декабрь, 2008 13:08 ]
Заголовок сообщения:  Re: Перенаправление stdout в лог

Евгений Темиргалеев писал(а):
- для выключения - выгрузить Kernel.UnloadMod("TestStdToLog").
Ошибся. Так не выгрузить. Для тестов DevDebug.UnloadThis; программно - можно экспортировать Install/Uninstall, подправив:
Код:
   PROCEDURE Install;
...
>>            IF a = NIL THEN NEW(a) END;
            Services.DoLater(a, Services.Ticks() + Services.resolution)

   PROCEDURE Uninstall;
      VAR   res: INTEGER;
   BEGIN
>>      IF a # NIL THEN Services.RemoveAction(a) END;

Проба с дельфовой dll - вывод не пошёл.
Код:
MODULE TestStdToLogDll ["Project1.dll"];

   PROCEDURE Test*;

END TestStdToLogDll.

MODULE TestTemp;
   
   IMPORT TestStdToLogDll, Log;
   
   PROCEDURE Do*;
   BEGIN
      Log.String("TestStdToLogDll.Test:"); Log.Ln;
      TestStdToLogDll.Test
   END Do;

END TestTemp.

"Kernel.LoadMod('TestStdToLog')"
TestTemp.Do
DevDebug.UnloadThis TestStdToLog

(* Delphi dll *)
library Project1;

uses
  SysUtils,
  Classes;

{$R *.res}

procedure Test;
begin
  writeln('x'#9'='#9, 1);
end;

exports Test;

begin
end.

Автор:  Edward Ivanov [ Среда, 03 Декабрь, 2008 14:33 ]
Заголовок сообщения:  Re: Перенаправление stdout в лог

IMHO, для dll (Delphi) нужно указать консольный режим.
{$APPTYPE CONSOLE}

Автор:  Евгений Темиргалеев [ Среда, 03 Декабрь, 2008 15:00 ]
Заголовок сообщения:  Re: Перенаправление stdout в лог

Спасибо, что обратили внимание. Но это, кажется, не то.
Delphi Help писал(а):
The $APPTYPE directive controls whether to generate a Win32 console or graphical UI application...

Автор:  Edward Ivanov [ Среда, 03 Декабрь, 2008 15:38 ]
Заголовок сообщения:  Re: Перенаправление stdout в лог

Write(ln) работает при этой включенной директиве. Иначе AV.

Автор:  Евгений Темиргалеев [ Среда, 03 Декабрь, 2008 15:42 ]
Заголовок сообщения:  Re: Перенаправление stdout в лог

Delphi Help писал(а):
The $APPTYPE directive is meaningful only in a program. It should not be used in a library, unit, or package.
...
In Delphi code, Write writes one or more values to a text file. F, if specified, is a text file variable. If F is omitted, the standard file variable Output is assumed.
...
var Output: Text;

In Delphi, the Output variable is a write-only Text file associated with the process's standard output file.
On Windows, most processes don't have a standard output file, and writing to Output raises an error. Delphi programs have a standard output file if they are linked as console applications.
Как для library, пока не понятно. Пробовал напрямую WriteFile(GetStdHandle(STD_OUTPUT_HANDLE)... из dll - печатает.

Автор:  Евгений Темиргалеев [ Среда, 03 Декабрь, 2008 15:58 ]
Заголовок сообщения:  Re: трансляция структуры из h файла

QWERTYProgrammer писал(а):
Что то на ночь глядя не соображу, что нужно сделать кроме импорта TestStdToLog, чтобы перехватить перехватить печать в stdout при вызове в ББ функции, определенной в dll? :roll:
Кроме загрузки TestStdToLog ничего не требуется. У Вас сишный длл? Тоже не работает?

Автор:  Trurl [ Среда, 03 Декабрь, 2008 16:15 ]
Заголовок сообщения:  Re: Перенаправление stdout в лог

Евгений Темиргалеев писал(а):
Trurl писал(а):
надо ещё добится того, чтобы SetStdHandle выполнилось раньше, чем dll-ка сделает GetStdHandle.
Для этого, мне кажется, достаточно грузить модуль до модуля с dll-м интерфейсом.


Начнем с того, что модуль с dll-м интерфейсом вообще не загружается. А если модуль использует dll, то она будет загружена и проинициализирована раньше модуля. Если dll выполнит GetStdHandle во время нициализации, а потом сохранит полученный дескриптор и будет использовать его, то все манипуляции со SetStdHandle будут бесполезными.

Автор:  Евгений Темиргалеев [ Среда, 03 Декабрь, 2008 17:42 ]
Заголовок сообщения:  Re: Перенаправление stdout в лог

Имелось ввиду грузить модуль TestStdToLog, который при инициализации вып. SetStdHandle. До загрузки некоторго другого модуля, который использует dll.

Автор:  QWERTYProgrammer [ Четверг, 04 Декабрь, 2008 00:52 ]
Заголовок сообщения:  Re: трансляция структуры из h файла

Евгений Темиргалеев писал(а):
QWERTYProgrammer писал(а):
Что то на ночь глядя не соображу, что нужно сделать кроме импорта TestStdToLog, чтобы перехватить перехватить печать в stdout при вызове в ББ функции, определенной в dll? :roll:
Кроме загрузки TestStdToLog ничего не требуется. У Вас сишный длл? Тоже не работает?


Да, dll - сишный. И увы тоже не работает.

Страница 1 из 2 Часовой пояс: UTC + 3 часа
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/