( تعداد نمایش : 2462 )

اجرای فایل exe از درون دلفی

یکی از مشکلاتی که بسیاری از تازه کاران در دلفی با آن مواجه می شوند اجرای فایلهای دیگر یا اجرای دستورات shell است.

در این مقاله سعی ما بر این است که شما را با اجرای فایلهای دیگر از درون دلفی و کنترل آنها آشنا کنیم. شما پس از خواندن این مقاله خواهید توانست به راحتی فایلهای دیگر -و نه تنها exe- را از درون دلفی اجرا کنید و حتی دستورات shell را نیز صادر کنید. به عنوان مثال ممکن است بخواهید یک فایل html را با Editor پیش فرض کاربر باز کنید. یا یک فایل html را با Browser پیش فرض کاربر باز کنید. یا شاید بخواهید یک فایل BMP را با ادیتور پیش فرض کاربر باز نمایید و یا این که یک فایل INF را نصب نمایید.

با خواندن این مقاله شما می توانید به راحتی به تمامی این اهداف برسید.
تابع APIی وجود دارد به نام WinExec. شکل کلی این تابع از این قرار است:

WinExec(lpCmdLine: PAnsiChar; uCmdShow: Cardinal);

که lpCmdLine خط دستور مورد نظر شما برای اجرای فایل و uCmdShow باید یکی از مقادیر زیر را داشته باشد:

SW_HIDE SW_MAXIMIZE SW_MINIMIZE
SW_RESTORE SW_SHOW SW_SHOWDEFAULT
SW_SHOWMAXIMIZED SW_SHOWMINIMIZED SW_SHOWMINNOACTIVE
SW_SHOWNA SW_SHOWNOACTIVATE SW_SHOWNORMAL

برای توضیحات بیشتر راجع به هر کدام از این ثابت ها به راهنمای Windows SDK دلفی یا MSDN مراجعه کنید.

تابع WinExec تابع بسیار قدیمی و محدودی است و ما قصد نداریم در این مقاله بیشتر از این راجع به این تابع صحبت کنیم.

تابع دیگری وجود دارد به نام ShellExecute. این تابع در یونیت ShellAPI تعریف شده است. بنابر این لازم است یونیت ShellAPI را در لیست uses یونیت خود وارد کنید. این یکی از توابع بسیار قدرتمند برای اجرای فایل است.
نگاهی به شکل کلی این تابع بیاندازید:

function ShellExecute(hWnd: HWND; Operation, FileName, Parameters, Directory: PChar; ShowCmd: Integer): HINST; stdcall;
hWnd Handle پنجره ای که این دستور را فراخوانده است.
Operation نوع دستوری مورد نظر جهت اجرا
FileName نام فایل یا شاخه
Parameters پارامترهای مورد نظر در هنگام اجرای فایل exe
Directory شاخه پیش فرض در هنگام اجرای فایل
ShowCmd مشخص کننده چگونی نمایش فایل در هنگام اجرا

پارامتر اول یک متغییر از نوع HWND است.
لازم است برای کسانی که با مفهوم Handle در ویندوز آشنا نیستند توضیحاتی راجع به Handle بدهم. هر پنجره یا آبجکتی در ویندوز دارای یک Handle است که برای دسترسی به آن پنجره یا آبجکت شما باید از این Handle استفاده کنید. در واقع یک Handle یک عدد در مبنای ۱۶ است. Handle یک عدد unique یا همتا است که ویندوز آن را مقداردهی می کند. اگر از یک پنجره دو Instance اجرا شده باشد (مثلا یک برنامه دو بار اجرا شده باشد) هر کدام از این Instanceها یک Handle جداگانه دارند.
با این تفاصیل پارامتر اول Handle پنجره ای است که این دستور را صادر کرده است. شما برای این پارامتر می توانید از Application.Handle استفاده کنید و یا آن را برابر ۰ قرار دهید. به علاوه می توانید Handle یک برنامه دیگر را بدهید.

در صورتی که شما می خواهید پیغام های اخطار آن فایل را دریافت کنید یا آن را کنترل نمایید و یا تا اجرای کامل آن اجرای برنامه را متوقف کنید با Handle برنامه خود را با استفاده از Application.Handle به این پارامتر بدهید.

