Winamp头上动土

原创 2005年05月06日 19:59:00

Winamp头上动土

 

遭遇

 

近日使用Winamp时发生极度不爽的事情,就是我打CS的时候习惯听着Winamp来打,但我的播放列表又很乱,有些我不想听的歌也在播放列表里面,我又懒得去整理,所以有时正要杀人的时候刚好播到我不喜欢听的歌,导致战斗力大减,被人凌辱,我总不能用Alt+Tab切换出桌面换了歌再进去厮杀吧,所以我只好拿Winamp开刀进行改造。

 

我想在打着CS的时候可以按键盘的某一个键就可以跳到下一首歌,这样我就不用忍受那些不喜欢的歌曲了,又可以提高战斗力,Winamp没有这样的热键,我只好自己动手丰衣足食。

 

关键问题:

 

1Winamp没有控制接口给我,而我要控制他,那我只好自己找到他的窗口句柄然后给他发送消息来控制他。

 

2)要在任何时候任何地方得到键盘消息,我知道的有2种方法,1.写键盘驱动;2.写全局键盘钩子,我不会写驱动,只好写键盘钩子。

 

3)我总不能每次在进入CS前都去启动一下我的钩子,即使我愿意,钩子是需要进程的,我不愿意在我的任务栏多一个窗口,我的任务栏已经很挤了,如果就为了控制winamp就在任务栏多一个东西,人家看到指指点点,面子不够光彩,所以我要做成windows服务的形式。

 

Winamp发送消息

 

通过SPY++发现Winamp播放主窗口的标题总是"歌曲名XXX - Winamp",所以可以用检查各个窗口的窗口标题的最后6个字符看是不是"Winamp"来判断一个窗口是不是Winamp的主播放窗口。

可以用

BOOL EnumWindows( 

WNDENUMPROC lpEnumFunc, 

LPARAM lParam 

); //枚举桌面的各个窗口

来枚举到所有在桌面的窗口,如果Winamp在播放,他当然在桌面上。

第一个参数是枚举回调函数,函数原型是

 

BOOL CALLBACK EnumWindowsProc(

HWND hwnd, 

LPARAM lParam 

); //枚举回调函数

这个函数在 EnumWindows 每枚举到一个窗口时都会调用,回调函数的第一个参数就是窗口句柄,有了窗口句柄就可以为所欲为了。

 

通过spy++事件探查器探查到Winamp在点击‘下一首’按钮时的要跳到下一首播放只要发送一个菜单消息WM_COMMANDWinampe那个playback-next 菜单项就可以跳到下一首播放,WM_COMMAND的第一个参数固定是0x00009C70(不知道什么意思),第二个参数固定是0

 

所以我只要用EnumWindows找到Winampe的主窗口,然后PostMessage发送一个WM_COMMAND消息就可以要Winamp跳到下一首播放。

 

键盘钩子

 

键盘钩子能够拦截应用程序的事件通知,抢在应用程序之前对事件进行处理,钩子API包括

 

HHOOK SetWindowsHookEx(      
    int idHook,

    HOOKPROC lpfn,

    HINSTANCE hMod,

    DWORD dwThreadId

);//安装钩子

 

BOOL UnhookWindowsHookEx(      

    HHOOK hhk

); //御除钩子

 

LRESULT CallNextHookEx(      

    HHOOK hhk,

    int nCode,

    WPARAM wParam,

    LPARAM lParam

);//通知下一个钩子

 

LRESULT CALLBACK KeyboardProc(      

    int code,

    WPARAM wParam,

    LPARAM lParam

);//键盘钩子回调原型

 

一般钩子只能钩到本进程的消息,要在我的进程里钩Winamp的消息就要安装一个全局的键盘钩子,只需要在Dll里安装钩子就可以做到这点。

 

建立一个Dll,代码如下

 

// WinampHook.cpp : 定义 DLL 应用程序的入口点。

//

//by Lingch

//2005-5-6

//http://www.lingch.net

//post2ling@hotmail.com

 

 

#include "stdafx.h"

#include<string>

 

using namespace std;

 

HANDLE hModule;

HHOOK hook;

 

BOOL CALLBACK EnumWindowsProc(HWND hwnd,DWORD lParam)

