Running Powershell(x86) from WScript.Shell - vba

I have the following statement I'd like to execute in Powershell(x86) using VBA:
%SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe Set-ExecutionPolicy -Scope CurrentUser Unrestricted -force;
$DBPath = "U:\Data\Distribution\Common\Dist\Weekly Hours Entry\DC Master Production DB V2\Packing Slip Generation\PackingSlipGeneratorBE.accdb";
$SQL = "SELECT VENDOR, PackingSlipID, EntryNumber, ShipDate, TrackingNumber, RA, EntryDate FROM tblPackingSlipLog WHERE EntryNumber>1000001 AND EntryNumber<1000011;";
$Output = "C:\Users\foo\Documents\Test.csv";
$Con= New-Object -TypeName System.Data.OleDb.OleDbConnection;
$con.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source= $DBpath";
$ConCommand= $Con.CreateCommand();$ConCommand.CommandText = $SQL;
$OAdapt = New-Object -TypeName System.Data.OleDb.OleDbDataAdapter $ConCommand;
$DS = New-Object -TypeName System.Data.DataSet;$OAdapt.Fill($DS);
export-csv -InputObject $DS.Tables[0] -Path $Output -NoTypeInformation -force;$Con.Close()
I am able to run just the Powershell(x86) in Powershell(x86) with no problem. Also, if I run the %SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe part in command prompt and then the powershell part separately, it also works as intended. However, when I try running them together it doesn't work.
The calling VBA function: GetPackingSlipData ("WHERE EntryNumber>1000001 AND EntryNumber<1000011")
Function GetPackingSlipData(strSQL_WhereClause As String) As String
On Error Resume Next
Dim wsShell As Object
Dim strPowerShellCommand As String
Dim strOutputFile As String
strOutputFile = GetDocuments & "\Test.csv"
Set wsShell = CreateObject("WScript.Shell")
strPowerShellCommand = "%SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe Set-ExecutionPolicy -Scope CurrentUser Unrestricted -force; $DBPath = ""U:\Data\Distribution\Common\Dist\Weekly Hours Entry\DC Master Production DB V2\Packing Slip Generation\PackingSlipGeneratorBE.accdb""; " & _
"$SQL = ""SELECT VENDOR, PackingSlipID, EntryNumber, ShipDate, TrackingNumber, RA, EntryDate FROM tblPackingSlipLog " & strSQL_WhereClause & ";"";" & _
"$Output = """ & strOutputFile & """;" & _
"$Con= New-Object -TypeName System.Data.OleDb.OleDbConnection;$con.ConnectionString = ""Provider=Microsoft.ACE.OLEDB.12.0;Data Source= $DBpath"";" & _
"$ConCommand= $Con.CreateCommand();$ConCommand.CommandText = $SQL;" & _
"$OAdapt = New-Object -TypeName System.Data.OleDb.OleDbDataAdapter $ConCommand;" & _
"$DS = New-Object -TypeName System.Data.DataSet;$OAdapt.Fill($DS);" & _
"export-csv -InputObject $DS.Tables[0] -Path $Output -NoTypeInformation -force;$Con.Close()"
Debug.Print strPowerShellCommand
Call wsShell.Run(strPowerShellCommand, 1, True)
If Err.Number <> 0 Then
GetPackingSlipData = "Error: " & Err.Number & ", " & Err.Description
Else
GetPackingSlipData = strOutputFile
End If
On Error GoTo 0
End Function
The command that gets printed:
%SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe Set-ExecutionPolicy -Scope CurrentUser Unrestricted -force; $DBPath = "U:\Data\Distribution\Common\Dist\Weekly Hours Entry\DC Master Production DB V2\Packing Slip Generation\PackingSlipGeneratorBE.accdb"; $SQL = "SELECT VENDOR, PackingSlipID, EntryNumber, ShipDate, TrackingNumber, RA, EntryDate FROM tblPackingSlipLog WHERE EntryNumber>1000001 AND EntryNumber<1000011;";$Output = "C:\Users\foo\Documents\Test.csv";$Con= New-Object -TypeName System.Data.OleDb.OleDbConnection;$con.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source= $DBpath";$ConCommand= $Con.CreateCommand();$ConCommand.CommandText = $SQL;$OAdapt = New-Object -TypeName System.Data.OleDb.OleDbDataAdapter $ConCommand;$DS = New-Object -TypeName System.Data.DataSet;$OAdapt.Fill($DS);export-csv -InputObject $DS.Tables[0] -Path $Output -NoTypeInformation -force;$Con.Close()

Related

Why is this powershell code only returning one result when I have multiple results?

I am running a query through powershell. I know I have multiple results from running the query through ssms. Why does the variable only have one result in powershell?
I have used many methods to do this query and I finally got it working but can't get all the results of the query.
[string] $Server= "mochqdb01";
[string] $Database = "MoCApp.Models.Item+ItemDBContext";
[string] $SQLQuery= $("Select smEmail from Items where DateRequested >= dateadd(day,datediff(day,1,GETDATE()),0)");
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server=$Server;Database=$Database;Integrated Security=True"
$SqlConnection.Open()
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SQLQuery
$SqlCmd.Connection = $SqlConnection
$dbname = $SqlCmd.ExecuteScalar()
$SqlConnection.Close()
Write-output "Database is " $dbname
Output:
Database is
Franziew#marketyyo.com
Should have multiple results. Should I save into an array?
I actually want to save the results into this format.
Send-ToEmail -email "js#marketyyo.com","mb#marketyyo.com";Is this possible?
ExecuteScalar() returns the first column of the first row of the first result set. You need ExecuteReader(). EG
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server=$Server;Database=$Database;Integrated Security=True"
$SqlConnection.Open()
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SQLQuery
$SqlCmd.Connection = $SqlConnection
$rdr = $SqlCmd.ExecuteReader()
while ($rdr.Read())
{
$smMail = $rdr[0]
write-output "email is $smMail"
}
$rdr.Close()
$SqlConnection.Close()

Hide some information like server name from WinSCP output

I want to use WinSCP command line in my application. I want to inline all commands and not have script file.
Is it possible to hide server name?
I don't want the users know the server name.
I need to have command window visible in order to see the progress of download file.
Call Shell("c:\program files (x86)\winscp\winscp.com /ini=nul /command ""open ftp://user:password#servername/ "" ""get -latest /public_ftp/incoming/* c:\local\"" ""exit""", vbNormalFocus)
What you want is doable, but it involves a lot of advanced code for redirecting an output of winscp.com, and filtering out the information you do not want to show.
Easier is to use WinSCP .NET assembly from PowerShell instead. That gives you a full control over the output.
Just execute something like this:
PowerShell -ExecutionPolicy bypass -Command "$sessionUrl = 'ftp://user:password#servername/' ; $remotePath = '/public_ftp/incoming' ; $localPath = 'c:\local' ; try { Add-Type -Path 'C:\Program Files (x86)\WinSCP\WinSCPnet.dll' ; $sessionOptions = New-Object WinSCP.SessionOptions ; $sessionOptions.ParseUrl($sessionUrl) ; echo 'Opening connection'; $session = New-Object WinSCP.Session ; $session.add_FileTransferProgress( { Write-Host -NoNewline ([char]13 + '{0} ({1:P0})' -f $_.FileName, $_.FileProgress) } ); $session.Open($sessionOptions) ; echo 'Finding latest file'; $directoryInfo = $session.ListDirectory($remotePath) ; $latest = $directoryInfo.Files | Where-Object { -Not $_.IsDirectory } | Sort-Object LastWriteTime -Descending | Select-Object -First 1 ; if ($latest -eq $Null) { Write-Host 'No file found' ; exit; }; echo 'Downloading file'; $session.GetFiles([WinSCP.RemotePath]::EscapeFileMask([WinSCP.RemotePath]::Combine($remotePath, $latest.Name)), $localPath + '\*').Check(); echo ' Done'; } catch { Write-Host $_.Exception.Message; }"
The PowerShell code is basically equivalent to WinSCP article on Downloading the most recent file.
It's just merged into a single command, so that you can execute it from VBA Shell function (after properly doubling the double quotes).
Call Shell ( _
"PowerShell -ExecutionPolicy bypass -Command """ & _
"$sessionUrl = 'ftp://user:password#servername/';" & _
"$remotePath = '/public_ftp/incoming';" & _
"$localPath = 'c:\local';" & _
"try {" & _
" Add-Type -Path 'C:\Program Files (x86)\WinSCP\WinSCPnet.dll'; " & _
" $sessionOptions = New-Object WinSCP.SessionOptions; " & _
" $sessionOptions.ParseUrl($sessionUrl); " & _
" echo 'Opening connection'; " & _
" $session = New-Object WinSCP.Session; " & _
" $session.add_FileTransferProgress( { Write-Host -NoNewline ([char]13 + '{0} ({1:P0})' -f $_.FileName, $_.FileProgress) } ); " & _
" $session.Open($sessionOptions); " & _
" echo 'Finding latest file'; " & _
" $directoryInfo = $session.ListDirectory($remotePath); " & _
" $latest = $directoryInfo.Files | Where-Object { -Not $_.IsDirectory } | " & _
" Sort-Object LastWriteTime -Descending | Select-Object -First 1; " & _
" if ($latest -eq $Null) { Write-Host 'No file found' ; exit; }; " & _
" echo 'Downloading file'; " & _
" $sourcePath = [WinSCP.RemotePath]::EscapeFileMask([WinSCP.RemotePath]::Combine($remotePath, $latest.Name)); " & _
" $session.GetFiles($sourcePath, $localPath + '\*').Check(); " & _
" echo ' Done'; " & _
"} catch { Write-Host $_.Exception.Message; }")