پارامتر دوم مشخص کننده وظیفه ای است که قرار است انجام شود. این پارامتر مقادیر پیش فرضی ندارد و بستگی به خصوصیات فایل اجرایی دارد. روی یک فایل از نوع Text کلید سمت راست ماوس را بزنید. احتمالا موارد بالای لیست “open”، “Edit with …” , “print” است. هر کدام از این رشته های می توانند یک عملیات یا Operation باشند. به عنوان مثال شما می خواهید یک فایل Text را چاپ کنید. در این صورت کافی است از عبارت print به عنوان operation استفاده کنید. یا می خواهید یک فایل rar را با استفاده از WinRar باز کنید. در این صورت می توانید از “Extract files” استفاده کنید. حتما تا به حال متوجه شده اید که دستور ShellExecute چه مقدار انعطاف پذیر است. با استفاده از این فرمان می توانید هر گونه دستور Shell را اجرا نمایید.

پارامتر سوم مشخص کننده نام فایل یا شاخه ای است که شما می خواهید عملیات بر روی آن انجام شود.
پارامتر چهارم لیست پارامترهایی است که تمایل دارید فایل exe با این پارامترها اجرا شود. پارامترها پنجم نیز نام شاخه پیش فرض در هنگام اجرای فایل مورد نظر شماست. اگر شما فایل exe ای را اجرا کنید و این فایل exe بخواهد از شاخه جاری فایلهای اضافه ای را استفاده کند ویندوز شاخه جاری را به اون مطابق با این شاخه اطلاع خواهد داد. و اما آخرین پارامتر مشخص کننده شکل اجرای فایل است. مقدار این پارامتر می تواند یکی از ثابت هایی لیستی باشد که در ابتدای این مقاله عنوان شد. به عنوان مثال شما می توانید از SW_HIDE استفاده کنید که در این صورت فایل اجرای شما مخفی خواهد بود و یا از SW_SHOWMINIMIZE استفاده کنید که در این صورت برنامه شما Minimizeشده اجرا می شود.

و حالا به یک نکته خیلی مهم توجه کنید:
۱- رشته های این تابع از نوع PChar هستند بنابراین شما باید رشته های string را به صورت PChar به این تابع بدهید. شما می توانید به طور عادی رشته مورد نظر خود را به این تابع بدهید و یا در صورتی که رشته مورد نظر شما string است باید با استفاده با استفاده از دستور PChar آن را Typecast کنید. به عنوان مثال:

ShellExecute(0, 'open', PChar(ExtractFilePath(Application.ExeName) + 'test.exe') , '', '', SW_SHOWNORMAL);

در این مثال با استفاده از تابع ExtractFilePath و Application.ExeName که حاوی آدرس کامل فایل Exe است شاخه ای که فایل exe در آن قرار دارد را پیدا کرده ایم و سپس فایل test.exe را که در کنار فایل اصلی اجرایی وجود دارد را به آن اضافه کرده ایم. کل این عبارت TypeCast شده است به PChar.
جهت اطلاع کسانی که نمی دانند TypeCast چیست. TypeCast فرآیندی است که شما متغییر یا Objectی را از یک نوع به نوع دیگری تبدیل میکنید. کد زیر را نگاه کنید و با نوع پیشرفته تری از TypeCast آشنا شوید:

procedure TForm1.Button1Click(Sender: TObject); begin TButton(Sender).Caption := 'Test'; end;

در این مثال Sender را از نوع TObject است TypeCast کرده ایم به TButton. و پراپرتی Caption آنرا تغییر داده ایم. برای اطلاعات بیشتر راجع به TypeCast به کتب دلفی مراجعه کنید.

و اجازه دهید به چند مثال جالب نیز نگاهی بیاندازیم:
edit کردن یک فایل HTML با Editor پیش فرض HTML:

ShellExecute(Handle, 'edit', 'test.htm', '', '', SW_SHOW);

نصب یک فایل INF

