CreateProcess из DLL убивает родителя

Вадиман

Привет всем!Подскажите, пожалуйста, что не так делаю... описание CreateProcess уже до дыр прочитал :(. В общем, есть прога, которая вызывает функцию в DLL:
<b>procedure</b> frmv_Exec(ExecFilename: PChar); cdecl;
<b>Var</b>
 Si: TStartupInfo;
 Pi: TProcessInformation;

<b>begin</b>
 FillMemory(@si, sizeof(STARTUPINFO), <b>0</b>);
 si.cb := sizeof(STARTUPINFO);
 si.dwFlags := STARTF_USESHOWWINDOW;
 Si.wShowWindow := SW_MAXIMIZE;
 <b>if</b> CreateProcess(<b>nil</b>, ExecFilename, <b>nil</b>, <b>nil</b>, False, CREATE_NEW_CONSOLE, <b>nil</b>, <b>nil</b>, Si, Pi) <b>then</b> 
 WaitForSingleObject(Pi.HProcess, Infinite);

<b>end</b>;
WaitForSingleObject на проблему не влияет, проверил. Так вот, как только управление возвращается в программу (т.е. после всей процедурки в DLL), прога закрывается. И есть ощущение, что она закрывается не в аварийном режиме (типа, со стеком косяки или еще че-нибудь), а совершенно нормально, как будто ей послали соответствующее сообщение. Пробовал уже и через ShellExecute - эффект тот же.Что не так?
8 ответов

Вадиман

cdecl;


Вадиман

хотя фиг знает... сделал так:
<b>library</b> Project2;

<i>{ Important note about DLL memory management: ShareMem must be the
 first unit in your library's USES clause AND your project's (select
 Project-View Source) USES clause if your DLL exports any procedures or
 functions that pass strings as parameters or function results. This
 applies to all strings passed to and from your DLL--even those that
 are nested in records and classes. ShareMem is the interface unit to
 the BORLNDMM.DLL shared memory manager, which must be deployed along
 with your DLL. To avoid using BORLNDMM.DLL, pass string information
 using PChar or ShortString parameters. }</i>

<b>uses</b>
 SysUtils,
 Classes,windows;
<i>{$R *.res}</i>
<b>procedure</b> frmv_Exec(ExecFilename: PChar); cdecl;

<b>Var</b>

 Si: TStartupInfo;

 Pi: TProcessInformation;


<b>begin</b>

 FillMemory(@si, sizeof(STARTUPINFO), <b>0</b>);

 si.cb := sizeof(STARTUPINFO);

 si.dwFlags := STARTF_USESHOWWINDOW;

 Si.wShowWindow := SW_MAXIMIZE;

 <b>if</b> CreateProcess(<b>nil</b>, ExecFilename, <b>nil</b>, <b>nil</b>, False, CREATE_NEW_CONSOLE, <b>nil</b>, <b>nil</b>, Si, Pi) <b>then</b> 

 WaitForSingleObject(Pi.HProcess, Infinite);


<b>end</b>;
<b>exports</b>
 frmv_Exec name 'frmv_Exec';
<b>begin</b>
<b>end</b>.
и
<b>unit</b> Unit1;

<b>interface</b>

<b>uses</b>
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls;

<b>type</b>
 TForm1 = <b>class</b>(TForm)
 Button1: TButton;
 <b>procedure</b> Button1Click(Sender: TObject);
 <b>private</b>
 <i>{ Private declarations }</i>
 <b>public</b>
 <i>{ Public declarations }</i>
 <b>end</b>;
<b>procedure</b> frmv_Exec(ExecFilename: PChar); cdecl; external 'project2.dll' name 'frmv_Exec';

<b>var</b>
 Form1: TForm1;

<b>implementation</b>

<i>{$R *.dfm}</i>

<b>procedure</b> TForm1.Button1Click(Sender: TObject);
<b>begin</b>
frmv_Exec('notepad');
<b>end</b>;

<b>end</b>.
все нормально работает... если в самоый проге убрать cdecl то ессно вообще ниче не работает.


Вадиман

Может я и туплю, но я не понял смысла проблемы. Какого родителя? И где ты это определяешь и как?


Вадиман

Есть программа, написанная на Oracle Forms 6i. Там есть проблемы с реализацией некоторых моментов... просто отсутствует механизм. Например, надо запустить программу. Там есть пара функций, но что они делают? Они запускают процесс и тут же передают управление дальше. А мне, допустим, надо разархивировать архив и потом что-нибудь сделать с его содержимым. Я запускаю команду (что-то наподобе " unrar32 ...."; неважно), и пошла разахривация... НО ПОКА ОНА ИДЕТ, управление уже передалось следующей команде, которая стоит после вызова WIN_API_SHELL.WinExec() ! В итоге, я понятия не имею, закончилась работа того процесса или нет. Это порождает массу неудобств или просто откровенных косяков. А ставить команду ожидания, скажем, 5 секунд - ну согласитесь, это маразм. А если тормознет... тогда 10 секунд? А если зависнет... может, тогда сразу полминуты?Вто я и решил сделать внешнюю подобную функцию, только добавить туда WaitForSingleObject. В Oracle Forms есть возможность подключить DLL через некий интерфейс и заюзать ее функции. А что там будет - да что угодно. Весь API в вашем распоряжении. То есть, пишется библиотека на PL/SQL (с расширением PLL), там регистрируется внешняя функция, находящаяся в такой-то DLL. То бишь, для работы библиотеки Forms потребуется моя DLL... ну, это нормально.Этот механизм рабочий, документированный. Я уже массу других функций импортировал таким образом. В частности, в Forms вообще не было аналога FindFirst и FindNext. Невозможно было удалить файлы по маске. Ну и т.д. Все это сделал, работает. А вот с этой функцией запуска... вызываю функцию FRMV.frmv_exec в библиотеке PLL, она вызывает внешнюю функцию frmv_exec() из DLL, указанный в качестве параметра файл запускается, все ок! Ожидаем его завершения. Но как только отрабатывает функция frmv_exec() в DLL, закрывается мое приложение на Oracle Forms. Причем, такое ощущение, что закрывается совершенно нормально (легально, так сказать). Были моменты в процессе отладки, когда забыл указать cdecl, накосячил с типом параметров и т.д... на стеке не понять что творилось... и после возврата управления в прогу на Oracle Forms винда выводила exception и закрывала ее. Записывались дампы памяти, логи... ну, словом, все как при обычном "зависоне" или access violation. А здесь - ничего нет! Такое ощущение, будто я в frmv_exec() вызвал что-то наподобе CloseHandle(). Но, как видите, ничего такого нет. Если убрать WaitForSingleObject - то указанный EXE-шник запускается, управление тут же возвращается в Oracle Forms, и Oracle Forms закрывается. А запущенный EXE-шник висит.Че делать... :(


Вадиман

хотя фиг знает... сделал так:
<b>library</b> Project2;

<i>{ Important note about DLL memory management: ShareMem must be the
 first unit in your library's USES clause AND your project's (select
 Project-View Source) USES clause if your DLL exports any procedures or
 functions that pass strings as parameters or function results. This
 applies to all strings passed to and from your DLL--even those that
 are nested in records and classes. ShareMem is the interface unit to
 the BORLNDMM.DLL shared memory manager, which must be deployed along
 with your DLL. To avoid using BORLNDMM.DLL, pass string information
 using PChar or ShortString parameters. }</i>

<b>uses</b>
 SysUtils,
 Classes,windows;
<i>{$R *.res}</i>
<b>procedure</b> frmv_Exec(ExecFilename: PChar); cdecl;

<b>Var</b>

 Si: TStartupInfo;

 Pi: TProcessInformation;


<b>begin</b>

 FillMemory(@si, sizeof(STARTUPINFO), <b>0</b>);

 si.cb := sizeof(STARTUPINFO);

 si.dwFlags := STARTF_USESHOWWINDOW;

 Si.wShowWindow := SW_MAXIMIZE;

 <b>if</b> CreateProcess(<b>nil</b>, ExecFilename, <b>nil</b>, <b>nil</b>, False, CREATE_NEW_CONSOLE, <b>nil</b>, <b>nil</b>, Si, Pi) <b>then</b> 

 WaitForSingleObject(Pi.HProcess, Infinite);


<b>end</b>;
<b>exports</b>
 frmv_Exec name 'frmv_Exec';
<b>begin</b>
<b>end</b>.
и
<b>unit</b> Unit1;

<b>interface</b>

<b>uses</b>
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls;

<b>type</b>
 TForm1 = <b>class</b>(TForm)
 Button1: TButton;
 <b>procedure</b> Button1Click(Sender: TObject);
 <b>private</b>
 <i>{ Private declarations }</i>
 <b>public</b>
 <i>{ Public declarations }</i>
 <b>end</b>;
<b>procedure</b> frmv_Exec(ExecFilename: PChar); cdecl; external 'project2.dll' name 'frmv_Exec';

<b>var</b>
 Form1: TForm1;

<b>implementation</b>

<i>{$R *.dfm}</i>

<b>procedure</b> TForm1.Button1Click(Sender: TObject);
<b>begin</b>
frmv_Exec('notepad');
<b>end</b>;

<b>end</b>.
все нормально работает... если в самоый проге убрать cdecl то ессно вообще ниче не работает.
Сорри, честно говоря, не понял... а чего ты написал такого, чего не написал я? :)Ну, разве что не привел пример, как я пользуюсь вызовом функции... но, как писал, это не Delphi, а Oracle Forms


Вадиман

А точно функция должна быть cdecl а не stdcall ?


Вадиман

А разве способ организации стека как-то может повлиять на мою проблему?Главное, чтобы функция, объявленная в DLL, была имплементирована с таким же модификатором и в Oracle Forms. Насколько знаю, Forms поддерживает два способа - CDECL и PASCAL. Но в случае объявления как PASCAL были глюки даже при передаче указателя на строку как параметр к функциям. Поэтому остановился на CDECL, здесь, по крайней мере, все документировано в Forms'е.


Вадиман

Блин... большое сорри :(Закрываю тему, сам накосячил. В DLL все было правильно (ну, разве что хэндлы процессов не позакрывал в конце :) ), некорректно объявил параметр к внешней функции в Oracle Forms. Всем большое "извините за беспокойство"! :)