Skype Preview - плагін предпросмотра посилань в скайпі

This post is also available in: English (Англійська)

English (Англійська)

Сидів я якось в одному затишному ЧАТИК в Skype, і тут одному з учасників в голову прийшла дуже цікава ідея: зробити плагін для скайпу, який дозволяв би переглядати вміст тих посилань, які вкідиваются в чат, без їх відкриття. "А чому б і ні?", - подумав я. Можна відкривати картинки в зменшеному варіанті, якщо посилання вказує на зображення (в тому числі анімовані). А якщо посилання веде на звичайну html-сторінку, то можна відображати її заголовок. Забігаючи вперед, покажу, що вийшло:

Забігаючи вперед, покажу, що вийшло:

А тепер трохи історії і деталей кодинга. Охочих відразу завантажити плагін прошу пройти в кінець статті. Отже, я тут же відкрив Visual Studio поліз в гугл, щоб подивитися, які на сьогоднішній день є відкриті API у Skype. Як, на жаль, виявилося, SDK ніякого у скайпу немає вже давно. Раніше був якийсь SDK на COM-інтерфейси (skype4com), але від його підтримки відмовилися, і використовувати його було б вже нерозумним. Залишився ущербний міні-API на URI ( ось його опис ), Але на цьому зробити що-небудь путнє не представлялося можливим. Таким чином, єдиним шляхом запиливания свого функціоналу в Skype залишилося дослідження нутрощів програми і спроби впровадження в неї свого коду (хукі, перехоплення віконних повідомлень або щось ще). На щастя, призначений для користувача інтерфейс скайпу побудований на вікнах (звичайних вікнах Windows, без всяких WPF і подібного), і це грає нам на руку, тому що можна буде перехоплювати віконні повідомлень і WinAPI-функції, відповідальні за отрисовку тексту (нагадаю, що потрібно відображати попередній вмісту посилання при наведенні на неї курсора). В результаті я прийшов до таких рішень зі зміни функціоналу скайпу:

1. Як додати в меню скайпу власний пункт меню? Я перехопив WinAPI-функції DrawMenuBar і PeekMessageW . У PeekMessageW ми перевіряємо, чи є потік, що викликав функцію, потоком, в якому працює головне вікно скайпу, і якщо це так, то проводиться перерисовка меню за допомогою вже хукнутой функції DrawMenuBar, після чого відключається хук PeekMessageW. Це потрібно для випадку, коли плагін Інжект в уже працює скайп. Якщо плагін Інжект в скайп до запуску, то скайп, швидше за все, сам викличе нашу похуканную функцію DrawMenuBar, але підстрахуватися не завадить в будь-якому випадку. Що ж відбувається всередині перехопленої DrawMenuBar? В цілому, нічого складного: проводиться сабклассінг головного вікна скайпу (щоб повісити обробку кліків по доданому меню) і безпосередньо додається меню плагіна.

2. Як зрозуміти, що користувач навів курсор на активне посилання у вікні чату скайпу? Для цього я перехопив WinAPI-функцію ExtTextOutW . Їй користується Skype при відображенні тексту. Непросто було відстежити, коли саме отрісовивается активне посилання у вікні чату, і як і раніше такий режим плагіна працює не дуже стабільно (іноді плагін вважає, що користувач навів курсор на посилання, хоча це не так), але допілівать до ідеалу мені поки що ліниво . Отже, в перехопленої ExtTextOutW виконується перевірка, що виклик прийшов саме з модуля Skype.exe, що курсор видно і жодна кнопка миші не було натиснуто, і що переданий саме URL, що починається з http: // або https: //. Іноді Skype любить перемальовувати посилання у відкритому вікні чату незалежно від того, водить користувач курсором над ним, і саме в цьому випадку плагіну може здатися, що користувач провів курсором над посиланням. Все хукі виконані в файлі main.cpp проекту, і в якості вправи можу запропонувати людям, розуміючим в КОДІНГ, зрозуміти, як можна точніше відокремити саме дію користувача в такому сценарії від якоїсь рандомних перемальовування посилання. Для початку, наприклад, можна спробувати переконатися, що курсор знаходиться дійсно над вікном повідомлень Skype.