ShellExecute(Handle, 'install', 'divx.inf', '', '', SW_SHOW);

فشرده ساختن یک فایل با استفاده از winrar و ارسال آن به ایمیل:

ShellExecute(Handle, 'compress and mail...', 'test.file', '', '', SW_SHOW);

ارسال ایمیل به mamouri@ganjafzar.com و با موضوع “Great Article”:

ShellExecute(0, 'open', 'mailto:mamouri@ganjafzar.com?subject=GreatArticle', '', '', SW_SHOWNORMAL);

چگونه یک فایل exe را اجرا کنیم و تا اتمام آن برنامه را متوقف کنیم؟

uses ShellAPI;
...
function ExecAndWait(const ExecuteFile, ParamString : string): boolean;
var
SEInfo: TShellExecuteInfo;
ExitCode: DWORD;
begin
FillChar(SEInfo, SizeOf(SEInfo), 0);
SEInfo.cbSize := SizeOf(TShellExecuteInfo);
with SEInfo do
begin
fMask := SEE_MASK_NOCLOSEPROCESS;
Wnd := Application.Handle;
lpFile := PChar(ExecuteFile);
lpParameters := PChar(ParamString);
nShow := SW_HIDE;
end;
if ShellExecuteEx(@SEInfo) then
begin
repeat
Application.ProcessMessages;
GetExitCodeProcess(SEInfo.hProcess, ExitCode);
until (ExitCode <> STILL_ACTIVE) or
Application.Terminated;
Result:=True;
end
else
Result:=False;
end;

همان طور که ملاحظه می کنید این تابع به یونیت ShellAPI نیاز دارد و باید این یونیت را در لیست uses یونیت خود اضافه کنید.
در این تابع از تابع دیگری به نام ShellExecuteEx استفاده شده است. این تابع بر خلاف ShellExecute فقط یک پارامتر دارد که باید برابر متغییری از نوع TShellExecuteInfo قرار بدهید. در ابتدا باید با استفاده از تابع FillChar آنرا مقداردهی کنید و وجود آنرا به ویندوز اطلاع دهید. در واقع آن را Create کنید:

FillChar(SEInfo, SizeOf(SEInfo), 0); SEInfo.cbSize := SizeOf(TShellExecuteInfo);

آبجکت ShellExecuteEx دارای پارامترهای زیر است:

cbSize: DWORD;
fMask: ULONG;
Wnd: HWND;
lpVerb: PAnsiChar;
lpFile: PAnsiChar;
lpParameters: PAnsiChar;
lpDirectory: PAnsiChar;
nShow: Integer;
hInstApp: HINST;

برای اطلاعات بیشتر راجع به این پارامترها به راهنمای Windows SDK دلفی یا MSDN مراجعه کنید. خروجی این تابع از نوع boolean است و مشخص کننده این است که آیا این تابع با موفقیت اجرا شده است یا نه؟
تابع ExecAndWait پس از این که اطمینان پیدا کرد که فایل با موفقیت اجرا شده است یک حلقه repeat..until تشکیل داده. در داخل repeat دستور Application.ProcessMessage صادر شده است تا برنامه بتواند messageها را دریافت کند. سپس با استفاده از GetExitCodeProcess مقدار خروجی پروسس اجرا شده دریافت می گردد. در صورتی که خروجی این تابع مخالف STILL_ACTIVE بود (که نشانگر اجرای پروسس است) حلقه با کار خود ادامه می دهد.

خروجی این تابع مشخص کننده اجرا یا عدم اجرای فایل اجرایی است.

و حالا به چند مثال جالب دیگر توجه کنید:
۱- دسترسی به HotMail از درون دلفی:

program dummy;
var ToAddress: string;
EightSpaces: string;
begin
ToAddress := 'john@pacbell.net';
// Don't know why but this is required to get the
// correct compose address...
EightSpaces := ' ';
ShellExecute(Handle, PChar('open'), PChar('rundll32.exe'), PChar('C:\PROGRA~1\INTERN~1\HMMAPI.DLL,MailToProtocolHandler' + EightSpaces + ToAddress), nil, SW_NORMAL)
end.

