?
Solved

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

Posted on 2014-04-16
8
Medium Priority
?
2,066 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Major Serverless Shift

Comparison of major players like AWS, Microsoft Azure, IBM Bluemix, and Google Cloud Platform

 
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 1500 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

On Demand Webinar: Networking for the Cloud Era

Did you know SD-WANs can improve network connectivity? Check out this webinar to learn how an SD-WAN simplified, one-click tool can help you migrate and manage data in the cloud.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Parsing a CSV file is a task that we are confronted with regularly, and although there are a vast number of means to do this, as a newbie, the field can be confusing and the tools can seem complex. A simple solution to parsing a customized CSV fi…
Wouldn’t it be nice if you could test whether an element is contained in an array by using a Contains method just like the one available on List objects? Wouldn’t it be good if you could write code like this? (CODE) In .NET 3.5, this is possible…
In this brief tutorial Pawel from AdRem Software explains how you can quickly find out which services are running on your network, or what are the IP addresses of servers responsible for each service. Software used is freeware NetCrunch Tools (https…
In this video, Percona Solutions Engineer Barrett Chambers discusses some of the basic syntax differences between MySQL and MongoDB. To learn more check out our webinar on MongoDB administration for MySQL DBA: https://www.percona.com/resources/we…
Suggested Courses

770 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