{

     //获取窗口标题

     char strTitle[128];

     ::GetWindowText (hwnd,strTitle,127);

     string strT=strTitle;

     //检查窗口标题是否以"Winamp"结尾

     if(strT.size ()>=6)

     {

         string subStr=strT.substr (strT.size ()-6,6);

         if(strcmp(subStr.c_str (),"Winamp")==0)

         {

              ::PostMessage (hwnd,WM_COMMAND,0x00009C70,0);//下一首

         }

     }

     return TRUE;

}

 

 

LRESULT CALLBACK KeyProc(int code,WPARAM wParam,LPARAM lParam)

{

     if(wParam==VK_F12)//F12

     {

         ::EnumWindows ((WNDENUMPROC)EnumWindowsProc,0);

     }

 

     return CallNextHookEx(hook,code,wParam,lParam);

}

 

 

//开始hook

extern "C" __declspec(dllexport)

int Hook()

{

     hook=SetWindowsHookEx(WH_KEYBOARD,KeyProc,(HINSTANCE)hModule,0);

     if(hook==NULL)

         return -1;

     return 0;

}

 

//停止hook

extern "C" __declspec(dllexport)

int UnHook()

{

     if(UnhookWindowsHookEx (hook))

         return 0;

     else

         return -1;

}

 

BOOL APIENTRY DllMain( HANDLE hMod,

                       DWORD  ul_reason_for_call,

                       LPVOID lpReserved

                        )

{

     hModule=hMod;

    return TRUE;

}

 

Windows服务

 

我要把我的钩子做成windows服务的形式,隐藏起来

Windows服务就是 控制 面板-管理工具-服务 里面那种,那种应用程序当成系统的一个服务,没有界面,没有交互,系统一启动还没用户登录就运行,直到关闭系统或者用户停止。

 

Windows服务有一个主控线程,这个线程控制服务的 开始、暂停、停止 等动作,这个主控线程的入口是 main 函数,但他只是控制服务的执行,服务的具体事务处理不是由这个主控线程执行,这个主控线程会启动一个服务线程用来执行服务的具体事务动作,服务线程的入口原型必须是

VOID WINAPI ServiceMain(DWORD dwArgc,LPTSTR* lpszArgv);

服务线程是服务的具体事务线程,要为他设计一个工作循环不能让他退出,如果他退出了,主控线程也没有存在的必要,到时主控线程会侦测到服务线程的退出,并也会退出,那服务就完了。

在主控线程创建了服务线程后,主控线程就进入

BOOL StartServiceCtrlDispatcher(const LPSERVICE_TABLE_ENTRY lpServiceTable);

函数阻塞,这个阻塞有点像GetMessage(),他不消耗CPU时间,只是阻塞在那里等待结束或者暂停服务的命令发生。一旦结束或者暂停服务的命令发生,这个线程将被唤醒去执行

一个服务控制回调函数,控制回调函数的原型是

VOID WINAPI Handler(DWORD fdwControl);

里面可以判断发生了什么命令,比如 停止或者暂停命令。

服务控制回调函数用

SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler(LPCTSTR lpServiceName,LPHANDLER_FUNCTION lpHandlerProc);

注册。

 

还有一个安装服务的问题,要把服务安装到 控制面板-管理工具-服务 里面和从里面御除还需要一些代码

安装服务用

SC_HANDLE CreateService(

  SC_HANDLE hSCManager,

  LPCTSTR lpServiceName,

  LPCTSTR lpDisplayName,

  DWORD dwDesiredAccess,

  DWORD dwServiceType,

  DWORD dwStartType,

  DWORD dwErrorControl,

  LPCTSTR lpBinaryPathName,

  LPCTSTR lpLoadOrderGroup,

  LPDWORD lpdwTagId,

  LPCTSTR lpDependencies,

  LPCTSTR lpServiceStartName,

  LPCTSTR lpPassword

);

御除服务用

BOOL DeleteService(

  SC_HANDLE hService);

 

下面是我的windows服务的代码

 

// WinampService.cpp : 定义控制台应用程序的入口点。

//

/*++

by Lingch

2005-5-6

http://www.lingch.net

post2ling@hotmail.com

 

--*/

 

#include "stdafx.h"

 

#define SZAPPNAME "WinampService"

#define SZSERVICENAME "WinampService"

#define SZSERVICEDISPLAYNAME "WinampService"

#define SZDEPENDENCIES ""

 

void WINAPI WinampServiceMain(DWORD argc, LPTSTR * argv);

void InstallService(const char * szServiceName);

void UnInstallService(const char * szServiceName);

int Start();

int Stop();

 

