Solved

VB.NET - How to use WTSSendMessage to send messages to users

Posted on 2014-04-16
8
1,801 Views
Last Modified: 2014-04-30
I am developing an application that members of desktop support use to perform certain support functions. One of these is to send a popup message to a user on the network.

Having found out that sending messages using the msg.exe thats built in to Vista and above is not possible VB.NET in Visual Studio I have learnt that an API call to WTSSENDMESSAGE is what I need.
However, I cannot find any useful code I can use to acheive this, coupled with the fact that I am not a programmer. I just find code that works and change it to my needs.

The best info I could find for this is:

Declare Function WTSSendMessage Lib "wtsapi32.dll" ( _
	ByVaL hServer As Long, _
	ByVaL SessionId As Long, _
	ByVaL pTitle As String, _
	ByVaL TitleLength As Long, _
	ByVaL pMessage As String, _
	ByVaL MessageLength As Long, _
	ByVaL Style As Long, _
	ByVaL timeout As Long, _
	ByRef pResponse As Long, _
	ByVaL bWait As Long) As Long

Open in new window

And this link has details and examples in C++
WTSSENDMESSAGE

Can anyone turn this into something useful? I will have to textboxes in the form called:
txtPCName
txtMessage
With a button that sends it:
btnSend

Many thanks
0
Comment
Question by:fruitloopy
  • 4
  • 3
8 Comments
 
LVL 96

Expert Comment

by:Bob Learned
ID: 40005737
This may or may not be helpful:

wtssendmessage (wtsapi32)
http://www.pinvoke.net/default.aspx/wtsapi32/wtssendmessage.html
0
 
LVL 2

Author Comment

by:fruitloopy
ID: 40006035
Thanks TheLearnedOne. I have already read that but I just don't know how to put it into useful code.
I am new to VB.NET.
0
 
LVL 96

Expert Comment

by:Bob Learned
ID: 40006421
Here is some sample code in VB.NET taken from that p/invoke topic:


Public Class MessageService

Public Shared Function SendMessage(title As String, message As String, ByRef errorCode As String) As Integer
	Dim WTS_CURRENT_SERVER_HANDLE As IntPtr = IntPtr.Zero
	Dim WTS_CURRENT_SESSION As Integer = -1

	Dim tlen As Integer = title.Length
	Dim mlen As Integer = msg.Length
	Dim response As Integer = 0
	Dim result As Boolean = WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, title, tlen, message, mlen, _
		0, 0, resp, False)
	Dim err As Integer = Marshal.GetLastWin32Error()
	Return response
End Function

End Class 

Open in new window


Example call:
Dim response As Integer = MessageService.SendMessage("Hello Kitty", "My name is Bob")
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 2

Author Comment

by:fruitloopy
ID: 40006502
That looks great, thank TheLearnedOne!

It produces an error:
Error      4      Argument not specified for parameter 'errorCode' of 'Public Shared Function SendMessage(title As String, message As String, ByRef errorCode As String) As Integer'.

For this line:
Dim response As Integer = MessageService.SendMessage("Hello Kitty", "My name is Bob")

Here's my full code
    Private Sub btnSend_Click(sender As Object, e As EventArgs) Handles btnSend.Click
        If Main.Reachable(txtPCName.Text) Then
            Dim MyReg As Microsoft.Win32.RegistryKey = Microsoft.Win32.RegistryKey.OpenRemoteBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, txtPCName.Text)
            Dim MyRegKey As Microsoft.Win32.RegistryKey
            MyRegKey = MyReg.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System", True)
            MyRegKey.SetValue("PromptOnSecureDesktop", 1, Microsoft.Win32.RegistryValueKind.DWord)
            MyRegKey = MyReg.OpenSubKey("SYSTEM\CurrentControlSet\Control\Terminal Server", True)
            MyRegKey.SetValue("AllowRemoteRPC", 1, Microsoft.Win32.RegistryValueKind.DWord)
            MyRegKey.Close()
        End If
        'Dim Message = txtMessage.Text
        Dim result = MessageBox.Show("The computer " & txtPCName.Text & " will be sent the message you typed, are you sure you want to do this?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
        If result = Windows.Forms.DialogResult.Yes Then
            Dim response As Integer = MessageService.SendMessage("Hello Kitty", "My name is Bob")
        ElseIf result = Windows.Forms.DialogResult.No Then
            Dim MyReg As Microsoft.Win32.RegistryKey = Microsoft.Win32.RegistryKey.OpenRemoteBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, txtPCName.Text)
            Dim MyRegKey As Microsoft.Win32.RegistryKey
            MyRegKey = MyReg.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System", True)
            MyRegKey.SetValue("PromptOnSecureDesktop", 0, Microsoft.Win32.RegistryValueKind.DWord)
            MyRegKey = MyReg.OpenSubKey("SYSTEM\CurrentControlSet\Control\Terminal Server", True)
            MyRegKey.SetValue("AllowRemoteRPC", 0, Microsoft.Win32.RegistryValueKind.DWord)
            MyRegKey.Close()
        End If
    End Sub

    Public Class MessageService
        Public Shared Function SendMessage(title As String, message As String, ByRef errorCode As String) As Integer
            Dim WTS_CURRENT_SERVER_HANDLE As IntPtr = IntPtr.Zero
            Dim WTS_CURRENT_SESSION As Integer = -1
            Dim tlen As Integer = title.Length
            Dim mlen As Integer = message.Length
            Dim response As Integer = 0
            Dim result As Boolean = WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, title, tlen, message, mlen, _
                0, 0, response, False)
            Dim err As Integer = Marshal.GetLastWin32Error()
            Return response
        End Function
    End Class
End Class

Open in new window


