Executing msg.exe from Visual Basic application - vb.net

I am trying to take text fields for old hostname, new hostname, username, and password and remotely change computer names. That part is working fantastic. It was all great until my manager saw it in action, since we have a policy against downloading and using freeware.
It's not freeware if I made it. Unfortunately, he sent it to my director, and know my director knows I know a little bit about Visual Basic, so he wants to loop the names from a CSV file, change the name, and send a message to the end user instructing them to save their files and reboot.
Unfortunately, net send has gone the way of XP since Vista. However, from Vista - Win8.1, there's a utility called msg.exe in C:\Windows\System32. In order to use it, the target computer has to have the registry value AllowRemoteRPC in HKLM\SYSTEM\CurrentControlSet\Control\Terminal Services set to 1.
So here's what the app does:
Reads the DWORD key AllowRemoteRPC and stores it to a variable (MyVal), changes the key to 1, attempts to send the message alerting the user they need to restart, changes the key back to MyVal, and then executes netdom renamecomputer and renames the PC. Everything works perfectly EXCEPT sending the message. I can open up a command prompt and type:
msg /server:hostname * /v /time:3600 "my message here
And it works perfectly (after manually editing the registry key to the needed value).
However, running it from VB doesn't work. Here's what I've tried:
"msg /server:" & hostname & " * /v /time:3600 ""my message here"""
"cmd.exe /D /c msg /server:" & hostname & " * /v /time:3600 ""my message here"""
Neither seems to work. I know the registry value is being changed. I put message boxes after each step in my and refreshed the regedit to actually see the value of the DWORD key, and it is changing. Everything APPEARS to be going smoothly, the message is just not getting sent.
I do have these commands running as arguments to a function I created in order to create a process so I could output the streamreader to a listbox.
Here's my code. Please keep in mind, I'm barely over 2 months into learning visual basic, so it's probably not the prettiest code out there:
Imports System
Imports System.IO
Imports System.Diagnostics
Imports System.Security.Permissions
Imports Microsoft.Win32
Public Class applicationMain
Private Sub btnExecute_Click(sender As Object, e As EventArgs) Handles btnExecute.Click
Dim oldPC As String = txtOldPC.Text
Dim newPC As String = txtNewPC.Text
Dim username As String = txtUsername.Text
Dim password As String = txtPassword.Text
If oldPC <> "" And newPC <> "" And username <> "" And password <> "" Then
Dim MyReg As Microsoft.Win32.RegistryKey = Microsoft.Win32.RegistryKey.OpenRemoteBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, oldPC)
Dim MyRegKey As Microsoft.Win32.RegistryKey
Dim MyVal As String
lbOutput.Items.Clear()
MyRegKey = MyReg.OpenSubKey("System\CurrentControlSet\Control\Terminal Server")
MyVal = MyRegKey.GetValue("AllowRemoteRPC", RegistryValueKind.DWord)
MyRegKey.Close()
lbOutput.Items.Add("Processing registry changes...")
Try
MyRegKey = MyReg.OpenSubKey("System\CurrentControlSet\Control\Terminal Server", True)
MyRegKey.SetValue("AllowRemoteRPC", &H1, RegistryValueKind.DWord)
Catch ex As Exception
MessageBox.Show("An Error Has Occured:" & vbCrLf & vbCrLf & ex.ToString())
lbOutput.Items.Add("")
lbOutput.Items.Add("ABORTED!")
Exit Sub
End Try
lbOutput.Items.Add("Success!")
lbOutput.Items.Add("Sending message to user:")
Try
ExecuteCommand("cmd.exe", "/D /c msg /SERVER:" & oldPC & ".na.int.grp * /v /TIME:3600 ""Changes have been made by IS to your computer that require a restart. Please save your files and restart your computer to avoid service interruption.""")
Catch ex As Exception
MessageBox.Show("An Error Has Occured:" & vbCrLf & vbCrLf & ex.ToString())
lbOutput.Items.Add("")
lbOutput.Items.Add("ABORTED!")
MyRegKey = MyReg.OpenSubKey("System\CurrentControlSet\Control\Terminal Server", True)
MyRegKey.SetValue("AllowRemoteRPC", MyVal, RegistryValueKind.DWord)
Exit Sub
End Try
lbOutput.Items.Add(" Message: ""Changes have been made by IS to your computer that require a restart. Please save your files and restart your computer to avoid service interruption."" ")
lbOutput.Items.Add("Reverting registry changes...")
Try
MyRegKey = MyReg.OpenSubKey("System\CurrentControlSet\Control\Terminal Server", True)
MyRegKey.SetValue("AllowRemoteRPC", MyVal, RegistryValueKind.DWord)
Catch ex As Exception
MessageBox.Show("An Error Has Occured:" & vbCrLf & vbCrLf & ex.ToString())
lbOutput.Items.Add("")
lbOutput.Items.Add("ABORTED!")
Exit Sub
End Try
Try
ExecuteCommand("netdom", "renamecomputer " & oldPC & " /newname:" & newPC & " /userD:na\" & username & " /passwordd:" & password & " /usero:na\" & username & " /passwordo:" & password & " /Force")
Catch ex As Exception
MessageBox.Show("An Error Has Occured:" & vbCrLf & vbCrLf & ex.ToString())
lbOutput.Items.Add("")
lbOutput.Items.Add("ABORTED!")
Exit Sub
End Try
lbOutput.Items.Add("Success!")
lbOutput.Items.Add("")
lbOutput.Items.Add("Rename successful for " & oldPC & "!")
lbOutput.Items.Add("******************************************************************")
lbOutput.Items.Add("")
End If
End Sub
Private Function ExecuteCommand(ByVal cmd As String, ByVal arguments As String)
Dim cmdProcess As New Process()
Dim cmdProcessStartInfo As New ProcessStartInfo()
Dim cmdStreamReader As IO.StreamReader
Dim output As String
cmdProcessStartInfo.UseShellExecute = False
cmdProcessStartInfo.CreateNoWindow = True
cmdProcessStartInfo.RedirectStandardOutput = True
cmdProcessStartInfo.FileName = cmd
cmdProcessStartInfo.Arguments = arguments
cmdProcess.StartInfo = cmdProcessStartInfo
cmdProcess.Start()
cmdStreamReader = cmdProcess.StandardOutput
Do While cmdStreamReader.EndOfStream = False
output = cmdStreamReader.ReadLine()
lbOutput.SelectedIndex = lbOutput.Items.Count - 1
lbOutput.Items.Add(output)
Loop
cmdProcess.WaitForExit()
cmdProcess.Close()
Return vbNull
End Function
End Class

What do you know. There's actually nothing wrong with my code at all. While trying to play around with the paths variable, I decided "Fuhgeddaboudit, I'll just add the executable to the project!". Right clicked the project, Add -> Existing Item. Selected Executable as the type, and went to C:\Windows\System32 and, get this now, msg.exe wasn't there. At all. Opened Explorer and went to System32, msg.exe was there. For whatever reason, Visual Studio cannot see the program at all. Which is in and of itself weird.
So I copied msg.exe to my desktop, added it to source, the program works like a charm now.

Related

Visual basic : Generic GDI+ error when saving file

When I executed my program I created in visual basic. I got a GDI+ error when I tried to save an image from a picturebox.
If I run it on the PC, where I created the program(windows 10), I don't have any problems. When I run it on 2 different windows 7 PC's, I got the error.
The mapped networkdrive is the same ( Z:\ ) and writeable.
Here is the code:
Private Sub SaveImage(ByVal pathToSaveTo As String)
Try
Using bmp As New Bitmap(Picimage.Image)
bmp.Save(pathToSaveTo, Drawing.Imaging.ImageFormat.Jpeg)
End Using
Catch ex As Exception
MessageBox.Show("An error occurred:" & vbCrLf & vbCrLf & _
ex.Message, "Error Saving Image File", _
MessageBoxButtons.OK, MessageBoxIcon.Asterisk)
End Try
End Sub
The button to start the action
Private Sub Button2_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim dt As String = My.Computer.FileSystem.SpecialDirectories.Desktop
Dim testOutput As String
testOutput = "Z:\" & naam & " " & Now.ToString("HH/mm/ss") & ".jpg"
SaveImage(testOutput)
nr.Focus()
End Sub
GDI is complaining about the filename you're using.
Here's the problem: testOutput = "Z:\" & naam & " " & Now.ToString("HH/mm/ss") & ".jpg"
You're generating a filename with slashes in the path. If those are meant to be directory names (a directory for each hour, minute and second respectively) then those directories need to ex that won't work as GDI will not create missing directories along a path for you. If the slashes are meant to be in the filename itself then it also won't work as slashes are not a valid filename character.
Change the slashes to underscores or hyphens or other characters allowed in filenames:
testOutput = "Z:\" & naam & " " & Now.ToString("HH_mm_ss") & ".jpg"

IOException issue with asynchronous PDF creation

I have an asynchronous method that creates a PDF file from XML retrieved from a database. Everything works great, but occasionally I get an IOException because when I try to cleanup the temporary .fo file after creating the PDF, the file is still in use.
Public Sub FormatObjectToPdf(ByVal intRxNo As Integer, ByVal strSourceFileName As String)
Dim startInfo As New ProcessStartInfo
Dim strPdfFile As String = g_strRootPath & "Paperwork\" & intRxNo & "M.pdf"
' if the PDF file already exists, no need to re-create it
If Not File.Exists(strPdfFile) Then
Try
startInfo.Arguments = "-fo """ & strSourceFileName & """ -pdf """ & strPdfFile & """"
startInfo.FileName = g_strAppPath & "FO.NET\fonet.exe"
startInfo.UseShellExecute = True
startInfo.WindowStyle = ProcessWindowStyle.Hidden
Using proc As Process = Process.Start(startInfo)
proc.WaitForExit()
If proc.HasExited Then
proc.Dispose()
End If
End Using
Catch ex As Exception
Call InsertLog("ErrorLog", "FormatObjectToPdf: " & ex.Message, ex)
MessageBox.Show(ex.Message, "Create PDF", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
End Try
End If
' wait 3 seconds to allow file to be released
System.Threading.Thread.Sleep(3000)
' delete the source FO file when processing is complete
If File.Exists(strSourceFileName) Then
Try
File.Delete(strSourceFileName)
Catch iEx As IOException
Call InsertLog("ErrorLog", "Could not delete file '" & strSourceFileName & "': " & iEx.Message, iEx)
Catch ex As Exception
Call InsertLog("ErrorLog", "Error deleting file '" & strSourceFileName & "': " & ex.Message, ex)
End Try
End If
End Sub
The FormatObjectToPdf method is called from another method, AsyncXmlToPdf, which is actually where the IOException is thrown. I had initially thought that the exception was in FormatObjectToPdf since that is where I am deleting the .fo file, so I had added a Sleep(3000) to see if giving it a few seconds would help.
Here is the AsyncXmlToPdf:
Public Sub AsyncXmlToPdf(ByVal state As Object)
Dim intRxNo = state(0)
Dim flgPrintResult As Boolean = state(1)
Dim strFileName As String = g_strAppPath & intRxNo & ".fo"
Dim strOutput As String
Dim strPdfFile As String = g_strRootPath & "Paperwork\" & intRxNo & "M.pdf"
Try
If File.Exists(strPdfFile) Then
File.Delete(strPdfFile)
End If
If Not File.Exists(strPdfFile) AndAlso Not File.Exists(strFileName) Then
strOutput = XmlToFormatObject(intRxNo, g_strAppPath & "FO.NET\immfo.xsl")
Using writer As StreamWriter = New StreamWriter(strFileName)
writer.Write(strOutput)
End Using
Call FormatObjectToPdf(intRxNo, strFileName)
End If
Catch ex As Exception
Call InsertLog("ErrorLog", "AsyncXmlToPdf: " & ex.Message, ex)
End Try
End Sub
The only part of either method other than the declaration of strFileName that even does anything with the .fo file is in FormatObjectToPdf and that method has a Catch block for IOException. Why is the exception being caught in AsyncXmlToPdf?? Here is the actual error message:
3/25/2015 11:15 AM: [IOException] AsyncXmlToPdf: The process cannot access the file 'C:\Users\<username>\AppData\Local\Apps\2.0\1M2D4TCB.REJ\3LH3JZY2.TQC\<clickonce app>\561964.fo' because it is being used by another process.
Everything works as expected, other than the occasional orphaned .fo file when this exception occurs. Anyone have any suggestions on how I might be able to find out where the problem is?
The only part of either method ... that even does anything with the .fo file is in FormatObjectToPdf It appears that AsyncXmlToPdf will also try to open a streamwriter on it.
If there is a chance that some other BackGroundWorker, Thread or Task could also be working on the same set of files, it would be possible for the same file to be in use in AsyncXmlToPdf and FormatObjectToPdf. MSDN warns of this in the File.Exists entry:
Be aware that another process can potentially do something with the file in between the time you call the Exists method and perform another operation on the file, such as Delete.
In your case it looks like it might be a a coin flip which method the Exception will happen in.
If an accidental double click could start the same process twice, it is possible that the same file could be in use in both methods at the same time. You could add another test to see if you can open the file for ReadWrite. Given that the file was not supposed to exist yet, you could at least be somewhat certain as to the reason.
Some sort of flag to prevent more than one set of jobs from starting might be the final solution.

Using SSIS Script Task to download files from FTP

I am trying to download files from FTP using SSIS script task, I have written some code to download files from FTP using the ftpWebRequest class which is working. But I also need to take in to account that we might be asked to download it from a sFTP. Since many are suggesting that WinSCP assemblies are the best way to download sFTP files, can some provide me with some examples to download files using a SSIS script with the WinSCP classes.
PS: I am new to Vb.net
Yes, as people have said the only option is to use a library like WinSCP.
Take a look at the examples here on the WinSCP website - http://winscp.net/eng/docs/script_vbnet_robust_example
Here is the source code from the example
Imports System
Imports System.IO
Imports System.Diagnostics
Imports System.Xml
Imports System.Xml.XPath
Imports System.Configuration.ConfigurationManager
Public Class SFTP
' SFTP support, built on WinSCP
Public Shared Function PutSFTP(ByRef filename As String, ByRef remotehost As String, ByRef username As String, ByRef password As String, _
Optional ByVal outfilename As String = Nothing, Optional ByVal output As String = Nothing,
Optional ByRef errmsg As String = Nothing) As Boolean
' Run hidden WinSCP process
Dim winscp As Process = New Process()
Dim logname As String = Path.ChangeExtension(Path.GetTempFileName, "xml")
With winscp.StartInfo
' SFTPExecutable needs to be defined in app.config to point to winscp.com
Try
.FileName = AppSettings("SFTPExecutable")
If .FileName Is Nothing OrElse .FileName.Length = 0 Then Throw (New Exception("from PutSFTP: SFTPExecutable not set in config file."))
Catch ex As Exception
errmsg = ex.Message
Return False
End Try
.Arguments = "/xmllog=" + logname
.UseShellExecute = False
.RedirectStandardInput = True
.RedirectStandardOutput = True
.CreateNoWindow = True
End With
Try
winscp.Start()
Catch ex As Exception
errmsg = "from PutSFTP: Could not run the WinSCP executable " & winscp.StartInfo.FileName & Environment.NewLine & ex.Message
Return False
End Try
' Feed in the scripting commands
With winscp.StandardInput
.WriteLine("option batch abort")
.WriteLine("option confirm off")
.WriteLine("open sftp://" & username & ":" & password & "#" & remotehost & "/")
If outfilename Is Nothing Then .WriteLine("put " & filename) Else .WriteLine("put " & filename & " """ & outfilename & """")
.Close()
End With
If output IsNot Nothing Then output = winscp.StandardOutput.ReadToEnd()
' Wait until WinSCP finishes
winscp.WaitForExit()
' Parse and interpret the XML log
' (Note that in case of fatal failure the log file may not exist at all)
If Not File.Exists(logname) Then
errmsg = "from PutSFTP: The WinSCP executable appears to have crashed."
Return False
End If
Dim log As XPathDocument = New XPathDocument(logname)
Dim ns As XmlNamespaceManager = New XmlNamespaceManager(New NameTable())
ns.AddNamespace("w", "http://winscp.net/schema/session/1.0")
Dim nav As XPathNavigator = log.CreateNavigator()
' Success (0) or error?
Dim status As Boolean = (winscp.ExitCode = 0)
If Not status Then
errmsg = "from PutSFTP: There was an error transferring " & filename & "."
' See if there are any messages associated with the error
For Each message As XPathNavigator In nav.Select("//w:message", ns)
errmsg &= Environment.NewLine & message.Value
Next message
End If
Try
My.Computer.FileSystem.DeleteFile(logname)
Catch ex As Exception
' at least we tried to clean up
End Try
Return status
End Function
End Class

VB.NET System32 Path FileNotFoundException

I have the following code :
Imports System.Management
Module Module1
Private MicrosoftProcs As New List(Of String)
Private Sub FindMicrosoftProcs()
Dim searcher As New ManagementObjectSearcher("SELECT * FROM Win32_Process")
For Each p2 As ManagementObject In searcher.Get()
If p2("Name") <> "System" Or p2("Name") <> "System Idle Process" Or p2("Name") <> Process.GetCurrentProcess.ProcessName & ".exe" Then
Dim x As String = p2("ExecutablePath")
If Not x Is Nothing Then
If x.Length > 2 Then
Dim fvi As System.Diagnostics.FileVersionInfo = System.Diagnostics.FileVersionInfo.GetVersionInfo(p2("ExecutablePath").ToString)
Dim sDescription As String = fvi.CompanyName & "/" & fvi.LegalCopyright & "/" & fvi.LegalTrademarks & "/" & fvi.ProductName & "/" & fvi.FileDescription & "/"
If sDescription.ToLower.Contains("microsoft") Then
MicrosoftProcs.Add(p2("ExecutablePath"))
Debug.WriteLine("Microsoft process : " & p2("ExecutablePath"))
End If
End If
End If
End If
Next
End Sub
End Module
I am working on 64bit Windows but the code is compiled for 32bit Windows (for compatibility). If I run the code compiled for 64bit , i have not problems with the code, but if I run it compiled for 32bit I get the FileNotFoundException :
A first chance exception of type 'System.IO.FileNotFoundException' occurred in System.dll
System.IO.FileNotFoundException: C:\Windows\system32\csrss.exe
at System.Diagnostics.FileVersionInfo.GetVersionInfo(String fileName)
at WindowsApplication1.Form1.Button1_Click(Object sender, EventArgs e) in C:\Users\Maximus\Documents\Visual Studio 2010\Projects\rupe\rupe\Form1.vb:line 81
And I don't know how to fix it. Can you help me please ? Thanks in advance.
I cannot seem to find an exact answer to this but I am quite sure that the problem is that 32bit processes do not have permission to access the modules of a 64 bit process. This is why you have no problems with your 64bit app but the 32bit app doesn't like certain processes. The best resource I could find for this was another SO question which you can read here: System.ArgumentException and System.ComponentModel.Win32Exception when getting process information.
That being said there doesn't appear to be a way to get the file information for these 64bit processes when you are running a 32bit app. If you absolutely need this information you have no choice but to build your app as 64bit. If you don't really need this information and want to just carry on with the processes that you have access to in a 32bit app you can try something like this:
Private Sub FindMicrosoftProcs()
Try
Dim searcher As New ManagementObjectSearcher("SELECT * FROM Win32_Process")
For Each p2 As ManagementObject In searcher.Get()
If p2("Name") <> "System" Or p2("Name") <> "System Idle Process" Or p2("Name") <> Process.GetCurrentProcess.ProcessName & ".exe" Then
Dim x As String = p2("ExecutablePath")
If Not x Is Nothing Then
If x.Length > 2 Then
Dim fvi As System.Diagnostics.FileVersionInfo
Try
fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(p2("ExecutablePath").ToString)
Dim sDescription As String = fvi.CompanyName & "/" & fvi.LegalCopyright & "/" & fvi.LegalTrademarks & "/" & fvi.ProductName & "/" & fvi.FileDescription & "/"
If sDescription.ToLower.Contains("microsoft") Then
MicrosoftProcs.Add(p2("ExecutablePath"))
Debug.WriteLine("Microsoft process : " & p2("ExecutablePath"))
End If
Catch ex As Exception
nSkipped += 1
End Try
End If
End If
End If
Next
MessageBox.Show("Found " & MicrosoftProcs.Count & " and skipped " & nSkipped & " files.")
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub

Shell in .Net displaying file not found

Folks,
I was using
Shell("LPR -P " & IP & " -S " & IP & "c:\label.txt", AppWinStyle.Hide)
in my code earlier in my code to send print to printer. suddenlt it stopped working. i started getting file not found error. then i chanegd my code to below, but still no show. now i an getting the following error
System.ComponentModel.Win32Exception was caught
ErrorCode=-2147467259 Message=The system cannot find the file
specified NativeErrorCode=2 Source=System StackTrace: at
System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo
startInfo) at System.Diagnostics.Process.Start() at
xxxxxx.LPRProcess(String pstrFilePath, String pstrQueue, String
pstrPrinter) in I:\Visual Studio 2010\Projects\xxxxxx\ScanInSn.vb:line
201 InnerException:
New code
Private Function LPRProcess(ByVal pstrFilePath As String, ByVal pstrQueue As String, ByVal pstrPrinter As String)
Dim prcLprInfo As New ProcessStartInfo
prcLprInfo.FileName = "Lpr"
prcLprInfo.CreateNoWindow = True
prcLprInfo.WindowStyle = ProcessWindowStyle.Hidden
prcLprInfo.UseShellExecute = False
prcLprInfo.RedirectStandardOutput = True
prcLprInfo.Arguments = "-S " & pstrPrinter & " -P " & pstrQueue & " """ & pstrFilePath & """"
Dim prcLpr As New Process
Dim strOutput As String
Try
'Stage = "Run Process.Start( )"
prcLpr.StartInfo = prcLprInfo
prcLpr.Start()
strOutput = prcLpr.StandardOutput.ReadToEnd()
'Stage = "Process started, wait for it to exit"
If Not prcLpr.HasExited Then prcLpr.WaitForExit()
Catch ex As Exception
Throw New Exception("Error running LPR process: " & ex.Message)
Finally
prcLpr.Close()
prcLpr.Dispose()
End Try
If strOutput.Length > 0 Then
Throw New Exception("LPR ERROR: " & strOutput)
End If
End Function
Any ideas? (using .net 4.0)
Resolved the issue by changing settings in Visual Studio Project Prpperties.
Visual Studio > project > properties > Compile > Target CUP.
Changes the Settings to "AnyCPU".
It was set to 64 bit.
Thank you Tom and Steve for your help..