|
Curl, командная строка и анализ ответа
#39573717
Ссылка:
Ссылка на сообщение:
Ссылка с названием темы:
|
|
|
|
Имеется следующий код, выполняющий отправку алкоголя в ЕГАИС при помощи Curl:
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129.
const
cSendToUTM = '%s -F "xml_file=@%s" %s/xml';
var
CurlPath, XMLPath, UTM_URL: string //Пути соответственно к Curl.exe и отправляемому XML-файлу, и URL, куда отправляем
function SendToUTM: boolean;
const
XmlSign = '<?xml';
var BatStr, SendResult: string;
XMLPos: integer;
begin
//Отправка чека в УТМ при помощи Curl
BatStr := Format(cSendToUTM, [CurlPath, XMLPath, UTM_URL]);
SendResult := CreateAndRunBatFile(ExtractFilePath(Application.ExeName) + 'SendToUTM.bat', BatStr, True);
XmlPos := Pos(XmlSign, SendResult);
if XmlPos > 0 then begin
{В ответе содержится XML-файл, все ОК, обработка и печать
...
}
Result := true;
end
else begin
{Обработка ошибок
...
}
Result := false;
end;
end;
function CreateAndRunBatFile(FilePath, BatStr: string; FromUTF: Boolean = False): string;
var SList: TStringList;
ResultStrDos: PChar;
begin
SList := TStringList.Create;
SList.Text := BatStr;
if FileExists(FilePath) then
DeleteFile(FilePath);
SList.SaveToFile(FilePath);
ResultStrDos := PChar(GetDosOutput(FilePath));
//Здесь спорный момент:
if FromUTF then begin
Result := UTF8Decode(ResultStrDos);
end
else begin
OemToAnsi(ResultStrDos, ResultStrDos);
Result := ResultStrDos;
end;
SList.Free;
DeleteFile(PChar(FilePath));
end;
function GetDosOutput(const CommandLine: string): string;
{Данная функция позаимствована где-то на просторах Интернета и включена практически без изменений}
var
SA: TSecurityAttributes;
SI: TStartupInfo;
PI: TProcessInformation;
StdOutPipeRead, StdOutPipeWrite: THandle;
WasOK: Boolean;
Buffer: array[0..255] of Char;
BytesRead: Cardinal;
WorkDir, Line: string;
begin
with SA do
begin
nLength := SizeOf(SA);
bInheritHandle := True;
lpSecurityDescriptor := nil;
end;
// создаем пайп для перенаправления стандартного вывода
CreatePipe(StdOutPipeRead, // дескриптор чтения
StdOutPipeWrite, // дескриптор записи
@SA, // аттрибуты безопасности
0 // количество байт принятых для пайпа - 0 по умолчанию
);
try
// Создаем дочерний процесс, используя StdOutPipeWrite в качестве стандартного вывода,
// а так же проверяем, чтобы он не показывался на экране.
with SI do
begin
FillChar(SI, SizeOf(SI), 0);
cb := SizeOf(SI);
dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
wShowWindow := SW_HIDE;
hStdInput := GetStdHandle(STD_INPUT_HANDLE); // стандартный ввод не перенаправляем
hStdOutput := StdOutPipeWrite;
hStdError := StdOutPipeWrite;
end;
// Запускаем компилятор из командной строки
WorkDir := ExtractFilePath(CommandLine);
WasOK := CreateProcess(nil, PChar(CommandLine), nil, nil, True, 0, nil, PChar(WorkDir), SI, PI);
// Теперь, когда дескриптор получен, для безопасности закрываем запись.
// Нам не нужно, чтобы произошло случайное чтение или запись.
CloseHandle(StdOutPipeWrite);
// если процесс может быть создан, то дескриптор, это его вывод
if not WasOK then
raise Exception.Create('Ошибка создания процесса')
else
try
// получаем весь вывод до тех пор, пока DOS-приложение не будет завершено
Line := '';
repeat
WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
// есть ли что-нибудь еще для чтения?
if BytesRead > 0 then
begin
// завершаем буфер PChar-ом
Buffer[BytesRead] := #0;
// добавляем буфер в общий вывод
Line := Line + Buffer;
end;
until not WasOK or (BytesRead = 0);
// ждем, пока завершится консольное приложение
WaitForSingleObject(PI.hProcess, INFINITE);
finally
// Закрываем все оставшиеся дескрипторы
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
finally
result := Line;
CloseHandle(StdOutPipeRead);
end;
end;
Работает с июля прошлого года, проблем почти не доставлял. Но сейчас, в связи с последними изменениями по УТМ, в сообщения об ошибках могут включаться ключевые фразы, содержащие русские символы, которые нужно анализировать. При том что сам ответ УТМ отправляет в формате UTF. Именно по этой причине в функции CreateAndRunBatFile появился параметр FromUTF. Одна такая ситуация с русскими символами уже имела место (у клиента на боевом УТМ), но тогда параметра FromUTF еще не было, и расшифровать ответ так и не смогли. В конце концов пришли к выводу, что портит все преобразование OemToAnsi, но это только предположение.
Собственно вопрос по функции CreateAndRunBatFile: достаточно ли будет в случае UTF преобразования UTF8Decode, или же все-таки его необходимо применять до/после OemToAnsi ? Смоделировать ситуацию искусственно не представляется возможным по причине отсутствия тестового УТМ, есть только боевой у клиентов, поэтому экспериментировать придется "по живому".
|
|
|