Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > Программа и интерфейс
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 13.04.2013, 22:51
Аватар для Uniq!
Uniq! Uniq! вне форума
Местный
 
Регистрация: 29.09.2010
Сообщения: 539
Версия Delphi: Delphi XE3
Репутация: 374
По умолчанию IDE для Ruby (развитие для себя)

Ребят, только не бросайтесь фразами: "А уже дофига всяких IDE, зачем тратить время, силы бла бла бла всё равно лучше не сделаешь"

Я для себя. Просто хочу разобраться как эти программы работают.

Значит проблема следующая: как отдать файл компилятору (интерпретатору), и вернуть результат в виде ошибок, если они есть.

Я так понимаю командная строка + CreateProcess?
Или есть более элегантные решения?

Последний раз редактировалось Uniq!, 13.04.2013 в 22:54.
Ответить с цитированием
  #2  
Старый 13.04.2013, 23:19
Аватар для M.A.D.M.A.N.
M.A.D.M.A.N. M.A.D.M.A.N. вне форума
Sir Richard Abramson
 
Регистрация: 05.04.2008
Сообщения: 5,505
Версия Delphi: XE10
Репутация: выкл
По умолчанию

Как-то обертку над консольным php.exe делал, результаты через пайпы затягивать приходилось. А рубивский компиль какой?
__________________
— Как тебя понимать?
— Понимать меня не обязательно. Обязательно меня любить и кормить вовремя.


На Delphi, увы, больше не программирую.
Рекомендуемая литература по программированию
Ответить с цитированием
  #3  
Старый 13.04.2013, 23:21
Аватар для Uniq!
Uniq! Uniq! вне форума
Местный
 
Регистрация: 29.09.2010
Сообщения: 539
Версия Delphi: Delphi XE3
Репутация: 374
По умолчанию

Он не компилер, он интерпретатор.

По факту своя ком строка, которая понимает синтаксис Ruby

Сейчас проблема такая:

