Running Access Macro in Powershell - powershell

I'm trying to run an Access 2010 macro in PowerShell (v4.0 Windows 8.1) with the below code:
$Access = New-Object -com Access.Application
$Access.OpenCurrentDatabase("SomePath", $False, "Password")
$Access.Run("SomeProc")
$Access.CloseCurrentDatabase()
$Access.Quit()
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($Access)
Remove-Variable Access
I get an error on the line $Access.Run("SomeProc") that there's not enough parameters specified:
Exception calling "Run" with "1" argument(s): "Invalid number of parameters. (Exception
from HRESULT: 0x8002000E (DISP_E_BADPARAMCOUNT))"
The procedure SomeProc does not require any parameters.
I've read the msdn article on the run method and only one parameter is required.
I've also tried this workaround which also failed to work for an unrelated reason.
Does anyone know what the cause of the error could be and how to get the method working?

This is a driver issue where the OLEDB libraries aren't loading correctly.
I was able to reproduce your error exactly, and I was able to work around it by opening Powershell from your SysWow directory instead of System32.
Try opening this version of Powershell (you'll have to run set-executionpolicy again), and see if it'll execute your script.
%SystemRoot%\syswow64\WindowsPowerShell\v1.0\powershell.exe
Helpful link: https://social.msdn.microsoft.com/Forums/en-US/4500877f-0031-426e-869d-bda33d9fe254/microsoftaceoledb120-provider-cannot-be-found-it-may-not-be-properly-installed?forum=adodotnetdataproviders

The C# signature is something like this:
public object Run(string Procedure, ref object Arg1, ... ref object Arg30) ...
It means that COM the Arg optional arguments are not optional in .NET because they are explicitly marked as [ref]. You need to provide all 32 args even if you don't use them.
Assuming you have the following VBA code:
Public Sub Greeting(ByVal strName As String)
MsgBox ("Hello, " & strName & "!"), vbInformation, "Greetings"
End Sub
You can either use call it like this:
$Access = New-Object -com Access.Application
$Access.OpenCurrentDatabase("Database1.accdb")
$runArgs = #([System.Reflection.Missing]::Value) * 31
$runArgs[0] = "Greeting" #Method Name
$runArgs[1] = "Jeno" #First Arg
$Access.GetType().GetMethod("Run").Invoke($Access, $runArgs)
In your case it will be:
$runArgs = #([System.Reflection.Missing]::Value) * 31
$runArgs[0] = "SomeProc"
$Access.GetType().GetMethod("Run").Invoke($Access, $runArgs)
I would probably try to add a helper to the access object:
Add-Member -InputObject $Access -MemberType ScriptMethod -Name "Run2" -Value {
$runArgs = #([System.Reflection.Missing]::Value) * 31
for($i = 0; $i -lt $args.Length; $i++){ $runArgs[$i] = $args[$i] }
$this.GetType().GetMethod("Run").Invoke($this, $runArgs)
}
Then you can use Run2 as you would expect:
$Access.Run2("Greeting", "Jeno")
$Access.Run2("SomeProc")

Related

catch error from param in Powershell without using try-catch

I’m writing a Powershell script to call a file convert function(to execute ANYTRAN file).
I was told to put param() in a try-catch by my boss but that seems to cause an error.
Then how can I catch the error from param()?
I think it’s possible to use if statement in a parent shell.
Please give me some advice.
Below is the code.
$ErrorActionPreference = "Stop"
try{
#-----------------------------------------------------------
# 初期処理
#-----------------------------------------------------------
#---# 環境変数定義(define common env)
#---& ".\commonEnv.ps1"
# 共通関数インクルード(include common func)
. (Resolve-Path ".\commonFunc.ps1").path
# 引数取得(get parameter)
Param(
$inFile
,$outFile
,$flgZeroByte
)
Your issue is not in params passing . "param", unless the syntax is incorrect, logically can't crash (and you can't handle whether it could, unless with error handling at scope higher than the definition of function) .
function sc1 {
param ( $v_f = '' )
process {
Write-Host $v_f
}
}
sc1 '& 2'

Powershell add type in class in PSM1

In a PS1, this works
class pxInitFailureMessage {
static [void] Send ([int32]$processID, [String]$title, [string]$message) {
Add-Type -AssemblyName:System.Windows.Forms
Add-Type -AssemblyName:System.Drawing
$balloonTip = New-Object System.Windows.Forms.NotifyIcon
$balloonTip.icon = [system.drawing.icon]::ExtractAssociatedIcon($(Get-Process -id:$processID | Select-Object -expandProperty:Path))
$balloonTip.balloonTipIcon = 'Error'
$balloonTip.balloonTipTitle = $title
$balloonTip.balloonTipText = $message
$balloonTip.visible = $true
$balloonTip.ShowBalloonTip(0)
$balloonTip.Dispose
}
}
[pxInitFailureMessage]::Send($pid, 'Title', 'Message is just some extra text')
But, move that class to a library.PSM1 file, and this in the PS1
Using module '\\Mac\iCloud Drive\Px Tools 4.#\Dev 4.0\#Spikes\Windows7\library.psm1'
[pxInitFailureMessage]::Send($pid, 'Title', 'Message is just some extra text')
And it works in the ISE, but not in the console when run from a shortcut. I get Unable to find type [system.drawing.icon].
Obviously the first Add-Type works, I get no error at New-Object. So why is the second type load failing? I also tried moving the two Add-Type lines out of the class, and into the root of the module file, with the same results. What DOES work is adding those lines to the PS1, between using module (which has to be the first non remarked line) and the call. That works, but then you don't have a self contained class, which seems to suck a bit. What am I misunderstanding here?
How does one make a self contained class that uses .NET types and work with it from a module file?
For anyone interested, the solution I came up with is as follows. In library.PSM1 I have...
class pxInitFailureMessage {
static [void] Send ([int32]$processID, [String]$title, [string]$message, [System.Drawing.Icon]$icon) {
Add-Type -AssemblyName:System.Windows.Forms
$balloonTip = New-Object System.Windows.Forms.NotifyIcon
$balloonTip.icon = $icon
$balloonTip.balloonTipIcon = 'Error'
$balloonTip.balloonTipTitle = $title
$balloonTip.balloonTipText = $message
$balloonTip.visible = $true
$balloonTip.ShowBalloonTip(0)
$balloonTip.Dispose
}
}
And in my scrip I have...
using assembly System.Drawing
Using module '.\library.psm1'
$processIcon = [system.drawing.icon]::ExtractAssociatedIcon($(Get-Process -id:$pID | Select-Object -expandProperty:Path))
[pxInitFailureMessage]::Send($pid, 'Title', 'Message is just some extra text', $processIcon)
The key is that the class IS now self contained, and follows good practice of providing the class with everything it needs, so it doesn't have to go looking outside itself. I could just as easily use my own ICO file for the icon, like so...
$processIcon = [system.drawing.icon]::New('\\Mac\iCloud Drive\Px Tools 4.#\Dev 4.0\Px_Resources\PxIcon.ico')
Again with a literal path in this example.
So, I learned a thing today. Woot.

PowerShell GUI observer several socket-connections

I have found lots of examples that are all very long - but I think it can be done in a lot shorter way.
I nee a GUI that displays and enables several options of the socket connections I need to adimn.
In the beginning I think I have:
# Load external assemblies
[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$OnLoadForm_StateCorrection = {
$form1.WindowState = $InitialFormWindowState
}
$myGUI = New-Object System.Windows.Forms.Form
$myGUI.Text = "Socket-Traffic"
$myGUI.Name = "myGUI"
$myGUI.DataBindings.DefaultDataSourceUpdateMode = 0
$myGUI.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,255)
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 332
$System_Drawing_Size.Height = 264
$myGUI.ClientSize = $System_Drawing_Size
Now I have the list of Hashes containing all relevant information of the variables and the connections. The groupe just has to be able to access their hash:
$Sockets = $HASH1,$HASH2,$HASH3,$HASH4 # all hasches have a [string] ID: $HASH1.ID = $port
$n = 0 # do I need that?
foreach ($h in $Sockets) {
makeTab $myGUI $h $n
$n++
}
the function that I have in mind should start like that:
function makeTab {
param (
[parameter(Mandatory=$true)]
[PSObject] $gui,
[parameter(Mandatory=$true)]
[PSObject] $hashTable, # with all info to connect and vars.
[parameter(Mandatory=$true)]
[int] $noTab
)
... ??
}
Each Socket-Tab has to have these internal groupes:
all function calls behind a buttonclick like:
$x_OnClick = { Write-host "Button clicked, do..." }
1) Send-a-Line Groupe
a) Line to enter test meant to sent.
b) Button to send # no 'cancle'
2) Login-Groupe:
a) status: Login=Green.ico, Connected=Orange.ico, nothing=red.ico
b) Button: Login (connect & login)
c) Button: Logout (Logout & disconnect)
3) Logging-Groupe:
a) last 2 Lines been sent
b) last 2 line received
4) Status Groupe
a) text lines with var. info. about the connection
b) text lines with var. info. about the connection
...
Global - Finally
a) Button to Exit the script with logout & disconnect all socket connections...
May s.o. can draft an example? After that I can determine the size and the place of the various and the buttons within a group.
Thank you very much in advance!
Gooly
If you are able to use PowerSHell 3.0 or higher, you can use WPF, where forms are just XML-like text. You can create an XML form in visual designer, like Visual Studio Express.
Like this: TreeView Example