PowerShell 2.0 Run one SQL File (having multi SP and Functions)

I have a .sql file which has multiple stored procedures and functions script in it.
I don't want to use Invoke commands because my server doesn't support them.
Currently I am using below code which works fine if my .sql file has only one stored procedure or function.
$scriptDetail = Get-Content $sqlScriptLocation | Out-String
$scriptDetail = $scriptDetail -creplace 'GO',''
$scriptDetail = $scriptDetail -replace 'SET ANSI_NULLS ON',''
$scriptDetail = $scriptDetail -replace 'SET QUOTED_IDENTIFIER ON',''
$ConnectionString = “Server=$dataSource;uid=$user; pwd=$cred;Database=$database;”
$sqlCon = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $ConnectionString
$sqlCon.Open()
$sqlCmd = New-Object -TypeName System.Data.SqlClient.SqlCommand
$sqlCmd.Connection = $sqlCon
$sqlCmd.CommandText = $scriptDetail
$sqlCmd.ExecuteNonQuery()
$sqlCon.Close()
I am replacing GO and other two commands in above code because they casused errors like
System.Data.SqlClient.SqlException (0x80131904): Incorrect syntax near 'GO'
System.Data.SqlClient.SqlException (0x80131904): 'CREATE FUNCTION' must be the first statement in a query batch
Must declare the scalar variable "#variableName"
I have came up with solution after Some digging, read the complete file and put it in an array and then run the ForEach loop, it will get each line in ForEach loop put a condition if its not equal to "GO" add it into another array else array(in which you are adding each line) execute it
Code is below
$SQLConnection = New-Object System.Data.SqlClient.SqlConnection
$SQLConnection.ConnectionString = "Server=" + $YourServerName + ";Database=" + $YourDatabaseName + ";User ID= " + $YourUserName + ";Password=" + $YourDbPassword + ";"
$DirectoryPath = "C:\FolderName"
$Dep_SqlFiles = get-childitem -Recurse $DirectoryPath -Include "*.sql"
$Dep_Info_DataEntry = #(Get-Content $Dep_SqlFiles.FullName) #Get all info of each file and put it in array
foreach($SQLString in $Dep_Info_DataEntry)
{
if($SQLString -ne "go")
{
#Preparation of SQL packet
$SQLToDeploy += $SQLString + "`n"
}
Else
{
try{
$SQLCommand = New-Object System.Data.SqlClient.SqlCommand($SQLToDeploy, $SQLConnection)
$SQLCommand.ExecuteNonQuery()
#use this if you want to log the output
#$SQLCommand.ExecuteScalar() | Out-File YourLogFile -Append
}
Catch
{
}
$SQLToDeploy = ""
}
}

