Windows服务模板

最近写了几个Windows服务,整理了一下服务模板,以备以后使用。
说实话,这些代码是n年前写的了,用了也n久了,但还是发现了一个明显的bug。
其中,有些内容,写的并不好,如静态指针pThis,大家还是尽量不要使用了。

1、服务入口myXXX.cpp

//===================================================
//XXX入口函数

//===================================================
#include <windows.h>
#include <tchar.h>
#include <stdio.h>

#include "XXX.h"

//===================================================
//入口函数
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
	UNREFERENCED_PARAMETER(hInstance);
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(nCmdShow);

	//获取可执行文件路径
	TCHAR szPath[MAX_PATH];
	TCHAR szLog[MAX_PATH];
	GetModuleFileName(NULL,szPath,MAX_PATH);

	//生成日志目录
	TCHAR *p = _tcsrchr(szPath, '\\');
	if(p!=NULL)*p='\0';
	if (_tcslen(szPath) + _tcslen(TEXT("Log\\")) <= MAX_PATH)
	{
		_stprintf_s(szLog, TEXT("%s\\%s"), szPath, TEXT("Log\\"));
	}
	else
	{
		return -1;
	}

	//判断运行参数
	XXX myService(szLog, true);
	myService.ParseParameters(lpCmdLine);

	return 0;
}

//===================================================

&#91;/code&#93;

2、服务类XXX.h
&#91;code lang="cpp"&#93;
//===================================================
//XXX 服务类封装
#include <windows.h>

//===================================================
//预定义
#define MAX_THREAD_COUNT 64

//===================================================
//XXX CLASS
class XXX
{
private:
	HANDLE	hSemaphore;
	CRITICAL_SECTION cs;

	bool		m_bLog;
	const		TCHAR	*m_szLogPath;
	LPCTSTR		lpServiceName;
	LPCTSTR		lpDisplayName;
	
	SERVICE_STATUS_HANDLE	gSvcStatusHandle;
	SERVICE_STATUS		gSvcStatus;
	HANDLE			ghSvcStopEvent;

	static	XXX	*pThis;
	bool	m_bFinished;

//构造与析构函数
public:
	XXX();
	XXX(const TCHAR *cLogPath,const bool bLog);
	~XXX();

//服务相关函数
private:
	static	void WINAPI ServiceMain(DWORD dwArgc,LPTSTR *lpszArgv);
	static	void WINAPI SvcCtrlHandler(DWORD dwCtrl);

	void	SvcInitAndRun(DWORD dwArgc,LPTSTR *lpszArgv);
	void	Show_Me_Err();

public:
	int		AddService();
	int		DelService();
	void	LogEvent(LPCTSTR lpMsg);
	void	ParseParameters(LPSTR lpCmdLine);
};

//===================================================

2、服务类XXX.cpp

//===================================================
//XXX服务类的封装实现

//===================================================
//头文件
#include "XXX.h"

//===================================================
//静态成员
XXX *XXX::pThis = NULL;

//===================================================
//构造函数
XXX::XXX(const TCHAR *cLogPath,const bool bLog)
{
	hSemaphore=NULL;

	m_bLog=bLog;
	lpServiceName=TEXT("XXX");
	lpDisplayName=TEXT("XXX");

	ghSvcStopEvent=NULL;
	m_szLogPath=cLogPath;
	m_bFinished = false;

	pThis=this;
}

//===================================================
//析构函数
XXX::~XXX()
{
}

//===================================================
//入口函数
void XXX::ParseParameters(LPSTR lpCmdLine)
{
	//判断运行参数
	//设置SERVICE_TABLE_ENTRY
	SERVICE_TABLE_ENTRY DispatchTable[]=
	{
		{TEXT("XXX"),(LPSERVICE_MAIN_FUNCTION)ServiceMain},
		{NULL,NULL}
	};

	//处理输入参数
	if((0==_stricmp(lpCmdLine,"/i")) || 0==_stricmp(lpCmdLine,"-i"))
	{
		//安装服务"/i -i"
		AddService();
		return;
	}
	else if((0==_stricmp(lpCmdLine,"/u")) || 0==_stricmp(lpCmdLine,"-u"))
	{
		//删除服务"/u -u"
		DelService();
		return;
	}
	else if((0==_stricmp(lpCmdLine,"/h")) || 0==_stricmp(lpCmdLine,"-h"))
	{
		//提示运行参数"/h -h"
		MessageBox(NULL,TEXT(" Install Service  : -i /i\n UnInstall Service: -u /u"),TEXT("XXX"),MB_OK);
		return;
	}
	else
	{
		//设置调试断点
		//DebugBreak();

		//StartServiceCtrlDispatcher运行服务
		if(!StartServiceCtrlDispatcher(DispatchTable))
		{
			LogEvent(TEXT("XXX: Failed To Start!"));
		}
		else
		{
			LogEvent(TEXT("XXX: Started!"));
		}
	}

	return;
}

//===================================================
//添加服务
int XXX::AddService()
{
	//SCMD与Service的句柄
	SC_HANDLE	hSCManager=NULL;
	SC_HANDLE	hService=NULL;

	//连接到SCM,打开SCMD
	hSCManager=OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);
	if(NULL==hSCManager)
	{
		MessageBox(NULL,TEXT("OpenSCManager Failed"),TEXT("XXX"),MB_OK);
		Show_Me_Err();
		return 1;
	}

	//检查服务是否安装
	hService=OpenService(hSCManager,lpServiceName,SERVICE_ALL_ACCESS);
	if(NULL==hService)
	{
		//若服务不存在,创建服务
		TCHAR szFilePath[MAX_PATH];
		GetModuleFileName(GetModuleHandle(NULL),szFilePath,MAX_PATH);

		//自动启动
		hService=CreateService(hSCManager,lpServiceName,lpDisplayName,SERVICE_ALL_ACCESS,
				SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
				SERVICE_AUTO_START,SERVICE_ERROR_NORMAL,szFilePath,NULL,NULL,NULL,NULL,NULL);

		if(NULL==hService)
		{
			MessageBox(NULL,TEXT("CreateService Failed"),TEXT("XXX"),MB_OK);
			Show_Me_Err();
			CloseServiceHandle(hSCManager);
			return 1;
		}
	}
	else
	{
		MessageBox(NULL,TEXT("Service Already Installed!"),TEXT("XXX"),MB_OK);
		CloseServiceHandle(hService);
		CloseServiceHandle(hSCManager);
		return 1;
	}

	//关闭句柄
	CloseServiceHandle(hService);
	CloseServiceHandle(hSCManager);

	LogEvent(TEXT("XXX: AddService Successed!"));
	//ForDebug
	//MessageBox(NULL,TEXT("AddService Successed!"),TEXT("XXX"),MB_OK);
	return 0;
}

