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

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», а у меня уже лежит обновлённая версия.

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

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

Именно им призван помочь описанный здесь скрипт, сохраняющий письма с бесплатных ящиков на локальный диск.... »»»

Очевидно, что char поместится в int. Во многих моделях памяти размер указателя также равен размеру int. Следовательно, структуру можно заменить массивом... »»»

Рекомендую

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

Copyright notice

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

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

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

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

Счётчики

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