使用VS C++ 2005使用ATL编写一个Windows服务,发现不知道如何单步调试。msdn的说法是:
如何:调试 Windows 服务应用程序更新:2007 年 11 月由于服务必须从服务控制管理器的上下文中运行,而不是从 Visual Studio 中运行,因此调试服务不像调试其他 Visual Studio 应用程序类型那样简单。若要调试服务,必须首先启动服务,然后将一个调试器附加到正在运行服务的进程中。然后可以使用 Visual Studio 的所有标准调试功能来调试应用程序。      我将程序编译成功后,在“管理工具”中的服务管理器中其中已经注册的服务,然后VS 2005的菜单栏“调试”——〉“附加到进程”,在可用进程列表中选择我编写的windows服务,接着在程序中设置断点,按F5开始调试,但是总是进入不了我的那个函数。但是如果我采用写日志的方式进行调试,发现确是可以进入那个函数的。   网上的另外一个说法是:可以先不注册成服务,按普通的exe程序那样执行,就可以调试了。   但是我试过了。也不行。   我的主要代码是这样的:
#include "stdafx.h"
#include "resource.h"
#include "StartThunder.h"#include <stdio.h>class CStartThunderModule : public CAtlServiceModuleT< CStartThunderModule, IDS_SERVICENAME >
{
public :
DECLARE_LIBID(LIBID_StartThunderLib)
DECLARE_REGISTRY_APPID_RESOURCEID(IDR_STARTTHUNDER, "{B3B6CFFE-DDFE-4397-88DC-B3DBE7D68D82}")
HRESULT InitializeSecurity() throw()
{
// TODO : 调用 CoInitializeSecurity 并为服务提供适当的 
// 安全设置
// 建议 - PKT 级别的身份验证、
// RPC_C_IMP_LEVEL_IDENTIFY 的模拟级别
// 以及适当的非 NULL 安全说明符。 return S_OK;
} //HRESULT PreMessageLoop(int nShowCmd) throw(); //   HRESULT  PostMessageLoop() throw(); HRESULT RegisterAppId(bool bService = false) throw(); HRESULT Start(int nShowCmd) throw(); HRESULT Run(int nShowCmd = SW_HIDE)throw();
};HRESULT CStartThunderModule::Start(int nShowCmd) throw()
{
// HRESULT hr = __super::Start(nShowCmd); HRESULT hr =  S_OK; if (SUCCEEDED(hr))
{
// 通过注册表获取迅雷的路径
// 打开键
HKEY hKEY;
LPCTSTR Rgspath = _T("SOFTWARE\\Thunder Network\\ThunderOem\\thunder_backwnd");
LONG ret = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,Rgspath,0, KEY_READ, &hKEY); if(ret != ERROR_SUCCESS)

RegCloseKey(hKEY);
return S_FALSE;  
}
// 读取键值内容
DWORD dwInfoSize;
DWORD type = REG_SZ;
BYTE UserInfo[255];
/*
注意RegQueryValueEx最后一个参数是个双向参数,入参时表示的是前一个参数的缓冲区大小,出参时表示的是返回的大小。
所以最好把UserInfo的大小给dwInfoSize,防止UserInfo溢出。
*/
dwInfoSize = sizeof(UserInfo)/sizeof(BYTE); 
// added end
ret = ::RegQueryValueEx(hKEY, _T("Path"), NULL, &type, UserInfo, &dwInfoSize);
if(ret!=ERROR_SUCCESS)