2- نمایش دیالوگ مشخصات یک فایل:

procedure ShowPropertiesDialog(Filename: string);
var
SEI: TShellExecuteInfo;
begin
FillChar(SEI, SizeOf(SEI), 0);
with SEI do
begin
cbSize := SizeOf(SEI);
lpFile := PChar(Filename);
lpVerb := 'properties';
fMask := SEE_MASK_INVOKEIDLIST;
end;
ShellExecuteEx(@SEI);
end;

3- اجرای دیالوگ Screen ویندوز (Control Panel > Display)

ShellExecute(HInstance, nil, PCHAR('rundll32.exe'), PCHAR('shell32.dll, Control_RunDLL desk.cpl, , 3') { 3 is the tab index }, NIL, 1);

همان طور که متوجه شدید ۳ شماره Tabی است که مورد نظر شماست.

۴- یک مثال کامل تر از فرستادن ایمیل با استفاده از Outlook یا ارسال کننده پیش فرض email:

var
mail: string;
begin
mail := 'mailto:you@you.com' +
'?subject=hello' +
'&cc=me@me.com' +
'&body=Delphi is cool! icon wink اجراي فايل exe از درون دلفي ';
ShellExecute(Self.Handle, 'open', PChar(mail), nil, nil, SW_SHOWNORMAL);

خروجی های تابع ShellExecute یا ShellExecuteEx

خروجی های این دو تابع می تواند یکی از مقادیر زیر باشد:

۰ سیستم عامل دارای resourceهای کافی یا حافظه کافی جهت اجرا نیست.
ERROR_FILE_NOT_FOUND فایل مورد نظر پیدا نشد.
ERROR_PATH_NOT_FOUND آدرس مشخص شده پیدا نشد.
ERROR_BAD_FORMAT فایل EXE نامعتبر است یا این یک فایل EXE از نوع Win32 نیست.
SE_ERR_ACCESSDENIED سیستم عامل دسترسی به فایل مشخص شده ندارد.
SE_ERR_ASSOCINCOMPLETE association فایل مورد نظر شما ناقص یا نامعتبر است. مثلا مشخص نشده که فایل bmp که شما می خواهید آن را اجراه کنید باید با چه برنامه ای باز شود.
SE_ERR_DDEBUSY DDE transaction مربوطه کامل نشد زیرا DDE transactionهای دیگری در حال اجرا بودند.
SE_ERR_DDEFAIL DDE transaction ناموفق بود.
SE_ERR_DDETIMEOUT DDE transaction نتوانست اجرا شود زیرا درخواست Time Out شد.
SE_ERR_DLLNOTFOUND فایل dynamic-link library یا dll مشخص شده پیدا نشد.
SE_ERR_FNF فایل مورد نظر پیدا نشد.
SE_ERR_NOASSOC هیچ برنامه ای با پسوند فعلی فایل منطبق نشده است. مثلا مشخص نشده که فایل bmp که شما می خواهید آن را اجراه کنید باید با چه برنامه ای باز شود.
SE_ERR_OOM حافظه کافی جهت اجرای عملیات وجود ندارد.
SE_ERR_PNF آدرس مشخص شده پیدا نشد.
SE_ERR_SHARE یک خطای sharin violation پیش آمد. یعنی فایل مورد نظر share شده بود.

خلاصه:
در این مقاله با سه تابع API مهم به نام های WinExec و ShellExecute و ShellExecuteEx آشنا شدیم و پارامترهای آنها را بررسی کردیم. به علاوه مثالهای متعددی راجع به استفاده از ShellExecute ارائه دادیم. به علاوه ثابتهای استفاده شده در این تابع ها را لیست کرده و بعضا آنها را مورد بررسی قرار دادیم. همچنین تابعی به نام ExecAndWait ارائه دادیم که کار آن اجرای یک فایل exe و متوقف کردن برنامه تا اتمام فایل exe آشنا شدیم و ساختمان داخلی این تابع را مورد بررسی قرار دادیم.

موفق و موید و منصور باشید

دیدگاه خود را بیان کنید.

باید وارد سایت شده باشید برای دیدگاه دادن