hur.cn - 华软网

 热门搜索

【求助】UAC环境下,NT服务中启动带窗口界面的程序总是失败

  作者:未知    来源:网络    更新时间:2009/3/30
最近在编写一个NT服务程序,功能就是当NT服务接收到指令后就去启动本地另一个带界面的MFC程序。

我在NT服务中尝试调用了ShellExecute、CreateProcess、WinExe这三个函数,结果都无法启动MFC程序。
NT服务在CreateService中已经添加了SERVICE_INTERACTIVE_PROCESS,可还是不行。

由于一开始我担心UAC的影响,但是手边没有VISTA,所以我的测试环境是Windows Server 2008。
以下是“创建NT服务”和“服务中启动MFC程序的代码”,请大家帮忙:
    <<        创建NT服务       >>
----------------------------------------
BOOL Install()
{
    if (IsInstalled())
        return TRUE;

//打开服务控制管理器
    SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (hSCM == NULL)
    {
        MessageBox(NULL, _T("Couldn't open service manager"), szServiceName, MB_OK);
        return FALSE;
    }

    // Get the executable file path
    TCHAR szFilePath[MAX_PATH];
    ::GetModuleFileName(NULL, szFilePath, MAX_PATH);

//创建服务
    SC_HANDLE hService = ::CreateService(
        hSCM, 
szServiceName, 
szServiceName,
        SERVICE_ALL_ACCESS, 
SERVICE_INTERACTIVE_PROCESS|SERVICE_WIN32_OWN_PROCESS,
        SERVICE_DEMAND_START, 
SERVICE_ERROR_NORMAL,
        szFilePath, NULL, NULL, _T(""), NULL, NULL);
    if (hService == NULL)
    {
        ::CloseServiceHandle(hSCM);
EShow.Format("Cound not create service !  Error: %d",GetLastError());
WriteLog(EShow);
        //MessageBox(NULL, _T("Couldn't create service"), szServiceName, MB_OK);
        return FALSE;
    }

if (!StartService(
hService,  // handle to service 
0,           // number of arguments 
NULL) )      // no arguments 
{
        WriteLog("Start Service Failed !");

CloseServiceHandle(hService); 
CloseServiceHandle(hSCM);
return FALSE; 
}

    ::CloseServiceHandle(hService);
    ::CloseServiceHandle(hSCM);
    return TRUE;
}

    <<      启动MFC程序     >>
------------------------------------------------
STARTUPINFO        si={sizeof(STARTUPINFO)};   
//ZeroMemory(&si,sizeof(STARTUPINFO));   
si.lpDesktop       = "WinSta0\\Default";
si.wShowWindow     = SW_SHOWNORMAL;   
si.dwFlags         = STARTF_USESHOWWINDOW;
PROCESS_INFORMATION   pi; 

int nRet=CreateProcess(NULL,strTarget, NULL,NULL,FALSE, 
                   NORMAL_PRIORITY_CLASS, 
   NULL,strPath, 
   &si, &pi);
if (nRet==0)
{
EShow.Format(_T("Type1 Failed! Error:  %d"),GetLastError() );
WriteLog(EShow);
}

---华软 网友回答---
要得到系统登录后(响应SERVICE_CONTROL_SESSIONCHANGE可以得知用户登录),用CreateProcessAsUser在用户session下创建进程。
---华软网友回复---
服务启动UI程序不能直接CreateProcess(),否则因为service session,程序UI不能看见
拿到当前explorer.exe的token,然后CreateProcessAsUser模拟当前用户启动进程...这样就可以显示界面了
---华软网友回复---
继续学习.....帮顶
---华软网友回复---
1楼和2楼的能否提供点代码供我参考一下,贴启动程序的核心部分就可以了,
我估计是连续好几个函数的调用,有点复杂,想看看代码
---华软网友回复---
引用 4 楼 dodott 的回复:
1楼和2楼的能否提供点代码供我参考一下,贴启动程序的核心部分就可以了, 
我估计是连续好几个函数的调用,有点复杂,想看看代码


  
C++">BOOL   GetTokenByName(HANDLE   &hToken,LPSTR   lpName)   
  {   
    if(!lpName)   
    {   
      return   FALSE;   
    }   
    HANDLE                   hProcessSnap   =   NULL;     
          BOOL                       bRet             =   FALSE;     
          PROCESSENTRY32   pe32             =   {0};     
      
          hProcessSnap   =   CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,   0);     
          if   (hProcessSnap   ==   INVALID_HANDLE_VALUE)     
                  return   (FALSE);     
      
          pe32.dwSize   =   sizeof(PROCESSENTRY32);     
      
          if   (Process32First(hProcessSnap,   &pe32))     
          {       
                  do     
                  {   
        if(!strcmp(_strupr(pe32.szExeFile),_strupr(lpName)))   
        {   
          HANDLE   hProcess   =   
OpenProcess(PROCESS_QUERY_INFORMATION,   
            FALSE,pe32.th32ProcessID);   
          bRet   =   OpenProcessToken(hProcess,TOKEN_ALL_ACCESS,&hToken);   
          CloseHandle   (hProcessSnap);     
          return   (bRet);   
        }   
                  }     
                  while   (Process32Next(hProcessSnap,   &pe32));     
                  bRet   =   TRUE;     
          }     
          else     
                  bRet   =   FALSE;   
      
          CloseHandle   (hProcessSnap);     
          return   (bRet);   
  }   
    
  BOOL   RunProcess(LPCSTR   lpImage)   
  {   
    if(!lpImage)   
    {   
      return   FALSE;   
    }   
    HANDLE   hToken;   
    if(!GetTokenByName(hToken,"
EXPLORER.EXE"))   
    {   
      return   FALSE;   
    }   
    STARTUPINFO   si;   
    PROCESS_INFORMATION   pi;   
      
    ZeroMemory(&si,   sizeof(STARTUPINFO));   
    si.cb=   sizeof(STARTUPINFO);   
    si.lpDesktop   =   TEXT("winsta0\\default");   
      
    BOOL   bResult   =   
CreateProcessAsUser(hToken,lpImage,NULL,NULL,NULL,   
      FALSE,NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi);   
    CloseHandle(hToken);   
    if(bResult)   
    {   
      OutputDebugString("CreateProcessAsUser   ok!\r\n");   
    }   
    else   
    {   
      OutputDebugString("CreateProcessAsUser   false!\r\n");   
    }   
    return   bResult;   
  }   
  

---华软网友回复---
如果要创建用户权限的进程,可以用WTSQueryUserToken获得token;如果要创建服务权限的进程,可以用DuplicateTokenEx复制自身的token,再用SetTokenInformation修改TokenSessionId,然后CreateProcessAsUser。
---华软网友回复---
根据oyljerry提供的代码我已经成功启动了我的程序,
只不过程序并不是默认最大化启动的,这点我可以自己查查。

但是CreateProcessAsUser还是出现过FALSE的情况,从GetLastError的结果来看是因为程序必须经过UAC验证,
毕竟从VISTA开始UAC就是让程序员比较烦心的东西,不得不考虑UAC的限制问题,
因此这就让我对6楼中提到的“用户权限”和“服务权限”之间的区别产生了疑问,两者有什么区别吗?

区别我相信肯定是有的,因为我用NSIS写了一个silent install安装包,用原先shellexecute函数可以绕开UAC的验证来执行这个安装包,
不知道两位高手能否简单介绍一下两种权限模式的使用范围吗?特别是两者的区别,谢谢。
---华软网友回复---
学习一下      
华软声明:本内容来自网络,如有侵犯您版权请来信指出,本站立即删除。