//===================================================
//删除服务
int XXX::DelService()
{
	//SCMD与Service的句柄
	SC_HANDLE	hSCManager=NULL;
	SC_HANDLE	hService=NULL;
	SERVICE_STATUS	ServiceStatus;

	//获取SCM句柄,打开SCMD
	hSCManager=OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);
	if(NULL==hSCManager)
	{
		MessageBox(NULL,TEXT("OpenSCManager Failed"),TEXT("XXX"),MB_OK);
		Show_Me_Err();
		return 1;
	}

	//打开服务句柄
	hService=OpenService(hSCManager,lpServiceName,SERVICE_ALL_ACCESS|DELETE);
	if(NULL==hService)
	{
		MessageBox(NULL,TEXT("OpenService Failed"),TEXT("XXX"),MB_OK);
		Show_Me_Err();
		CloseServiceHandle(hSCManager);
		return 1;
	}

	//查询服务状态
	if(0==QueryServiceStatus(hService,&ServiceStatus))
	{
		MessageBox(NULL,TEXT("QueryServiceStatus Failed"),TEXT("XXX"),MB_OK);
		Show_Me_Err();
		CloseServiceHandle(hService);
		CloseServiceHandle(hSCManager);
		return 1;
	}
	else
	{
		//如果服务正在运行,则停止
		if(SERVICE_STOPPED!=ServiceStatus.dwCurrentState)
		{
			if(0==ControlService(hService,SERVICE_CONTROL_STOP,&ServiceStatus))
			{
				MessageBox(NULL,TEXT("ControlService Failed"),TEXT("XXX"),MB_OK);
				Show_Me_Err();
				CloseServiceHandle(hService);
				CloseServiceHandle(hSCManager);
				return 1;
			}
		}

		//删除服务
		if(0==DeleteService(hService))
		{
			MessageBox(NULL,TEXT("DeleteService Fialed!"),TEXT("XXX"),MB_OK);
			Show_Me_Err();
			CloseServiceHandle(hService);
			CloseServiceHandle(hSCManager);
			return 1;
		}
	}

	CloseServiceHandle(hService);
	CloseServiceHandle(hSCManager);

	LogEvent(TEXT("XXX: DelService Successed!"));
	//ForDebug
	//MessageBox(NULL,TEXT("DelService Successed!"),TEXT("XXX"),MB_OK);
	return 0;
}

//===================================================
//服务入口函数
void WINAPI XXX::ServiceMain(DWORD dwArgc,LPTSTR *lpszArgv)
{
	//注册服务控制程序
	pThis->gSvcStatusHandle=RegisterServiceCtrlHandler(pThis->lpServiceName,SvcCtrlHandler);
	if(0==pThis->gSvcStatusHandle)
	{
		//注册失败,返回
		pThis->LogEvent(TEXT("XXX: RegisterServiceCtrlHandler Failed"));
		return;
	}

	//设置服务状态SERVICE_START_PENDING
	pThis->gSvcStatus.dwServiceType=SERVICE_WIN32_OWN_PROCESS;
	pThis->gSvcStatus.dwCurrentState=SERVICE_START_PENDING;
	pThis->gSvcStatus.dwControlsAccepted=0;
	pThis->gSvcStatus.dwWin32ExitCode=NO_ERROR;
	pThis->gSvcStatus.dwServiceSpecificExitCode=0;
	pThis->gSvcStatus.dwCheckPoint=0;
	pThis->gSvcStatus.dwWaitHint=10000;
	SetServiceStatus(pThis->gSvcStatusHandle,&(pThis->gSvcStatus));

	//运行服务函数
	pThis->SvcInitAndRun(dwArgc, lpszArgv);

	//停止服务
	pThis->gSvcStatus.dwCurrentState=SERVICE_STOPPED;
	pThis->gSvcStatus.dwWaitHint=0;
	pThis->gSvcStatus.dwControlsAccepted=SERVICE_ACCEPT_STOP;
	pThis->gSvcStatus.dwCheckPoint=0;
	SetServiceStatus(pThis->gSvcStatusHandle,&(pThis->gSvcStatus));
}

//===================================================
//服务函数
void XXX::SvcInitAndRun(DWORD dwArgc,LPTSTR *lpszArgv)
{
	UNREFERENCED_PARAMETER(dwArgc);
	UNREFERENCED_PARAMETER(lpszArgv);

	//创建事件
	ghSvcStopEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
	if(ghSvcStopEvent==NULL)
	{
		//创建事件失败,停止服务,返回
		gSvcStatus.dwCurrentState=SERVICE_STOPPED;
		gSvcStatus.dwWaitHint=0;
		gSvcStatus.dwControlsAccepted=SERVICE_ACCEPT_STOP;
		gSvcStatus.dwCheckPoint=0;
		SetServiceStatus(gSvcStatusHandle,&gSvcStatus);
		return;
	}

	//设置服务状态SERVICE_RUNNING
	gSvcStatus.dwCurrentState=SERVICE_RUNNING;
	gSvcStatus.dwWaitHint=0;
	gSvcStatus.dwControlsAccepted=SERVICE_ACCEPT_STOP;
	gSvcStatus.dwCheckPoint=0;
	SetServiceStatus(gSvcStatusHandle,&gSvcStatus);

	//设置断点
	while (!IsDebuggerPresent()) 
	{ 
		Sleep(100); 
	} 
	DebugBreak();

	//创建信号量
	hSemaphore=CreateSemaphore(NULL,MAX_THREAD_COUNT,MAX_THREAD_COUNT,TEXT("NAS_TEST_SEMAPHORE"));
	if(NULL==hSemaphore)  
	{
		return;
	}

	//初始化关键区
	InitializeCriticalSection(&cs);

	//处理请求
	while(!m_bFinished)
	{
		//进入关键区
		EnterCriticalSection(&cs);
		while (!m_bFinished)
		{
			if(WAIT_TIMEOUT==WaitForSingleObject(ghSvcStopEvent,100))
			{
				//这里处理你的线程逻辑
				//每个线程启动的时候WaitForSingleObject占用一个信号量
				//线程退出时ReleaseSemaphore释放信号量

			}
			else
			{
				LogEvent(TEXT("XXX is gonging down."));
				m_bFinished = true;
			}
		}
		//退出关键区
		LeaveCriticalSection(&cs);

		if (!m_bFinished)
		{
			//等待线程0.2秒
			Sleep(200);
		}
	}

	//等待所有线程结束
	WaitForMultipleObjects(MAX_THREAD_COUNT, &hSemaphore, true, 10000L);
	//WaitForMultipleObjects(1,&hSemaphore,true,INFINITE);

	//释放信号量
	CloseHandle(hSemaphore);
	
	//释放关键区
	DeleteCriticalSection(&cs);

	return;
}