Function Returned Data Type

I've written a function that will create an Active Directory user based on the supplied parameters. The user creates fine; however the problem that I'm running into is the returned data from the function. I'm merely looking to return a boolean response based on if the user was created or not. Instead, it's passing back an array.
A sample of the function:
Function CreateUser () {
param([string]$ParentDN,
[string]$FirstName,
[string]$LastName,
[string]$Username,
[string]$EmailAddress,
[string]$Password);
Try {
$UserOU = [ADSI] "LDAP://$LDAPServer/$ParentDN";
$NewUser = $UserOU.Create("user","cn=$Username");
$NewUser.Put("sAMAccountName","$Username");
$NewUser.Put("givenName","$FirstName");
$NewUser.Put("sn","$LastName");
$NewUser.Put("UserPrincipalName","$Username#$NetworkFQDN");
$NewUser.Put("mail","$EmailAddress");
$NewUser.SetInfo();
$NewUser.SetPassword("$Password");
$NewUser.SetInfo();
$flag = $NewUser.userAccountControl.Value -bxor 65536; #Password Never Expires flag
$NewUser.userAccountControl = $flag;
$NewUser.InvokeSet("AccountDisabled","False") #Enables Account
$NewUser.SetInfo();
return $true;
}
Catch {
return $false;
}
}
And I'm calling it using the following syntax:
$CreateUserResults = CreateUser -FirstName $User_FirstName -LastName $User_LastName -EmailAddress $User_EmailAddress -ParentDN $User_ParentOU -Password $User_Password -Username $User_SamAccountName
Any advise or direction would be appreciated.
Thanks,
Ryan
I'm not where I can test, but I suspect those setinfo() methods are returning data that needs to be redirected to $null to prevent it from being returned by the function.

