.NET Windows服务(WTSSendMessage):在XP上显示消息但不在Windows 7上显示消息

时间:2013-09-11 02:31:41

标签: c# windows

在Windows 7上使用.NET窗口服务显示消息时出现问题。它在Windows XP上正常工作。我知道它在Windows 7上不起作用,如Microsoft网站和包括Stackoverflow在内的一些论坛中所述。我已经按照Stackoverflow中提到的Pinvoke.NET中的以下示例(使用WTSSendMessage)进行了操作。但它也无法正常工作。该示例在Windows XP上正常工作。请帮助我,因为这是一个非常大的问题需要因为我们已经迁移到Windows7,所以要尽快修复。

签名:

[DllImport("wtsapi32.dll", SetLastError=true)]
static extern bool WTSSendMessage(
            IntPtr hServer,
            [MarshalAs(UnmanagedType.I4)] int SessionId,
            String pTitle,
            [MarshalAs(UnmanagedType.U4)] int TitleLength,
            String pMessage,
            [MarshalAs(UnmanagedType.U4)] int MessageLength,
            [MarshalAs(UnmanagedType.U4)] int Style,
            [MarshalAs(UnmanagedType.U4)] int Timeout,
            [MarshalAs(UnmanagedType.U4)] out int pResponse,
            bool bWait);

变量声明:

public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
public static int WTS_CURRENT_SESSION = -1;

代码:

  bool result = false;
    String title = "Hello";
    int tlen = title.Length;
    String msg = "Terminal Service!";
    int mlen = msg.Length;
    int resp = 0;
    result = WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, title, tlen, 
    msg, mlen, 0, 0, out resp, false);
    int err = Marshal.GetLastWin32Error();
    System.Console.WriteLine("result:{0}, errorCode:{1}, response:{2}", result, err, resp);

4 个答案:

答案 0 :(得分:5)

对于Windows 7或更高版本上的服务,

WTS_CURRENT_SESSION将等同于会话0,这不允许用户交互。您必须枚举所有活动会话并向每个活动会话发送消息(除非计算机正在运行终端服务,否则它应该只是一个)。如下所示:

<强>签名:

    [DllImport("wtsapi32.dll", SetLastError = true)]
    static extern int WTSEnumerateSessions(
                    System.IntPtr hServer,
                    int Reserved,
                    int Version,
                    ref System.IntPtr ppSessionInfo,
                    ref int pCount);

    public enum WTS_CONNECTSTATE_CLASS
    {
        WTSActive,
        WTSConnected,
        WTSConnectQuery,
        WTSShadow,
        WTSDisconnected,
        WTSIdle,
        WTSListen,
        WTSReset,
        WTSDown,
        WTSInit
    }

辅助功能:

public static List<Int32> GetActiveSessions(System.IntPtr server)
{
    List<Int32> ret = new List<int>();
    IntPtr ppSessionInfo = IntPtr.Zero;

    Int32 count = 0;
    Int32 retval = WTSEnumerateSessions(server, 0, 1, ref ppSessionInfo, ref count);
    Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));

    Int64 current = (int)ppSessionInfo;

    if (retval != 0)
    {
        for (int i = 0; i < count; i++)
        {
            WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO));
            current += dataSize;

            if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive)
                ret.Add(si.SessionID);
        }

        WTSFreeMemory(ppSessionInfo);
    }

    return ret;
}
代码中

    List<Int32> sessions = GetActiveSessions(WTS_CURRENT_SERVER_HANDLE);
    if (sessions.Count < 1)
    {
        int err = Marshal.GetLastWin32Error();
        _SUDSEventLog.WriteEntry("No active sessions found: errorCode:", err);
    }
    else
    {
        bool result = false;
        String title = "Hello";
        int tlen = title.Length;
        String msg = "Terminal Service!";
        int mlen = msg.Length;
        int resp = 0;
        result = WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, sessions[0], title, tlen,
        msg, mlen, 0, 0, out resp, false);
        int err = Marshal.GetLastWin32Error();
        System.Console.WriteLine("result:{0}, errorCode:{1}, response:{2}", result, err, resp);
    }