//===================================================
//服务控制函数
void WINAPI XXX::SvcCtrlHandler(DWORD dwCtrl)
{
	switch(dwCtrl)
	{
		case SERVICE_CONTROL_STOP:
			//设置服务状态SERVICE_ACCEPT_STOP
			pThis->gSvcStatus.dwCurrentState=SERVICE_STOP_PENDING;
			pThis->gSvcStatus.dwControlsAccepted=SERVICE_ACCEPT_STOP;
			pThis->gSvcStatus.dwCheckPoint=0;
			pThis->gSvcStatus.dwWaitHint=10000;
			SetServiceStatus(pThis->gSvcStatusHandle,&(pThis->gSvcStatus));
			//触发事件
			SetEvent(pThis->ghSvcStopEvent);
			break;
		case SERVICE_CONTROL_PAUSE:
			break;
		case SERVICE_CONTROL_CONTINUE:
			break;
		case SERVICE_CONTROL_INTERROGATE:
			break;
		case SERVICE_CONTROL_SHUTDOWN:
			break;
		default:
			pThis->LogEvent(TEXT("XXX: Bad Service Request!"));
	}
}

//===================================================
//在事件查看器中记录消息
void XXX::LogEvent(LPCTSTR lpMsg)
{
	HANDLE	hEventSource;
	LPCTSTR	lpszStrings[1];

	lpszStrings[0]=lpMsg;

	//在“事件查看器->应用程序->信息”中进行记录
	hEventSource=RegisterEventSource(NULL,lpServiceName);
	if(NULL!=hEventSource)
	{
		ReportEvent(hEventSource,EVENTLOG_INFORMATION_TYPE,0,0,NULL,1,0,lpszStrings,NULL);
		DeregisterEventSource(hEventSource);
	}
}

//===================================================
//输出GetLastErr()信息
void XXX::Show_Me_Err()
{
	if(!m_bLog)return;

	DWORD	dwErrNo;
	LPTSTR	lpBuffer;
	dwErrNo=GetLastError();
	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,dwErrNo,LANG_NEUTRAL,(LPTSTR)&lpBuffer,0,NULL);
	MessageBox(NULL,lpBuffer,TEXT("XXX GetLastError"),MB_OK);
	LocalFree(lpBuffer);
}

//===================================================

C#写WindowsService

1、新建一个Windows Service项目,重命名服务类名

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;

namespace ServiceTestCS
{
    public partial class ServiceTestCS : ServiceBase
    {
        //安装卸载时,最好用绝对路径哦
        //Installutil NASTestCS
        //Installutil -u NASTestCS

        private const int waitInterval = 200;
        private AutoResetEvent stopEvent = new AutoResetEvent(false);
        private AutoResetEvent pauseContinueEvent = new AutoResetEvent(false);
        private Thread workingThread = null;

        public NASTestCS()
        {
            InitializeComponent();
            this.CanPauseAndContinue = true;
            this.CanShutdown = true;
        }

        protected override void OnStart(string[] args)
        {
            #if DEBUG
            /*
            //等待调试器,但调试Windows服务时,提示没有加载符号
            while (!Debugger.IsAttached)
            {
                Thread.Sleep(waitInterval);
            }
            */
            Debugger.Launch();
            #endif

            workingThread = new Thread(new ThreadStart(MaiLoop));
            workingThread.Start();
        }

        protected override void OnStop()
        {
            stopEvent.Set();
        }

        protected override void OnShutdown()
        {
            stopEvent.Set();
        }

        protected override void OnPause()
        {
            pauseContinueEvent.Set();
        }

        protected override void OnContinue()
        {
            pauseContinueEvent.Set();
        }

        protected void MaiLoop()
        {
            Boolean bEnd = false;
            while (!bEnd)
            {
                //处理你的正常事务
                //但要保证下面的代码,可以定期被调用到
                //......

                //截获停止事件
                if(stopEvent.WaitOne(waitInterval))
                {
                    bEnd = true;
                    break;
                }

                //截获暂停事件
                if (pauseContinueEvent.WaitOne(waitInterval))
                {
                    //等待继续时间
                    while (!pauseContinueEvent.WaitOne(waitInterval))
                    {
                        //少占用一些资源
                        Thread.Sleep(waitInterval);

                        //暂停时也可以退出
                        if (stopEvent.WaitOne(waitInterval))
                        {
                            bEnd = true;
                            break;
                        }
                    }
                }
            }
        }
    }
}

2、服务设计界面右击,添加Service Installer
配置Service Installer,配置服务类名称,显示名称,启动类型,登录用户,服务依赖等
但如果要可以与桌面交互的话,需要添加代码实现。
双击serviceInstaller,添加下面方法:

        using System.Management;

        private void serviceInstaller_AfterInstall(object sender, InstallEventArgs e)
        {
            //要用LocalSystem用户登录才有效哦
            ManagementObject wmiService = new ManagementObject(string.Format("Win32_Service.Name='{0}'", this.serviceInstaller.ServiceName));
            ManagementBaseObject changeMethod = wmiService.GetMethodParameters("Change");
            changeMethod["DesktopInteract"] = true;
            ManagementBaseObject OutParam = wmiService.InvokeMethod("Change", changeMethod, null);
        }

3、安装与反安装,用命令行实现