Calling a local function from a dot sourced file

I have a main script that I am running. What it does is read through a directory filled with other powershell scripts, dot includes them all and runs a predefined method in each made up of the first portion of the dot delimited file name. Example:
Run master.ps1
Master.ps1 dot sources .\resource\sub.ps1
Sub.ps1 has defined a function called 'dosub'
Master.ps1 runs 'dosub' using Invoke-Expression
Also defined in sub.ps1 is the function 'saysomething'. Implemented in'dosub' is a call to 'saysomething'.
My problem is I keep getting the error:
The term 'saysomething' 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.
Why can't the method 'dosub' find the method 'saysomething' which is defined in the same file?
master.ps1:
$handlersDir = "handlers"
$handlers = #(Get-ChildItem $handlersDir)
foreach ( $handler in $handlers ) {
. .\$handlersDir\$handler
$fnParts = $handler.Name.split(".")
$exp = "do" + $fnParts[0]
Invoke-Expression $exp
}
sub.ps1:
function saysomething() {
Write-Host "I'm here to say something!"
}
function dosub() {
saysomething
Write-Host "In dosub!"
}
Your code works on my system. However you can simplify it a bit:
$handlersDir = "handlers"
$handlers = #(Get-ChildItem $handlersDir)
foreach ( $handler in $handlers )
{
. .\$handlersDir\$handler
$exp = "do" + $handler.BaseName
Write-Host "Calling $exp"
& $exp
}
Note the availability of the BaseName property. You also don't need to use Invoke-Expression. You can just call the named command ysing the call (&) operator.
What you have given works as needed. You probably don't have the directories etc proper on your machine. Or you are running something else and posting a different ( working!) code here.
You can also make following corrections:
. .\$handlersDir\$handler
instead of above you can do:
. $handler.fullname
Instead the splitting of the filename you can do:
$exp = "do" + $handler.basename