none
如何单步调试ATL编写的Windows服务呢? RRS feed

  • 问题

  • 使用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);
    }
    



    前无古人,后无来者
    2010年1月14日 14:31

答案

  • 呵呵,我原来的做法是在最开始加个Sleep(XXXX);把断点设到下一条,然后在Sleep的时间内启动再Attach上去.找不到啥好办法,但是起码能用.
    0xBAADF00D
    • 已标记为答案 Nancy Shao 2010年1月25日 7:22
    2010年1月15日 16:50
    版主

全部回复

  • 既然你都写日志了,可以继续用写日志的方式跟踪程序呀。
    2010年1月15日 2:56
    版主
  •          老大,我就是想学习怎么进行单步调试。要学就得学牛叉一点啊!
    前无古人,后无来者
    2010年1月15日 5:30
  • 呵呵,我原来的做法是在最开始加个Sleep(XXXX);把断点设到下一条,然后在Sleep的时间内启动再Attach上去.找不到啥好办法,但是起码能用.
    0xBAADF00D
    • 已标记为答案 Nancy Shao 2010年1月25日 7:22
    2010年1月15日 16:50
    版主