rem 安装服务
%PATH_TO_INSTALLUTIL%\installutil.exe %PATH_TO_SERVICE%\NASTestCS.exe
rem 卸载服务
%PATH_TO_INSTALLUTIL%\installutil.exe -u %PATH_TO_SERVICE%\NASTestCS.exe

使用命名管道实现进程间通信(下)

1、服务端C#

        private const String MY_PIPE_NAME = "__MY__PIPE__TEST__";
        private const int BUFFER_SIZE = 1024;
        NamedPipeServerStream pipe;
        StreamWriter writer;
        String msg = "Message is comming";

        private void PipeCreate()
        {
            if (pipe != null && pipe.IsConnected)
            {
                pipe.Close();
            }

            pipe = new NamedPipeServerStream(MY_PIPE_NAME, PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.None);
            

            if (pipe == null)
            {
                textBox1.Text = textBox1.Text + "Pipe is null";
                return;
            }

            if (pipe.IsConnected)
            {
                textBox1.Text = textBox1.Text + "Pipe is already connected";
                return;
            }

            pipe.WaitForConnection();

            textBox1.Text = textBox1.Text + "Pipe is ready to connect\r\n";
        }

        private void PipeWrite()
        {
            if (pipe!=null && pipe.IsConnected)
            {
                if(writer==null)
                {
                    //关闭BOM(Byte Order Mark 0xfeff)
                    UnicodeEncoding unicodeWithoutBom = new System.Text.UnicodeEncoding(false, false);
                    writer = new StreamWriter(pipe, unicodeWithoutBom);
                    //写完后直接flush,会阻塞
                    writer.AutoFlush = true;
                }
                writer.Write(msg);

                textBox1.Text = textBox1.Text + "Pipe message sent \r\n";
            }
            else
            {
                textBox1.Text = textBox1.Text + "Pipe is not connected \r\n";
            }
        }

        private void PipeClose()
        {
            if (pipe!=null && pipe.IsConnected)
            {
                writer.Close();
                pipe.Close();

                writer = null;
                pipe = null;
            }
        }

2、客户端C#

        private const String MY_PIPE_NAME =  "__MY__PIPE__TEST__";
        private const int BUFFER_SIZE = 1024;
        NamedPipeClientStream pipe;
        StreamReader reader;

        private void PipeConnect()
        {
            if (pipe != null && pipe.IsConnected)
            {
                pipe.Close();
            }
            pipe = new NamedPipeClientStream(".", MY_PIPE_NAME, PipeDirection.InOut);

            if (pipe != null)
            {
                if (!pipe.IsConnected)
                {
                    pipe.Connect();
                    textBox1.Text = textBox1.Text + "\r\npipe is connected";
                }
                else
                {
                    textBox1.Text = textBox1.Text + "\r\npipe is already connected";
                }
            }
            else
            {
                textBox1.Text = textBox1.Text + "\r\npipe is null";
            }

            
        }

        private void PipeRead()
        {
            if (pipe.IsConnected)
            {
                if (reader == null)
                {
                    reader = new StreamReader(pipe, Encoding.Unicode);
                }

                char[] buffer = new char[BUFFER_SIZE];
                int byteRead = reader.Read(buffer, 0, BUFFER_SIZE);
                String msgTxt = new String(buffer, 0, byteRead);
                textBox1.Text = textBox1.Text + "\r\nPipe msg received: " + msgTxt;
            }
            else
            {
                textBox1.Text = textBox1.Text + "\r\nPipe is not connected";
            }
        }

        private void PipeClose()
        {
            if (reader != null)
            {
                reader.Close();
                reader = null;
            }
            if (pipe != null && pipe.IsConnected)
            {
                pipe.Close();
            }

            textBox1.Text = textBox1.Text + "\r\npipe is closed";
        }

使用命名管道实现进程间通信(上)

1、服务端MFC

#define MY_PIPE_NAME L"\\\\.\\pipe\\__MY__PIPE__TEST__"
#define BUFSIZE 1024

HANDLE m_hPipe;
BOOL m_bConnected;
int m_bMsgNum;

//使用ConnectNamedPipe会阻塞,直到客户端进行连接
//不使用ConnectNamedPipe则不会阻塞,但一样可以做后续操作
void XXX::PipeCreate()
{
	m_hPipe = CreateNamedPipe(
		MY_PIPE_NAME,             // pipe name 
		PIPE_ACCESS_DUPLEX,       // read/write access 
		PIPE_TYPE_MESSAGE |       // message type pipe 
		PIPE_READMODE_MESSAGE |   // message-read mode 
		PIPE_WAIT,                // blocking mode 
		1,                        // max. instances  
		BUFSIZE,                  // output buffer size 
		BUFSIZE,                  // input buffer size 
		0,                        // client time-out 
		NULL);                    // default security attribute 

	if (m_hPipe == INVALID_HANDLE_VALUE)
	{
		::MessageBox(NULL, L"CreateNamedPipe Error", L"CreateNamedPipe", MB_OK);
	}
	else
	{
		::MessageBox(NULL, L"CreateNamedPipe OK", L"CreateNamedPipe", MB_OK);
	}

	m_bConnected = ConnectNamedPipe(m_hPipe, NULL);

	if (m_bConnected)
	{
		::MessageBox(NULL, L"ConnectNamedPipe OK", L"ConnectNamedPipe", MB_OK);
	}
	else
	{
		::MessageBox(NULL, L"ConnectNamedPipe Error", L"ConnectNamedPipe", MB_OK);

	}

	m_bMsgNum = 0;
}

//WriteFile会阻塞,等待客户端读取完毕
void XXX::PipeWrite()
{
	DWORD	dwWritten;
	TCHAR	buffer[BUFSIZE];
	int n = sizeof(buffer);

	_stprintf_s(buffer, L"This the %d message", m_bMsgNum++);
	if (!WriteFile(m_hPipe, buffer, n, &dwWritten, NULL))
	{
		::MessageBox(NULL, L"WriteFile Failed", L"WriteFile", MB_OK);
	}
}

//两边都关闭,才可以重新建立管道
void XXX::PipeClose()
{
	if (m_bConnected)
	{
		DisconnectNamedPipe(m_hPipe);
		m_bConnected = FALSE;
	}
	
	if (m_hPipe!=NULL && m_hPipe != INVALID_HANDLE_VALUE)
	{ 
		CloseHandle(m_hPipe);
		m_hPipe=NULL;
	}
}