Код:
procedure TForm1.aRunExecute(Sender: TObject);
begin
  aSave.Execute;
  rLog.Items.Add('[' + TimeToStr(Now) + '] Running ' + rSave.FileName);
  ShellExecute(0, 'open', 'cmd.exe', PWideChar('/k ruby ' + rSave.FileName),
    'C:\Windows\system32\', SW_SHOW);
end;

если в rSave.FileName лежит строка с пробелами\русскими символами\символами типа !"№;%:?*()_ полный путь к интерпретируемому файлу обрывается

Последний раз редактировалось Uniq!, 13.04.2013 в 23:56.
Ответить с цитированием
  #4  
Старый 14.04.2013, 12:00
Pyro Pyro вне форума
Так проходящий
 
Регистрация: 18.07.2011
Сообщения: 805
Версия Delphi: 7Lite
Репутация: 6063
По умолчанию

если только для руби, и если https://github.com/charliesome/better_errors работает под windows, то можно попробовать создать в той папке временный файл, примерно как тот, что там в секции Usage, но в get запускающий тот файл, который должен выкинуть ошибку, запустить временный файл, и webbrowser.navigate(туда). если выскочит ошибка, то по идее должна отоброзится в webbrowser
__________________
>woweook<
Ответить с цитированием
  #5  
Старый 14.04.2013, 12:13
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Цитата:
если в rSave.FileName лежит строка с пробелами\русскими символами\символами типа !"№;%:?*()_ полный путь к интерпретируемому файлу обрывается
А взять имя файла в кавычки не помогает?
Код:
...PWideChar('/k ruby "' + rSave.FileName + '"'), ...
Цитата:
если в rSave.FileName лежит строка с !"№;%:?*()_
Кавычка, двоеточие, звездочка и вопрос в имени запрещены.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.

Последний раз редактировалось Bargest, 14.04.2013 в 12:15.
Ответить с цитированием
  #6  
Старый 14.04.2013, 12:27
Аватар для Uniq!
Uniq! Uniq! вне форума
Местный
 
Регистрация: 29.09.2010
Сообщения: 539
Версия Delphi: Delphi XE3
Репутация: 374
По умолчанию

Я же написал "символами типа"
Проблема не решена, но...

Я за ночь докопался вот до такой процедуры:
Код:
procedure ExecConsoleApp(CommandLine: AnsiString; Output: TStringList;
  Errors: TStringList);
var
  sa: TSECURITYATTRIBUTES;
  si: TSTARTUPINFO;
  pi: TPROCESSINFORMATION;
  hPipeOutputRead: THANDLE;
  hPipeOutputWrite: THANDLE;
  hPipeErrorsRead: THANDLE;
  hPipeErrorsWrite: THANDLE;
  Res, bTest: Boolean;
  env: array [0 .. 100] of Char;
  szBuffer: array [0 .. 256] of Char;
  dwNumberOfBytesRead: DWORD;
  Stream: TMemoryStream;
begin
  sa.nLength := sizeof(sa);
  sa.bInheritHandle := true;
  sa.lpSecurityDescriptor := nil;
  CreatePipe(hPipeOutputRead, hPipeOutputWrite, @sa, 0);
  CreatePipe(hPipeErrorsRead, hPipeErrorsWrite, @sa, 0);
  ZeroMemory(@env, sizeof(env));
  ZeroMemory(@si, sizeof(si));
  ZeroMemory(@pi, sizeof(pi));
  si.cb := sizeof(si);
  si.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
  si.wShowWindow := SW_HIDE;
  si.hStdInput := 0;
  si.hStdOutput := hPipeOutputWrite;
  si.hStdError := hPipeErrorsWrite;

  { Если вы хотите запустить процесс без параметров, заnil`те второй параметр
    и используйте первый
  }
  Res := CreateProcess(nil, pchar(CommandLine), nil, nil, true,
    CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, @env, nil, si, pi);

  // Если не получилось - то выходим
  if not Res then
  begin
    CloseHandle(hPipeOutputRead);
    CloseHandle(hPipeOutputWrite);
    CloseHandle(hPipeErrorsRead);
    CloseHandle(hPipeErrorsWrite);
    Exit;
  end;
  CloseHandle(hPipeOutputWrite);
  CloseHandle(hPipeErrorsWrite);

  // Читаем вывод
  Stream := TMemoryStream.Create;
  try
    while true do
    begin
      bTest := ReadFile(hPipeOutputRead, szBuffer, 256,
        dwNumberOfBytesRead, nil);
      if not bTest then
      begin
        break;
      end;
      Stream.Write(szBuffer, dwNumberOfBytesRead);
    end;
    Stream.Position := 0;
    Output.LoadFromStream(Stream);
  finally
    Stream.Free;
  end;

  // Вывод о ошибках
  Stream := TMemoryStream.Create;
  try
    while true do
    begin
      bTest := ReadFile(hPipeErrorsRead, szBuffer, 256,
        dwNumberOfBytesRead, nil);
      if not bTest then
      begin
        break;
      end;
      Stream.Write(szBuffer, dwNumberOfBytesRead);
    end;
    Stream.Position := 0;
    Errors.LoadFromStream(Stream);
  finally
    Stream.Free;
  end;

  WaitForSingleObject(pi.hProcess, INFINITE);

  CloseHandle(pi.hProcess);
  CloseHandle(hPipeOutputRead);
  CloseHandle(hPipeErrorsRead);
end;

При вызове CreateProcess ошибка: (вложения)
Изображения
Тип файла: bmp error_createprocess.bmp (300.1 Кбайт, 5 просмотров)
Ответить с цитированием
  #7  
Старый 14.04.2013, 12:32
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Ну env тут явно лишний. Но это не ошибка.
Какая делфя? Судя по юзанию AnsiString, скорее что-то после 10й. В таком случае нехорошо преобразовывать AnsiString к PChar. Тогда уж к PAnsiChar, или строку юзать (Wide)String.
Но вообще взятие в кавычки должно было все решить. Параметры в кавычках передаются CMD в прогу без изменений как один параметр.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.
Ответить с цитированием
  #8  
Старый 14.04.2013, 12:41
Аватар для Uniq!
Uniq! Uniq! вне форума
Местный
 
Регистрация: 29.09.2010
Сообщения: 539
Версия Delphi: Delphi XE3
Репутация: 374
По умолчанию

@env - уже убрал, зачем передавать указатель на левые переменные o.O ?

Ошибка сейчас ( как можно заметить ) не в текстовых преобразованиях, а в доступе к kernel32.dll ибо в неё "записать" не может.

Delphi XE3
Ответить с цитированием
  #9  
Старый 14.04.2013, 12:43
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Ошибка возникает внутри kernel32 (большой адрес), но при попытке записать в маленькие адреса (write of address 5accc0). Так что это не запись В kernel32, это запись по недоступному адресу ИЗ нее.
С МСДНа:
Цитата:
The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.

Последний раз редактировалось Bargest, 14.04.2013 в 12:51.
Ответить с цитированием
Этот пользователь сказал Спасибо Bargest за это полезное сообщение:
Uniq! (14.04.2013)
  #10  
Старый 14.04.2013, 12:50
Аватар для Uniq!
Uniq! Uniq! вне форума
Местный
 
Регистрация: 29.09.2010
Сообщения: 539
Версия Delphi: Delphi XE3
Репутация: 374
По умолчанию

Вот собственно всё и прояснилось. Действительно ошибки из-за преобразований туда сюда...

Я в этом полный ноль, особенно когда речь идёт о разных версиях Delphi.

Сама команда CreateProcess должна принимать PWideChar (по документации), я же ей передаю нечто в одинарных кавычках (это какой тип? ) и как мне от него к PWideChar грамотно перейти.

И за одно чего-нибудь почитать дайте, пожалуйста.
Ответить с цитированием
  #11  
Старый 14.04.2013, 12:56
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Нечто в кавычках - это делфовая строка. Преобразование в PChar/PAnsiChar/PWideChar по сути одно и то же - возвращает переданный функции преобразования параметр без изменений (кроме случая строки нулевой длины вроде). Потому что делфийская строка - оригинальный класс, в котором адрес класса есть адрес начала строки (массива байт), а все поля класса отсчитываются в минус от этого адреса. Там 3 поля. Длина, размер выделенного блока и количество использований (для многопоточности, чтобы не снести строку, пока ее кто-то юзает).
Если строка имеет изначальный тип WideString (в XE3 WideString = String), то при записи туда строки 'Hello, world!' она автоматически записывается как Wide. И преобразовывать к PWideChar можно. Если она Ansi, то записывается как Ansi (однобайтовые символы), и преобразовывать к Wide не так просто. Разве что сделать
Код:
var s:AnsiString;
w:WideString;
...
s := '1234';
w := s;
PWideChar(w);
потому что в данном случае, насколько я помню, вызовется преобразователь строк в момент w := s.
Почитать - дизассемблированный листинг делфийских программ. Я не видел инфы о внутреннем устройстве делфийских строк в инете. Хотя не особо искал. Но вывод о том, что адрес строки есть адрес именно массива байт можно сделать по исходникам преобразователей в P<чего-нибудь>Char или ручками сделав
Код:
for i := 1 to length(s) do
wirite(AnsiChar(Pointer(s)^));
__________________
jmp $ ; Happy End!
The Cake Is A Lie.

Последний раз редактировалось Bargest, 14.04.2013 в 13:12.
Ответить с цитированием
Этот пользователь сказал Спасибо Bargest за это полезное сообщение:
Uniq! (14.04.2013)
  #12  
Старый 14.04.2013, 13:16
Аватар для Uniq!
Uniq! Uniq! вне форума
Местный
 
Регистрация: 29.09.2010
Сообщения: 539
Версия Delphi: Delphi XE3
Репутация: 374
По умолчанию

Для будущих поколений: ссылка к прочтению, и куча инфы о том, как это всё преобразовывать.

Проблема решена.
Обязательным условием должно быть то, что lpCommandLine - переменная (write acessable), а не константа. Функцией
Код:
UniqueString(CommandLine); 
мы это условие выполняем, а преобразование к PWideChar делаем внутри вызову CreateProcess:

Код:
CreateProcessW(nil, PWideChar(CommandLine), nil, nil, false,
    CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil, si, pi);

Новая проблема: в output и error пусто.

Передаю я ей в качестве CommabdLine следующее:
C:\Ruby200\bin\ruby.exe c:/Uni1.rb

в Unit1.rb лежит
Код:
puts 'hello world'
Вернуть должно hello world


Проблема тут:
Код:
Stream := TMemoryStream.Create;
    try
      while true do
        if ReadFile(hPipeOutputRead, szBuffer, 256, dwNumberOfBytesRead, nil)
        then
          Stream.Write(szBuffer, dwNumberOfBytesRead)
        else
          break;
      Stream.Position := 0;
      Output.LoadFromStream(Stream);
    finally
      Stream.Free;
    end;

Внутрь условия не заходит, т.е. ReadFile возвращает False;причина я так думаю всё в тех же
Код:
szBuffer: array [0 .. 256] of Char;

GetLastError вернул
Код:
ERROR_BROKEN_PIPE
109 (0x6D)

The pipe has been ended.

Разобрался! Ошибка означает, что приложение уже закрылось Т.е. запускать ruby.exe надо с ключём /K
Код:
  ExecConsoleApp('cmd /K ruby c:/Unit1.rb', Output, error);

Последний раз редактировалось Uniq!, 14.04.2013 в 14:08.
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 06:49.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2023

ВКонтакте   Facebook   Twitter