![]() |
|
|
|||||||
| Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
![]() |
|
|
Опции темы | Поиск в этой теме | Опции просмотра |
|
#1
|
||||
|
||||
|
Ребят, только не бросайтесь фразами: "А уже дофига всяких IDE, зачем тратить время, силы бла бла бла всё равно лучше не сделаешь"
Я для себя. Просто хочу разобраться как эти программы работают. Значит проблема следующая: как отдать файл компилятору (интерпретатору), и вернуть результат в виде ошибок, если они есть. Я так понимаю командная строка + CreateProcess? Или есть более элегантные решения? Последний раз редактировалось Uniq!, 13.04.2013 в 22:54. |
|
#2
|
||||
|
||||
|
Как-то обертку над консольным php.exe делал, результаты через пайпы затягивать приходилось. А рубивский компиль какой?
|
|
#3
|
||||
|
||||
|
Он не компилер, он интерпретатор.
По факту своя ком строка, которая понимает синтаксис 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
|
|||
|
|||
|
если только для руби, и если https://github.com/charliesome/better_errors работает под windows, то можно попробовать создать в той папке временный файл, примерно как тот, что там в секции Usage, но в get запускающий тот файл, который должен выкинуть ошибку, запустить временный файл, и webbrowser.navigate(туда). если выскочит ошибка, то по идее должна отоброзится в webbrowser
|
|
#5
|
||||
|
||||
|
Цитата:
Код:
...PWideChar('/k ruby "' + rSave.FileName + '"'), ...Цитата:
Последний раз редактировалось Bargest, 14.04.2013 в 12:15. |
|
#6
|
||||
|
||||
|
Я же написал "символами типа"
![]() Проблема не решена, но... Я за ночь докопался вот до такой процедуры: Код:
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 ошибка: (вложения) |
|
#7
|
||||
|
||||
|
Ну env тут явно лишний. Но это не ошибка.
Какая делфя? Судя по юзанию AnsiString, скорее что-то после 10й. В таком случае нехорошо преобразовывать AnsiString к PChar. Тогда уж к PAnsiChar, или строку юзать (Wide)String. Но вообще взятие в кавычки должно было все решить. Параметры в кавычках передаются CMD в прогу без изменений как один параметр. |
|
#8
|
||||
|
||||
|
@env - уже убрал, зачем передавать указатель на левые переменные o.O ?
Ошибка сейчас ( как можно заметить ) не в текстовых преобразованиях, а в доступе к kernel32.dll ибо в неё "записать" не может. Delphi XE3 |
|
#9
|
||||
|
||||
|
Ошибка возникает внутри kernel32 (большой адрес), но при попытке записать в маленькие адреса (write of address 5accc0). Так что это не запись В kernel32, это запись по недоступному адресу ИЗ нее.
С МСДНа: Цитата:
Последний раз редактировалось Bargest, 14.04.2013 в 12:51. |
| Этот пользователь сказал Спасибо Bargest за это полезное сообщение: | ||
Uniq! (14.04.2013)
| ||
|
#10
|
||||
|
||||
|
Вот собственно всё и прояснилось. Действительно ошибки из-за преобразований туда сюда...
Я в этом полный ноль, особенно когда речь идёт о разных версиях Delphi. Сама команда CreateProcess должна принимать PWideChar (по документации), я же ей передаю нечто в одинарных кавычках (это какой тип? ) и как мне от него к PWideChar грамотно перейти. И за одно чего-нибудь почитать дайте, пожалуйста. |
|
#11
|
||||
|
||||
|
Нечто в кавычках - это делфовая строка. Преобразование в 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); Почитать - дизассемблированный листинг делфийских программ. Я не видел инфы о внутреннем устройстве делфийских строк в инете. Хотя не особо искал. Но вывод о том, что адрес строки есть адрес именно массива байт можно сделать по исходникам преобразователей в P<чего-нибудь>Char или ручками сделав Код:
for i := 1 to length(s) do wirite(AnsiChar(Pointer(s)^)); Последний раз редактировалось Bargest, 14.04.2013 в 13:12. |
| Этот пользователь сказал Спасибо Bargest за это полезное сообщение: | ||
Uniq! (14.04.2013)
| ||
|
#12
|
||||
|
||||
|
Для будущих поколений: ссылка к прочтению, и куча инфы о том, как это всё преобразовывать.
Проблема решена. Обязательным условием должно быть то, что lpCommandLine - переменная (write acessable), а не константа. Функцией Код:
UniqueString(CommandLine); Код:
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' ![]() Проблема тут: Код:
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. |