2、客户端MFC

#define MY_PIPE_NAME L"\\\\.\\pipe\\__MY__PIPE__TEST__"
#define BUFSIZE 1024
HANDLE m_hPipe;
BOOL m_bConnected;

//WaitNamedPipe会等待ConnectNamedPipe
void XXX::PipeConnect()
{
	if (WaitNamedPipe(MY_PIPE_NAME, NMPWAIT_WAIT_FOREVER) == 0)
	{
		MessageBox(L"WaitNamedPipe failed");
		return;
	}

	m_hPipe = CreateFile(MY_PIPE_NAME,
		GENERIC_READ,
		0,
		NULL, OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (m_hPipe == INVALID_HANDLE_VALUE)
	{
		m_bConnected = FALSE;
		::MessageBox(NULL, L"CreateFile Error", L"CreateNamedPipe", MB_OK);
	}
	else
	{
		m_bConnected = TRUE;
		::MessageBox(NULL, L"CreateFile OK", L"CreateNamedPipe", MB_OK);
	}
}

//ReadFile会阻塞等待写入
void XXX::PipeRead()
{
	DWORD	dwBytesRead;
	TCHAR	buffer[BUFSIZE];
	int bufsize = sizeof(buffer);

	memset(buffer, 0x00, bufsize);
	if (m_bConnected)
	{
		//C#程序不处理的话,第一次读会读到BOM(Byte Order Mark 0xfeff)
		if (ReadFile(m_hPipe, buffer, bufsize, &dwBytesRead, NULL))
		{
			::MessageBox(NULL, buffer, L"ReadFile", MB_OK);
		}
	}
}

//两边都关闭,才可以重新建立管道
void XXX::PipeClose()
{
	if (m_hPipe!=NULL && m_hPipe != INVALID_HANDLE_VALUE)
	{ 
		CloseHandle(m_hPipe);
		m_hPipe=NULL;
	}
}

匿名管道重定向命令行输出

首先是MFC,注意事项:
1、管道读写是FIFO
2、读写指针,要记得关闭
3、编译时用了UNICODE,但CMD读取回来时ANSI,所以要转换一下字符集

	SECURITY_ATTRIBUTES sa;
	ZeroMemory(&sa, sizeof(sa));
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;

	HANDLE hRead, hWrite;
	if (!CreatePipe(&hRead, &hWrite, &sa, 0)) {
		MessageBox(L"Error On CreatePipe()");
		return;
	}

	STARTUPINFO si;
	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(STARTUPINFO);
	GetStartupInfo(&si);
	si.hStdError = hWrite;
	si.hStdOutput = hWrite;
	si.wShowWindow = SW_HIDE;
	si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
	PROCESS_INFORMATION pi;
	ZeroMemory(&pi, sizeof(pi));
	if (!::CreateProcess(L"C:\\Windows\\System32\\cmd.exe", L"/c dir /b D:\\Downloads"
		, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi)) {
		showMeErrorInfo();	
		return;
	}
	CloseHandle(hWrite);

	char buffer[4096] = { 0 };
	DWORD bytesRead;
	while (true) {
		if (ReadFile(hRead, buffer, 4095, &bytesRead, NULL) == NULL)
		{
			break;
		}
		
		UINT CodePage = 0;
		DWORD dwNum;
		dwNum = MultiByteToWideChar(CodePage, 0, buffer, -1, NULL, 0);
		if (dwNum)
		{
			wchar_t *pwText;
			pwText = new TCHAR[dwNum];
			if (pwText)
			{
				MultiByteToWideChar(CodePage, 0, buffer, -1, pwText, dwNum);
			}
			//m_Edit.SetWindowText(pwText);
			delete[]pwText;
			pwText = NULL;
		}
		UpdateData(false);
		Sleep(200);
	}
	CloseHandle(hRead);

C#的话,就简单多了:

            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.FileName = "C:\\Windows\\System32\\cmd.exe";
            startInfo.Arguments = "/c dir /b D:\\Downloads";
            startInfo.RedirectStandardOutput = true;
            //startInfo.RedirectStandardError = true;
            startInfo.UseShellExecute = false;
            startInfo.CreateNoWindow = false;
           
            Process p=Process.Start(startInfo);

            String cmdOut = "";
            while(!p.HasExited)
            { 
                cmdOut = p.StandardOutput.ReadLine();
                textBox1.Text += cmdOut + "\r\n";
                p.WaitForExit(10);
            }
            cmdOut = p.StandardOutput.ReadToEnd() + "\r\n";
            textBox1.Text += cmdOut ;

使用WM_COPYDATA实现跨进程通讯(下)

请注意:
A、SendMessage在接收方处理完毕前不会返回,会产生严重阻塞
B、由于使用了非托管内存,要注意进行清理

1、发送方WinForm

        public const int WM_COPYDATA = 0x004A;
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct COPYDATASTRUCT
        {
            public IntPtr dwData;
            public int cbData;
            public IntPtr lpData;
        }

        public void sendData(String msg, Boolean isUnicode)
        {
            COPYDATASTRUCT cds = new COPYDATASTRUCT();
            cds.cbData = (msg.Length + 1) * (isUnicode ? 2:1);
            cds.lpData = (isUnicode ? Marshal.StringToCoTaskMemUni(msg) : Marshal.StringToCoTaskMemAnsi(msg));

            IntPtr cdsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cds));
            Marshal.StructureToPtr(cds, cdsPtr, false);

            IntPtr clientWnd = Win32Helper.GetCurrentWindowHandle();
            SendMessage(clientWnd, WM_COPYDATA, IntPtr.Zero, cdsPtr);

            Marshal.FreeHGlobal(cdsPtr);
            Marshal.FreeCoTaskMem(cds.lpData);
        }