3. В плагіні є режим роботи, коли превью посилання відображається при кліці на посилання, а саме посилання можна відкрити подвійним кліком. Це зроблено простим хуком функції ShellExecuteW . Найскладнішим було написати свій обробник, який відрізняє клік від подвійного кліка, і він зроблений на boost :: asio (double_click_monitor.h, double_click_monitor.cpp).

Тепер ще коротко про збірку. Я вирішив не пиляти свої велосипеди і по максимуму скористався готовими бібліотеками. Ось список бібліотек, які потрібні для того, щоб зібрати повноцінну версію плагіна:
1. curl 7.35.0 - ну, тут зрозуміло. Використовується для серфінгу по Web'у з підтримкою cookies, редиректів.
1.1. OpenSSL 1.0.0e - використовується при складанні curl для того, щоб працювали https-посилання.
1.2. zlib 1.2.8 - використовується при складанні curl для того, щоб працював gzip.
1.3. libidn 1.28 - використовується при складанні curl для того, щоб підтримувалися кириличні та інші японські домени. Без цієї бібліотеки можна обійтися, зібравши curl так, щоб він використовував WinAPI, що надає аналогічну функціональність, але працювати це буде тільки на Windows Vista і вище. Щоб зібрати curl з libidn під Windows, треба буде добряче потрахаться з CMakeLists curl'а (бо батники для складання під Windows, що поставляються з curl'ом, не вміють підчіплювати libidn). Якщо комусь буде це цікаво, можу розповісти деталі в коментарях.
2. libiconv - для того, щоб можна було відображати прев'ю заголовків сторінок в різних кодуваннях. Збірка під Windows здійснювалася за допомогою цього проекту .
3. MinHook - для здійснення хуков WinAPI-функцій.
4. Boost 1.55 - для інших потреб на кшталт відстеження подвійних кліків у вікні скайпу або збереження-завантаження налаштувань в XML-форматі.
5. GdiPlus - стандартна бібліотека Windows, використовується для зміни розмірів зображень і малювання тексту.

Крім безпосередньо dll-файлу, який Інжект в адресний простір скайпу, я написав ще й завантажувач (exe-файл), який дозволяє довантажити плагін в скайп (якщо той вже запущений), або запустити скайп з завантаженим плагіном.

Завантажити комплект для збірки можна ТУТ (Тільки проекти для VS2010 без бібліотек і бінарників). Всі проекти збираються в Visual Studio 2010. Для складання плагіна і лоадера використовується конфігурація Debug або Release. Конфігурація DebugExe дозволяє створити замість dll-файлу отладочную версію плагіна (exe-файл, см код в main.cpp). Якщо надумаєте збирати все самі, то приготуйтеся до знатного баттхерту викачування великої кількості бібліотек і їх не дуже простій збірці. В налаштуваннях проектів Visual Studio все шляху до бібліотек вказані так, як вони лежали у мене на комп'ютері, тому вам при складанні доведеться все шляху підправити під себе.

Для тих, хто не читав статтю (або хто читав, і не хоче збирати): Завантажити плагін (бінарники) . Запускати SkypeLoader.exe (неважливо, чи запущений сам Skype). Можна помістити SkypePreview.dll і SkypeLoader.exe в будь-яку папку, після чого створити на робочому столі або в меню "Пуск" ярлик для SkypeLoader.exe, а потім користуватися ним для запуску Skype.

А чому б і ні?
1. Як додати в меню скайпу власний пункт меню?
Що ж відбувається всередині перехопленої DrawMenuBar?
2. Як зрозуміти, що користувач навів курсор на активне посилання у вікні чату скайпу?