保持表单应用程序和 Windows 服务(或任何 n 层,实际上)之间的设置同步

发布于 2024-09-08 00:30:53 字数 714 浏览 9 评论 0原文

我有一个执行许多定期活动的 Windows 服务,并且我想从 Windows 窗体应用程序更改此服务的设置。不过,我不确定确保服务具有最新的用户首选项的最佳方法(运行频率、使用哪些文件夹、用户可以指定的其他内容)。用户可以随时随意更改设置,我希望服务几乎立即知道这一点。以下是我正在权衡的选项:

  1. 表单和服务共享使用来自第三个共享项目的相同“Settings”对象,并且表单使用 WCF“UpdateSettings(newSettings)”调用来让服务知道已存在更改(或者,可选地,调用更新每个单独的设置,尽管这在不同的调用中似乎很多)。我目前使用 WCF 来处理基本消息,但设置对象可能很大,因为表单和服务中还有很多其他内容
  2. 使用通用配置文件(XML,或来自 #1 的相同设置对象,但序列化到磁盘) 。表单只是在更改后写入对象的新副本,服务会经常检查并拾取它(如果是新的),更新其设置副本
  3. 与 #2 相同,但使用基本的 WCF 调用来告诉去获取设置的服务。本质上是#2 的“按需”版本,而不是“轮询”版本。

我最清楚的是主观的,但我对这些选择的任何明显的赞成或反对理由感兴趣。由于我必须在应用程序运行之间(重新启动等)保存我的设置,所以无论如何我都必须将设置序列化到磁盘,所以我已经倾向于#2 或#3。我需要在磁盘上有一个位置来保存设置,但也许 AppData 文件夹可以正常工作,尽管这只允许管理员更改设置,因为他们是唯一有权写入此位置的人(每个用户,包括服务帐户,都可以读取它)。

感谢您的见解!

I have a Windows Service that performs a number of periodic activities, and I want to change the settings of this service from a Windows Forms app. I'm not sure, though, about the best way to make sure the service has the most updated user preferences in it (how often to run, what folders to use for things, whatever else the user can specify). The user can change settings any time, at will, and I'd like the service know about it almost immediately. Here are the options I'm weighing:

  1. The form and service share use the same "Settings" object from a third, shared project, and the form uses a WCF "UpdateSettings(newSettings)" call to let the service know that there have been changes (or, optionally, a call to update each individual setting, though this seems like a lot over different calls). I currently use WCF for basic messages, but the settings object can be huge, since there's a lot of other stuff in there
  2. Form and Service use a common config file (XML, or the same settings object from #1, but serialized to disk). The Form just writes a new copy of the object after it's been changed, and the service checks every so often and picks it up if it's new, updating its copy of the settings
  3. Same as #2, but with a basic WCF call that that tells the service to go get the settings. Essentially, an "on-demand" instead of "polling" version of #2.

I know best is subjective, but I'm interested in any obvious pro or con reasons for these choices. Since I'll have to save my settings between runnings of the application (reboots, etc), I'll have to serialize the settings to disk anyway, so I'm already leaning towards #2 or #3. I'll need a place on disk where I can save the settings, but maybe the AppData folder will work okay, though that will only allow Administrators to change the settings, since they're the only ones that have permission to write to this location (where every user, including the service account, can read it).

Thanks for your insight!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蓝眼睛不忧郁 2024-09-15 00:30:53

我有点使用你的号码 2。

但我只在 .NET 2 中使用我的应用程序,但它应该仍然适用。

我有一个在两个程序中使用的设置类。在这个设置类中,我设置了一个 FileSystemWatcher 对象,该对象查看设置文件。

如果其他应用程序更新了设置文件,我的当前应用程序会收到一个事件触发器来指示需要重新加载设置。

您还可以在设置屏幕中应用相同的原则,以便如果(服务)其他应用程序在设置编辑期间更新任何内容,则会反映在您的屏幕中。

我使用AppData(我的公司/应用程序名称目录)来存储文件。

另一件要记住的事情是,在写入文件时可能会锁定文件,因此您可以使用临时名称保存、删除旧的、重命名临时方法,或者在写入后读取文件时对文件进行一些保护性锁定。 filewatcher 事件触发已进行更改。

之前,我在 FileSystemWatcher 中使用此方法

IPSDependency.FileSystem.WaitForLockOnFile(Me.mFilePath)

在继续 代码是这样的。 (现在阅读本文后,可能有更好的方法,我在这里使用一些睡眠来减少 CPU 抖动)

Public Shared Function IsLockAvailable(ByVal filename As String, ByVal fnfIsOK As Boolean) As Boolean
    Dim fi As FileInfo
    fi = New FileInfo(filename)
    Return IsLockAvailable(New FileInfo(filename), fnfIsOK)
End Function

Public Shared Function IsLockAvailable(ByVal theFile As FileInfo, ByVal fnfIsOK As Boolean) As Boolean
    Dim fs As FileStream
    Try
        If theFile.Exists Then
            fs = New FileStream(theFile.FullName, FileMode.Open, FileAccess.ReadWrite, FileShare.None)
            fs.Close()
            Return True
        Else
            Return fnfIsOK
        End If
    Catch ex As IOException
        'we just let the exception go, because we are only testing the file rather than trying to use it.
        Return False
    End Try
End Function

Public Shared Sub WaitForLockOnFile(ByVal theFilename As String)
    WaitForLockOnFile(New FileInfo(theFilename))
End Sub

Public Shared Sub WaitForLockOnFile(ByVal theFile As FileInfo)
    Dim lockAvailable As Boolean
    If theFile.Exists Then
        While Not lockAvailable
            lockAvailable = IsLockAvailable(theFile, False)
        End While
    End If
End Sub

I kinda use your number 2.

But I'm only working in .NET 2 with my application, but it should still apply.

I have a settings class that I use across my 2 programs. Inside this settings class I setup a FileSystemWatcher object that looks at the Settings file.

If the settings file is updated by the other application, my current gets an event trigger to indicate that the settings need to reload.

You can also apply the same principle in your settings screen so that if the (service) other application updates anything during the settings edit, that is reflected in your screen.

I use the AppData (my company/application name directory) to store the file.

The other thing to bear in mind, is that there can be locking on the file while it is being written so you can either use a temp name save, delete old, rename temp method or put some protective locking on the file when reading after the filewatcher event fires that changes have been made.

I use this approach in my FileSystemWatcher before proceeding

IPSDependency.FileSystem.WaitForLockOnFile(Me.mFilePath)

the code for that is like this. (upon reading this now, there may be a better method my using some sleep in here to reduce CPU thrashing)

Public Shared Function IsLockAvailable(ByVal filename As String, ByVal fnfIsOK As Boolean) As Boolean
    Dim fi As FileInfo
    fi = New FileInfo(filename)
    Return IsLockAvailable(New FileInfo(filename), fnfIsOK)
End Function

Public Shared Function IsLockAvailable(ByVal theFile As FileInfo, ByVal fnfIsOK As Boolean) As Boolean
    Dim fs As FileStream
    Try
        If theFile.Exists Then
            fs = New FileStream(theFile.FullName, FileMode.Open, FileAccess.ReadWrite, FileShare.None)
            fs.Close()
            Return True
        Else
            Return fnfIsOK
        End If
    Catch ex As IOException
        'we just let the exception go, because we are only testing the file rather than trying to use it.
        Return False
    End Try
End Function

Public Shared Sub WaitForLockOnFile(ByVal theFilename As String)
    WaitForLockOnFile(New FileInfo(theFilename))
End Sub

Public Shared Sub WaitForLockOnFile(ByVal theFile As FileInfo)
    Dim lockAvailable As Boolean
    If theFile.Exists Then
        While Not lockAvailable
            lockAvailable = IsLockAvailable(theFile, False)
        End While
    End If
End Sub
滴情不沾 2024-09-15 00:30:53

假设所有内容都在同一台计算机上运行,​​那么这样如何:

  1. 定义一个用于定义设置的通用 C# 结构。所有项目都包含此 .cs 文件。将此类定义为具有 SequentialExplicitStructLayoutstruct,以便它可以直接映射到非托管共享记忆。例如:

    [StructLayout(LayoutKind.Sequential)]
    不安全的结构 MySharedSettings
    {
    公共整数设置1;
    公共整数设置2;
    公共字符串设置3;
    // 在这里添加更多字段。
    }

  2. 使用命名共享内存(又名:内存映射文件)。这允许同一台计算机上的多个进程共享数据,而无需远程处理WCF的开销。共享内存速度非常快,并且与管道不同,它提供对共享内存数据的随机访问。该服务将创建指定的共享内存,并且 UI 应用程序将打开该共享内存。您必须使用 pinvoke 才能使用底层 Windows API,但这没什么大不了的。

  3. UI 应用程序将 MySharedSettings 写入共享内存,而服务读取共享内存。

    UI

  4. 使用命名信号量和/或命名互斥体来保护对共享内存的访问并指示新设置的可用性。该服务有一个专用的后台线程,它只需对信号量执行 WaitOne(),并且 UI 线程将在写入新数据时发出信号。

Assuming that everything is running on the same machine, how about this:

  1. Define a common c# structure that defines the settings. All projects include this .cs file. Define this class as a struct with a StructLayout of Sequential or Explicit so it can be mapped directly into unmanaged shared memory. For example:

    [StructLayout(LayoutKind.Sequential)]
    unsafe struct MySharedSettings
    {
    public int setting1;
    public int setting2;
    public string setting3;
    // add more fields here.
    }

  2. Use named shared memory (aka: memory-mapped files). This allows multiple processes on the same computer to share data, without the overhead of Remoting or WCF. Shared memory is extremely fast and, unlike pipes, offers random access to the shared memory data. The service would create the named shared memory, and the UI applications would open the shared memory. You would have to use pinvoke to use the underlying Windows APIs, but this is not a big deal.

  3. The UI applications write the MySharedSettings to the shared memory, while the service reads the shared memory.

  4. Use a named Semaphore and/or named Mutex to protect access to the shared memory and to signal the availability of new settings. The service has a dedicated background thread that simply perform a WaitOne() on the semaphore, and the UI thread will signal when new data is written.

∞琼窗梦回ˉ 2024-09-15 00:30:53

通常,执行“轮询操作”(即同步文件)的服务在其轮询间隔中有足够的滞后时间,您可以轻松地在每个循环中甚至根据需要重新读取所有设置。

如果您的服务更接近 SOA 后端,那么这些更改可能会影响通常在服务生命周期内仅使用一次的设置。如果这是您的应用程序类型,那么您上面描述的选项 2 是最可靠的。我不能说我很关心 Paul 的实现,因为轮询这样的文件会产生不可靠的结果。我建议使用全局命名的等待句柄来向您的进程发出更改信号。我相信你可以在这里找到一个例子。如果您不想这样做,那么您可以轮询配置文件的上次修改时间更改。

总的来说,我更喜欢第一种使用注册表进行存储的方法。将所有设置以离散值写入注册表配置单元中,并在服务中按需读取它们。它比您想象的要快,并且在前端和后端都易于实现。

Usually services that perform a 'polling operation' (i.e. sync files) have enough lag time in their poll interval that you can just as easily re-read all settings each loop, or even as needed.

If your service is more along the lines of a SOA back-end, then the changes may affect settings that are generally only used once during a service's lifetime. If this is your type of application, then the option #2 you describe above is the most reliable. I can't say I care much for Paul's implementation as polling a file like that will yield unreliable results. I would recommend using a globally named wait handle to signal your process for changes. I'm sure you can find an example here on SO. If you don't want to do that then you could poll for the configuration file's last-modified time changing.

Overall my preference is for the first approach using the registry for storage. Write all your settings in discrete values in a registry hive and read them on-demand in your service. It's faster than you might think and easy to implement on both front and back-end.

木有鱼丸 2024-09-15 00:30:53

我必须同意你最初倾向于#2 和#3。我特别喜欢#3,因为我不喜欢投票,但最终我认为#2 或#3 之间的决定将由您的服务要求决定。

至于用户设置的存储,我建议探索独立存储( http://msdn .microsoft.com/en-us/library/3ak841sy.aspx)。它提供了一种出色的机制,可以安全、一致且可靠地访问用户文件。除非管理员完全关闭独立存储,否则您不必担心用户是否拥有权限。另外,如果您启用漫游,如果用户在同一域中使用不同的系统,他们甚至可以随身携带他们的设置,非常灵活,不是吗?

I have to agree with your initial lean toward #2 and #3. I particularly like the #3 since I'm not a fan of polling but ultimately I think the decision between #2 or #3 will be driven by the requirements of your service.

As for storage of user settings I would recommend exploring Isolated Storage ( http://msdn.microsoft.com/en-us/library/3ak841sy.aspx ). It provides an excellent mechanism for secure, consistent, and reliable access to user files. You won't have to worry about user's having permission unless the Administrator has completely turned off Isolated Storage. Plus if you enable roaming, users can even take their settings with them if they utilize different systems on the same domain, pretty slick eh?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文