пятница, декабря 07, 2012

Немножко про unit tests


"А по-моему, они одинаковы"

понедельник, декабря 03, 2012

Что, мальчики, не получается?

Несчастные вы бедолажки в Эпле... Ну, кое-как портировали уродца своего на Windows, ладно. Он, правда, почти год после этого не понимал русский язык, ну, тоже спишем на вашу экстраординарную нетрадиционность. Но вы же всё-таки инженеры, нет? Я там не говорю, что все как один состоите в IETF, но хотя бы общие представления о платформе, на которую делает порт, надо иметь!

Safari это поганое: DDE не поддерживает, с COM не знакомо, требованиям Windows по проектированию оконных интерфейсов не удовлетворяет.

Что же так слабо? Не умеете? Не получается? Ай-яй-яй. Да... это вам не подсветочку вокруг кнопки рисовать. Срамота!

Простой пример DDE и C

Потребовалось получить URL из Firefox. FF вообще написан чудным образом, весьма далёким от того, как надо писать программы для Windows. Поэтому пройти по иерархии окон и получить текст окна - невозможно. Зато возможно получить нужное через DDE. О командах, которые можно использовать в DDE для получения информации из браузера, примерно можно почитать тут: http://support.microsoft.com/kb/160957 Не знаю, какие именно из них поддерживает FF, но получить URL таким образом можно. Для этого нужно использовать команду WWW_GetWindowInfo.

Несмотря на то, что DDE уже старенькая технология (в этом году ей исполнилось 22 года), она по-прежнему работает и неплохо. Её суть примерно в следующем.
  1. Клиент-серверная архитектура DDE подразумевает, что одна сторона (клиент) будет слать запросы, а другая (сервер) - их выполнять. Используется парадигма "разговора", а именно: вопрос-ответ.
  2. Клиент может задавать темы "разговора" (не знаю, для чего это нужно). Выглядит, в переводе на человеческий язык это так. Клиент говорит: "Эй, ты, сервер по имени Firefox, хочу с тобой потолковать насчёт WWW_GetWindowInfo. Ты как, в настроении?". Сервер отвечает: "Ну, давай. Дескриптор этого разговора равен 1232134. Если дальше будешь спрашивать про своё - не забывай мне напоминать, о чём толкуем, подставляя это число как идентификатор. А то много вас тут лезет поговорить, не уследишь по-другому"
  3. Если сервер согласился поддержать "разговор" (выдав HCONV не равный NULL), дальше можно задавать ему вопросы. Для каждой темы, на которую можно клиент может потолковать с сервером есть допустимый "список вопросов". В нашем случае для темы WWW_GetWindowInfo браузер предполагает, что дальше его будут спрашивать про URL и WindowText. Задать вопросы про прогресс закачки файла в разговоре, посвящённом WWW_GetWindowInfo, не выйдет.
  4. Задавание вопроса заключается в последовательном вызове 3х функций:
    1. DdeCreateStringHandle - создаёт DDE-описатель для "вопроса", такого как "URL"
    2. DdeClientTransaction - начинает транзакцию передачи данных. Можно это себе представлять как фразу "Эй, сервер, хочу тебя спросить... Ну, вот мы говорим, про WWW_GetWindowInfo. Так что там с URL?"
    3. DdeGetData - это вызывается для получения ответа на свой вопрос. В ответ на этот вызов DDE скопирует данные в переданный вами буфер и в нём будет искомый ответ.
Таким образом, для получения URL из FF надо:
  1. Инициализировать DDE, чтобы можно было дальше вызывать его функции
  2. Установить соединение с сервером DDE, каковым является FF. При установке соединения задать тему разговора WWW_GetWindowInfo
  3. Спросить FF про URL
 
Подключаемся к FF (никакой обработки ошибок и дурацкие имена для понятности)

TCHAR ddeServer[] = L"Firefox";
TCHAR conversationTopic[] = L"WWW_GetWindowInfo";
TCHAR topicSpecificCommand[] = L"URL";

DWORD idInst=0;
DdeInitialize(&idInst, (PFNCALLBACK)DdeCallback, APPCLASS_STANDARD | APPCMD_CLIENTONLY, 0 );

HSZ appStringHandle, topicStringHandle;
HCONV conversationHandle;
appStringHandle = DdeCreateStringHandle(idInst, ddeServer, 0);
topicStringHandle = DdeCreateStringHandle(idInst, conversationTopic, 0);

conversationHandle = DdeConnect(idInst, appStringHandle, topicStringHandle, NULL);
DdeFreeStringHandle(idInst, appStringHandle);
DdeFreeStringHandle(idInst, topicStringHandle);
Спрашиваем у FF, какой URL у его активной закладки активного экземпляра

HSZ commandStringHandle = DdeCreateStringHandle(idInst, topicSpecificCommand, 0);
HDDEDATA hData = DdeClientTransaction(NULL,0,conversationHandle,commandStringHandle, CF_TEXT, XTYP_REQUEST, 5000, NULL); // CF_TEXT для получения однобайтовой строки
// CF_UNICODETEXT - для юникодной

char res[255] = {0};
DdeGetData(hData, (unsigned char*)res, 255, 0);
printf("%s\n", szResult);
Всё. В res лежит URL закладки, с которой пользователь работал последней в экземпляре FF с которым пользователь работал последним.

P.S. Само собой, не забывайте проверять ошибки после вызовов. А то всякое может быть. Например, нету у вас запущенного экземляра FireFox, вы не знаете и стремитесь с ним поговорить. Не выйдет! :)

P.P.S. Удаляйте всё, что создаёте: DdeCreateStringHandle -> DdeFreeStringHandle, DdeCreateDataHandle -> DdeFreeDataHandle

P.P.P.S. То же самое работает и для Opera, только содержимое ddeServer должно быть "Opera"