LPVOID lpMsgBuf;
DWORD dw = ::GetLastError();  ::FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | 
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
::MessageBox(NULL,(LPCTSTR)lpMsgBuf,_T("系统错误"),MB_OK|MB_ICONSTOP); 
::LocalFree(lpMsgBuf);
::RegCloseKey(hKEY);
return S_FALSE;
}   _tfopen(_T("C:\\Thunder.txt"),_T("w"));
}
return hr;
}
HRESULT CStartThunderModule::RegisterAppId(bool bService) throw()
{          
HRESULT hr = S_OK;
BOOL res = __super::RegisterAppId(bService); if (bService)
{
if (IsInstalled())
{       
SC_HANDLE hSCM = ::OpenSCManagerW(NULL, NULL, SERVICE_CHANGE_CONFIG);
SC_HANDLE hService = NULL;
if (hSCM == NULL)
hr = AtlHresultFromLastError();
else
{
hService = ::OpenService(hSCM, m_szServiceName, SERVICE_CHANGE_CONFIG);
if (hService != NULL)
{
TCHAR szDisplayName[128];
ZeroMemory(szDisplayName, 128);
lstrcpy(szDisplayName, _T("迅雷自动启动"));
::ChangeServiceConfig(hService, SERVICE_NO_CHANGE, SERVICE_AUTO_START, NULL, NULL, NULL, NULL, NULL, NULL, NULL, szDisplayName); SERVICE_DESCRIPTION Description;
TCHAR    szDescription[1024]; ZeroMemory(szDescription, 1024);
ZeroMemory(&Description, sizeof(SERVICE_DESCRIPTION));
lstrcpy(szDescription, _T("XXX软件有限公司 版权所有(R) 2001-2008"));
Description.lpDescription = szDescription;
::ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &Description); ::CloseServiceHandle(hService);
}
else
hr = AtlHresultFromLastError(); ::CloseServiceHandle(hSCM);
} }
}     
return hr;
}HRESULT CStartThunderModule::Run(int nShowCmd)throw()
{

HRESULT hr = S_OK; hr = ::CoInitialize(NULL); Start(nShowCmd);
hr =  __super::PreMessageLoop(nShowCmd);
if (hr == S_OK)
{
if (m_bService)
{
//可以在这里启动线程,或者什么其他东西来做自己的工作的啦
//这里是什么都没有做了,只输出一条信息
// LogEvent(_T("widebright 的服务启动咯,呵呵 "));
SetServiceStatus(SERVICE_RUNNING);
}
//进入消息循环,不停的处理消息,可能最后分发到Handler去处理,调用了OnShutdown等函数的。
__super::RunMessageLoop();
} if (SUCCEEDED(hr))
{
hr =  PostMessageLoop();
} //可以在适当的时候调用Uninstall函数来卸载掉服务
//__super::Uninstall(); ::CoUninitialize(); return hr;
}
CStartThunderModule _AtlModule;//
extern "C" int WINAPI _tWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, 
                                LPTSTR /*lpCmdLine*/, int nShowCmd)
{
    return _AtlModule.WinMain(nShowCmd);
}

解决方案 »

  1.   

    只有动态链接库才需要附加到一个进程中调试的。你使用ATL编写的服务本身就是一个独立的进程,所以不需要附加到进程中。你可以按F5使进程启动处于可调试状态。然后在控制面板中启动-》管理工具-》服务
    在列表中找到你的服务,单击右键选择启动即可。
      

  2.   

    我将程序编译成功后,在“管理工具”中的服务管理器中其中已经注册的服务,然后VS 2005的菜单栏“调试”——〉“附加到进程”,在可用进程列表中选择我编写的windows服务,接着在程序中设置断点,按F5开始调试,但是总是进入不了我的那个函数。但是如果我采用写日志的方式进行调试,发现确是可以进入那个函数的。你按F5开始调试,没有进入断点愿意是你没有在服务中启动这个服务,也就是系统没有调用你的服务,因此不能进入任何你需要的函数。
    不过事实上,程序是进入了某些函数的,比如_tWinMain启动函数等等,你可以在_tWinMain处设置断点试一试
      

  3.   

    MARK...
    我也很想知道怎么调试服务程序,原来的做法是写个相同功能的EXE,当EXE没问题后,再做成服务。如果发现服务程序和原来的EXE有差异,只好写日志了。
      

  4.   

    在你要调试的函数里面写一行
    __asm int 3;
    生成程序后正常启动服务,当服务程序执行到上述代码时,会产生一个异常被VC捕获到,然后即可开始调试。
      

  5.   


    呵呵,好像DebugBreak也可以。
    不过貌似不是正宗的方法吧。MSDN上的方法,应该是比较标准的。
    MSDN上说,需要启动服务,不知道楼主的服务启动了没?
    看到了注册的服务,未必说服务就启动了。
      

  6.   

    我就是用的Attach to Process.
      

  7.   

    你可以在你服务启动的一开始地方就弹一个MessageBox等,然后正常注册服务,启动它,当它弹出对话框后,再用vc attach上去,然后点击对话框,就可以继续debug了
      

  8.   


        我是先启动服务,再在VS 2005里进入调试状态,再Attach to Process.貌似顺序搞错了,应该是先在VS 2005里进入调试状态,再启动服务,,接着再Attach to Process啊!
      

  9.   

    如果要调试的代码在“Attach to Process”之前就执行了,用这种方法自然也就不能调试了。
      

  10.   

    在要下断点的地方用
    _ASSERT( 0 );然后启动服务时候出错,按下重试调试即可。如果要断的地方是在初始化代码里,要在15秒内调试完成或F5跳过,否则会被SCM杀掉