Программизм
 
Программизм
На главную | Графомания | Программизм | Книги | Всячина | Скачать | Ъ?  

Batch-файлы в Windows NT

Пользователи всех клонов *nix давно уже могут писать командные файлы на любом языке. Для этого надо лишь указать в первой строке имя интерпретатора. Так, например, если бы хотим, чтобы скрипт исполнялся при помощи Korn shell, надо начать его строчкой

#!/bin/ksh

А чем хуже пользователи Windows NT? Мне, например, очень удобно писать свои скрипты на REXX или gawk. Можно было бы добавить соответствующее расширение к переменной среды PATHEXT, но при этом процесс-интерпретатор запускается в новом окне, и смотреть его вывод неудобно.

В unix’овом синтаксисе имеется символ #, который Windows специальным образом не обрабатывает. Значит, ничто не мешает назвать так свою программу. Программа должна читать аргументы и запускать их как команду. Надеюсь, почему нельзя просто первой строкой batch-файла написать имя интерпретатора, объяснять не нужно.

Самый простой способ сделать это — воспользоваться функцией system(). Но мало того, что она помечена в MSDN как deprecated — в результате мы всё равно получим запуск программы в новом окне. Значит, не надо бояться вызвать CreateProcess():

BOOL CreateProcess(
  // pointer to name of executable module
  LPCTSTR lpApplicationName,
  // pointer to command line string
  LPTSTR lpCommandLine,
  // process security attributes
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  // thread security attributes
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  // handle inheritance flag
  BOOL bInheritHandles,
  // creation flags
  DWORD dwCreationFlags,
  // pointer to new environment block
  LPVOID lpEnvironment,
  // pointer to current directory name
  LPCTSTR lpCurrentDirectory,
  // pointer to STARTUPINFO
  LPSTARTUPINFO lpStartupInfo,
  // pointer to PROCESS_INFORMATION
  LPPROCESS_INFORMATION lpProcessInformation
);

Несмотря на кажущуюся сложность, функция чрезвычайно проста, если нужно сделать простую вещь, и в то же время достаточно гибкая, чтобы делать сложные вещи. Нам понадобятся всего лишь три параметра:

  • lpCommandLine — сюда передадим всё, что получено в командной строке, откусив предварительно имя исполняемого модуля;
  • bInheritHandles — TRUE для того, чтобы запускаемый процесс использовал те же дескрипторы (хэндлы) для стандартного ввода/вывода;
  • lpStartupInfo — сюда записывается информация о положении окна, дескрипторах ввода/вывода и т.д. Здесь все значения — по умолчанию, включая информацию о стандартных дескрипторах, которые берутся из родительского процесса.

После того, как новый процесс запущен, необходимо дождаться его завершения, чтобы batch-файл мог дождаться окончания работы интерпретатора, а не запускающей программы. Вот, собственно, и весь текст программы:

#include <windows.h>

int main(void) {
  char *p = GetCommandLine(), brkchar; 
  PROCESS_INFORMATION _processinfo;
  STARTUPINFO _startupinfo = {
    sizeof(_startupinfo),
    NULL, NULL, NULL,
    0,0, 0,0, 0,0,
    0, 0, 0,
    0, NULL,
    0, 0, 0
  };

  brkchar = (*p=='"') ? ++p,'"' : ' ';
  while(*p && *p!=brkchar) p++;
  if ((*p)=='"') p++; if (*p) p++; else return 1;
  if (CreateProcess(
    NULL,p,NULL,NULL,TRUE,0,NULL,NULL,
    &_startupinfo,&_processinfo)
  ) {
    WaitForSingleObject(_processinfo.hProcess, INFINITE);
    CloseHandle(_processinfo.hProcess);
    CloseHandle(_processinfo.hThread);
  }
  return 0;
}

Как теперь этим пользоваться? А почти так же, как и в *nix’е. Скомпилировать или скачать исполняемый модуль, переименовать его в #.exe и положить туда, куда протоптан PATH. Вот, например, как будет выглядеть программа на gawk:

# gawk -f "%~f0" %* & exit

BEGIN {
  print "Hello, world!";
}

Очевидно, в Windows 95/98 работать это не будет, так как там нет ни %~f0, ни & как разделителя команд.

И ещё один нюанс. Параметры командной строки передаются в программу на C/C++ в виде параметров функции main(int argv, char *argc[]). В данной программе нам нужна командная строка целиком, никак не разобранная. Её можно получить при помощи вызова Win32 API GetCommandLine(). Следовательно, кусок кода, который разбирает командную строку, нам не нужен, и его можно выкинуть. Поэтому если наш компилятор — Visual C/C++ 6.0 (кстати, если нет, то почему?), то открываем Project Settings (Alt+F7) и на странице Link в разделе General ставим Ignore all default libraries, а в разделе OutputEntry-point symbol = main.

Размер получившегося исполняемого модуля — 12 K, меньше просто не бывает. Скачать программу можно в соответствующем разделе.

И, наконец, ложка дёгтя в цистерне мёда. При запуске скрипта первой строкой вывода будет первая строка файла, т.к. echo по умолчанию всё-таки on. При запуске скрипта из другого batch-файла можно пользоваться конструкцией cmd /q /c script.cmd. Если же кто знает, как решить эту проблему при запуске из командной строки, а заодно побороть корявость синтаксиса — милости просим, пишите письма.

8.01.2003

P.S. Оказывается, 12 K — далеко не предел миниатюризации, что не может не радовать. Стоит добавить в исходный текст программы магические комментарии, как программа помещается в 1 (один!) килобайт. Вся магия подробно описана в статье «Дзэн — Си — Win32», а у меня уже лежит обновлённая версия.

Назад | К оглавлению | Для печати | Комментарии | Вперёд
Поиск
См. также

Иногда мне приходится слышать, что Oracle очень сложен в настройке, «вот ××× — совсем другое дело». Перестать обращать внимание на подобные заявления мне помог случай... »»»

Контрольный вопрос: какая из компонент аппаратуры сильнее всего влияет на скорость исполнения приложений?... »»»

во-первых, процедуру копирования нужно вызвать из программы на VisualBasic, а во-вторых, у пользователя нет прав на запись в каталог... »»»

Рекомендую

e.g.Orius’
Игорь Иртеньев
Вячеслав Шевченко

Copyright notice

ъ) Все материалы, размещённые на странице, являются неотъемлемой собственностью автора с вытекающими отсюда правами, как ©, так и (ъ). Некоммерческое их распространение всячески приветствуется, разумеется, при условии сохранения ссылки на оригинал. Что касается коммерческого использования — пишите письма, договориться можно всегда.

Удивительное рядом

lj userhardsign
Закладки Карта Королёва

Пишите письма

Счётчики

XPEHOMETP™ Рейтинг@Mail.ru