Working on my first form for PowerShell. At the top of the form is a ReadOnly textbox that shows the Hostname, Serial Number, IP Address, and MAC address for the machine it's being used on. I have a button below it to Refresh this data which will update to reflect current network connection (it's supposed to tell you if it's on WiFi, Ethernet, or disconnected and update the text box accordingly) As far as I can tell, it works! Except when media is disconnected. Then it doesn't change at all, it just keeps the same data.
I'm using Powershell Studio to make the Form but I get the same results when using a Form built with POSH.
When running this in a normal PS1 script using the console as the output, it works perfectly! Showing "Disconnected" for IP Address and MAC.
#The form's 'Shown' Event and the Refresh Button trigger this function
$UpdateSysInfo={
$MHN = hostname
$MSN = gwmi win32_bios | Select –ExpandProperty SerialNumber
$MYIPAddress = ([System.Net.Dns]::GetHostByName($MHN).AddressList[0]).IpAddressToString
$MYIPMAC = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -ComputerName $MHN
$MYMAC = ($MYIPMAC | where { $_.IpAddress -eq $MYIPAddress }).MACAddress
$wifi = netsh interface show interface | findstr /C:"Wi-Fi" /C:"Name"
if ($wifi -like '*Connected*')
{
$WiFi = "(Wi-Fi)"
}
Else
{
$WiFi = "(Ethernet)"
}
if ($WiFi -like '*(Wi-Fi)*')
{
$MACWiFi = "(Wi-Fi)"
}
Else
{
$MACWiFi = ""
}
#If media is disconnected, then $MYIPAddress will be 127.0.0.1
if ($MYIPAddress -like '127.0.0.1')
{
$ShowIP = "DISCONNECTED"
$ShowMAC = "DISCONNECTED"
}
Else
{
$ShowIP = "$MYIPAddress $WiFi"
$ShowMAC = "$MYMAC $MACWiFi"
#Should be pretty obvious, SysInfo is the Textbox where it's showing the accumulated data here
$SysInfo.text = "Hostname: $MHN │ Serial Number: $MSN │ IP Address: $ShowIP │ MAC Address: $ShowMAC"
}
}
No error messages. It works perfectly when connected to internet, but when network connection is disabled and it's supposed to show "Disconnected" it does not update the textbox to show that. As far as the info in that box is concerned, the network connection never changed. This is the case no matter how long I wait after disabling the network connection.
if ($wifi -like '*Connected*')
{
$WiFi = "(Wi-Fi)"
}
Is true for both "connected" and "disconnected"
and will always return $WiFi = "(Wi-Fi)"
Related
I have a script, on which I connect to MicrosoftTeams (connect-microsoftteams)
since I have a long running operation, I am trying to run that code in a separate job. however, I do not know how I can pass along the already existing connection to MicrosoftTeams, to that scriptblock.
if (!$Connection) {$Connection = Connect-MicrosoftTeams}
$scriptBlocks = #{
$btns[0] =
{
param ([Parameter(Mandatory=$true)]$Users)
#Connect-MicrosoftTeams
$agents = #()
foreach ($a in $Users) {
$user = get-csonlineuser $a.ObjectID
if ($user) {
$agents += $user
}
}
return $agents
}
}
I launch this using:
foreach ($btn in $btns) {
$btn.Add_Click({
# Temporarily disable this button to prevent re-entry.
$this.IsEnabled = $false
# Show a status message in the associated text box.
$txtBoxes[$this].Visibility = "Visible"
# Asynchronously start a background thread job named for this button.
# Note: Would work with Start-Job too, but that runs the code in *child process*,
# which is much slower and has other implications.
$null = Start-Job -Name $this.Name $scriptBlocks[$this]
})
}
I later do some checks to see if the job completed.
However, this creates a new Powershell instance, that does not have the $Connection to MicrosoftTeams as the 'parent' has..
[edit]
I tried some more, like passing in a Get-Credential, this is almost willing to work.. except MFA is being a problem
$scriptBlocks = #{
$btns[0] = {
param ($Credential)
Connect-MicrosoftTeams -Credential $Credential
$global:AllCallQueues = Get-CsCallQueue -first 10000
}
}
this Produces:
Loaded Module 'Microsoft.Teams.ConfigAPI.Cmdlets'
One or more errors occurred.
AADSTS50076: Due to a configuration change made by your administrator, or because you moved to a new location, you must use multi-factor authentication to access 'xxx'.
Trace ID: xxx
Correlation ID: xx
Timestamp: 2021-10-26 18:58:45Z
One or more errors occurred.
Exception calling "GetSteppablePipeline" with "1" argument(s): "Exception calling "GetRemoteNewCsOnlineSession" with "1" argument(s): "Run either Connect-MicrosoftTeams or new-csonlinesession before runnin
g cmdlets.""
all credits for this code go to https://stackoverflow.com/a/65531439/3014542
Something strange is happening with a database server I had to rebuild and restore from backup.
I'm pointing an old VB6 application using ADODB.Connection and a modern C# EF6 application at it, using what should be the same connection string for both, of the form
servername\INSTANCE
When run on the same machine that's running SQL Server, the VB6 application and EF6 application are both able to connect using this connection string.
When run on a different machine on the network, the VB6 application connects, but the EF6 application doesn't.
(with a standard "server not found" message, error: 26 - Error Locating Server/Instance Specified at System.Data.SqlClient.SqlInternalConnectionTds..ctor)
If I look at the specific instance port and connect with
servername,instance_port_number
then both applications connect, whatever machine I run them on. So it seems like something might be happening with SQL Server Browser to cause the issue.
Is there a way I can get some kind of diagnostic information out of SQL Server Browser, what data it's sent to where, without going as far as to monitor all network traffic?
An alternative to a network trace for troubleshooting is to send an instance enumeration query to the SQL Server Browser service and examine the results. This will verify the SQL Server Browser is reachable over UDP port 1434 and that the returned datagram contains the instance name and port information needed for the client to connect to a named instance.
Run the PowerShell script below on the problem machine.
# verify UDP port 1433 connectivity and query SQL Server Browser for all instances
Function Get-SqlServerBrowerDatagramForAllInstances($hostNameOrIpAddress)
{
Write-Host "Querying SQL Browser for all instances on host $hostNameOrIpAddress ..."
try
{
$udpClient = New-Object Net.Sockets.UdpClient($hostNameOrIpAddress, 1434)
$bufferLength = 1
$browserQueryMessage = New-Object byte[] 1
$browserQueryMessage[0] = 2
$bytesSent = $udpClient.Send($browserQueryMessage, $browserQueryMessage.Length)
$udpClient.Client.ReceiveTimeout = 10000
$remoteEndPoint = New-Object System.Net.IPEndPoint([System.Net.IPAddress]::Broadcast, 0)
$browserResponse = $udpClient.Receive([ref]$remoteEndPoint)
$payloadLength = $browserResponse.Length - 3
$browserResponseString = [System.Text.ASCIIEncoding]::ASCII.GetString($browserResponse, 3, $payloadLength)
$elements = $browserResponseString.Split(";")
Write-Host "SQL Server Browser query results:`r`n"
for($i = 0; $i -lt $elements.Length; $i = $i + 2)
{
if ($elements[$i] -ne "")
{
Write-Host "`t$($elements[$i])=$($elements[$i+1])"
}
else
{
Write-Host ""
# next instance
$i = $i - 1
}
}
}
catch [Exception]
{
Write-Host "ERROR: $($_.Exception.Message)" -ForegroundColor Red
}
}
# verify UDP port 1433 connectivity and query SQL Server Browser for single instance
Function Get-SqlServerBrowerDatagramByInstanceName($hostNameOrIpAddress, $instanceName)
{
Write-Host "Querying SQL Browser for host $hostNameOrIpAddress, instance $instanceName ..."
try
{
$instanceNameBytes = [System.Text.Encoding]::ASCII.GetBytes($instanceName)
$udpClient = New-Object Net.Sockets.UdpClient($hostNameOrIpAddress, 1434)
$bufferLength = $InstanceNameBytes.Length + 2
$browserQueryMessage = New-Object byte[] $bufferLength
$browserQueryMessage[0] = 4
$instanceNameBytes.CopyTo($browserQueryMessage, 1)
$browserQueryMessage[$bufferLength-1] = 0
$bytesSent = $udpClient.Send($browserQueryMessage, $browserQueryMessage.Length)
$udpClient.Client.ReceiveTimeout = 10000
$remoteEndPoint = New-Object System.Net.IPEndPoint([System.Net.IPAddress]::Broadcast, 0)
$browserResponse = $udpClient.Receive([ref]$remoteEndPoint)
$payloadLength = $browserResponse.Length - 3
$browserResponseString = [System.Text.ASCIIEncoding]::ASCII.GetString($browserResponse, 3, $payloadLength)
$elements = $browserResponseString.Split(";")
$namedInstancePort = ""
Write-Host "SQL Server Browser query results:`r`n"
for($i = 0; $i -lt $elements.Length; $i = $i + 2)
{
if ($elements[$i] -ne "")
{
Write-Host "`t$($elements[$i])=$($elements[$i+1])"
if($elements[$i] -eq "tcp")
{
$namedInstancePort = $elements[$i+1]
}
}
}
}
catch [Exception]
{
Write-Host "ERROR: $($_.Exception.Message)" -ForegroundColor Red
}
}
Get-SqlServerBrowerDatagramForAllInstances -hostNameOrIpAddress "servername"
Get-SqlServerBrowerDatagramByInstanceName -hostNameOrIpAddress "servername" -instanceName "INSTANCE"
In entity framework 6 you can take the dbcontext object and do something like. Yourcontext.Database.log = s => mylogger.Debug(s);
The right hand side is a lambda function that takes a string s and logs it.
All of the sql and parameters get logged.
I'm writing a little toolbox at work for the common vSphere tasks. This time around, I wanted to dip into WPF.
I have created an initial connect-to-server GUI, and everything works as designed. But now I'm stuck at the following:
When clicking the Connect button (btnConnect), I want it to become greyed out/disabled, followed by the connection attempt. Once the attempt is done, it can become active again. This is to prevent people from clicking on it multiple times in a row.
This is my first attempt (I'm unable to show the complete thing, given that it's 4 files and there's some stuff in it that I'm not allowed to freely share, so I will post what's relevant):
$Window.btnConnect.Add_Click({
$Window.btnConnect.IsEnabled = $False
$Window.btnConnect.Content = 'Connecting...'
Connect-CompanyVIServer -VIServer 'ServerName' -VICredential 'PSCredentialObject'
$Window.btnConnect.Content = 'Connect'
$Window.btnConnect.IsEnabled = $True
})
Essentially, I disable the button, change it's text to Connecting..., and connect to the VMware vCenter Server. Afterwards I change the button label back and re-enable it.
When I click on the button, the form hangs while it's loading and making the connection attempt. This is to be expected, being that they're in the same thread. That's fine, since the form should do anything until the attempt is completed anyway (and learning both WPF AND runspaces at the same time would make it too complex).
The problem is that even though the $Window.btnConnect.IsEnabled = $False is at the top, the form doesn't actually update until the entire block is done processing. Until that time, I can continue clicking on the button and it'll just buffer the attempts and execute them after one-another.
So I figured I'd split them up into separate events:
$Window.btnConnect.Add_Click({ $Window.btnConnect.IsEnabled = $False })
$Window.btnConnect.Add_IsEnabledChanged({
If ($Window.btnConnect.IsEnabled -eq $False) {
$Window.btnConnect.Content = 'Connecting...'
Connect-CompanyVIServer -VIServer 'ServerName' -VICredential 'PSCredentialObject'
$Window.btnConnect.Content = 'Connect'
$Window.btnConnect.IsEnabled = $True
}
})
A valiant attempt, didn't work though. I get the exact same thing.
So now I'm out of ideas. I was considering writing a trigger into the WPF code to disable the button, but I have no idea if that works. If I do something to run the connection attempt asynchronously, I'd need to be able to query fir the outcome somehow. I'm still experimenting. In the meantime, I come here for help.
My goal is to open a new window once the connection is present and then let it close this window. The important part is that I prevent users from clicking on the button more than once.
Anyone?
As a comment above alludes to, turning the button off, doing work, and then turning it on again is on a particular thread, but not the thread that updates the GUI. It seems as though it happens instantaneously from the GUI thread perspective, i.e. off then on, nothing happens.
If you use the dispatcher to send a message to the GUI thread, i.e.
$Window.Dispatcher.invoke([action]{$Window.btnConnect.IsEnabled -eq $False})
you would see that now you have successfully disabled the button (grayed it out) after a single click. Unfortunately, that's now a permanent situation on the GUI.
To do the "turn off, work, turn on" in a different thread, using a runspace, would go something like (forgive me if not perfect):
function my_func {
param($Window,$btnConnect)
$Runspace = [runspacefactory]::CreateRunspace()
$Runspace.ApartmentState = "STA"
$Runspace.ThreadOptions = "ReuseThread"
$Runspace.Open()
$Runspace.SessionStateProxy.SetVariable("Window",$Window)
$Runspace.SessionStateProxy.SetVariable("btnConnect",$btnConnect)
#add server name/credentials with SetVariable so they can be seen in code block
$code = {
$Window.Dispatcher.invoke(
[action] {$Window.Dispatcher.btnConnect.IsEnabled = $False})
$Window.btnConnect.Content = 'Connecting...'
Connect-CompanyVIServer -VIServer 'ServerName' -VICredential 'PSCredentialObject'
$Window.btnConnect.Content = 'Connect'
#below line throws away any clicks since we've disabled button
[System.Windows.Forms.Application]::DoEvents()
$Window.Dispatcher.invoke(
[action] {$Window.Dispatcher.btnConnect.IsEnabled = $True})
}
$PSinstance = [powershell]::Create().AddScript($code)
$PSinstance.Runspace = $Runspace
$job = $PSinstance.BeginInvoke()
}
$Window.btnConnect.Add_Click({
my_func $Window $Window.btnConnect
})
I am trying to connect to a MS SQL database through Unity. However, when I try to open a connection, I get an IOException: Connection lost.
I have imported System.Data.dll from Unity\Editor\Data\Mono\lib\mono\2.0. I am using the following code:
using UnityEngine;
using System.Collections;
using System.Data.Sql;
using System.Data.SqlClient;
public class SQL_Controller : MonoBehaviour {
string conString = "Server=myaddress.com,port;" +
"Database=databasename;" +
"User ID=username;" +
"Password=password;";
public string GetStringFromSQL()
{
LoadConfig();
string result = "";
SqlConnection connection = new SqlConnection(conString);
connection.Open();
Debug.Log(connection.State);
SqlCommand Command = connection.CreateCommand();
Command.CommandText = "select * from Artykuly2";
SqlDataReader ThisReader = Command.ExecuteReader();
while (ThisReader.Read())
{
result = ThisReader.GetString(0);
}
ThisReader.Close();
connection.Close();
return result;
}
}
This is the error I get:
IOException: Connection lost
Mono.Data.Tds.Protocol.TdsComm.GetPhysicalPacketHeader ()
Mono.Data.Tds.Protocol.TdsComm.GetPhysicalPacket ()
Mono.Data.Tds.Protocol.TdsComm.GetByte ()
Mono.Data.Tds.Protocol.Tds.ProcessSubPacket ()
Mono.Data.Tds.Protocol.Tds.NextResult ()
Mono.Data.Tds.Protocol.Tds.SkipToEnd ()
Rethrow as TdsInternalException: Server closed the connection.
Mono.Data.Tds.Protocol.Tds.SkipToEnd ()
Mono.Data.Tds.Protocol.Tds70.Connect (Mono.Data.Tds.Protocol.TdsConnectionParameters connectionParameters)
Mono.Data.Tds.Protocol.Tds80.Connect (Mono.Data.Tds.Protocol.TdsConnectionParameters connectionParameters)
Please disregard any security risks with this approach, I NEED to do this for testing, security will come later.
Thank you for your time.
Please disregard any security risks with this approach
Do not do it like this. It doesn't matter if security will come before or after. You will end of re-writing the whole code because the password is hard-coded in your application which can be decompiled and retrieved easily. Do the connection the correct way now so that you won't have to re-write the whole application.
Run your database command on your server with php, perl or whatever language you are comfortable with but this should be done on the server.
From Unity, use the WWW or UnityWebRequest class to communicate with that script and then, you will be able to send and receive information from Unity to the server. There are many examples out there. Even with this, you still need to implement your own security but this is much more better than what you have now.
You can also receive data multiple with json.
Below is a complete example from this Unity wiki. It shows how to interact with a database in Unity using php on the server side and Unity + C# on the client side.
Server Side:
Add score with PDO:
<?php
// Configuration
$hostname = 'localhot';
$username = 'yourusername';
$password = 'yourpassword';
$database = 'yourdatabase';
$secretKey = "mySecretKey"; // Change this value to match the value stored in the client javascript below
try {
$dbh = new PDO('mysql:host='. $hostname .';dbname='. $database, $username, $password);
} catch(PDOException $e) {
echo '<h1>An error has ocurred.</h1><pre>', $e->getMessage() ,'</pre>';
}
$realHash = md5($_GET['name'] . $_GET['score'] . $secretKey);
if($realHash == $hash) {
$sth = $dbh->prepare('INSERT INTO scores VALUES (null, :name, :score)');
try {
$sth->execute($_GET);
} catch(Exception $e) {
echo '<h1>An error has ocurred.</h1><pre>', $e->getMessage() ,'</pre>';
}
}
?>
Retrieve score with PDO:
<?php
// Configuration
$hostname = 'localhost';
$username = 'yourusername';
$password = 'yourpassword';
$database = 'yourdatabase';
try {
$dbh = new PDO('mysql:host='. $hostname .';dbname='. $database, $username, $password);
} catch(PDOException $e) {
echo '<h1>An error has occurred.</h1><pre>', $e->getMessage() ,'</pre>';
}
$sth = $dbh->query('SELECT * FROM scores ORDER BY score DESC LIMIT 5');
$sth->setFetchMode(PDO::FETCH_ASSOC);
$result = $sth->fetchAll();
if(count($result) > 0) {
foreach($result as $r) {
echo $r['name'], "\t", $r['score'], "\n";
}
}
?>
Enable cross domain policy on the server:
This file should be named "crossdomain.xml" and placed in the root of your web server. Unity requires that websites you want to access via a WWW Request have a cross domain policy.
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*"/>
</cross-domain-policy>
Client/Unity Side:
The client code from Unity connects to the server, interacts with PDO and adds or retrieves score depending on which function is called. This client code is slightly modified to compile with the latest Unity version.
private string secretKey = "mySecretKey"; // Edit this value and make sure it's the same as the one stored on the server
public string addScoreURL = "http://localhost/unity_test/addscore.php?"; //be sure to add a ? to your url
public string highscoreURL = "http://localhost/unity_test/display.php";
//Text to display the result on
public Text statusText;
void Start()
{
StartCoroutine(GetScores());
}
// remember to use StartCoroutine when calling this function!
IEnumerator PostScores(string name, int score)
{
//This connects to a server side php script that will add the name and score to a MySQL DB.
// Supply it with a string representing the players name and the players score.
string hash = Md5Sum(name + score + secretKey);
string post_url = addScoreURL + "name=" + WWW.EscapeURL(name) + "&score=" + score + "&hash=" + hash;
// Post the URL to the site and create a download object to get the result.
WWW hs_post = new WWW(post_url);
yield return hs_post; // Wait until the download is done
if (hs_post.error != null)
{
print("There was an error posting the high score: " + hs_post.error);
}
}
// Get the scores from the MySQL DB to display in a GUIText.
// remember to use StartCoroutine when calling this function!
IEnumerator GetScores()
{
statusText.text = "Loading Scores";
WWW hs_get = new WWW(highscoreURL);
yield return hs_get;
if (hs_get.error != null)
{
print("There was an error getting the high score: " + hs_get.error);
}
else
{
statusText.text = hs_get.text; // this is a GUIText that will display the scores in game.
}
}
public string Md5Sum(string strToEncrypt)
{
System.Text.UTF8Encoding ue = new System.Text.UTF8Encoding();
byte[] bytes = ue.GetBytes(strToEncrypt);
// encrypt bytes
System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
byte[] hashBytes = md5.ComputeHash(bytes);
// Convert the encrypted bytes back to a string (base 16)
string hashString = "";
for (int i = 0; i < hashBytes.Length; i++)
{
hashString += System.Convert.ToString(hashBytes[i], 16).PadLeft(2, '0');
}
return hashString.PadLeft(32, '0');
}
This is just an example on how to properly do this. If you need to implement session feature and care about security, look into the OAuth 2.0 protocol. There should be existing libraries that will help get started with the OAuth protocol.
An alternative would be to create your own dedicated server in a command prompt to do your communication and connecting it to unity to Handel multiplayer and SQL communication. This way you can stick with it all being created in one language. But a pretty steep learning curve.
Unity is game engine.
so That's right what the answer says.
but, Some domains need to connect database directly.
You shouldn't do that access database directly in game domain.
Anyway, The problem is caused by NON-ENGLISH computer name.
I faced sort of following errors at before project.
IOException: Connection lost
Mono.Data.Tds.Protocol.TdsComm.GetPhysicalPacketHeader ()
Mono.Data.Tds.Protocol.TdsComm.GetPhysicalPacket ()
Mono.Data.Tds.Protocol.TdsComm.GetByte ()
Mono.Data.Tds.Protocol.Tds.ProcessSubPacket ()
Mono.Data.Tds.Protocol.Tds.NextResult ()
Mono.Data.Tds.Protocol.Tds.SkipToEnd ()
Rethrow as TdsInternalException: Server closed the connection.
Mono.Data.Tds.Protocol.Tds.SkipToEnd ()
Mono.Data.Tds.Protocol.Tds70.Connect (Mono.Data.Tds.Protocol.TdsConnectionParameters connectionParameters)
Mono.Data.Tds.Protocol.Tds80.Connect (Mono.Data.Tds.Protocol.TdsConnectionParameters connectionParameters)
And after changing computer name as ENGLISH, It works.
I don't know how it's going. But It works.
Mono's System.Data.dll has some issues in P.C has NON-ENGLISH Computer name.
So, at least Unity project.
You should tell your customer Do not set their computer name as NON-ENGLISH.
I don't know people in mono knows these issue or not.
---------- It's OK in after 2018 version ----------
Api Compatibility Level > .Net 4.x
You can connect database in Non-english computer name machine.
I'm trying to build a script that will display a WPF window to a user indicating progress. The script itself will run from the system account, but will need to show progress to the end user. The script will be launched in the System account either through PSExec or SCCM (Note in both cases the initial script can't be run with 'user interaction' enabled. Yeah. I know. Its a requirement though).
Is there a way to create that window from the System context so that a user can interact with it? Alternatively, can a Runspace be opened in another User's context? Or are neither of these a viable route?
Something like:
Start-Process powershell.exe -credential {cred. maybe a stored cred?} -nonewwindow -working directory {wherever you want it to start from. cred value must have access} -argumentlist "-file yourfile.ps1"
"yourfile.ps1" would have the code block for interacting with the user. -nonewwindow is of course up to you. I'm not sure how you'd communicate across userspace. Im still more sysadmin than programmer so I'd do something hacky. Like re-draw the window every 10% or something. Depends what feedback you want from the user.
The only solution I found was this function: Send-TSMessageBox.
http://pinvoke.net/default.aspx/wtsapi32.WTSSendMessage
This could be run as SYSTEM but shows up on the user's desktop.
One downside: On a virtual machine, the message box shows up in session 0 (which is the Hyper-V "integrated" connection window). If you are connected via RDP (mstc) session you will not see the message box.
But on a Citrix desktop it works. The message box pops up within the user session and not on the Citrix host.
Here's the full function:
Function Send-TSMessageBox {
<#
.SYNOPSIS
Send a message or prompt to the interactive user with the ability to get the results.
.DESCRIPTION
Allows the administrator to send a message / prompt to an interactive user.
.EXAMPLE
"Send a message immediately w/o waiting for a responce."
Send-TSMessageBox -Title "Email Problem" -Message "We are currently having delays and are working on the issue."
"Send a message waiting 60 seconds for a reponse of [Yes / No]."
$Result = Send-TSMessageBox -Title "System Updated" -Message "System requires a reboot. Would you like to the reboot system now?" `
-ButtonSet 4 -Timeout 60 -WaitResponse $true
.ButtonSets
0 = OK
1 = Ok/Cancel
2 = Abort/Retry/Ignore
3 = Yes/No/Cancel
4 = Yes/No
5 = Retry/Cancel
6 = Cancel/Try Again/Continue
.Results
"" = 0
"Ok" = 1
"Cancel" = 2
"Abort" = 3
"Retry" = 4
"Ignore" = 5
"Yes" = 6
"No" = 7
"Try Again" = 10
"Continue" = 11
"Timed out" = 32000
"Not set to wait" = 32001
.NOTES
Author: Raymond H Clark
Twitter: #Rowdybullgaming
.RESOURCES
http://technet.microsoft.com/en-us/query/aa383488
http://technet.microsoft.com/en-us/query/aa383842
http://pinvoke.net/default.aspx/wtsapi32.WTSSendMessage
#>
Param([string]$Title = "Title", [string]$Message = "Message", [int]$ButtonSet = 0, [int]$Timeout = 0, [bool]$WaitResponse = $false)
$Signature = #"
[DllImport("wtsapi32.dll", SetLastError = true)]
public 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);
[DllImport("kernel32.dll")]
public static extern uint WTSGetActiveConsoleSessionId();
"#
[int]$TitleLength = $Title.Length;
[int]$MessageLength = $Message.Length;
[int]$Response = 0;
$MessageBox = Add-Type -memberDefinition $Signature -name "WTSAPISendMessage" -namespace "WTSAPI" -passThru
$SessionId = $MessageBox::WTSGetActiveConsoleSessionId()
$MessageBox::WTSSendMessage(0, $SessionId, $Title, $TitleLength, $Message, $MessageLength, $ButtonSet, $Timeout, [ref] $Response, $WaitResponse)
$Response
}
Unfortunately the design of the messsage box is very limited. Actually it looks ugly :-)