SERVICE_STATUS servicestatus;

SERVICE_STATUS_HANDLE servicestatushandle;

 

typedef int (*pHook)(void);

typedef int (*pUnHook)(void);

 

pHook Hook=NULL;

pUnHook UnHook=NULL;

 

HANDLE evStop=NULL;

 

int main(int argc, char* argv[])

{

     //获取DLL输出函数

     HMODULE h=::LoadLibrary (".//WinampHook.dll");

     if(h==NULL)

     {

         printf("装载WinampHook.dll错误");

         return -1;

     }

     Hook=(pHook)::GetProcAddress (h,"Hook");

     UnHook=(pUnHook)::GetProcAddress (h,"UnHook");

     if(Hook==NULL || UnHook==NULL)

     {

         printf("获取Dll输出函数错误");

         ::FreeLibrary (h);

         return -1;

     }

 

     //安装和执行服务

     if (argc==2)

     {

         if(::strcmp(argv[1]+1, "Install")==0)

         {

              InstallService("WinampService");

              return 0;

         }

         else if(::strcmp(argv[1]+1, "UnInstall")==0)

         {

              UnInstallService("WinampService");

              return 0;

         }

     }

 

     //建立停止服务用的event

     evStop=::CreateEvent (NULL,false,false,"evStop");

     if(evStop==NULL)

     {

         printf("建立event失败");

         return -1;

     }

 

     //开始服务

     SERVICE_TABLE_ENTRY service_table_entry[] =

     {

         { "WinampService",WinampServiceMain},

         {NULL,NULL}

     };

     ::StartServiceCtrlDispatcher(service_table_entry);

 

     ::FreeLibrary (h);

     return 0;

}

 

//安装服务

void InstallService(const char * szServiceName)

{

     SC_HANDLE handle = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

     char szFilename[256];

     ::GetModuleFileName(NULL, szFilename, 255);

     SC_HANDLE hService = ::CreateService(handle, szServiceName,

         szServiceName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS,

         SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, szFilename, NULL,

         NULL, NULL, NULL, NULL);

     ::CloseServiceHandle(hService);

     ::CloseServiceHandle(handle);

}

 

//御除服务

void UnInstallService(const char * szServiceName)

{

     SC_HANDLE handle = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

     SC_HANDLE hService=::OpenService (handle,szServiceName,SC_MANAGER_ALL_ACCESS);

     ::DeleteService (hService);

     ::CloseServiceHandle(hService);

     ::CloseServiceHandle(handle);

}

//服务事件回调

void WINAPI ServiceCtrlHandler(DWORD dwControl)

{

     switch (dwControl)

     {

     case SERVICE_CONTROL_PAUSE:

        

         servicestatus.dwCurrentState = SERVICE_PAUSE_PENDING;

         servicestatus.dwCheckPoint =1;

         servicestatus.dwWaitHint =5;

         ::SetServiceStatus(servicestatushandle, &servicestatus);

         Stop();

 

         servicestatus.dwCurrentState = SERVICE_PAUSED;

         break;

 

     case SERVICE_CONTROL_CONTINUE:

         servicestatus.dwCurrentState = SERVICE_CONTINUE_PENDING;

         servicestatus.dwCheckPoint =1;

         servicestatus.dwWaitHint =5;

         ::SetServiceStatus(servicestatushandle, &servicestatus);

         Start();

 

         servicestatus.dwCurrentState = SERVICE_RUNNING;

 

         break;

 

     case SERVICE_CONTROL_STOP:

         servicestatus.dwCurrentState = SERVICE_STOP_PENDING;

         servicestatus.dwCheckPoint =1;

         servicestatus.dwWaitHint =5;

         ::SetServiceStatus(servicestatushandle, &servicestatus);

         Stop();

         servicestatus.dwCurrentState = SERVICE_STOPPED;

 

         break;

 

     case SERVICE_CONTROL_SHUTDOWN:

         Stop();

         break;

 

     case SERVICE_CONTROL_INTERROGATE:

 

         servicestatus.dwCurrentState = SERVICE_RUNNING;

         break;

     }

     ::SetServiceStatus(servicestatushandle, &servicestatus);

}

 

//服务入口点

void WINAPI WinampServiceMain(DWORD argc, LPTSTR * argv)

{

     servicestatus.dwServiceType = SERVICE_WIN32;

     servicestatus.dwCurrentState = SERVICE_START_PENDING;

     servicestatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;

     servicestatus.dwWin32ExitCode = 0;

     servicestatus.dwServiceSpecificExitCode = 0;

     servicestatus.dwCheckPoint = 0;

     servicestatus.dwWaitHint = 0;

 

     servicestatushandle =

     ::RegisterServiceCtrlHandler("WinampService", ServiceCtrlHandler);

     if (servicestatushandle == (SERVICE_STATUS_HANDLE)0)

     {

         return;

     }

 

     bool bInitialized = false;

 

     if(Start()==0)

         bInitialized = true;

     else

         bInitialized= false;

 

     servicestatus.dwCheckPoint = 0;

     servicestatus.dwWaitHint = 0;

     if (!bInitialized)

     {

         servicestatus.dwCurrentState = SERVICE_STOPPED;

         servicestatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;

         servicestatus.dwServiceSpecificExitCode = 1;

     }

     else

     {

         servicestatus.dwCurrentState = SERVICE_RUNNING;

     }

     ::SetServiceStatus(servicestatushandle, &servicestatus);

 

     while(::WaitForSingleObject (evStop,1000)==WAIT_TIMEOUT)

         ;

     return;

}

 

int Start()

{

     //安装钩子

     if(Hook()!=0)

     {

         return -1;

     }

     else

     {

         return 0;

     }

}

 

int Stop()

{

     ::SetEvent (evStop);

     //御除钩子

     if(UnHook()!=0)

         return -1;

     else

         return 0;

}

 

最后要注意的,F12右边的3个键是 Wake up Sleep Power ,小心按错。

从此我的电脑可以用F12控制Winamp播放下一首歌.

编译好打包的程序可以在我的主页上下载 http://www.lingch.net

 

“太岁头上动土”的解释

太岁”到底是什么东西?据专家介绍,太岁是一种黏菌,是介于生物和真菌之间的一种原质体生物,既有原生物特点,也有真菌特点。 是自然界中非植物、非动物和非菌类的第四种生命形式。又称肉灵芝, 进化程度介于藻...

给别人头上有条件将会给国家

  • 2010年04月24日 09:17
  • 827KB
  • 下载

CVE-2013-4694 WinAmp 5.63 栈溢出漏洞分析

WinAmp 5.63 - Stack-based BufferOverflow 前天在exploit-db找到这个漏洞的描述。闲着没事,就调试了一下。这个漏洞的触发条件是在winamp.ini中s...

缓冲区溢出分析第10课:Winamp缓冲区溢出研究

《缓冲区溢出分析》这一系列的内容是我为“i春秋”(www.ichunqiu.com)所录制的同名视频课程的讲稿汇总。每次我都是在写完课程的文档后,再依据文档内容进行课程的讲解。而本系列的内容也是从零开...
  • ioio_jy
  • ioio_jy
  • 2016年01月25日 00:47
  • 1022

不小心把JRE System Library中的某些Jar包删除了,运行Java程序提示找不到类,并且在项目的头上出现红色的感叹号

解决的方式如下: 如果是提示Java程序找不到类,那么请先查看你的Java  和javac 命令是否可用,如果可用说明不是环境变量配置的问题,反之,那就是环境变量配置问题。 例如: 进入 ...

Unity用GUI在角色头上显示名字(C#脚本)

using UnityEngine; using System.Collections; public class wenzi : MonoBehaviour { //主摄像机对象 priv...

请问Winamp风格的一起移动窗口的实现,MoveWindow()/SetWindowPos()不完美

请问Winamp风格的一起移动窗口的实现,MoveWindow()/SetWindowPos()不完美 (50分) [问题点数:50分,结帖人ecai]   收藏 ecai ecai 等...

如何把手头上的apk安装到电视上

在使用电视的过程中,经常遇到这种情况,需要把一个现成的apk安装到电视上去。大概有两种方式: 1.直接用U盘安装2.借助远程助手 很多用户苦于手头没有U盘,这时可以借助一些电视助手的远程工具进行安...

关于大屏幕适配,不要把工作都压到程序员一个人头上

文章内容援引知乎上一个来自手机淘宝设计师pigtwo的回答,原题链接来自知乎。 apple大屏幕出现以后,可能google搜索率上升较明显的就是iOS 的autolayout,但是我并不想说这个。因为...
  • zsc0504
  • zsc0504
  • 2015年04月30日 14:53
  • 569
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Winamp头上动土
举报原因:
原因补充:

(最多只允许输入30个字)