2009-07-29 6 views
3

Я пытаюсь расширить стороннее приложение, чтобы его можно было вызывать через командную строку в дополнение к использованию графического интерфейса Windows (желательно смешанный режим). Это довольно простая программа, которая в основном загружает файл, а затем вы нажимаете кнопку, и она начинает отправлять сетевые пакеты UDP.Добавлена ​​директива {APPTYPE CONSOLE}, и теперь мое приложение работает очень медленно. Перемещение мыши заставляет его работать быстрее

Мне нужно вызвать приложение из другого и хотел бы передать аргумент и должен иметь возможность вернуть ExitCode вызывающему приложению. Из того, что я прочитал, для этого вам нужно добавить директиву компилятора {APPTYPE CONSOLE}.

Я сделал это, и мое приложение работало так, как я этого хотел, за исключением того, что сетевые пакеты замедлялись до обхода. Я обнаружил, что всякий раз, когда я передвигал мышь вокруг формы. То, что скорость передачи сети значительно увеличилась. Я подозреваю, что существует некоторая проблема в очереди сообщений Windows, и перемещение мыши вызывает прерывания, которые, в свою очередь, вызывают обработку очереди сообщений?

Я столкнулся с ошибкой и попробовал вызвать Application.ProcessMessages и PeekMessages в таймере с интервалом 1 мс, и это совсем не помогло. Я нашел в этом user manual for some other application, что он говорит, что Indy 10 поддерживается как в типах APPTYPE CONSOLE, так и в GUI. Честно говоря, это меня просто смущает, поскольку я предполагал, что вся сетевая библиотека будет работать в обоих режимах ... но, как я уже сказал, я не знаком с Delphi.

Я уверен, что проблема изолирована от одной строки в моем приложении, и это означает, включен или нет {APPTYPE CONSOLE} или нет.

У кого-нибудь есть идеи?

Version Info:
Delphi 7 Персональные (Build 4.453)
Инди 9.0.4

ответ

6

Если вы добавили {APPTYPE CONSOLE} к вашему приложению, даже если вы хотите выполнить смешанный режим, вам придется жить с консолью, даже если приложение находится в режиме графического интерфейса. Вы можете, конечно, закрыть консоль, но это вызовет некоторое мерцание и станет для меня немного хакерским.

Вы должны быть в состоянии делать то, что хотите, без консольной программы. Небольшая тестовая программа доказывает, что код выхода может быть считан из программы GUI:

procedure TForm1.Timer1Timer(Sender: TObject); 
begin 
    Close; 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    ExitCode := 42; 
    Timer1.Interval := 1000; 
    Timer1.Enabled := TRUE; 
end; 

Если это выполняется с помощью следующего CMD файла:

@echo off 
start /WAIT project1.exe 
echo %ERRORLEVEL% 

программа показывает свою основную форму в течение 1 секунды , закрывается, и сценарий печатает 42 в окне консоли.

Теперь для захвата вывода - делать это из программы GUI на самом деле проще, чем делать это из консольной программы, если вы разрешаете использовать временный файл. Вам все равно нужно запустить программу с параметром командной строки, так почему бы не дать ей имя временного файла, дождаться завершения приложения, прочитать его в файле и удалить его потом?

+0

@mghie Спасибо за полноту этого сообщения. Первоначально я думал, что ExitCode не работает при использовании {APPTYPE CONSOLE}, но это оказалось ошибкой в ​​моем коде. – blak3r

2

Если вы хотите, чтобы приложение вернуть "ошибка" код не существует никакой необходимости делать это консольное приложение. Вам нужно только установить ExitCode, например.

ExitCode := 10; 

в пакетном файле

@Echo off 
project1 
echo %errorlevel% 

Покажет приложение, а затем отображать 10, когда.

Примечание. Также возможно динамическое создание окна консоли из API окон с помощью AllocConsole или для присоединения с помощью AttachConsole.

Я создал обертку объекта для этого один раз, но больше не имею доступного кода. Из памяти он не поддерживал перенаправление (потому что он мне не нужен).

+0

Я попробовал. Он будет эхо 0. При вызове приложения из командной строки, когда приложение не включает строку {APPTYPE CONSOLE}, оно немедленно возвращается. Однако ... Если может работать «start/wait ». Я попробую завтра. – blak3r

+0

@Gerry Я дал вам +1 за то, что первым предложил подход ExitCode. Я думал, что ответ mghie был немного более полным, поэтому я выбрал его как принятое решение. Благодарим за вклад! – blak3r

