Shutting down a pc in vb.net is easy:
Process.Start("shutdown", "-s -t 00")
unless the user has locked the pc in which case the above fails.
How do I get around this in vb.net? How do I shutdown a locked PC?
The program will be running locally.
System.Diagnostics.Process.Start("shutdown", "-s -f -t 00")
This will force a shutdown In 00ms silently. The code you have to invoke each process is redundant, use the code above. Just do a System.Imports.IO at the top and you are good to go.
You could P/Invoke ExitWindowsEx
There is an example in C# there, but I'm sure you can convert it.
I think you're looking for the '-f' flag to force a shutdown.
Quote from a MS KB article:
When the computer is locked, you can shut down the computer, if you run the Shutdown.exe command together with the -f option.
For posterity:
Dim ms As ManagementScope = New ManagementScope("\\LocalHost")
ms.Options.EnablePrivileges = True
Dim oq As ObjectQuery = New ObjectQuery("SELECT * FROM Win32_OperatingSystem")
Dim query1 As ManagementObjectSearcher = New ManagementObjectSearcher(ms, oq)
Dim queryCollection1 As ManagementObjectCollection = query1.Get()
For Each mo As ManagementObject In queryCollection1
Dim ss As String() = {"5"}
mo.InvokeMethod("Win32Shutdown", ss)
Next
Google "Win32Shutdown" for more details of the flags available (ss above). 5 is a forced shutdown for when the pc is locked but it's more graceful than shutdown /f and doesn't appear to cause any problems with programs or services on restart.
Have a look at this article on CodeProject which illustrates forcing a computer to shutdown remotely to give you an idea on how to do it.
Using the System.Management namespace is more elegant than starting an external tool. Here is a code example in C#, which should be fairly easy to convert:
http://www.dreamincode.net/forums/topic/33948-how-to-shut-down-your-computer-in-c%23/
Related
I was trying to recreate the sourcecode for a launcher to a game I made that requires making and launching a .bat file.
When it came time to wrap up the appended lines into a .bat, I found this error.
I have researched, thoroughly even. The reason I am asking myself is because none of the answers I came across matched my case. The batch sets variables, echoes some text, then will launch the game. Here's the code, thank you for helping me. I will add more information if you need it, I'll be as helpful as I can.
sb.AppendLine("#echo off")
sb.AppendLine("set ttiUsername=" + username)
sb.AppendLine("set ttiPassword=password")
sb.AppendLine("set TTI_GAMESERVER=10.0.0.77")
sb.AppendLine("set TTI_PORT=7198")
sb.AppendLine("set /P PPYTHON_PATH=<PPYTHON_PATH")
sb.AppendLine("echo ===============================")
sb.AppendLine("echo Welcome to Toontown Rebuilt, %ttiUsername%!")
sb.AppendLine("echo You are connecting to server %TTI_GAMESERVER%!")
sb.AppendLine("echo The server port is %TTI_PORT%")
sb.AppendLine("echo ===============================")
sb.AppendLine("%PPYTHON_PATH% -m toontown.toonbase.ToontownStart")
Dim File As New System.IO.StreamWriter
File.WriteLine(sb.ToString())
Process.Start("C:\Toontown Rebuilt Source\ToontownRebuilt\Launcher.bat")
System.IO.StreamWriter constructor requires a parameter. The name of the file or the already created Stream where the successive Write will dump the content of your string. You are missing that parameter.
But there are other issues that need a change here
Using File = New System.IO.StreamWriter("C:\Toontown Rebuilt Source\ToontownRebuilt\Launcher.bat")
File.WriteLine(sb.ToString())
End Using
The encapsulation in the Using statement ensures a proper closing and disposing of the stream
Another useful approach is File.WriteAllText
Dim file = "C:\Toontown Rebuilt Source\ToontownRebuilt\Launcher.bat"
File.WriteAllText(file, sb.ToString())
Hi i use your code and fix your bug for launch bat.
This is my code in VB.NET:
Dim sb As New StringBuilder
Dim username As String
username = "test"
sb.AppendLine("#echo off")
sb.AppendLine("set ttiUsername=" + username)
sb.AppendLine("set ttiPassword=password")
sb.AppendLine("set TTI_GAMESERVER=10.0.0.77")
sb.AppendLine("set TTI_PORT=7198")
sb.AppendLine("set /P PPYTHON_PATH=<PPYTHON_PATH")
sb.AppendLine("echo ===============================")
sb.AppendLine("echo Welcome to Toontown Rebuilt, %ttiUsername%!")
sb.AppendLine("echo You are connecting to server %TTI_GAMESERVER%!")
sb.AppendLine("echo The server port is %TTI_PORT%")
sb.AppendLine("echo ===============================")
sb.AppendLine("%PPYTHON_PATH% -m toontown.toonbase.ToontownStart")
Dim file As System.IO.StreamWriter
file = My.Computer.FileSystem.OpenTextFileWriter("C:\tmp\Launcher.bat", True)
file.WriteLine(sb.ToString)
file.Close()
Process.Start("C:\tmp\Launcher.bat")
The problem:
On a windows server 2012 r2 box, I'm trying to use Chef to programmatically replace a .dll command component (aka a vb 6 library that I've registered on the box using regsvr32.exe) but when I try to copy over the file, the app pool of the website has a lock on it. I'm not sure if it matters, but the w3wp process is set to run as 32 bit via IIS.
My Solution (which isn't working):
In order to fix it, I was thinking about using a command line tool to find the reference to the dll and then recycling the app pool that's using it. Unfortunately, while I can get SysInternals' process explorer to find the dll, Handles.exe (the supposed command line version of process explorer) does not return anything. I was hoping that someone might be able to tell me how I was using handles incorrectly, or if there was a better tool for this.
Process Explorer - it has found my dll ComHelper.dll
Handles via command line - it has not found my dll ComHelper.dll
-- Edit --
This is the output of handles when I point it at w3wp while running as Admin
I would suspect you are running into access issues. Are you running Handle from an elevated command prompt ? Are you able to get any output covering handles in w3wp.exe (by using the pid of the process in handle.exe command line) ?
Looking at the handle enum output of w3wp.exe it seems,
listdll.exe -d ComHelper.dll
may be what you are looking for. Handle seems to be focused on files opened not dlls loaded. listdll is a tool that can be downloaded from sysinternals.
Alright so 32 bitness did matter. I ended up having to resort to powershell as opposed to trying to use handles. The code for finding a PID that has a lock on your file is scattered around the internet, but here's the link:
http://blogs.technet.com/b/heyscriptingguy/archive/2013/12/01/weekend-scripter-determine-process-that-locks-a-file.aspx (it's marv the robot's answer at the bottom)
For the record, this is what was suggested
$lockedFile="C:\Windows\System32\acproxy.dll"
$isLocked = $false
Get-Process | foreach{
$processVar = $_;$_.Modules | foreach{
if($_.FileName -eq $lockedFile){
$isLocked = $true
$processVar.Name + " PID:" + $processVar.id
}
}
}
This is what I had translated it into with my powershell noobishness
$lockedFile = "E:\Components\___ComHelper.dll"
$list = Get-Process
foreach ($process in $list)
{
foreach ($module in $process.Modules)
{
if ($module.FileName -ne $lockedFile) { continue }
$process.Name + " PID:" + $process.Id
}
}
I am writing a VB.NET PowerShell Cmdlet which needs to start, and later stop, the Transcript file (Start-Transcript).
So how do I manage to run the command Start-Transcript from within the cmdlet? I have tried this:
Dim myRunSpace As Runspace = RunspaceFactory.CreateRunspace()
myRunSpace.Open()
Dim MyPipeline As Pipeline = myRunSpace.CreatePipeline()
MyPipeline.Commands.AddScript("Start-Transcript -Path $pwd\session.txt")
Dim results As Collection(Of PSObject) = MyPipeline.Invoke()
myRunSpace.Close()
Produces the error "Start-transcript : this host does not support transription". Yet when I enter the command manually
no error is produced and transcription starts.
The Start-Transcript method is host specific and implemented by the host. The reason for this, I'm guessing, is that it is not clear what input and output is available on different hosts and therefore not easy to create a generic implementation which works in all cases.
The PowerShell Console host supports the Start-Transcript function (which you see, since it works when you call it), but other hosts need to implement it before it is usable in that host. If you, for example, try to write Start-Transcript in the PowerShell ISE host you'll get an error like the following:
Start-Transcript : This host does not support transcription.
At line:1 char:1
+ Start-Transcript
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : NotImplemented: (:) [Start-Transcript], PSNotSupportedExce
ption
+ FullyQualifiedErrorId : NotSupported,Microsoft.PowerShell.Commands.StartTranscript
Command
In order to get it to work using a custom host such as the one you're creating in VB you would have to find a way to implement support for transcription.
That is a limitation of the cmdlet. MSFT made it so Start-Transcript only works when invoked from powershell.exe console, it doesn't even work from their ISE. The workaround has been to use add-content / out-file / tee-object to create your own log file.
It's a lot more work, but can be done. maybe these cmdlets can help.
http://www.powertheshell.com/transcript/
I have followed one of your earlier links and patched this VB.NET code from the PowerShell script there:
Dim thisType As Object = MyBase.Host.GetType().GetProperty("ExternalHost",Reflection.BindingFlags.NonPublic Or BindingFlags.Instance).GetValue(MyBase.Host, New Object() {})
Dim abc As Object = thisType.GetType().GetProperty("IsTranscribing", Reflection.BindingFlags.NonPublic Or BindingFlags.Instance).GetValue(thisType, New Object() {})
WriteObject(abc.ToString)
This returns correctly True or False. So can I do any more than this here, ie like Stop-Transcript? Apologies for the comment above, but this link is slow and the text became garbled.
When using SQL Server Express 2005's User Instance feature with a connection string like this:
<add name="Default" connectionString="Data Source=.\SQLExpress;
AttachDbFilename=C:\My App\Data\MyApp.mdf;
Initial Catalog=MyApp;
User Instance=True;
MultipleActiveResultSets=true;
Trusted_Connection=Yes;" />
We find that we can't copy the database files MyApp.mdf and MyApp_Log.ldf (because they're locked) even after stopping the SqlExpress service, and have to resort to setting the SqlExpress service from automatic to manual startup mode, and then restarting the machine, before we can then copy the files.
It was my understanding that stopping the SqlExpress service should stop all the user instances as well, which should release the locks on those files. But this does not seem to be the case - could anyone shed some light on how to stop a user instance, such that it's database files are no longer locked?
Update
OK, I stopped being lazy and fired up Process Explorer. Lock was held by sqlserver.exe - but there are two instances of sql server:
sqlserver.exe PID: 4680 User Name: DefaultAppPool
sqlserver.exe PID: 4644 User Name: NETWORK SERVICE
The file is open by the sqlserver.exe instance with the PID: 4680
Stopping the "SQL Server (SQLEXPRESS)" service, killed off the process with PID: 4644, but left PID: 4680 alone.
Seeing as the owner of the remaining process was DefaultAppPool, next thing I tried was stopping IIS (this database is being used from an ASP.Net application). Unfortunately this didn't kill the process off either.
Manually killing off the remaining sql server process does remove the open file handle on the database files, allowing them to be copied/moved.
Unfortunately I wish to copy/restore those files in some pre/post install tasks of a WiX installer - as such I was hoping there might be a way to achieve this by stopping a windows service, rather then having to shell out to kill all instances of sqlserver.exe as that poses some problems:
Killing all the sqlserver.exe instances may have undesirable consequencies for users with other Sql Server instances on their machines.
I can't restart those instances easily.
Introduces additional complexities into the installer.
Does anyone have any further thoughts on how to shutdown instances of sql server associated with a specific user instance?
Use "SQL Server Express Utility" (SSEUtil.exe) or the command to detach the database used by SSEUtil.
SQL Server Express Utility,
SSEUtil is a tool that lets you easily interact with SQL Server,
http://www.microsoft.com/downloads/details.aspx?FamilyID=fa87e828-173f-472e-a85c-27ed01cf6b02&DisplayLang=en
Also, the default timeout to stop the service after the last connection is closed is one hour. On your development box, you may want to change this to five minutes (the minimum allowed).
In addition, you may have an open connection through Visual Studio's Server Explorer Data Connections, so be sure to disconnect from any database there.
H:\Tools\SQL Server Express Utility>sseutil -l
1. master
2. tempdb
3. model
4. msdb
5. C:\DEV_\APP\VISUAL STUDIO 2008\PROJECTS\MISSICO.LIBRARY.1\CLIENTS\CORE.DATA.C
LIENT\BIN\DEBUG\CORE.DATA.CLIENT.MDF
H:\Tools\SQL Server Express Utility>sseutil -d C:\DEV*
Failed to detach 'C:\DEV_\APP\VISUAL STUDIO 2008\PROJECTS\MISSICO.LIBRARY.1\CLIE
NTS\CORE.DATA.CLIENT\BIN\DEBUG\CORE.DATA.CLIENT.MDF'
H:\Tools\SQL Server Express Utility>sseutil -l
1. master
2. tempdb
3. model
4. msdb
H:\Tools\SQL Server Express Utility>
Using .NET Refector the following command is used to detach the database.
string.Format("USE master\nIF EXISTS (SELECT * FROM sysdatabases WHERE name = N'{0}')\nBEGIN\n\tALTER DATABASE [{1}] SET OFFLINE WITH ROLLBACK IMMEDIATE\n\tEXEC sp_detach_db [{1}]\nEND", dbName, str);
I have been using the following helper method to detach MDF files attached to SQL Server in unit tests (so that SQ Server releases locks on MDF and LDF files and the unit test can clean up after itself)...
private static void DetachDatabase(DbProviderFactory dbProviderFactory, string connectionString)
{
using (var connection = dbProviderFactory.CreateConnection())
{
if (connection is SqlConnection)
{
SqlConnection.ClearAllPools();
// convert the connection string (to connect to 'master' db), extract original database name
var sb = dbProviderFactory.CreateConnectionStringBuilder();
sb.ConnectionString = connectionString;
sb.Remove("AttachDBFilename");
var databaseName = sb["database"].ToString();
sb["database"] = "master";
connectionString = sb.ToString();
// detach the original database now
connection.ConnectionString = connectionString;
connection.Open();
using (var cmd = connection.CreateCommand())
{
cmd.CommandText = "sp_detach_db";
cmd.CommandType = CommandType.StoredProcedure;
var p = cmd.CreateParameter();
p.ParameterName = "#dbname";
p.DbType = DbType.String;
p.Value = databaseName;
cmd.Parameters.Add(p);
p = cmd.CreateParameter();
p.ParameterName = "#skipchecks";
p.DbType = DbType.String;
p.Value = "true";
cmd.Parameters.Add(p);
p = cmd.CreateParameter();
p.ParameterName = "#keepfulltextindexfile";
p.DbType = DbType.String;
p.Value = "false";
cmd.Parameters.Add(p);
cmd.ExecuteNonQuery();
}
}
}
}
Notes:
SqlConnection.ClearAllPools() was very helpful in eliminating "stealth" connections (when a connection is pooled, it will stay active even though you 'Close()' it; by explicitely clearing pool connections you don't have to worry about setting pooling flag to false in all connection strings).
The "magic ingredient" is call to the system stored procedure sp_detach_db (Transact-SQL).
My connection strings included "AttachDBFilename" but didn't include "User Instance=True", so this solution might not apply to your scenario
I can't comment yet because I don't have high enough rep yet. Can someone move this info to the other answer so we don't have a dupe?
I just used this post to solve my WIX uninstall problem. I used this line from AMissico's answer.
string.Format("USE master\nIF EXISTS (SELECT * FROM sysdatabases WHERE name = N'{0}')\nBEGIN\n\tALTER DATABASE [{1}] SET OFFLINE WITH ROLLBACK IMMEDIATE\n\tEXEC sp_detach_db [{1}]\nEND", dbName, str);
Worked pretty well when using WIX, only I had to add one thing to make it work for me.
I had took out the sp_detach_db and then brought the db back online. If you don't, WIX will leave the mdf files around after the uninstall. Once I brought the db back online WIX would properly delete the mdf files.
Here is my modified line.
string.Format( "USE master\nIF EXISTS (SELECT * FROM sysdatabases WHERE name = N'{0}')\nBEGIN\n\tALTER DATABASE [{0}] SET OFFLINE WITH ROLLBACK IMMEDIATE\n\tALTER DATABASE [{0}] SET ONLINE\nEND", dbName );
This may not be what you are looking for, but the free tool Unlocker has a command line interface that could be run from WIX. (I have used unlocker for a while and have found it stable and very good at what it does best, unlocking files.)
Unlocker can unlock and move/delete most any file.
The downside to this is the apps that need a lock on the file will no longer have it. (But sometimes still work just fine.) Note that this does not kill the process that has the lock. It just removes it's lock. (It may be that restarting the sql services that you are stopping will be enough for it to re-lock and/or work correctly.)
You can get Unlocker from here: http://www.emptyloop.com/unlocker/
To see the command line options run unlocker -H
Here they are for convenience:
Unlocker 1.8.8
Command line usage:
Unlocker.exe Object [Option]
Object:
Complete path including drive to a file or folder
Options:
/H or -H or /? or -?: Display command line usage
/S or -S: Unlock object without showing the GUI
/L or -L: Object is a text file containing the list of files to unlock
/LU or -LU: Similar to /L with a unicode list of files to unlock
/O or -O: Outputs Unlocker-Log.txt log file in Unlocker directory
/D or -D: Delete file
/R Object2 or -R Object2: Rename file, if /L or /LU is set object2 points to a text file containing the new name of files
/M Object2 or -M Object2: Move file, if /L or /LU is set object2 points a text file containing the new location of files
Assuming your goal was to replace C:\My App\Data\MyApp.mdf with a file from your installer, you would want something like unlocker C:\My App\Data\MyApp.mdf -S -D. This would delete the file so you could copy in a new one.
Does anyone know of an easy way to create a script that can connect to a telnet server, do some usual telnet stuff, and then log off? I am dealing with users who are not familiar with telnet and the commands they will need to run. All I want is for them to double-click on a script, and have that script automatically execute the commands for them.
You're probably wondering, "What platform are the users on?" They will be on both Windows and Linux. Implementations in languages like Perl, Java, or Python are acceptable. I see that Perl has a Net:: Telnet module. Has anyone used that?
My ideal solution would be to create two script files. a BAT file for windows, and a shell script for Linux. While this would make dual maintenance an issue, it would mean I wouldn't have to install Perl/Java/Python/etc... on every machine. Unfortunately, I have not seen any way to automate a telnet session with batch files or shell scripts.
Thanks.
I've used various methods for scripting telnet sessions under Unix, but the simplest one is probably a sequence of echo and sleep commands, with their output piped into telnet. Piping the output into another command is also a possibility.
Silly example
(echo password; echo "show ip route"; sleep 1; echo "quit" ) | telnet myrouter
This (basically) retrieves the routing table of a Cisco router.
Expect is built for this and can handle the input/output plus timeouts etc. Note that if you're not a TCL fan, there are Expect modules for Perl/Python/Java.
EDIT: The above page suggests that the Wikipedia Expect entry is a useful resource :-)
Another method is to use netcat (or nc, dependent upon which posix) in the same format as vatine shows or you can create a text file that contains each command on it's own line.
I have found that some posix' telnets do not handle redirect correctly (which is why I suggest netcat)
This vbs script reloads a cisco switch, make sure telnet is installed on windows.
Option explicit
Dim oShell
set oShell= Wscript.CreateObject("WScript.Shell")
oShell.Run "telnet"
WScript.Sleep 1000
oShell.Sendkeys "open 172.25.15.9~"
WScript.Sleep 1000
oShell.Sendkeys "password~"
WScript.Sleep 1000
oShell.Sendkeys "en~"
WScript.Sleep 1000
oShell.Sendkeys "password~"
WScript.Sleep 1000
oShell.Sendkeys "reload~"
WScript.Sleep 1000
oShell.Sendkeys "~"
Wscript.Quit
It may not sound a good idea but i used java and used simple TCP/IP socket programming to connect to a telnet server and exchange communication. ANd it works perfectly if you know the protocol implemented. For SSH etc, it might be tough unless you know how to do the handshake etc, but simple telnet works like a treat.
Another way i tried, was using external process in java System.exec() etc, and then let the windows built in telnet do the job for you and you just send and receive data to the local system process.
Check for the SendCommand tool.
You can use it as follows:
perl sendcommand.pl -i login.txt -t cisco -c "show ip route"
import telnetlib
user = "admin"
password = "\r"
def connect(A):
tnA = telnetlib.Telnet(A)
tnA.read_until('username: ', 3)
tnA.write(user + '\n')
tnA.read_until('password: ', 3)
tnA.write(password + '\n')
return tnA
def quit_telnet(tn)
tn.write("bye\n")
tn.write("quit\n")
Couple of questions:
Can you put stuff on the device that you're telnetting into?
Are the commands executed by the script the same or do they vary by machine/user?
Do you want the person clicking the icon to have to provide a userid and/or password?
That said, I wrote some Java a while ago to talk to a couple of IP-enabled power strips (BayTech RPC3s) which might be of use to you. If you're interested I'll see if I can dig it up and post it someplace.
I like the example given by Active State using python. Here is the full link. I added the simple log in part from the link but you can get the gist of what you could do.
import telnetlib
prdLogBox='142.178.1.3'
uid = 'uid'
pwd = 'yourpassword'
tn = telnetlib.Telnet(prdLogBox)
tn.read_until("login: ")
tn.write(uid + "\n")
tn.read_until("Password:")
tn.write(pwd + "\n")
tn.write("exit\n")
tn.close()
Bash shell supports this out-of-box, e.g.
exec {stream}<>/dev/tcp/example.com/80
printf "GET / HTTP/1.1\nHost: example.com\nConnection: close\n\n" >&${stream}
cat <&${stream}
To filter and only show some lines, run: grep Example <&${stream}.
Write the telnet session inside a BAT Dos file and execute.