Блогът на GunSmoker

. когато промяната на нечий ум става толкова лесна, колкото програмирането на компютър, какво означава да си човек.

Защо не трябва да използвате ShellExecute (Ex)

Последният път научихме защо никога не трябва да използвате функцията ShellExecute .

Този път ще ви кажа защо не трябва да използвате функцията ShellExecuteEx .

Заглавията за тази и предишната публикация са внимателно подбрани. В предишната публикация се казваше, че ако пишете код през 1995 г. или по-късно, не трябва да използвате функцията ShellExecute. Тъй като сме през 2015 г., това означава, че вашият код изобщо не трябва да използва ShellExecute. В заглавието на тази публикация липсва думата "никога", което намеква, че понякога може да се използва ShellExecuteEx. В допълнение заглавието използва двойно име, обозначаващо както функцията ShellExecute, така и функцията ShellExecuteEx - това показва, че значението не е в определена функция, а в логическите действия, които те изпълняват.

Та какъв е проблема?

Много често начинаещите програмисти използват функцията ShellExecute, за да директен стартиране на програми. С други думи, те правят нещо подобно:
Добре, надявам се, че са прочели предишната статия и са започнали да правят това: (този пример на код използва функцията обвивка от предишната статия; ако не сте прочели предишната статия, помислете, че този код е еквивалентен на извикване на ShellExecuteEx с правилна обработка на грешки, COM и асинхронни операции).

Да, стана по-добре, но проблемът не изчезна. Какъв е този проблем?

Факт е, че ShellExecute (Ex) (по-нататък ще използвам комбинираното име на функции, за да обознача и двете функции) е функция на Windows Shell (Explorer), която предназначен за отваряне на файл в съответната програма - тези. в тази, зададена от потребителя за този тип файл. Имайте предвид, че тази цел е различна от „стартиране на изрично посочена програма“.

Горният код работи по простата причина, че действието по подразбиране за .exe файлове е да ги стартирате.

Разбира се, не е нужно да отваряте програмата в свързаната програма, за да я стартирате. Трябва да го стартирате изрично - и за това се използва функцията CreateProcess, а не ShellExecute (Ex).

Но защо да не използваме ShellExecute (Ex) за стартиране на програми? Ето няколко причини (някои от причините може да не са винаги приложими):

  1. Тежест. ShellExecute (Ex) е много по-тежък от CreateProcess, тъй като се нуждае от множество четения в регистъра, за да намира и разрешава асоциации, да проверява за замествания и т.н. (можете да видите колко лошо е от тази статия - вижте раздела The Nitty Gritty и по-долу). Също така ShellExecute (Ex) е функция Shell (импортирана от shell32.dll), а CreateProcess е функция на ядрото (импортирана от kernel32.dll). С други думи, в компактни и/или невизуални програми, плъзнете цялата Shell (shell32.dll) към себе си - заради само една функция, без която можете да се справите;
  2. Уязвимост. ShellExecute (Ex) приема само пълния команден ред, докато CreateProcess ви позволява да посочите изрично името на програмата и отделно командния ред.

Проблемът тук е, че ако посочите само командния ред, ако поставите кавички неточно, можете да създадете уязвимост или грешка в кода си.

И въпреки че определено можете да правите всичко правилно само с един команден ред, идеята тук е, че тук е лесно да се правят грешки - следователно би било по-добре да използвате опцията, където името на програмата може да бъде посочено изрично, тогава ние сме гарантирани за защита срещу грешки; По-малка гъвкавост - тъй като ShellExecute (Ex) не е проектиран да изпълнява програми, липсват много специфични функции. По-специално, I/O пренасочване. По този начин, ако сте свикнали да използвате ShellExecute (Ex) за стартиране на програми и възникне нестандартна ситуация, вие не знаете какво да правите. И ако използвате CreateProcess, тогава можете да адаптирате/коригирате кода.

Разбира се, функцията ShellExecuteEx предоставя някои допълнителни възможности в сравнение с ShellExecute (например изчакване на завършването на работеща програма) - но нейните възможности са далеч от пълноценния CreateProcess;

  • Не можете да стартирате програма, ако тя има нестандартно разширение - тъй като ShellExecute (Ex) не стартира програмата, а я отваря в свързаната програма. Асоциацията се основава на разширението на файла. По този начин, ако файлът няма разширение .exe (или .com), тогава програмата не може да бъде стартирана чрез ShellExecute (тази операция е възможна за ShellExecuteEx, ако използвате замяна на типа файл). В същото време CreateProcess не разглежда файловите асоциации, а просто стартира програмата - така че няма значение дали програмата има разширението .exe, .dat, .MyFile или нещо друго;
  • Понякога потребителят може да назначи друга програма за отваряне на .exe файлове (разбира се по погрешка). Или някой бяка може да го направи вместо него. В този случай ShellExecute (Ex) ще използва заменени асоциации за .exe файлове и няма да може да стартира програмата. CreateProcess така или иначе ще стартира програмата, тъй като не разглежда асоциации;
  • COM зависимост. ShellExecute (Ex) е функция на Shell. Откъдето следва, че е необходима COM инициализация, за да работи ShellExecute (Ex). Въпреки че Delphi прави това за вас автоматично в основната VCL нишка на приложението, в други случаи няма да работи (фонови нишки). От това следва, че ShellExecute може да не работи в MTA среди. Знаете ли по принцип какво е COM инициализация и как се прави правилно?

    Да, ако сте прочели предишната статия, тогава знаете как да го направите правилно. Освен това по подразбиране COM инициализацията не се изисква специално за .exe файлове (добре, освен ако не ги стартирате с кота - това се прави чрез COM). Но за да направите това, трябва поне да знаете за такава функция, за да не се паникьосвате при вида на съобщението „Извикването на CoInitialize от текущата нишка не е направено“.

    Функцията CreateProcess е функция на ядрото и не изисква познаване на спецификата на черупката на Windows; По същия начин ShellExecute (Ex), като функции на Shell, изисква цикъл за извличане на съобщение и трябва да направите специална обработка, когато нямате този цикъл.

    Както по-горе, това не е проблем с .exe файлове, но идеята е, че безмислено да използвате ShellExecute (Ex) вместо най-простия CreateProcess, няма да мислите за ситуация, в която това наистина има значение; За да се справите правилно с обработката на грешки за ShellExecute, трябва да напишете код по следния начин:

    Колко по-просто е правилното обработване на грешки за CreateProcess: