C++ 如何将Golang编写的应用程序作为Windows服务运行
我无法将使用Win32 api开发的屏幕截图应用程序用作Windows后台服务。 我作为Windows后台服务安装并运行它,到目前为止我没有问题。 我的问题是:服务没有给我打印输出。没有截图。 我试着制作另一个简单的应用程序。我尝试使用该功能发送消息,但问题没有解决C++ 如何将Golang编写的应用程序作为Windows服务运行,c++,c,go,winapi,service,C++,C,Go,Winapi,Service,我无法将使用Win32 api开发的屏幕截图应用程序用作Windows后台服务。 我作为Windows后台服务安装并运行它,到目前为止我没有问题。 我的问题是:服务没有给我打印输出。没有截图。 我试着制作另一个简单的应用程序。我尝试使用该功能发送消息,但问题没有解决 无法使用Win32 api开发Windows后台应用程序 为什么我会有这个问题 如何使用win32 api作为windows后台服务运行屏幕截图应用程序 我的Windows后台服务未生成输出 package main
- 无法使用Win32 api开发Windows后台应用程序
- 为什么我会有这个问题
- 如何使用win32 api作为windows后台服务运行屏幕截图应用程序
package main
import (
"fmt"
"log"
"time"
"github.com/checkgo/win"
"github.com/kardianos/service"
)
var logger service.Logger
type program struct {
exit chan struct{}
}
func (p *program) Start(s service.Service) error {
if service.Interactive() {
logger.Info("Running in terminal.")
} else {
logger.Info("Running under service manager.")
}
p.exit = make(chan struct{})
// Start should not block. Do the actual work async.
go p.run()
return nil
}
func (p *program) run() {
logger.Infof("I'm running %v.", service.Platform())
ticker := time.NewTicker(2 * time.Second)
for {
select {
case tm := <-ticker.C:
win.OutputDebugString(fmt.Sprintf("%s : %v", "This is test message", tm))
case <-p.exit:
ticker.Stop()
}
} // link to whaterver image from the web
}
func (p *program) Stop(s service.Service) error {
// Stop should not block. Return with a few seconds.
return nil
}
func main() {
svcConfig := &service.Config{
Name: "GoServiceExampleSimple",
DisplayName: "Go Service Example",
Description: "This is an example Go service.",
}
prg := &program{}
s, err := service.New(prg, svcConfig)
if err != nil {
log.Fatal(err)
}
logger, err = s.Logger(nil)
if err != nil {
log.Fatal(err)
}
err = s.Run()
if err != nil {
logger.Error(err)
}
}
主程序包
进口(
“fmt”
“日志”
“时间”
“github.com/checkgo/win”
“github.com/kardianos/service”
)
变量记录器服务
类型程序结构{
退出chan结构{}
}
func(p*程序)启动(s service.service)错误{
if service.Interactive(){
logger.Info(“正在运行的终端”)
}否则{
logger.Info(“在服务管理器下运行”)
}
p、 exit=make(chan结构{})
//启动不应阻塞。异步执行实际工作。
跑
归零
}
func(p*程序)运行(){
logger.Infof(“我正在运行%v.”,service.Platform()
股票代码:=time.NewTicker(2*time.Second)
为了{
挑选{
案例tm:=
如果服务正在本地系统的安全上下文中运行
帐户,但不包括服务\u交互\u过程
属性,它使用以下窗口站和桌面:
服务-0x0-3e7$\default。此窗口站不是交互式的,因此
服务无法显示用户界面。此外,进程
由服务创建的无法显示用户界面
参考:
交互式窗口站是唯一可以
显示用户界面或接收用户输入。它被分配给
交互式用户的登录会话,并包含键盘,
鼠标和显示设备。它始终命名为“WinSta0”。所有其他
窗口工作站是非交互的,这意味着它们不能显示
用户界面或接收用户输入
参考:
据说您要捕获的屏幕位于交互式窗口站(Winsta0\default)的默认桌面上。您可以在Winsta0\default
中创建子进程,子进程用于屏幕截图。用于调用服务中的子进程
参考下面的C++代码。虽然这不是GO语言,但我认为熟悉WiAPI的调用是足够的。
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
{
OutputDebugString(_T("My Sample Service: ServiceWorkerThread: Entry"));
WCHAR station[] = L"Winsta0\\default";
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = station;
si.dwFlags = STARTF_USESTDHANDLES;
PROCESS_INFORMATION pi;
HANDLE hToken;
bool err = WTSQueryUserToken(WTSGetActiveConsoleSessionId(), &hToken);
WCHAR path[] = L"D:\\child.exe"; //child.exe is used to take screenshots
if (CreateProcessAsUser(hToken, path, NULL, 0, 0, true, CREATE_NO_WINDOW, 0, 0, &si, &pi))
{
...
用函数求解
我用来解决问题的好的源代码
Windows后台服务应用程序的源代码:
package main
import (
"log"
"reflect"
"sync"
"syscall"
"github.com/checkgo/win"
"github.com/kardianos/service"
"golang.org/x/sys/windows"
)
var logger service.Logger
type program struct {
exit chan struct{}
}
func (p *program) Start(s service.Service) error {
if service.Interactive() {
logger.Info("Running in terminal.")
} else {
logger.Info("Running under service manager.")
}
p.exit = make(chan struct{})
// Start should not block. Do the actual work async.
go p.run()
return nil
}
func (p *program) run() {
var wgT sync.WaitGroup
wgT.Add(1)
test(&wgT)
wgT.Wait()
}
func test(wg *sync.WaitGroup) {
var saAttr win.SECURITY_ATTRIBUTES
saAttr.NLength = uint32(reflect.TypeOf(syscall.SecurityAttributes{}).Size())
saAttr.BInheritHandle = win.TRUE
saAttr.LpSecurityDescriptor = uintptr(0)
var si syscall.StartupInfo
si.Cb = uint32(reflect.TypeOf(syscall.SecurityAttributes{}).Size())
si.Desktop = windows.StringToUTF16Ptr("Winsta0\\default")
si.Flags = windows.STARTF_USESTDHANDLES
var hToken windows.Token
id := win.WTSGetActiveConsoleSessionId()
err := windows.WTSQueryUserToken(uint32(id), &hToken)
if err != nil {
logger.Info(err)
}
path := windows.StringToUTF16Ptr("C:\\Users\\cingo\\go\\src\\srv\\agent\\test_agent.exe")
var pi syscall.ProcessInformation
syscall.CreateProcessAsUser(syscall.Token(hToken),
path,
nil,
nil,
nil,
true,
windows.CREATE_NO_WINDOW,
nil,
nil,
&si,
&pi)
}
func (p *program) Stop(s service.Service) error {
// Stop should not block. Return with a few seconds.
return nil
}
func main() {
svcConfig := &service.Config{
Name: "GoServiceExampleSimple",
DisplayName: "Go Service Example",
Description: "This is an example Go service.",
}
prg := &program{}
s, err := service.New(prg, svcConfig)
if err != nil {
log.Fatal(err)
}
logger, err = s.Logger(nil)
if err != nil {
log.Fatal(err)
}
err = s.Run()
if err != nil {
logger.Error(err)
}
}
运行应用程序
package main
import (
"fmt"
"github.com/checkgo/win"
"time"
)
func main() {
for {
time.Sleep(time.Second * 1)
win.OutputDebugString(fmt.Sprintf("%s", "This is test message"))
}
}
IIRC,Windows服务在不同的桌面实例下运行,因此无法拍摄用户屏幕截图。@MartinJames是正确的,服务在会话0中在单独的WindowsStation和桌面上独立运行。请看我理解您的回答。如果有人对解决方案有想法,我将非常感谢您的分享。如果您的目标是从用户会话截图您应该在用户会话中从您的服务生成一个进程(使用wtsqueryusertoken
获取令牌,然后调用CreateProcessAsUser
)您是否开发了应用程序并在另一个Windows系统上编译了它?Windows不允许任何“未知”应用程序运行。Windows认为这是一个有害的文件。他只是忽略了他。尝试:右键单击可执行文件并授予其运行权限。感谢@Strive Sun-MSFT,我遇到了此解决方案建议,但我还没有机会尝试。我将在测试后提供信息。我在提出解决方案后从评论中了解您解决方案,您耐心等待您的返回。:)@Recacisingöz如果您有任何问题,请随时通知我。再次感谢@Strive Sun-MSFT。问题已解决。