На написание этого поста меня, как это ни странно, сподвиг сегодняшний выход Firefox 3. Я имел неосторожность поделиться с товарищем ссылкой на дистрибутив этого самого Файрфокса. По этой ссылке сам скачивал, так что всё должно было быть чики-пуки. Но вот незадача, я-то по привычке своей скачивал оригинальную версию, "en-us", что называется, и товарищ этим фактом возмутился. Завязалась дискуссия :-) Не стану, наверное, приводить здесь его аргументацию, расскажу только что я думаю по этому поводу.
Локализация, оно, конечно, хорошо. Особенно если ты "домохозяйка", которой компьютер нужен, чтобы иногда посмотреть в интернете рецепт нового пирога, прогноз погоды в районе расположения дачи или лунный посевной календарь на этот год. Но если вы котёнок, и вас зовут Василием... Вобщем лично мне кажется, что программисту лучше чем "en-us" ничего и не надо и более того, даже вредно. Да, соглашусь, пожалуй, что иногда меня посещали мысли о том, чтобы какое-нибудь новое/непонятное приложение было локализовано, чтобы я хотя бы мог понять что значат все эти подписи к кнопкам и переключалкам, но в случае с браузером, или текстовым редактором -- ни за что.
Тут можно снова скатиться к извечной теме нужен ли русскому (или китайскому или любому_другому_из_неанглоговорящей_страны) программисту английский как таковой... Моё мнение на этот счёт однозначное - ещё как нужен! Ибо всё кругом пляшет от английского: документация - английский, блоги - английский, форумы и ФАКи - английский, туториалы - догадайтесь сами :-). Вобщем, английский - язык международного общения и не признавать этого глупо и недальновидно, хотя я не стану отрицать, что и не зная его можно вполне себе заниматься программированием, только кому оно надо.
Примеры из жизни, с которыми сталкивался я, и которые ещё больше уверяют меня в мысли, что локализация -- решение не для меня.
Пример 1
Есть у меня ноутбук с предустановленной русской Вистой. Кроме этого ноутбука у меня есть привычка иногда пользоваться Панелью управления Windows. Так как ноутбук - это не основной компьютер, а на основном стоит очень даже нелокализованная версия Windows XP, то где-то в глубине души панель управления помнится мне под именем Control Panel и обычно я добираюсь до неё чере меню Start/Settings, ну вы поняли. В то же время в Vista есть замечательная возможность, нажав на WinKey и набрав первые несколько буковок названия приложения (в худшем случае, всё название), быстро найти его в списке доступных программ. Возможность жутко удобная и вообще must have, но! ну никак не могу я запомнить, что Control Panel это Панель управления, а уж о названии "Центр управления обновлениями" и говорить не приходится. К тому же, надо переключать раскладку.
Знающие люди могут мне напомнить о PuntoSwitcher'e, и будут, наверное, правы. Скажу честно, не пробовал, да и свитчер этот я довольно странно использую.
Пример 2
Есть у меня в Windows любимое сочетание клавиш: Alt+Space+x. Ну очень полезная штука, почти такая же полезная как Alt+Space+e+p, только ещё лучше. (Для тех, кто не знал и поленился попробовать: Alt+Space+x максимизирует активное окно). Основной вариант использования этого шортката: ходил-бродил по файлам в Far, нашёл что было надо, написал в строке "gvim filename", открылся Vim в окошке, а файл большой, надо окошко увеличить, за мышкой руку тянуть лень, бац-бац пару кнопок и вуа-ля! окошко размером во весь экран. Красота да и только.
Есть, как обычно, один большой недостаток даже в нелокализованной версии. Шорткаты (никак не могу привыкнуть называть их "клавиатурные сокращения") категорически не хотят работать в русской раскладке клавиатуры, но это не беда, потому что бОльшую часть времени русская раскладка отдыхает (разве что в IM клиенте только она и работает :-)).
Так вот, в локализованной версии пункт "Maximize" был благополучно переведён, и 'x' больше не является hotkey для этого действия, и, как следствие перевода, hotkey теперь должен набираться в русской раскладке, что категорически неудобно, ибо, как я уже написал выше, русская раскладка используется редко.
В качестве ещё одного минуса локализации могу привести тот факт, что локализатор (или как его звать этого мастера? переводчик, наверное) иногда использует не вполне подходящие русские слова. Они может и лучше отражают суть действия или понятия, но иногда делают невозможной задачу восстановления исходного английского термина. В этом смысле мне кажется странным инициатива Microsoft о переводе интерфейса Visual Studio на русский язык. MSDN library ещё куда ни шло, но студию...
Я почти уверен, что каждый программист хотя бы раз в жизни, но сталкивается с проблемой перенаправления потока вывода процесса куда-то наружу. Например, при написании GUI обёртки для консольного приложения, которое "кто-то написал давным давно, да к тому же под *nix, а потом добрые люди спортировали это чудо в Windows и так и оставили". Я долго избегал этой участи, может быть подсазнательно, но вот и моё время пришло.
Напомню, что дело имею в основном с C# и .Net, так что разговаривать будем с использованием этих слов. Хотя, наверное, теория, она одна, и на чём ты её реализовываешь - дело десятое.
Начну, пожалуй, издалека. Если обратить внимание на правый side-bar данного блога можно запросто заметить иконку замечательного текстового редактора Notepad++, которым я иногда пользуюсь, когда это удобнее Vim'а. Так вот, некоторое время назад я столкнулся с ним вплотную с точки зрения использования его в качестве некого подобия IDE. И существенной частью этого использования должна была стать возможность запускать некие скрипты, набираемые в поле редактирования, прямо из редактора, используя внешний интерпретатор. Сейчас это реализовано через plug-in, кому интересно, могут посмотреть в документацию и узнать подробности.
Внимательному читателю уже стало ясно, что именно вызов внешнего интерпретатора из оконного приложения и есть то самое место, где просто необходимо перенаправление вывода дочернего процесса, так как никому не охота смотреть на чёрное окно, закрывающее вид на редактор. Вот тогда я, пожалуй, впервые столкнулся с проблемой. Как оказалось не зря столкнулся, потому что слегка корявая реализация (или плохая настройка) этой возможности, заставила меня задуматься над сутью вещей, и именно в тот самый момент я заинтересовался как же это работает.
Позволю себе углубиться ещё дальше в историю вопроса. Многим известно, что перенаправление потока вывода консольного приложения можно осуществить, так сказать, голыми руками, просто набрав в консоли "program.exe > out". Известно это было и мне, более того, я всегда активно пользовался этой возможностью, за что однажды в институте получил упрёк от преподавателя за "недружелюбность интерфейса". Вобщем, знание о магическом операторе ">" было мне ведомо и хранилось до поры до времени внутри головного мозга.
Ну что ж. Задача понятна. Будем искать решение. Куда идём? Правильно! Для начала MSDN Library.
ProcessStartInfo info = new ProcessStartInfo(executable_path, command_line_parameters);
info.UseShellExecute = false; // это важно
info.RedirectStandardOutput = true; // и это тоже не менее важно
Process proc = Process.Start(info);
string output = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
Поход ясен как день, но проблемы не решает. Хотелось бы чего-то более интерактивного. "ReadToEnd", конечно, гарантирует, что весь вывод, который когда-то предполагался для консоли, попадёт в мои руки через "output", но произойдёт это только после того, как процесс завершится, а в моём случае произойти это может сколь угодно нескоро. А значит, пользователь, который имел неосторожность нажать кнопку "Старт" в моём приложении, будет недоумевать, почему всё встало и ничто не булькает и не моргает, чтобы хоть каким-то способом заявить о кипящей где-то глубоко внутри работе.
Стоит отметить, что в МСДНе есть ещё много иностранных слов о том, что чтение перенаправленного потока вывода можно осуществлять как синхронно, так и не очень. Но все мои потуги вызвать "proc.StandardOutput.BeginReadToEnd()" или даже "proc.BeginOutputReadLine()" привели к тому же печальному результату, даже более того, в последнем случае по завершении процесса я получал только первую строку его вывода.
Собственно, настало время перестать слушать маму читать что написали другие, и написать что-то самому. Вот здесь как раз вспомнился опыт общения с Notepad++, где одна из переменных для настройки называлась как-то вроде "OutputPingTimeout", или по-другому но с тем же смыслом.
Идея в том, чтобы периодически тыкать поток вывода дочернего процесса на момент наличия в нём "чего почитать". Ну и конечно, эти тычки должны происходить с некоторой периодичностью в процессе работы процесса. (Да, я знаю, что масло масляное.) В этом нам помогут старые добрые треды (будем называть их так, чтобы не путать с потоками ввода/вывода). Дабы не городить огород из "public static void Main", возьмём в руки IronPython и забацаем, так сказать, по-быстрому.
import clr
from System.Diagnostics import ProcessStartInfo, Process
from System.IO import StreamReader
from System.Threading import Thread, ThreadStart
from System import Exception
def create_start_info():
info = ProcessStartInfo(r"...")
info.Arguments = "..."
info.UseShellExecute = False
info.RedirectStandardOutput = True
return info
def run_proc():
proc = Process.Start(create_start_info())
return proc
reader = None
def thread_proc():
while True:
s = reader.ReadLine()
if not s:
break
print 'read from reader:', s
if __name__ == "__main__":
try:
try:
proc = run_proc()
reader = proc.StandardOutput
Thread.CurrentThread.Join(100)
thread = Thread(ThreadStart(thread_proc))
thread.Start()
proc.WaitForExit()
print 'Process finished'
thread.Join()
print 'Thread finished'
except Exception, ex:
print ex.ToString()
finally:
proc.Close()
Ну вот как-то так. И что самое удивительное - этот код заработал! Вроде бы всё должно быть понятно.
Один только момент, который хотелось бы уточнить, это окончание треда. Тред крутится до тех пор, пока "reader.ReadLine" не вернёт null (или None в случае с Python), а это должно произойти только когда поток закончится, а значит, ккогда закочнится выполнение дочернего процесса. Следовательно, если исходить из предположения, что процесс рано или поздно закончит своё существование, можно надеяться, что и "thread.Join()" без таймаута не приведёт к "мёртвому висяку", известному так же под именем "dead lock".
Вот на этой оптимистичной ноте, позвольте закончить и откланяться.
Я почти уверен, что каждый программист хотя бы раз в жизни, но сталкивается с проблемой перенаправления потока вывода процесса куда-то наружу. Например, при написании GUI обёртки для консольного приложения, которое "кто-то написал давным давно, да к тому...(
read more)