run 2 seperate cmd commands from vb forms appliaction

i want my program to open cmd , change directory and then do the command : "copy /B file1 file2 output"
this is what i have at the moment. but all that happens is a cmd window flashes for a second but no file gets created
Dim cmd1 As String
Dim cmd2 As String
cmd1 = "cd " & FolderFromFileName(imagename)
cmd2 = "copy /B " & NameOnlyFromFullPath(imagename) & "+" & "TEMP.txt" & " " & TextBox1.Text
Shell("cmd /c" & " " & cmd1 & " " & cmd2, AppWinStyle.NormalFocus)
please help, thanks :)
Do you really need to have a command prompt appear? You could do all this without a separate process by using the system.io library. If you really need the cmd prompt you can create a process.
Dim NewProcess = New Process
' a new process is created
NewProcess.StartInfo.UseShellExecute = False
' redirect IO
' PVWCmd.StartInfo.RedirectStandardOutput = True
' PVWCmd.StartInfo.RedirectStandardError = True
' PVWCmd.StartInfo.RedirectStandardInput = True
' don't even bring up the console window
NewProcess.StartInfo.CreateNoWindow = False
' executable command line info
NewProcess.StartInfo.FileName = "cmd"
NewProcess.StartInfo.WorkingDirectory = "C:\"
' NewProcess.StartInfo.Arguments = +" > """ + "LogFile.log" + """ 2>&1"
NewProcess.Start()

