Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Последние темы форума

Показать новые сообщения »

Почтовая рассылка

Подписчиков: 11631
Последний выпуск: 19.06.2015

Создание плагина для Winamp

Автор: Max V. Irgiznov
www.vbrussian.com

Данная статья расскажет вам о том, как можно создавать свои плагины для популярного мультимедиа плеера Winamp.

Вступление

В 2002 году я работал в одной компании системным администратором, и по долгу службы 80% времени мне приходилось находится в окружении серверов FreeBSD. У меня был еще в распоряжении был сервер с Windows на котором крутилась музыка чтобы не скучать, и было не удобно менять треки в плейлисте да и вообще работать с винампом (по некоторым причинам я не мог пользоваться такими вещами как Terminal Service, переключатели мониторов, и.т.п.), и я задался целью сделать управляющую программу для Винампа. Языком программирования был выбран VB, т.к. на этом языке я решения такой задачки не встречал и это мой любимый язык, также нужна была быстрота разработки.

Для программирования под API Winamp`а нам потребуется:

Правда есть небольшое ограничение, этим набором можно создавать только основные (gen_*) плагины.

Данный текст рассчитан на программистов уже имеющих опыт работы в VB с сетевыми приложениями и WinAPI.

Часть 1. Пишем простую управляющую программу.

Для начала напишем простую программу (не плагин) для управления винампом. Например программу которая будет принимать команду на определенном TCP порту и транслировать ее Винампу.

Запускаем VB и создаем новый проект Standard EXE и добавляем в проект Microsoft Winsock Control 6.0, несколько API функций и констант, больше нам ничего не потребуется. 

Вот декларации функций, которые нам понадобятся:

Private Declare Function FindWindow  Lib "user32" Alias "FindWindowA" ( _
                ByVal lpClassName As String, _
                ByVal lpWindowName As String _
) As Long

функция возвращает хендл на окно с заданным классом и/или строкой заголовка;

Private Declare Function SendMessage   Lib "user32" Alias "SendMessageA" ( _
   ByVal hWnd As Long, _
   ByVal wMsg As Long, _
   ByVal wParam As Long, _
   lParam As Any _
) As Long

функция посылает сообщение окну в указанным хендлом;

Private Declare Sub Sleep  Lib "kernel32" ( _
   ByVal dwMilliseconds As Long _
   ) 

функция позволяющая процессу (система не выделяет процессу процессорного времени) на указанное число миллисекунд; 

Размещаем эти функции в секции General, также нам потребуются следующие управляющие сообщения винампа (полный их список есть в SDK к нему) и системы:

'system
Private Const WM_USER = &H400
Private Const WM_COMMAND = &H111
'winamp
Private Const WM_Raise_Volume = 40058       'increase 1%
Private Const WM_Lower_Volume = 40059       'decrease 1%
Private Const WM_Close_Winamp = 40001
Private Const WM_Previous = 40044
Private Const WM_Next = 40048
Private Const WM_Play = 40045
Private Const WM_Pause_Unpause = 40046
Private Const WM_Stop = 40047
Private Const WM_Toggle_Shuffle = 40023
Private Const WA_SETVOLUME = 122    

Также добавляем пару переменных уровня формы:

Dim Response As String
Dim Connections As Long

первая переменная потребуется для принятия строки от клиента, вторая для счета числа подключений. Внешними они сделаны по той причине, что они могут вам потребоваться в других методах, если нет, то переменную Response можно убрать в метод DataArrival. 

Закончив с секцией General переходим к форме и основному коду. 

Размещаем на форме Winsock и задаем ему имя wnsServer и устанавливаем его свойство Index = 0, в событие Form_Load пишем следующий код:

wnsServer(0).Protocol = sckTCPProtocol
wnsServer(0).LocalPort = 806
wnsServer(0).Listen

Тут указываем что будем использовать только протокол TCP и указываем что для приема данных используем порт с номером 806.

Начинаем писать обработчики событий винсока,

первое - опишем процесс подключения клиента:

Private Sub wnsServer_ConnectionRequest(index As Integer, ByVal requestID As Long)
If index = 0 Then 
    Connections = Connections + 1
        Load wnsServer(Connections) 'Load New control
            wnsServer(Connections).LocalPort = 0
        wnsServer(Connections).Accept requested
        end if
    DoEvents
End Sub

вот, мы приняли (принимает запросы только сокет с индексом 0) входящие соединение и выделили для его обслуживания отдельный сокет. При этом основной сокет может принять следующего клиента. LocalPort = 0 применено для того чтобы клиенту порт выделился динамически из числа свободных. 

Ну и наконец ядро программы собственно обработка команд поступаемых в сокет: 

Private Sub wnsServer_DataArrival(index As Integer, ByVal bytesTotal As Long)
    Dim hWnd As Long
    
    hWnd = FindWindow("Winamp v1.x", vbNullString)
    'если к нам подконектились и если у нас присутствует
    'винамп, ждем команду для отправки
    If bytesTotal  0 Then
      
      wnsServer(index).GetData Response 'получаем данные
          
      'если нет винампа то можно только выходить
      If hWnd = 0 Then
        Exit Sub
      End If

  'обработка поступившей команды
      'Next Track
      If InStr(1, Response, "next", vbTextCompare)  0 Then
        SendMessage hWnd, WM_COMMAND, WM_Next, vbNull
        Exit Sub
      End If
      'Previous Track
      If InStr(1, Response, "previous", vbTextCompare)  0 Then
        SendMessage hWnd, WM_COMMAND, WM_Previous, vbNull
        Exit Sub
      End If
      'Play
      If InStr(1, Response, "play", vbTextCompare)  0 Then
        SendMessage hWnd, WM_COMMAND, WM_Play, vbNull
        Exit Sub
      End If
      'Stop
      If InStr(1, Response, "stop", vbTextCompare)  0 Then
        SendMessage hWnd, WM_COMMAND, WM_Stop, vbNull
        Exit Sub
      End If
      'Shuffle
      If InStr(1, Response, "shuffle", vbTextCompare)  0 Then
        SendMessage hWnd, WM_COMMAND, WM_Toggle_Shuffle, vbNull
        Exit Sub
      End If
      'Pause/UnPause
      If InStr(1, Response, "pause", vbTextCompare)  0 Then
        SendMessage hWnd, WM_COMMAND, WM_Pause_Unpause, vbNull
        Exit Sub
      End If
      'Close
      If InStr(1, Response, "close", vbTextCompare)  0 Then
        SendMessage hWnd, WM_COMMAND, WM_Close_Winamp, vbNull
        Exit Sub
      End If
      'Volume inc
      If InStr(1, Response, "+", vbTextCompare)  0 Then
        If Response = "+" Then Response = "+1"
        Volume hWnd, CInt(Mid$(Response, InStr(1, Response, "+") + 1, 3)), 1
        Exit Sub
      End If
      'Volume dec
      If InStr(1, Response, "-", vbTextCompare)  0 Or
         InStr(1, Response, "0", vbTextCompare)  0 Then
        If Mid$(Response, InStr(1, Response, "-") + 1, 3) 'bytes
End Sub

Метод проверяет загружен ли Винамп и если да то переходит к обработке пришедших данных.

Рассмотрим чуть подробнее один из блоков проверок приведенного кода:

If InStr(1, Response, "next", vbTextCompare)  0 Then
        SendMessage hWnd, WM_COMMAND, WM_Next, vbNull
    CloseSocket Index
        Exit Sub
      End If

собственно говоря это простейший вариант проверки поступивший команды и отправка сообщения Винампу. Параметры функции SendMessage: hWnd это хендл на окно винампа определенный в начале метода, WM_COMMAND - системное сообщение показывающие что в последующем параметре функции идет команда, WM_Next - собственно сама команда для окна винампа и последний параметр это дополнительные данные для Винампа или например параметры команды посылаемой окну. После отработки команды закроем сокет (для обеспечения сбалансированной нагрузки) и выйдем из метода.

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

Ну и последние несколько вспомогательных методов:

Посылка команды увеличения или уменьшения (зависимости от параметра incdec) громкости.

Private Sub Volume(hWnd As Long, percent As Integer, incdec As Long)
    Dim i As Long
        For i = 0 To percent - 1
            Select Case incdec
                    Case -1
                        SendMessage hWnd, WM_COMMAND, WM_Lower_Volume, vbNull
                    Case 1
                        SendMessage hWnd, WM_COMMAND, WM_Raise_Volume, vbNull
            End Select
        Next i
End Sub

Событие происходит когда клиенту переданы все данные, как только оно возникает, выдерживаем интервал и закрываем сокет.

Private Sub wnsServer_SendComplete(index As Integer)
    Sleep 1000
    CloseSocket index
End Sub

'Само закрытие сокета и выгрузка его из памяти.
Private Sub CloseSocket(index As Integer)    
    wnsServer(index).Close
    Unload wnsServer(Connections)
    Connections = Connections - 1
    DoEvents                
End Sub

Ну вот, теперь если все сделано без ошибок проект успешно откомпилируется и запустится сервер на ожидающий подключение на 806 порту, транслирующий команды Винампу. Для проверки его работы можно воспользоваться программкой TRCClient из каталога src, любители языка Perl могут воспользоваться управляющим скриптом от моего плагина VbTRC для Винампа.

Надеюсь это не стало для вас затруднением и на этом закончим первую часть нашей статьи.

Часть 2. От простой программы к настоящему плагину.

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

Распаковываем скаченный GenWrapper.exe, оттуда нам понадобятся файлы GenWrapper.dll и GenWrapper.tlb, а также из каталога Template класс Plugin.cls. Создаем проект ActiveX DLL с именем tcpctrl удаляем из него Class1.cls и добавляем распакованный Plugin.cls. После добавления открываем пункт меню Project->References и добавляем ссылку на GenWrapper.tlb, не забыв также добавить компонент Microsoft Winsock Control. Ядро плагина мы создали, теперь мы можем использовать сокеты так как делали это в нашей первой программе, для любителей WinAPI скажу сразу что в данном случае лучше пользоваться сокетами напрямую через АПИ в этом случае можно будет отказаться от использования формы-контейнера.

Итак приступим к работе.

Создадим модуль main.bas, он нам понадобится для того чтобы корректно загрузить форму на которой будут располагаться наши элементы управления. Напрямую форму инициализировать нельзя, т.к. Винамп не поддерживает отображение форм на этапе своей загрузки и инициализации(даже когда она скрыта). В модуль поместим декларации АПИ функций из первой программы, а также добавим одну глобальную переменную Global This As Plugin (Где Plugin это имя нашего класса, его необходимо будет запомнить) для создания указателя на класс плагина.

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

Public Sub ld()
    Load frmHidden
End Sub

Открываем класс Plugin и следуем в метод IRjlWinAmpGenPlugin_Configure он вызывается при нажатии кнопочки Configure в диалоге настроек плагинов Винампа, т.к. в простейшем случае у нас параметров плагина нет, то просто выведем описание плагина: MsgBox App.FileDescription.

Следующий метод Info() вызывается при нажатии кнопочки "About" в диалоге настроек плагинов Винампа, тут может быть все что вам угодно я например вывожу такой MsgBox:

MsgBox "Plugin Description: " & vbCrLf & m_Wrapper.Description & vbCrLf & _
    "WinAmp Window Handle: 0x" & Hex(m_Wrapper.HWndParent) _
    , vbInformation, "tcpctrl Information"
Метод заслуживающий отдельного внимания: IRjlWinAmpGenPlugin_Initialize он вызывается при загрузке винампа и инициализации его списка плагинов, в нем мы поменяем строчку - описание для списка найденных плагинов, например на такую: m_Wrapper.Description = "tcpctrl Plugin v." & App.Major & "." & App.Minor & "." & App.Revision & " (gen_tcpctrl.dll)". Как я уже и говорил напрямую Load frmHidden тут сделать нельзя из-за особенностей работы винампа, поэтому придется сделать косвенный вызов установив при этом ссылку на наш класс:
If Not This Is Nothing Then
    Err.Raise vbObjectError + 1, , "Already have a plugin instance"
    Exit Sub
End If
Set This = Me
main.ld

все, форма загружена и инициализирована.

В методе IRjlWinAmpGenPlugin_Quit все просто, выгружаем нашу форму Unload frmHidden.

Вот и все, с классом мы закончили, приступим к созданию формы-контейнера для контролов. Добавляем форму в проект, даем ей имя frmHidden и устанавливаем ее свойство Visible равное False. Помещаем на нее Winsock с именем аналогичным как в первой программе, также помещаем сюда те же константы и переменные.

Событие Load формы будет выглядеть так:

Private Sub Form_Load()  
On Error Resume Next
  Me.Visible = False  
  wnsServer(0).Protocol = sckTCPProtocol
  wnsServer(0).LocalPort = 806
  wnsServer(0).Listen
End Sub
Код для события Unload:
Private Sub Form_Unload(Cancel As Integer)
   Dim i As Long
   If Connections > 0 Then
        For i = Connections To 1
            wnsServer(i).Close
            Unload wnsServer(Connections)
        Next i
  End If    
  DoEvents
End Sub

Код в данных местах практически идентичен коду в первом приложении, поступим также и с остальными методами, т.е можно просто скопировать следующие методы и функции: wnsServer_ConnectionRequest, wnsServer_DataArrival, Volume, wnsServer_SendComplete, СloseSocket.

Компилируем, надеюсь все прошло замечательно? Нет, тогда исправляем ошибки.

Теперь самое интересное, т.к. Винамп не понимает ActiveX DLL, то мы воспользовались обверткой , которая требует чтобы ее DLL переименовали следующим образом, например наша DLL называется tcpctrl.dll, а класс плагина называется Plugin, то GenWrapper.dll переименовываем так: gen_tcpctrl.Plugin.dll. И наконец обе библиотеки копируем в каталог Plugins Винампа.

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

Желаю удачи и творческих успехов.

P.S В своем плагине vbTRC я реализовал дополнительные функции управления Винампом такие как: работа с пультом ДУ от ТВ-Тюнера AverMedia(основные клавиши управления плюс любимые треки и предпрослушка треков), добавил также веб-интерфейс для управления и конфигурирования, простейшие списки доступа, автостарт после загрузки, запись NP и Uptime в файл для вставки в другие программы и другие разные улучшения и нововведения. Базовое ядро я использовал то же, что и приведено в данной статье плюс мои дополнения. 

P.P.S На самом деле VB можно заставить делать настоящие не ActictiveX DLL которые Винамп поймет с легкостью. Но как всегда процесс этот весьма не прост и далеко не безглючен. Жалающие могут попробовать реализовать его на практике, буду рад если таким образом вы допишете 3ю часть статьи. Подробнее о создании простых DLL можно почитать вот в этой статье: http://www.fawcette.com/archives/listissue.asp?pubID=1&MagIssueId=215#.

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

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 

Комментарии

1.
91K
26 июня 2013 года
Вячеслав Краснов
0 / / 26.06.2013
+1 / -0
Мне нравитсяМне не нравится
26 июня 2013, 16:20:28
а что так всё сложно ..готового что ли плагина нет ?
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог