Getting actual powershell return value in vb6? - shell

Alright, so I have been trying to do this for a while, and I have come to the realization that this isn't really something that is often asked, and with vb6 getting phased out more and more, there seems to be less help than I would like regarding the language.
The title doesn't say it all actually, as I am looking to do something very specific. I need to execute a shell command (that I know how to do), however, after I execute it, I want to be able to save the return value of that command as a string. For example, if the command is ipconfig, I want the entire return value of that, all the text I would see in powershell after executing that command, saved to a string in my program.
As far as I know, I need to "import" a few things, because I have to use WshShell, which I don't know where to get. So that's part of the question, what classes do I have to add and how, or if there is a way to do it without adding classes then even better. In addition, I have heard a lot about the use of CreatePipe and such regarding similar problems, but I don't know how to use it.
Basically, what I'm saying is that I am quite uneducated regarding the subject, and any insight would be much appreciated, and thanks to all who reply.

There are many ways. Using VBScript's WSHShell.Exec is the easiest.
This is VBScript but VBScript can be pasted into VB (not vice versa though).
Dim WshShell, oExec
Set WshShell = CreateObject("WScript.Shell")
Set oExec = WshShell.Exec("ipconfig")
Do While oExec.Status = 0
WScript.Sleep 100
Loop
MsgBox oExec.StdOut.ReadAll
Slightly modified from help.
This is how to ping from VBS/VB
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * From win32_PingStatus where address='104.43.195.251'")
'msgbox colItems
For Each objItem in colItems
msgbox "Status" & objItem.statuscode & " Time " & objItem.ResponseTime
Next

Related

Encoded VBS not working when called from HTA

We have an HTA used for automatic log in to the servers using VBS. To maintain security, we wanted to encode the VBS, which had the credentials for logging in to the server. We came across a VBS script that encoded the VBS file when dragged on to it, the output is a VBE file.
Now, when this VBE is called from HTA, it shows and error, which seems like it is not able to read the VBE properly.
Below is how we are linking the VBE to out HTA :
<script language="VBScript" src="hola.vbe" > </script>
Also, below is the code for encoding :
Option Explicit
dim oEncoder, oFilesToEncode, file, sDest
dim sFileOut, oFile, oEncFile, oFSO, i
dim oStream, sSourceFile
set oFilesToEncode = WScript.Arguments
set oEncoder = CreateObject("Scripting.Encoder")
For i = 0 to oFilesToEncode.Count - 1
set oFSO = CreateObject("Scripting.FileSystemObject")
file = oFilesToEncode(i)
set oFile = oFSO.GetFile(file)
Set oStream = oFile.OpenAsTextStream(1)
sSourceFile=oStream.ReadAll
oStream.Close
sDest = oEncoder.EncodeScriptFile(".vbs",sSourceFile,0,"")
sFileOut = Left(file, Len(file) - 3) & "vbe"
Set oEncFile = oFSO.CreateTextFile(sFileOut)
oEncFile.Write sDest
oEncFile.Close
Next
According to my understanding the encoded VBS should work as normal one, not sure why we are fading issue in this case.
In order to use an encoded VBScript, you need to specify the language engine to use with language="VBScript.Encode" rather than just language="VBScript".
Also, be quite wary if you want to use it "To maintain security". The purpose of the script encoder is to deter casual inspection, but it doesn't "encrypt" the code in any conventional sense, and it's not that hard to get the plain script back.

Activating (bring to foreground) a specific window with vbscript

I'm not even sure where to start with my question, I tried a hundred things and googled for hours but didn't find anything useful. (I'm open to every dirty trick.)
Here's my problem:
I have a .hta-file with a listbox that looks like this:
It lists all sessions/modi of my SAP Gui running.
Set SapGuiAuto = GetObject("SAPGUI")
Set application = SapGuiAuto.GetScriptingEngine
If application.Connections.Count > 0 Then
Set connection = application.Children(0)
If connection.Sessions.Count > 0 Then
Set session = connection.Children(0)
End If
End If
If IsObject(WScript) Then
WScript.ConnectObject session, "on"
WScript.ConnectObject application, "on"
End If
Set optGroup = Document.createElement("OPTGROUP")
optGroup.label = "Server"
'count all connected servers
ConnectionCount = application.Connections.Count
If ConnectionCount > 0 Then
Sessionlist.appendChild(optGroup)
Else
optGroup.label = "No connection here."
End If
'count all sessions per server
If ConnectionCount > 0 Then
For Each conn in application.Connections
'Text output connections and sessions
SessionCount = conn.Sessions.Count
whatIsIt = conn.Description
ConnectionFeld.innerhtml = ConnectionFeld.innerhtml & " <br> " & SessionCount & " Sessions auf " & whatIsIt
'fill listbox with all connections
Set objOption = nothing
Set optGroup = Document.createElement("OPTGROUP")
optGroup.label = conn.Description
Sessionlist.appendChild(optGroup)
i = 0
'fill listbox with all sessions
For Each sess In conn.Sessions
i = i + 1
Set objOption = Document.createElement("OPTION")
objOption.Text = "Session " & i & ": " & sess.ID
objOption.Value = sess.ID
SessionList.options.add(objOption)
Next
Next
Else
Exit Sub
End If
My goal: When I doubleclick on one of the entries in that list, the selected instance of my SAP Gui should come to the foreground/get activated.
Unfortunately my taskmanager only lists one task and that is "SAP Logon". One of my opened windows also has the name "SAP Logon", all others have the same name: "SAP Easy Access".
The only way I can see the IDs of the connection (servername) and the IDs of the session is via extracting them with vbscript. (see above)
Is there any way to do that? The only workarounds I could think of after trying a thousand solutions are these two:
extremely ugly workaround:
If sessionID = sess.ID Then
Set objShell = CreateObject("shell.application")
objShell.MinimizeAll
sess.findById("wnd[0]").maximize
End If
It minimizes all windows an then maximizes the selected SAP window. Unfortunately My HTA-GUI also gets minimized which kinda sucks.
Second idea:
Somehow get to these clickable thingies by shortcut and put that in my script or some other ugly way.
By hand you have to do this:
Click on that little arrow, rightclick on the icon and then leftclick on the name.
Is there any way to automate this? It's driving me crazy.
Hope someone can help me, it would be GREATLY appreciated.
PS: I'm sitting on a machine with restricted rights and so I may not be able to tackle this with Windows API-ish solutions.
EDIT concerning comments:
It is not possible:
to change registry entries
create COM objects
work with anything else than VBScript
Similarly, it also works with the following commands:
session.findById("wnd[0]").iconify
session.findById("wnd[0]").maximize
I found it...
The resizeWorkingPane method - for changing the size of a window - also works on windows in the background. If you change the parameters, the window will come to the foreground.
session.findById("wnd[0]").resizeWorkingPane 300,200,false
I have to partially revoke this, because it doesnt work on all windows. I'm still not sure why, but it keeps failing sometimes. Still, it seems to me, that this is the closest you can get.
From Help.
Activates an application window.
object.AppActivate title
object
WshShell object.
title
Specifies which application to activate. This can be a string containing the title of the application (as it appears in the title bar) or the application's Process ID.
I don't know what access to info you have about the window. Some COM objects have a HWnd property. This post gets you how to convert a hwnd to a ProcessID to be used above.
How to find the window Title of Active(foreground) window using Window Script Host
This shows how to convert a process command-line to a ProcessID. To see what properties and methods are available use the command-line tool wmic (wmic process get /? and wmic process call /?)
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * From Win32_Process")
For Each objItem in colItems
msgbox objItem.ProcessID & " " & objItem.CommandLine
Next
This is a 100% of the time solution. It's ugly but it works. You can swap out the IQS3 t code for any other one you can confirm the user won't be in and will have access to. Also part of my reasoning for selection of this code is it loads fast.
Set objShell = CreateObject("wscript.shell")
session.findById("wnd[0]/tbar[0]/okcd").text = "/nIQS3"
session.findById("wnd[0]").sendVKey 0
objShell.AppActivate(cstr(session.ActiveWindow.Text))
session.findById("wnd[0]/tbar[0]/btn[3]").press

VBS Scripting to move computers from one OU to the next

I am working on packaging a script for the company that I work for that will allow field service techs to convert computers in a private workstation OU to a team workstation OU and vice versa.
That is a small part of this script for now and one that has had me puzzled for most of the day. I've tried different variations of this script and landed on one that I believe will get me on the right track.
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select Name from Win32_ComputerSystem",,48)
For Each objItem in colItems
strPCName = objItem.Name
Next
Set objNewOU = GetObject("LDAP://OU=Computers,OU=Corporate,DC=xxxxx,DC=net")
Set objMoveComputer = objNewOU.MoveHere("LDAP://CN=" & strPCName & ",OU=Computers,OU=Corporate,DC=xxxxx,DC=net",vbnullstring)
I get an error, There is no such object on the server. When I put the computer manually in the OU in question, I don't get that error message. This is where I'm stuck at the moment.
The scripting is in my personal lab at the moment.
I was able to get a solution that did work, by using the following script.
Set objSysInfo = CreateObject("ADSystemInfo")
strComputerDN = objSysInfo.ComputerName
Set objNewOU = GetObject("LDAP://OU=Private Workstations,OU=xxxx,OU=xxxx,DC=xxxx,DC=xxxx")
Set objMoveComputer = objNewOU.MoveHere _ ("LDAP://" & strComputerDN, vbNullString)
Judging by the script's behavior, it uses strComputerDN to determine where the computer is, and the objNewOU determines where the computer is going. The objmoveComputer consolidates this information as best as I can determine to move the computer to it's OU.

VB script: Message while Unzipping a file

I am using the below vb script to un-zip the files, so while un-zipping is going on, i am seeing a pop up messgae(Copying/extracting), is there any way to get rid of popup message?
FileToGetUnZipped = "InstallDir\UI_Files.zip"
DestPathForUnzippedFile = "InstallDir\system"
Set objFSO = CreateObject("Scripting.FileSystemObject")
If Not objFSO.FolderExists(DestPathForUnzippedFile) Then
objFSO.CreateFolder(DestPathForUnzippedFile)
End If
UnZipFile FileToGetUnZipped, DestPathForUnzippedFile
Sub UnZipFile(strArchive, DestPathForUnzippedFile)
Set objApp = CreateObject( "Shell.Application" )
Set objArchive = objApp.NameSpace(strArchive).Items()
Set objDest = objApp.NameSpace(DestPathForUnzippedFile)
objDest.CopyHere objArchive
End Sub
The CopyHere method takes a second argument which can be a combination of various options, including
(4)
Do not display a progress dialog box.
However, I have not had much success on getting many of these options to work reliably - I think it varies by Windows version as much as anything else.
As a side note, I think you may have issues with the CopyHere method being asynchronous - your script may complete before CopyHere does, which may kill the copying process.

How do I close Word (or other app) if an error occurs in a VBScript?

I wrote a VBScript app to open Word and Excel documents and search and replace blocks of text and various sections, pulling the new text from a plain text file. I purposely avoided any error checking, primarily because I couldn't figure it out at the time (and the script ran reliably anyway). Now months later on my local machine, I am inexplicably getting error messages about Normal.dot being changed and a message box asking what I want to do about it (which requires three more dialogs to finally answer). Of course this kills my ability to run the script and simply walk away, as it causes the script to fail. Currently when this happens, I have to open the Task Manager, find Winword.exe (of which the GUI isn't running) and kill it then re-run my script.
What's a reasonable way of catching the error and successfully shutting down Word (or Excel). Based on this question I'm trying this:
Set objDoc = objWord.Documents.Open(curDir1 + "\docs\template_spec.dot")
If Err.Number <> 0 Then
WScript.Echo "Error in Word Open:" & Err.Description
objWord.Quit
Else
Set objSelection = objWord.Selection
'Do replacement activities'
ReplaceText(objSelection)
objDoc.SaveAs(curDir1 + "\docs\mynewdocument.doc")
objWord.Quit
End If
Set objShell = Nothing
Set objWord = Nothing
Set objExcel = Nothing
Of course, as fate would have it, I cannot replicate the problem, so it works like normal. Does this solution seem reasonable? And a side question: How the heck do I get Word to stop complaining about Normal.dot (or get the script to handle it)? It's as if Word leaves itself open in the background after I have closed the GUI in some cases.
have you considered wrapping everything into an 'On Error Resume Next' statement so that your script ignores all the errors and continues to run as much as possible before calling the objWord.quit regardless of success or fail.
if you want more information on the correct use of 'On Error Resume Next' then go over to the msdn article on it!
Hope this helps!
Paul
I'm afraid that
WScript.Echo "..."
if it ever fires, is going to stall your script. Other than that, everything looks right. I'll play with it when I get home.
Edit: Word does hang out in the background, quite frequently. For one thing, if you use Outlook, and use Word as your Outlook editor, Word won't go away until Outlook is gone.
I'd agree with the use of "on error resume next".
If you really need to forcefully terminate Word, you can use WMI and the Win32_Process class to find and kill the process. This should be a last resort if everything else fails.
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set colProcess = objWMIService.ExecQuery("Select * from Win32_Process Where Name = 'winword.exe'")
For Each objProcess in colProcess
objProcess.Terminate()
Next
This was a modified example from:
http://www.computerperformance.co.uk/vbscript/wmi_process_stop.htm
Also, make sure all your references to the Word automation object are closed and/or set to nothing before you terminate the process.
The most reliable way to terminate all ActiveX instances, clean up garbage, and release resources is to put the code for that purpose into Sub Class_Terminate() of a dummy class, created instance of the class allows to handle script quit event.
Option Explicit
Dim objBeforeQuitHandler, objWord
' create a dummy class instance
Set objBeforeQuitHandler = New clsBeforeQuitHandler
' create word app instance
Set objWord = CreateObject("Word.Application")
objWord.Visible = True
objWord.Documents.Add.ActiveWindow.Selection.TypeText "80040000 error was raised. About to terminate the script." & vbCrLf & "Word will be quitted without saving before script termination just you close popped up error message."
' your code here...
' raise an error
Err.Raise vbObjectError
Class clsBeforeQuitHandler
' dummy class for wrapping script quit event handler
Private Sub Class_Terminate()
Dim objDoc
On Error Resume Next ' to prevent errors in case of unexpected word app termination
If TypeName(objWord) <> "Object" Then ' word app has not been closed yet
objWord.DisplayAlerts = False
For Each objDoc In objWord.Documents
objDoc.Saved = True ' to prevent save as dialog popping up
objDoc.Close
Next
objWord.Quit
End If
End Sub
End Class

Resources