打造Windows信使服务程序
信使服务是Windows 2000/XP下面的一种通信服务,通过它我们可以在网上象QQ一样进行实时的交流,但和QQ不同的是信使服务不需要拥有一个号码,它只要知道对方的IP地址--如果在局域网中,只要知道对方的计算机名就可以了。我们可以利用"net send"在命令行下面发送消息,也可以通过"控制面板-管理工具-计算机管理"中的" *** 作"菜单里面的"所有任务-发送控制台消息…"来发送。接收方的计算机要求必须是Win2000/XP,因为 Win9x本身是不提供信服服务的,除非你手动启动了WinPopup.。程序编译运行后,在Windows2000下启动控制台,输入:netsend 192.168.0.10 127.0.0.1 "我的信使程序!"运行以上命令后,在Windows桌面上将d出一个对话框,其上标有"我的信使程序!"字符和相关IP信息。
一、实现方法
信使服务为大家在网络上提供了简便、快捷的'聊天方式,所以网上也有一些改进了的信使服务发送程序,使信息的发送可以按照自己的意愿来进行而不受微软提供的"net send"所限制。如果你经常上网,也许会收到通过信使服务发来的广告或者其他莫名其妙的信息,而在"信使服务"窗口中所显示出来的发送方的计算机名或者IP地址和你自己的竟然一样。如果你遇到过这种情况,那么你想不想知道这种改进了的信使服务发送程序是怎么实现的呢?
本实例将要带你去剖析一下Windows的信使服务,我们将从一个程序员的角度来看看这个信使服务发送程序是如何实现的。其实实现信使服务的整个过程很简单,我们只需要调用一个API函数:
NET_API_STATUS NetMessageBufferSend(
LPWSTR servername,
LPWSTR msgname,
LPWSTR fromname,
LPBYTE buf,
DWORD buflen
)
其中该函数的参数说明如下:
servername:计算机名,该函数将在指定的这个计算机上面执行。如果为NULL,则表示为本地计算机,通常我们都将其设置为NULL。
msgname:目标计算机的名字或者IP地址。
fromname:源计算机的名字或者IP地址。
buf:指向一个信息缓冲区的指针,在这里你可以填上你想发送的信息。要注意的是,这个参数用的是Unicode编码。
buflen:上述信息缓冲区中的字节数。
现在函数已经介绍完了,通过这个函数我们可以发现如果想实现匿名的信使服务实在是太简单--只需要在fromname中填上一个假冒的计算机名或者IP地址就行了。下面给出一个实际例子,在这个程序中,我们从命令行参数接受目标计算机和源计算机名字(IP地址),同时我们还可以设定信息发送的次数。
二、编程步骤
1、启动Visual C++6.0,新建项目netsend,选择控制台模式
2、在项目中添加新文件netsend.c
3、添加代码,编译运行程序。
三、程序代码
#define _UNICODE
#define UNICODE
#include
#include
#include
#include
#include
#include
#include
#pragma comment(lib,"netapi32.lib")
int wmain(int argc, wchar_t *argv[])
{
int count
wchar_t *wdest, *wfrom, *buffer,*wtarget
DWORD dwReturn
if((argc 5))
{
printf("Usge: %S [Count] ",argv[0])
printf("Count: Count means number of times to send message,default is 1. ")
return 0
}
wdest = argv[1]//目标计算机
wfrom = argv[2]//源计算机
buffer = argv[3]//发送的信息
count = _wtoi(argv[4])//发送次数,缺省为1次
if(count ==0)
count = 1
printf("count = %d ",count)
dwReturn = NetMessageBufferSend(NULL, wdest, wfrom,
(LPBYTE)buffer, 2*lstrlen(buffer))//因为buffer是Unicode编码,所以需要乘以2
if(dwReturn == NERR_Success)
{
printf("Send OK!")
while(count1)
{
NetMessageBufferSend(
这两天想学习关于如何编写windows服务程序的知识,就上网查了些资料。看了一篇文章《用C 语言编写Windows 服务程序的五个步骤》。但可能由于译者的疏忽,忘记了将关键代码放入该文档,导致初学者可能会看不懂。所以又查阅了相关资料。自己完成了《用C 语言编写Windows 服务程序的五个步骤》中的样例程序。
在本文中我希望能给初学者一些帮助,大致讲一下编写windows服务程序需要的知识。
首先Microsoft Windows 服务(即,以前的NT 服务)使您能够创建在它们自己的Windows 会话中可长时间运行的可执行应用程序。这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面。这使服务非常适合在服务器上使用,或任何时候,为了不影响在同一台计算机上工作的其他用户,需要长时间运行功能时使用。还可以在不同于登录用户的特定用户帐户或默认计算机帐户的安全上下文中运行服务。
服务是有状态的,当我们使用windows自带的服务管理程序sc.exe查看服务状态时可以显示服务的当前状态,这个状态是由我们在程序代码中进行控制的。你最好在服务初始化的时候将服务设置为SERVICE_START_PENDING,当初始化完毕时设为SERVICE_RUNNING,这些状态是系统自定义的状态,可通过msdn查看其他状态。这个状态信息你会在sc.exe中看到。
在编写windows服务程序过程中你需要关注的函数有:
1.首先是main函数,由于windows服务不需要界面,所以大部分程序为win32控制台应用程序,所以程序主函数为main 而不是WinMain()。在主函数要做的主要工作就是初始化一个SERVICE_TABLE_ENTRY 分派表结构体,然后调用StartServiceCtrlDispatcher()这将把调用进程的主线程转换为控制分派器。该分派器启动一个新线程,该线程运行分派表中对应于你的服务的ServiceMain()函数。ServiceMain()函数将在下面提到。
此过程示例代码如下:
SERVICE_TABLE_ENTRY entrytable[2]
entrytable[0].lpServiceName="testservice"
entrytable[0].lpServiceProc=(LPSERVICE_MAIN_FUNCTION)ServiceMain
entrytable[1].lpServiceName=NULL
entrytable[1].lpServiceProc=NULL
StartServiceCtrlDispatcher(entrytable)
在这之后系统将自动创建一个线程去执行ServiceMain函数的内容,你应该将你要执行的任务在ServiceMain中循环,这样服务就开始运行了。
2.ServiceMain函数为void WINAPI ServiceMain(int argc, char** argv)格式的函数,函数名字可以任意定义。它的作用就是:将你需要执行的任务放到该函数中循环执行即可。这就是服务程序的工作函数。在ServiceMain执行你的任务前,需要给SERVICE_TABLE_ENTRY 分派表结构体进行赋值,注意由于此时服务还没有开始执行你的任务所以我们将服务的状态设置为SERVICE_START_PENDING,即正在初始化。我们进行如下赋值:
servicestatus.dwServiceType = SERVICE_WIN32
servicestatus.dwCurrentState = SERVICE_START_PENDING
servicestatus.dwControlsAccepted=SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEPT_STOP
//在本例中只接受系统关机和停止服务两种控制命令
servicestatus.dwWin32ExitCode = 0
servicestatus.dwServiceSpecificExitCode = 0
servicestatus.dwCheckPoint = 0
servicestatus.dwWaitHint = 0
hstatus = ::RegisterServiceCtrlHandler("testservice", CtrlHandler)
CtrlHandler为void WINAPI CtrlHandler(DWORD request)型的函数,函数名字可以任意设定。将在下一点讲到。
Hstatus为SERVICE_STATUS_HANDLE类型的全局变量。当需要改变服务状态时SetServiceStatus()函数需要它做为参数来标识一个服务。
3. void WINAPI CtrlHandler(DWORD request),函数的主要功能是,接收系统传递的控制命令,比如当你通过sc.exe关闭服务时,该函数会收到SERVICE_CONTROL_STOP消息,你就可以对服务进行必要的管理。在本例子程序中就只接收SERVICE_ACCEPT_SHUTDOWN和SERVICE_ACCEPT_STOP消息,这是通过前面给servicestatus赋值设定的。
这样一个基本的服务程序就完成了。
下面贴出我的示例代码仅供参考。该代码在vs2008中调试通过。本文结束的时候会附上如何安装服务。
#include <stdio.h>
#include <Windows.h>
#define SLEEP_TIME 5000 //间隔时间
#define FILE_PATH "C:\\log.txt" //信息输出文件
bool brun=false
SERVICE_STATUS servicestatus
SERVICE_STATUS_HANDLE hstatus
int WriteToLog(char* str)
void WINAPI ServiceMain(int argc, char** argv)
void WINAPI CtrlHandler(DWORD request)
int InitService()
int WriteToLog(char* str)
{
FILE* pfile
fopen_s(&pfile,FILE_PATH,"a+")
if (pfile==NULL)
{
return -1
}
fprintf_s(pfile,"%s\n",str)
fclose(pfile)
return 0
}
void WINAPI ServiceMain(int argc, char** argv)
{
servicestatus.dwServiceType = SERVICE_WIN32
servicestatus.dwCurrentState = SERVICE_START_PENDING
servicestatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEPT_STOP//在本例中只接受系统关机和停止服务两种控制命令
servicestatus.dwWin32ExitCode = 0
servicestatus.dwServiceSpecificExitCode = 0
servicestatus.dwCheckPoint = 0
servicestatus.dwWaitHint = 0
hstatus = ::RegisterServiceCtrlHandler("testservice", CtrlHandler)
if (hstatus==0)
{
WriteToLog("RegisterServiceCtrlHandler failed")
return
}
WriteToLog("RegisterServiceCtrlHandler success")
//向SCM 报告运行状态
servicestatus.dwCurrentState = SERVICE_RUNNING
SetServiceStatus (hstatus, &servicestatus)
//下面就开始任务循环了,你可以添加你自己希望服务做的工作
brun=true
MEMORYSTATUS memstatus
char str[100]
memset(str,'\0',100)
while (brun)
{
GlobalMemoryStatus(&memstatus)
int availmb=memstatus.dwAvailPhys/1024/1024
sprintf_s(str,100,"available memory is %dMB",availmb)
WriteToLog(str)
Sleep(SLEEP_TIME)
}
WriteToLog("service stopped")
}
void WINAPI CtrlHandler(DWORD request)
{
switch (request)
{
case SERVICE_CONTROL_STOP:
brun=false
servicestatus.dwCurrentState = SERVICE_STOPPED
break
case SERVICE_CONTROL_SHUTDOWN:
brun=false
servicestatus.dwCurrentState = SERVICE_STOPPED
break
default:
break
}
SetServiceStatus (hstatus, &servicestatus)
}
void main()
{
SERVICE_TABLE_ENTRY entrytable[2]
entrytable[0].lpServiceName="testservice"
entrytable[0].lpServiceProc=(LPSERVICE_MAIN_FUNCTION)ServiceMain
entrytable[1].lpServiceName=NULL
entrytable[1].lpServiceProc=NULL
StartServiceCtrlDispatcher(entrytable)
}
如何安装服务:
运行命令提示符cmd.exe
输入sc create testservicename binpath= D:\test.exe
输入sc start testservicename 启动服务
输入sc query 会在最底部显示你的服务当前的状态
输入sc stop testservicename 停止服务
输入sc delete testservicename删除服务,该服务将在下次重启后删除,在重启之前将不能注册同一个名字的服务。
第一步:主函数和全局定义第二步:ServiceMain
函数
第三步:处理控制请求
第四步:安装和配置服务
第五步:测试服务
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)