The term 'connect-nacontroller' is not recognized as the name of a cmdlet, function, script file, or operable program

Doesn't it figure. Moments after I post this, I finally get it to work. I'll post what I did just in case anyone else has this issue in the futures. It's a big oversight for me!
Right before the username line, I added this line:
"Import-Module DataOnTap" & Environment.NewLine & _**
I'm trying to run a PowerShell script from within VB.Net 2010 code. When I run the code in PowerShell, it runs fine. When I run it from within VS2010, I get the error:
"The term 'connect-nacontroller' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again."
The error occurs when it gets to the "Dim results As Collection(Of PSObject) = MyPipeline.Invoke()" line.
Here is my code (I changed the username and password and will be changing how it comes into the code once I get it to work. I also changed the name of the volume):
Protected Sub ExecuteCode_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
resultBox.Text = RunScript(powerShellCodeBox.Text)
End Sub
Private Function RunScript(ByVal scriptText As String) As String
Dim MyRunSpace As Runspace = RunspaceFactory.CreateRunspace()
MyRunSpace.Open()
Dim MyPipeline As Pipeline = MyRunSpace.CreatePipeline()
Dim userName As String = "abc"
Dim password As String = "123"
Dim myscript As String = "$username = " & Chr(34) & userName & Chr(34) & Environment.NewLine & _
"$password = ConvertTo-SecureString " & Chr(34) & password & Chr(34) & " -AsPlainText -Force" & Environment.NewLine & _
"$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $userName,$password" & Environment.NewLine & _
"connect-nacontroller mydrive -credential $cred" & Environment.NewLine
MyPipeline.Commands.AddScript(myscript)
MyPipeline.Commands.Add("Out-String")
Dim results As Collection(Of PSObject) = MyPipeline.Invoke()
MyRunSpace.Close()
Dim MyStringBuilder As New StringBuilder()
For Each obj As PSObject In results
MyStringBuilder.AppendLine(obj.ToString())
Next
Return MyStringBuilder.ToString()
End Function
Right before the username line, I added this line:
"Import-Module DataOnTap" & Environment.NewLine & _**