【问题标题】:Change wallpaper with service用服务换壁纸
【发布时间】:2019-08-06 04:32:38
【问题描述】:

我正在尝试使用安装的服务作为特定用户(在本例中为我)运行来更改壁纸。

这是我的 Wallpaper 类,它有一个 SetWallpaper 函数:

public sealed class Wallpaper
{
   [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError=true)]
   private static extern Int32 SystemParametersInfo(
      UInt32 action, UInt32 uParam, String vParam, UInt32 winIni);

   private static readonly UInt32 SPI_SETDESKWALLPAPER = 0x14;
   private static readonly UInt32 SPIF_UPDATEINIFILE = 0x01;
   private static readonly UInt32 SPIF_SENDWININICHANGE = 0x02;

   public static void SetWallpaper(String path)
   {
      System.IO.Stream s = new System.Net.WebClient().OpenRead(path.ToString());
      System.Drawing.Image img = System.Drawing.Image.FromStream(s);
      string tempPath = Path.Combine(Path.GetTempPath(), "wallpaper.bmp");
      ImgurWallpaperSetter.ImgurWallpaperSetter.log(tempPath);
      img.Save(tempPath, System.Drawing.Imaging.ImageFormat.Bmp);
      SystemParametersInfo(SPI_SETDESKWALLPAPER, 1, tempPath,
      SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
      int error = Marshal.GetLastWin32Error();
      ImgurWallpaperSetter.ImgurWallpaperSetter.log("Last error: " + error);
   }
}

当我从单元测试中运行SetWallpaper 时,它可以完美运行,但是当我安装并启动该服务时,它根本不起作用。

这里是服务启动代码:

protected override void OnStart(string[] args) {    
   //WallpaperScheduler.ScheduleWallpaperFetch(DateTime.Now.Hour, DateTime.Now.Minute+1);
   //Debugger.Launch();
   Uri imageUrl = WallpaperRetriever.mostPopularImgurWallpaper();
   log(imageUrl.AbsoluteUri);
   Wallpaper.SetWallpaper(imageUrl.AbsoluteUri);
}

我已经确认它正在正确地将图像下载到我的临时目录中,但它没有设置壁纸。它不会出错或在事件日志中记录任何内容。

这是我在本地服务查看器中安装的服务:

运行它什么都不做。

A similar thread I've read

编辑: 添加此代码以在我的 serviceInstaller_Committed 事件上运行,该事件应允许服务与桌面交互,但我发现服务运行与实际切换壁纸之间存在巨大延迟:

ConnectionOptions coOptions = new ConnectionOptions();
coOptions.Impersonation = ImpersonationLevel.Impersonate;

ManagementScope mgmtScope = new ManagementScope(@"root\CIMV2", coOptions);
mgmtScope.Connect();

ManagementObject wmiService;
wmiService = new ManagementObject(
   "Win32_Service.Name='" + serviceInstaller1.ServiceName + "'"
);

ManagementBaseObject InParam = wmiService.GetMethodParameters("Change");
InParam["DesktopInteract"] = true;
ManagementBaseObject OutParam = wmiService.InvokeMethod("Change", InParam, null);

编辑2: 我已经更新了我的服务以记录到系统事件GetLastError()。现在我看到服务抛出错误 1459(“此操作需要交互式窗口站。”)。但是,这并不能解释为什么我的壁纸最终会切换(我认为通常是在从睡眠中醒来之后)。也更新了上面的 Wallpaper 类。

编辑3 我已经确认睡觉后设置了新壁纸。谁能解释这是为什么?是不是我需要重新启动才能设置交互式桌面功能?

编辑4 我正在做的是感觉很hacky。如果我让服务除了下载壁纸什么都不做,并且如果已经下载了新壁纸并且用户已登录,并且可能有另一个非服务应用程序来更改壁纸,那会更好吗?

【问题讨论】:

  • 运行该服务的用户是否有权更改壁纸?您可能想添加一些 try catch 并查看是否抛出任何错误。
  • 我现在在自己的帐户上运行它只是为了测试应用程序。我的计算机上只有一个帐户,它是我的管理员帐户。
  • 我相信 UAC 已启用,但我在管理命令提示符下安装该服务,并且该服务要求我提供凭据,以便它可以充当我的角色。 UAC 是否可以阻止它,即使它在我的权限下运行?

标签: c# windows-services


【解决方案1】:

你知道Session 0 Isolation吗?这意味着您的服务在用户永远不会登录的桌面上运行,并且受限环境很可能会影响您的程序的行为。

您说代码“不会出错或在事件日志中记录任何内容”,但是,根据您所展示的内容,您需要改进错误检查以发现更细微的问题。例如,SystemParametersInfo() 在失败时返回 FALSE(随后对GetLastError() 的调用可能会提供非常丰富的信息!)但您的代码不会检查该结果。您不能只依赖显式异常。

【讨论】:

  • 我不知道 Session 0 的隔离,但我感觉存在类似的东西,因为我读到服务根本不应该再与桌面交互。此外,开始记录 GetLastError() 肯定会有所帮助。如果这有帮助,我会告诉你。
  • 查看我上次的编辑。我收到错误“需要交互式窗口站”
【解决方案2】:

添加此类:http://pastebin.com/ERsnqMEy

像这样使用它:http://pastebin.com/RYvvT7bH

在使用 WMI 模拟来自 Windows 系统服务的登录用户时会产生奇迹。祝你好运。

【讨论】:

  • 请在答案中包含链接内容(至少最相关)。不鼓励仅提供链接的答案。
  • 有两行引用了不存在的 OneADSysService 类,但它们所做的只是写入日志,所以我将它们注释掉。
  • 此文本出错:“令牌不能为零。”
【解决方案3】:

在“服务”中,转到“属性”,然后选中“使用本地帐户”和“允许使用桌面”之类的选项。我不确定名称,因为我的 Windows 使用不同的语言,但您应该可以找到它。

【讨论】:

  • 查看我的编辑。我尝试以编程方式使用该功能,但它不起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多