2、接收方WinForm

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct COPYDATASTRUCT
        {
            public IntPtr dwData;
            public int cbData;
            public string lpData;
        }

        public const int WM_COPYDATA = 0x004A;
        protected override void WndProc(ref System.Windows.Forms.Message msg)
        {
            string msgTxt = "";
            COPYDATASTRUCT cds = new COPYDATASTRUCT();
            switch (msg.Msg)
            {
                case WM_COPYDATA:
                    if(msg.LParam!=IntPtr.Zero)
                    {
                        cds = (COPYDATASTRUCT)msg.GetLParam(cds.GetType());
                        String msgstr = cds.lpData;
                        //MessageBox.Show(msgstr);
                    }
                    break;
                default:
                    base.WndProc(ref msg);
                    break;
            }
        }

使用WM_COPYDATA实现跨进程通讯(上)

1、发送方MFC

void XXX::SendCopyDataMessage(CString strWinTitle, CString strMsg)
{
	HWND hdlg;
	hdlg = ::FindWindow(NULL, strWinTitle);
	if (NULL != hdlg)
	{
		COPYDATASTRUCT cds = { 0 };
		cds.dwData = 0;
		cds.cbData = (strMsg.GetLength()+1)*sizeof(TCHAR);
		cds.lpData = strMsg.GetBuffer(strMsg.GetLength());
		::SendMessage(hdlg, WM_COPYDATA, (WPARAM)this->m_hWnd, (LPARAM)&cds);
		strMsg.ReleaseBuffer();
	}
}

2、接收方MFC

BOOL XXX::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
	CString strMsg;
	if (pCopyDataStruct && pCopyDataStruct->cbData)
	{
		strMsg = (LPCTSTR)(pCopyDataStruct->lpData);
		MessageBox(strMsg, L"WM_COPYDATA",  MB_OK);
	}

	return CDialogEx::OnCopyData(pWnd, pCopyDataStruct);
}

ActiveX控件使用SDI特性(上)

大家都知道,使用微软的MFC ActiveX控件向导生成的控件类,是基于COleControl的,而COleControl是基于CWnd类。这样很多MFC的特性,如CFrameWnd的特性,CSingleDocTemplate(SDI)的特性等就无法使用了。如果要把用到这些类的MFC程序封装为ActiveX插件,就会有不少麻烦。

其实这个问题,在上个世纪MFC诞生不久,就有人解决了,并提供了完整的示例代码。微软官方的这篇文章《Designing ActiveX Components with the MFC Document/View Model》,在原地址http://www.microsoft.com/mind/0497/mfc.asp已经无法找到了。

但好在这里还有完整的备份《Designing ActiveX Components with the MFC Document/View Model》

原文的作者,实现了CActiveXDocTemplate和CActiveXDocControl两个类,而用户只需要将控件的基类从COleControl换为CActiveXDocControl,并做简单修改,就可以方便的使用SDI的特性了。

整体思路其实是使用CActiveXDocControl扩展了COleControl。在CActiveXDocControl中,作者初始化了CDocManager,使用CActiveXDocTemplate控制了document、view、frame的生成,处理了OnSize事件,并使用Timer定时获取消息处理更新。

我们使用的时候,主要就是用这两个类,代码如下(两个类相互引用,所以放到了同一文件中):

1、ActivDoc.h

class CActiveXDocTemplate : public CSingleDocTemplate
{
    enum { IDR_NOTUSED = 0x7FFF };

    CWnd* m_pParentWnd;
    CFrameWnd* m_pFrameWnd;
    CString m_docFile;

public:
    CActiveXDocTemplate(CRuntimeClass* pDocClass,
        CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass);

    CFrameWnd* CreateDocViewFrame(CWnd* pParentWnd);
    void SaveDocumentFile();

    virtual CFrameWnd* CreateNewFrame(CDocument* pDoc,
        CFrameWnd* pOther);
    virtual CDocument* OpenDocumentFile(
        LPCTSTR lpszPathName, BOOL bVerifyExists = TRUE);
};

//===================================================

class CActiveXDocControl : public COleControl
{
    enum { WM_IDLEUPDATECMDUI = 0x0363 };

    static BOOL m_bDocInitialized;
    CActiveXDocTemplate* m_pDocTemplate;
    CFrameWnd* m_pFrameWnd;

    DECLARE_DYNAMIC(CActiveXDocControl)

protected:
    void AddDocTemplate(CActiveXDocTemplate* pDocTemplate);
    CDocTemplate* GetDocTemplate() { return m_pDocTemplate; }

    //{{AFX_MSG(CActiveXDocControl)
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnTimer(UINT nIDEvent);
    afx_msg void OnDestroy();
    //}}AFX_MSG
    //{{AFX_DISPATCH(CActiveXDocControl)
    //}}AFX_DISPATCH
    //{{AFX_EVENT(CActiveXDocControl)
    //}}AFX_EVENT

    DECLARE_MESSAGE_MAP()
    DECLARE_DISPATCH_MAP()
    DECLARE_EVENT_MAP()

public:
    CActiveXDocControl();
    virtual ~CActiveXDocControl();

    enum {
    //{{AFX_DISP_ID(CActiveXDocControl)
    //}}AFX_DISP_ID
    };
};

2、ActivDoc.cpp

// ActivDoc.cpp : implementation file
//

#include "stdafx.h"
#include "ActivDoc.h"

CActiveXDocTemplate::CActiveXDocTemplate(CRuntimeClass* pDocClass,
    CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass)
        : CSingleDocTemplate(IDR_NOTUSED, pDocClass, pFrameClass,
            pViewClass)
{
    ASSERT(pFrameClass);
}

CFrameWnd* CActiveXDocTemplate::CreateDocViewFrame(CWnd* pParentWnd)
{
    ASSERT(pParentWnd && IsWindow(*pParentWnd));
    ASSERT_KINDOF(CActiveXDocControl, pParentWnd);
    m_pParentWnd = pParentWnd;
    m_pFrameWnd = NULL;

    // OpenDocumentFile is a virtual method (implemented in
    // the CSingleDocTemplate base class) that creates
    // the document (either empty or loaded from the specified
    // file) and calls our CreateNewFrame function. Passing
    // NULL to the function creates a new document. Incidentally,
    // the view is created by the CFrameWnd's OnCreateClient()
    // method.

    if (!OpenDocumentFile(NULL))
        return NULL;

    // Since OpenDocumentFile sets m_pFrame, we can now use it.

    ASSERT(m_pFrameWnd);
    ASSERT_KINDOF(CFrameWnd, m_pFrameWnd);
    m_pFrameWnd->ShowWindow(SW_SHOWNORMAL);
    return m_pFrameWnd;
}