0

Таймер с 1 мс будет срабатывать только каждые 40 мс (из-за ограничений Windows), поэтому он не поможет. Я видел такие эффекты, как вы описываете, со смешанными консольными и графическими приложениями, а другой - с тем, что они не сводят к минимуму.

Вместо включения консоли в проекте вы, вероятно, можете использовать вызов API CreateConsole (не уверен, правильно ли это имя), чтобы создать его после запуска программы. Я не видел никаких побочных эффектов в одной (!) Программе, которую я сделал.

Но это необходимо, только если вы хотите писать на консоль.Если вы хотите обрабатывать параметры командной строки и возвращать код выхода, вам не нужна консоль. Просто оцените параметры ParamCount/ParamStr для параметров и установите ExitCode для возвращаемого значения.

+0

@dummzeuch спасибо за предложение. См. Комментарии, которые я только что добавил в сообщение Джерри. Я надеялся использовать реальное окно консоли, чтобы вызывающее приложение могло захватить его стандартный вывод. Что касается таймера, я думаю, что даже работает на 40 мс, это немного улучшило бы время ... но это не так. Каждый образец занял 14-е. Если бы это сработало, я, вероятно, добавил бы 20-40 таймеров в качестве обходного пути ха-ха. Но, поскольку это не так, я думаю, что это ключ к тому, что Application.ProcessMessages не проблема для начала ... – blak3r

+1

И таймер работает из-за сообщения Windows, поэтому смешно пытаться управлять насосом сообщений реагируя на сообщения от того же насоса сообщений. –

+0

@Lars Truijens - +1, когда это так нелепо. – blak3r

0

Если некоторые темы в приложении консоли называют Синхронизировать (и я думаю, Инди материал на самом деле делает это), вы должны сделать некоторые приготовления:

Назначьте метод к переменной WakeMainThread. Этот метод должен иметь подпись TNotifyEvent.

Внутри этого метода вызывают CheckSynchronize.

Дополнительную информацию см. В помощи Delphi для этих двух элементов.

+0

@Uwe Raabe - я закончил тем, что реализовал его более легко. Кто-то может выиграть от этого, так что спасибо за вклад. Я сделал очень быстрый поиск того, как это сделать, и не натолкнулся ни на что, поэтому, если вы ... может быть хорошей идеей разместить его для кого-то другого. – blak3r

1

Если я вас правильно понял, вы хотите, чтобы ваше приложение имеет два режима:

  1. Если аргумент не передается, работать в графическом режиме
  2. Run в режиме без графического интерфейса в противном случае

Проще всего, если вы можете централизовать свою логику, чтобы ее можно было вызвать по одному методу (CoreLogic в моем примере).

Нижеприведенное приложение должно работать нормально.

Два приемы:

  1. Application.ShowMainForm: = False;, который не будет показывать MainForm вообще.
  2. ExitCode: = 327;, который установит ваш код возврата (например, mghie и Gerry уже упоминалось).

Несколько замечаний:

  • потому что CoreLogic не обрабатывает окна сообщений, ничего в своем приложении зависит от сообщений Windows, которые обрабатываются заглохнет.
  • если вам нужны окна обработки сообщений, то просто все Application.ProcessMessages() внутри вашего CoreLogic
  • если вам нужна ваша форма, чтобы быть видимыми, то изменить логику внутри MainForm для проверки параметров командной строки, и выйдите, когда это будет сделано (по телефону Application.Terminate()). Лучшим местом для ввода этой логики является метод события для события MainForm.OnShow.

Надеется, что это помогает :-)

program VCLAppThatDoesNotShowMainForm; 

uses 
    Forms, 
    MainFormUnit in 'MainFormUnit.pas' {MainForm}, 
    Windows; 

{$R *.res} 

procedure CoreLogic; 
begin 
    Sleep(1000); 
    ExitCode := 327; 
end; 

procedure TestParams; 
begin 
    if ParamCount > 0 then 
    begin 
    MessageBox(0, CmdLine, PChar(Application.Title), MB_ICONINFORMATION or MB_OK); 
    CoreLogic(); 
    Application.ShowMainForm := False; 
    end; 
end; 

begin 
    Application.Initialize(); 
    Application.MainFormOnTaskbar := True; 
    TestParams(); 
    Application.CreateForm(TMainForm, MainForm); 
    Application.Run(); 
end.