答案 1 :(得分:1)

我认为这可能是Windows Messenger中不存在的Windows Messenger的一部分。

link显示原因(默认情况下禁用RPC)

和可能的解决方案:

  

默认情况下,在Windows 7上禁用了远程RPC。可以通过将'HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ Terminal Server'值'AllowRemoteRPC'设置为1来启用它。但是,这只会给您带来另一个错误 - 0x721 - 我猜测是因为Windows 7不允许'nul'用户访问。有几种可能的方法解决这个问题,但我没有尝试过任何一种方法。一种方法是将远程机器的凭证添加到发送机器上的凭证缓存中,但我不知道它有多实用。可能有一种方法允许'nul'用户访问,但这也许不是一个好主意。

这个MSDN Article:在看起来像Windows 7的内容上展示了一个长期工作的例子。

答案 2 :(得分:1)

您可以在Windows服务中向用户显示非阻止消息框。为此,首先,从“services.msc”启动名为“Interactive Service Detection”的服务。然后从Windows服务启动一个线程。在线程方法中使用“MessageBox.Show()”方法向用户显示您的消息框。而不是手动启动“交互式服务检测”服务,您可以启动它c#program。

答案 3 :(得分:1)

是的,可以做到!

我知道这有点晚了,这个建议似乎有点可怕它实际上并不是:你可以确保 FIRST 当前登录用户仅通过更改行

从服务发送消息
 public static int WTS_CURRENT_SESSION = -1;

 public static int WTS_CURRENT_SESSION = 1;

这是因为Vista(EG:2008Vista2008 R2Win7Win 2012Win 8Win 2016Win Ten等)ID只是用户中的首次记录始终为1,接下来是2等。

同样地(...因此)你可以通过反向操作来确保最后登录用户,然后按下我知道它的数字,但你只需要向下循环直到你得到一个非错误。您需要在WTSSendMessage调用等待结果(即锁定/等待当前线程)时向下移动

因此:一种解决方案,您向所有用户发送消息(通过将timeout设置为zero)并调度猜测数字并行,通过所有不同的线程,并简单地忽略死线程。

我使用NuGet包将我的 c#控制台代码转换为 c#服务,通过TopShelf following this tutorial并使用{ {1}}以确保此方法有效。它总是会调用LAST登录用户,在Windows Ten中只有一个人。

Thread[STAThread]

如果您想知道这确实有效,如果您正在关注样板class Service { [DllImport("wtsapi32.dll", SetLastError = true)] static extern bool WTSSendMessage( IntPtr hServer, [MarshalAs(UnmanagedType.I4)] int SessionId, String pTitle, [MarshalAs(UnmanagedType.U4)] int TitleLength, String pMessage, [MarshalAs(UnmanagedType.U4)] int MessageLength, [MarshalAs(UnmanagedType.U4)] int Style, [MarshalAs(UnmanagedType.U4)] int Timeout, [MarshalAs(UnmanagedType.U4)] out int pResponse, bool bWait); public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero; public static int WTS_CURRENT_SESSION = 1; public void Start() { for (int user_session = 10; user_session>0; user_session--) { Thread t = new Thread(() => { try { bool result = false; String title = "Alert"; int tlen = title.Length; String msg = "hi"; int mlen = msg.Length; int resp = 7; result = WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, user_session, title, tlen, msg, mlen, 4, 0, out resp, true); int err = Marshal.GetLastWin32Error(); if (err == 0) { if (result) //user responded to box { if (resp == 7) //user clicked no { } else if (resp == 6) //user clicked yes { } Debug.WriteLine("user_session:" + user_session + " err:" + err + " resp:" + resp); } } } catch (Exception ex) { Debug.WriteLine("no such thread exists", ex); } //Application App = new Application(); //App.Run(new MessageForm()); }); t.SetApartmentState(ApartmentState.STA); t.Start(); } } public void Stop() { // write code here that runs when the Windows Service stops. } } 示例

,则只需上面的代码