CFrameWnd* CActiveXDocTemplate::CreateNewFrame(CDocument* pDoc,
        CFrameWnd* pOther)
{
    ASSERT(pOther == NULL);
    ASSERT(m_pFrameClass != NULL);
    if (pDoc != NULL)
        ASSERT_VALID(pDoc);

    // Create a frame wired to the specified document

    CCreateContext context;
    context.m_pCurrentFrame = pOther;
    context.m_pCurrentDoc = pDoc;
    context.m_pNewViewClass = m_pViewClass;
    context.m_pNewDocTemplate = this;

    m_pFrameWnd = (CFrameWnd*)m_pFrameClass->CreateObject();
    if (m_pFrameWnd == NULL)
    {
        TRACE1("Warning: Dynamic create of frame %hs failed.\n",
            m_pFrameClass->m_lpszClassName);
        return NULL;
    }
    ASSERT_KINDOF(CFrameWnd, m_pFrameWnd);

    if (context.m_pNewViewClass == NULL)
        TRACE0("Warning: creating frame with no default view.\n");

    // The frame is created as a menu-less child of the
    // CActiveXDocControl in which it will reside.

    ASSERT_KINDOF(CActiveXDocControl, m_pParentWnd);
    if (!m_pFrameWnd->Create(NULL, "", WS_CHILD|WS_VISIBLE,
        CFrameWnd::rectDefault, m_pParentWnd, NULL, 0, &context))
    {
        TRACE0("Warning: CDocTemplate couldn't create a frame.\n");
        return NULL;
    }

    return m_pFrameWnd;
}

CDocument* CActiveXDocTemplate::OpenDocumentFile(
    LPCTSTR lpszPathName, BOOL bVerifyExists)
{
    SaveDocumentFile();
    m_docFile = lpszPathName;

    if (bVerifyExists)
    {
        DWORD dwAttrib = GetFileAttributes(m_docFile);
        if (dwAttrib == 0xFFFFFFFF ||
            dwAttrib == FILE_ATTRIBUTE_DIRECTORY)
        {
            lpszPathName = NULL;
        }
    }

    return CSingleDocTemplate::OpenDocumentFile(
        lpszPathName, TRUE);
}

void CActiveXDocTemplate::SaveDocumentFile()
{
    if (m_pOnlyDoc != NULL)
    {
        if (!m_docFile.IsEmpty())
            m_pOnlyDoc->OnSaveDocument(m_docFile);
        else
            m_pOnlyDoc->SetModifiedFlag(FALSE);
    }
}

//===================================================
// CActiveXDocControl

IMPLEMENT_DYNAMIC(CActiveXDocControl, COleControl)
BEGIN_MESSAGE_MAP(CActiveXDocControl, COleControl)
    //{{AFX_MSG_MAP(CActiveXDocControl)
    ON_WM_CREATE()
    ON_WM_SIZE()
    ON_WM_TIMER()
    ON_WM_DESTROY()
    //}}AFX_MSG_MAP
    ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
END_MESSAGE_MAP()
BEGIN_DISPATCH_MAP(CActiveXDocControl, COleControl)
    //{{AFX_DISPATCH_MAP(CActiveXDocControl)
    //}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()
BEGIN_EVENT_MAP(CActiveXDocControl, COleControl)
    //{{AFX_EVENT_MAP(COleFrameCtrl)
    //}}AFX_EVENT_MAP
END_EVENT_MAP()

int CActiveXDocControl::m_bDocInitialized = FALSE;

CActiveXDocControl::CActiveXDocControl()
{
    m_pDocTemplate = NULL;
    m_pFrameWnd = NULL;

    // Since we're in an OCX, CWinApp::InitApplication() is
    // not called by the framework. Unfortunately, that method
    // performs CDocManager initialization that is necessary
    // in order for our DocTemplate to clean up after itself.
    // We simulate the same initialization by calling the
    // following code the first time we create a control.

    if (!m_bDocInitialized)
    {
        CDocManager docManager;
        docManager.AddDocTemplate(NULL);
        m_bDocInitialized = TRUE;
    }
}

CActiveXDocControl::~CActiveXDocControl()
{
    // Note that the frame, the document, and the view are
    // all deleted automatically by the framework!
    
    delete m_pDocTemplate;
}

void CActiveXDocControl::AddDocTemplate(CActiveXDocTemplate* pDocTemplate)
{
    // I've decided to call this function AddDocTemplate to
    // be consistent with naming of CWinApp::AddDocTemplate.
    // However, only one DocTemplate is allowed per control.
    
    ASSERT(pDocTemplate);
    ASSERT(m_pDocTemplate == NULL);
    m_pDocTemplate = pDocTemplate;
}

int CActiveXDocControl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    if (COleControl::OnCreate(lpCreateStruct) == -1)
        return -1;

    // The CActiveXDocTemplate object will create the
    // document, the view, and the frame inside the
    // control. The reason we need a handle to the frame
    // is so that we can resize it when the control is
    // resized.
    
    ASSERT(m_pDocTemplate);    // Set in call to AddDocTemplate
    m_pFrameWnd = m_pDocTemplate->CreateDocViewFrame(this);
    ASSERT_KINDOF(CFrameWnd, m_pFrameWnd);

    // By default, we'll create the control with a border,
    // since it looks better for frames containing a toolbar.

    SetBorderStyle(TRUE);
    SetTimer(WM_IDLEUPDATECMDUI, 300, NULL);
    return 0;
}

void CActiveXDocControl::OnSize(UINT nType, int cx, int cy) 
{
    COleControl::OnSize(nType, cx, cy);

    // The CFrameWnd should always fill up the entire client
    // area of the control.

    if (m_pFrameWnd != NULL)
    {
        ASSERT(IsWindow(*m_pFrameWnd));
        CRect area;
        GetClientRect(area);
        m_pFrameWnd->MoveWindow(area.left, area.top, area.right,
            area.bottom);
    }
}

void CActiveXDocControl::OnTimer(UINT nIDEvent) 
{
    // Since we're in an OCX, we don't control the message loop,
    // so CWinThread::OnIdle is never called. That means we have
    // to periodically pump the ON_UPDATE_COMMAND_UI messages
    // by hand.
    
    SendMessageToDescendants(WM_IDLEUPDATECMDUI, TRUE);
    COleControl::OnTimer(nIDEvent);
}