I realise I still may not be calling it correctly but I just cant figure out what's missing.
0
 
LVL 2

Author Comment

by:fruitloopy
ID: 40006688
I cannot see how I can send this to a remote PC either. Sorry!
0
 
LVL 96

Accepted Solution

by:
Bob Learned earned 500 total points
ID: 40006951
See what coding in the comment block gets ya ;)

Dim errorCode As String
Dim response As Integer = MessageService.SendMessage("Hello Kitty", "My name is Bob", errorCode)

That code uses current server and session:

Dim WTS_CURRENT_SERVER_HANDLE As IntPtr = IntPtr.Zero
Dim WTS_CURRENT_SESSION As Integer = -1

If you need more information, then that is where it gets trickier.  I believe that you need WTSOpenServer(server name) to get a handle to the server, and WTSEnumerateSessions to get the active sessions on the server.
0
 
LVL 2

Author Comment

by:fruitloopy
ID: 40032134
This was all far to complex to implement so I took a different direction. The msg.exe must be run with elevated rights so I used a handy little elevate64.exe file to elevate the  msg.exe and then pass commands to it.
First I have to check if the remote registry service is running before changing two required registry values. If the registry values are not changed it will not display the message on the remote PC.

Here's the code if anyone wants to use it:
Imports System.Net.NetworkInformation
Imports System.Net
Imports System.Threading
Imports System.Text
Imports System.Diagnostics
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Management
Imports System.ServiceProcess

Public Class Message

    Private Sub Message_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        txtPCName.Text = Main.txtHostName.Text()
    End Sub
    ' Is the service running?
    ' Returns true if we need to start the service
    Public Function CheckService(ByVal PC As String) As Boolean
        Dim obj As ManagementObject

        obj = New ManagementObject("\\" & txtPCName.Text & "\root\cimv2:Win32_Service.Name='RemoteRegistry'")
        If Not IsNothing(obj) Then
            If obj("StartMode").ToString <> "Disabled" And obj("State").ToString = "Running" Then
                Return False
            End If
        End If
        Return True
    End Function
    Public Sub StartService(ByVal PC As String)
        Dim obj As ManagementObject
        'Dim inParams, outParams As ManagementBaseObject
        'Dim Result As Integer
        Dim sc As New ServiceController

        obj = New ManagementObject("\\" & txtPCName.Text & "\root\cimv2:Win32_Service.Name='RemoteRegistry'")
        ' Start the service
        If obj("State").ToString <> "Running" Then
            ' now start the service
            sc = New ServiceController("RemoteRegistry", PC)
            sc.Start()
            sc.WaitForStatus(ServiceControllerStatus.Running)
        End If
    End Sub

    Private Sub btnSend_Click(sender As Object, e As EventArgs) Handles btnSend.Click
        If Main.Reachable(txtPCName.Text) Then
            CheckService(txtPCName.Text)
            If CheckService(txtPCName.Text) = True Then
                StartService(txtPCName.Text)
            End If
            Dim MyReg As Microsoft.Win32.RegistryKey = Microsoft.Win32.RegistryKey.OpenRemoteBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, txtPCName.Text)
            Dim MyRegKey As Microsoft.Win32.RegistryKey
            MyRegKey = MyReg.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System", True)
            MyRegKey.SetValue("PromptOnSecureDesktop", 1, Microsoft.Win32.RegistryValueKind.DWord)
            MyRegKey = MyReg.OpenSubKey("SYSTEM\CurrentControlSet\Control\Terminal Server", True)
            MyRegKey.SetValue("AllowRemoteRPC", 1, Microsoft.Win32.RegistryValueKind.DWord)
            MyRegKey.Close()


        End If
        Dim result = MessageBox.Show("The computer " & txtPCName.Text & " will be sent the message you typed, are you sure you want to do this?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
        If result = Windows.Forms.DialogResult.Yes Then
            Dim SendMessage As New ProcessStartInfo
            SendMessage.WindowStyle = ProcessWindowStyle.Hidden
            SendMessage.CreateNoWindow = True
            SendMessage.UseShellExecute = False
            SendMessage.FileName = """C:\Temp\Elevate64.exe"""
            SendMessage.Arguments = " msg.exe /server:" & txtPCName.Text & " /time:0" & " * " & txtMessage.Text
            Process.Start(SendMessage)
            MsgBox("Your message has been sent")
        ElseIf result = Windows.Forms.DialogResult.No Then
            Dim MyReg As Microsoft.Win32.RegistryKey = Microsoft.Win32.RegistryKey.OpenRemoteBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, txtPCName.Text)
            Dim MyRegKey As Microsoft.Win32.RegistryKey
            MyRegKey = MyReg.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System", True)
            MyRegKey.SetValue("PromptOnSecureDesktop", 0, Microsoft.Win32.RegistryValueKind.DWord)
            MyRegKey = MyReg.OpenSubKey("SYSTEM\CurrentControlSet\Control\Terminal Server", True)
            MyRegKey.SetValue("AllowRemoteRPC", 0, Microsoft.Win32.RegistryValueKind.DWord)
            MyRegKey.Close()
        End If
    End Sub

    Private Sub btnClear_Click(sender As Object, e As EventArgs) Handles btnClear.Click
        txtMessage.Text = ""
        txtMessage.Focus()
    End Sub
End Class

Open in new window

0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

In my previous article (http://www.experts-exchange.com/Programming/Languages/.NET/.NET_Framework_3.x/A_4362-Serialization-in-NET-1.html) we saw the basics of serialization and how types/objects can be serialized to Binary format. In this blog we wi…
Many of us here at EE write code. Many of us write exceptional code; just as many of us write exception-prone code. As we all should know, exceptions are a mechanism for handling errors which are typically out of our control. From database errors, t…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now