void CActiveXDocControl::OnDestroy() 
{
    AfxGetApp()->m_pMainWnd = NULL;
    m_pDocTemplate->SaveDocumentFile();
    COleControl::OnDestroy();
}

困了,回去再写。。。

C#获取dll自身路径

            //获取dll的绝对路径,请根据不同情况自己选用
            MessageBox.Show(System.Reflection.Assembly.GetExecutingAssembly().Location);
            MessageBox.Show(System.Reflection.Assembly.GetEntryAssembly().Location);   
            MessageBox.Show(System.Windows.Forms.Application.ExecutablePath);
            MessageBox.Show(System.Windows.Forms.Application.StartupPath);
            MessageBox.Show(System.Environment.CurrentDirectory);
            MessageBox.Show(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);

C#枚举窗口句柄

在CS程序中启动其他应用后,要获取进程的主窗体其实很简单:

        Process p = Process.Start(exePath);
        //p.WaitForInputIdle();
        p.Refresh(); 
        IntPtr mainWnd = p.MainWindowHandle;

但是,总有很多特殊的情况,上面的方法根本无法用,所以,要用Windows API来搞定了

1、如果窗口信息很固定而且没有重名的话,可以用Findwindow搞定

        [DllImport("User32.dll", EntryPoint = "FindWindow")]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        IntPtr clientWnd = FindWindow(null,"FormClient");

2、根据标题枚举窗口句柄

        //枚举窗体
        [DllImport("User32.dll", EntryPoint = "EnumWindows", SetLastError = true)]
        public static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, uint lParam);
        //获取窗体标题
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern int GetWindowText(IntPtr hWnd, StringBuilder lpText, int nCount);
        //设置错误状态
        [DllImport("kernel32.dll", EntryPoint = "SetLastError")]
        public static extern void SetLastError(uint dwErrCode);

        //线程主窗口句柄
        private static IntPtr processMainWnd = IntPtr.Zero;
        //要查找的窗口名称
        private static String winTitle = "__Web__Form__Main__";

        //声明委托函数
        public delegate bool WNDENUMPROC(IntPtr hwnd, uint lParam);

        //枚举进程句柄,非线程安全
        [SecuritySafeCritical]
        public static IntPtr GetCurrentWindowHandle()
        {
            IntPtr ptrWnd = IntPtr.Zero;

            bool bResult = EnumWindows(new WNDENUMPROC(EnumWindowsProc), (uint)0);
            if (!bResult && Marshal.GetLastWin32Error() == 0)
            {
                ptrWnd = processMainWnd;
            }

            return ptrWnd;
        }

        //枚举函数,获取主窗口句柄然后退出
        [SecuritySafeCritical]
        private static bool EnumWindowsProc(IntPtr hwnd, uint lParam)
        {
            //根据标题获取窗体
            var sb = new StringBuilder(50);
            GetWindowText(hwnd, sb, sb.Capacity);

            if (winTitle.Equals(sb.ToString()))
            {
                processMainWnd = hwnd;
                SetLastError(0);
                return false;
            }

            return true;
        }

3、跟进进程id枚举获取主窗体句柄

        //枚举窗体
        [DllImport("User32.dll", EntryPoint = "EnumWindows", SetLastError = true)]
        //获取父窗体
        [DllImport("user32.dll", EntryPoint = "GetParent", SetLastError = true)]
        //根据窗口获取线程句柄
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern int GetWindowThreadProcessId(IntPtr hwnd, out uint pid);
        //设置错误状态
        [DllImport("kernel32.dll", EntryPoint = "SetLastError")]
        public static extern void SetLastError(uint dwErrCode);

        //线程主窗口句柄
        private static IntPtr processMainWnd = IntPtr.Zero;

        //枚举进程句柄
        [SecuritySafeCritical]
        public static IntPtr GetCurrentWindowHandle(int processId)
        {
            IntPtr ptrWnd = IntPtr.Zero;

            bool bResult = EnumWindows(new WNDENUMPROC(EnumWindowsProc), (uint)processId);
            if (!bResult && Marshal.GetLastWin32Error() == 0)
            {
                ptrWnd = processMainWnd;
            }

            return ptrWnd;
        }

        //枚举函数,获取主窗口句柄然后退出
        [SecuritySafeCritical]
        private static bool EnumWindowsProc(IntPtr hwnd, uint lParam)
        {
            uint uiPid = 0;
            //根据启动方式不同,这里要有相应修改
            if (GetParent(hwnd) == IntPtr.Zero)
            {
                GetWindowThreadProcessId(hwnd, out uiPid);
                if (uiPid == lParam)
                {
                    processMainWnd = hwnd;
                    SetLastError(0);
                    return false;
                }
            }
        }

4、跟进窗口WindowsClassName举获取主窗体句柄

        //查找桌面
        [DllImport("User32.dll", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)]
        public static extern IntPtr GetDesktopWindow();
        //获取窗体句柄
        [DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
        public static extern IntPtr GetWindow(IntPtr hwnd, int wFlag);
        //获取窗体类
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

        public const int GW_CHILD = 5;
        public const int GW_HWNDNEXT = 2;

        //枚举桌面窗口,根据WindowsClassName获取句柄
        [SecuritySafeCritical]
        public static IntPtr GetWinHandleByClasName(String windowClassNmae)
        {   
            //取得桌面窗口,再获取第一个子窗口
            IntPtr hwnd = GetDesktopWindow();
            hwnd = GetWindow((IntPtr)hwnd, System.Convert.ToInt32(GW_CHILD));
            
            //通过循环来枚举所有的子窗口
            while ((long)hwnd != 0)
            {
                System.Text.StringBuilder ClassName = new System.Text.StringBuilder(256);
                GetClassName((IntPtr)hwnd, ClassName, ClassName.Capacity);
                String tmpClassName= ClassName.ToString().Trim();
                if (tmpClassName.Length > 0)
                {
                    if (tmpClassName.Equals(windowClassNmae))
                    {
                        break;
                    }
                }

                hwnd = GetWindow((IntPtr)hwnd, System.Convert.ToInt32(GW_HWNDNEXT));
            }

            return hwnd;
        }