I am trying to create a powershell 'AccessObject' comobject for my MS Access app. Basically, I will trying to create a powershell script that gets queries in a database and the tables and/or queries a particular query depends on. To do that i will need to have an instance of the MS Access 'AccessObject' and 'DependencyInfo' classes in my powershell script. I have attached a snippet of the function i intend to use. This is not the complete function, please note. All i want is to know how to create an instance of the DependencyInfo and AccessObjects in powershell.
function getQueries([string] $database)
{
$dbEng = New-Object -ComObject DAO.DBEngine.120
$AccessApp= New-Object -ComObject Access.Application
$Dependency = $AccessApp.DependencyInfo
$AccessObject=$AccessApp.AccessObjects
...
}
All i want is to know how to create an instance of the DependencyInfo
and AccessObjects in powershell.
The following creates a new Access process, opens a local accdb file, and retrieves the dependencies for a given form:
$db = new-object -ComObject 'Access.Application'
$db.OpenCurrentDatabase('C:\temp\deezNutz.accdb')
$dependency_info = $db.Application.CurrentProject().AllForms('frm_person').GetDependencyInfo()
foreach ($dependency in $dependency_info.Dependencies) { $dependency.FullName }
$db.CloseCurrentDatabase()
$db.Application.DoCmd.Quit()
If you're trying to pro grammatically manipulate the objects in a Microsoft Access database e.g., forms, reports, queries, etc. Your best bet is to search for solutions using VBA then convert those to Powershell. For this example, I first wrote the solution in VBA then converted it to Powershell.
Thanks #Lord Adam. This was really helpful. In my case i had to modify the logic a little bit:
$AccessApp= New-Object -ComObject 'Access.Application'
$AccessApp.OpenCurrentDatabase($database)
$AccessApp.Application.SetOption("Track Name AutoCorrect Info", $true)
$QryDependency = $AccessApp.Application.CurrentData.AllQueries.Item($query.Name).GetDependencyInfo()
ForEach($di in $QryDependency.Dependencies)
{
$QryObjects= $QryObjects + $di.Name +","
}
Related
I've written a Powershell script which connects to our TFS server, creates a workspace, downloads the latest source and performs a nightly build and release.
The issue I have is that it always connects with my own credentials, and from what I've read this is because I'm logged onto the machine as me. I've had a new domain user account created and we've given this admin permissions within TFS, however I'm having trouble making the script use these credentials.
Here's the part of the script which deals with the initial connection and workspace creation as it currently stands:
$subfolder = [System.Guid]::NewGuid().ToString()
$tfsServer = "http://tfsserver:8080/tfs/xyz"
$tfsCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($tfsServer)
$tfsVersionCtrlType = [Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer]
$tfsVersionCtrl = $tfsCollection.GetService([type] $tfsVersionCtrlType)
$tfsWorkspace = $tfsVersionCtrl.CreateWorkspace($subfolder, $tfsVersionCtrl.AuthenticatedUser)
For completeness, here's the rest of the "Get" logic:
$tfsWorkspace.Map($serverLocation, $localLocation)
$recursion = [Microsoft.TeamFoundation.VersionControl.Client.RecursionType]::Full
$versionSpec = [Microsoft.TeamFoundation.VersionControl.Client.VersionSpec]::Latest
$itemSpec = New-Object Microsoft.TeamFoundation.VersionControl.Client.ItemSpec($serverLocation, $recursion)
$fileRequest = New-Object Microsoft.TeamFoundation.VersionControl.Client.GetRequest($itemSpec, $versionSpec)
$getStatus = $tfsWorkspace.Get($fileRequest, [Microsoft.TeamFoundation.VersionControl.Client.GetOptions]::Overwrite)
The main problem I have when working with TFS via Powershell is the fact Microsoft keep changing their implementation, in some cases quite radically, and what we're left with is scraps of code posts and documentation littered around the internet (including here on SO) which refer to old addins and other now obsolete references which take you down a myriad of wrong paths.
Anyway, so I've had a play around with trying to create Windows credentials, PSCredentials, and the like (which don't seem to be accepted by anything), the old methods to supply an ICredential are now obsolete and I'm really not sure where to turn.
Basically, I just want to create a workspace, check out items, update files and check them back in - all as our new "tfsService" user account. Please help...
UPDATE:
Based on the answer from #Nick, I needed to make the following changes. Note the use of [System.Uri] which was required to get this working for me (not sure if that's a quirk of my setup as others didn't seem to require this). Also, I needed to put the constructor call for TfsTeamProjectCollection all on one line, as splitting it onto separate lines, as per Nick's example, wouldn't work for me either.
$cred = New-Object System.Net.NetworkCredential("username", "password", "domain")
$tfsCollection = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection([System.Uri]$tfsServer, $cred)
#$tfsCollection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($tfsServer)
$tfsVersionCtrlType = [Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer]
$tfsVersionCtrl = $tfsCollection.GetService([type] $tfsVersionCtrlType)
$tfsWorkspace = $tfsVersionCtrl.CreateWorkspace($subfolder, $tfsVersionCtrl.AuthenticatedUser)
I cannot test this effectively but this seems to be a common answer on SO for this issue.
$cred = New-Object NetworkCredential("myuser", "myPassword", "mydomain")
$tfsCollection = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection
(
$tfsServer,
$cred
)
I have a question about Outlook Rules in Powershell. I wrote some code that successfully stores any incoming e-mail from a certain sender to the deleted-items folder. I did this because when the mails enter the junk folder, the junk folder still has the counter token of mails, so in the end it will say I have 10-mails in the junk folder.
I want to avoid this by just throwing the incoming mails from that sender to the deleted-items folder and also marking the mail as "read" so that I don't see the clutter in the deleted items folder.
The question is really:
Can I add multiple actions to the same outlook rule in powershell? if so, how?
What is the syntax / code for the "run script" action?
My code so far:
$ol = New-Object -ComObject Outlook.Application
$ns = $ol.GetNamespace("MAPI")
$olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders"
$outlook = New-Object -ComObject outlook.application
$namespace = $Outlook.GetNameSpace("MAPI")
$inBox = $ns.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox)
$deleted = $ns.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderDeletedItems)
$rules = $outlook.session.DefaultStore.GetRules()
$rule = $rules.create("Move mail: to DeletedItems", [Microsoft.Office.Interop.Outlook.OlRuleType]::olRuleReceive)
$rule_Address = $rule.Conditions.SenderAddress
$rule_Address.Enabled = $true
$rule_Address.Address = #("<Sender Address>")
$action = $rule.Actions.MoveToFolder
$action.Enabled = $true
[Microsoft.Office.Interop.Outlook._MoveOrCopyRuleAction].InvokeMember("Folder",[System.Reflection.BindingFlags]::SetProperty,$null, $action, $deleted)
$rules.Save()
This code works so far.
Please help.
Thanks!
Can I add multiple actions to the same outlook rule in powershell? if so, how?
Took a bit but I got a working test that uses multiple actions applied to a single rule. It is actually easy and you just need to repeat the steps you have already done and create a different action variable.
In my example, just showing the end of the code, I have added a action to display a message in the New Item Alert window.
...
$action = $rule.Actions.MoveToFolder
$action.Enabled = $true
$anotherAction = $rule.Actions.NewItemAlert
$anotherAction.Text = "I am awesome!"
$anotherAction.Enabled = $true
[Microsoft.Office.Interop.Outlook._MoveOrCopyRuleAction].InvokeMember("Folder",[System.Reflection.BindingFlags]::SetProperty,$null, $action, $deleted)
$rules.Save()
You quite possibly already tried something like this. If not there is an important reference for this that you need to be aware of.
What is the syntax / code for the "run script" action?
This is one of the actions you cannot programatically set as per this reference for Office 2007 or this one for Office 2010/2013. The tables are similar and rather large to include here but I will reference the one in your 2nd bullet.
Action : Start a script
Constant in olRuleActionType : olRuleActionRunScript
Supported when creating new rules programmatically? : No
Apply to olRuleReceive rules? : Yes
Apply to olRuleSend rules? : No
There are others as well where you are restricted. So you need to keep that in mind when you are making your rules.
I am using the following script to convery a directory full of txt and rtf documents to Word documents.
$global:word = new-object -comobject word.application
$word.Visible = $False
$srcfiles = \\Path\to\files
$savepath = \\Path\to\docfiles
$saveFormatDoc = [Enum]::Parse([Microsoft.Office.Interop.Word.WdSaveFormat], 0);
function saveas-document ($docs) {
"Opening $($docs.FullName)"
$opendoc = $word.documents.open($docs.FullName)
$savepath = "$docPath$($docs.BaseName)"
"Converting to $savepath.doc"
$opendoc.saveas([ref]"$savepath", [ref]$saveFormatDoc)
}
ForEach ($doc in $srcfiles) {
saveas-document -docs $doc
}
With the way it's coded right now, I think it's opening a new instance of Word, opening the rtf/txt document in Word, saving as a Word doc and repeating. I'm not quitting Word (unless there's an implicit $word.quit() in there somewhere. I fear I'm killing performance with multiple instances of Word, but don't know if that's a concern I need to have or not.
What's a more efficient way of coding this to open a single instance of Word for document handling, and what's the syntax for opening Word as an application rather than opening a series of documents that launch the Application based on their file associations (which is what I fear it's doing now)?
Your New-Object is outside your loop so you're only using the one instance. When you assign the New-Object to a variable (the object reference) and reuse it, that same object get's reused unless you destroy it or create another. Your script, in it's current form, is not creating a new object for each $doc in $srcfiles as you might be thinking.
With COM Objects, you are not launching the application, or dozens of them. You are opening a binary interface to libraries
You don't need or want to open a Com Object over and over, as that would cause a delay and performance hit.
You should close the Com-Object ($word.Quit()) at the end of the script though.
I would like to leverage the following
$wi=new-object -com WindowsInstaller.Installer
If I do a $wi |gm I do not see the method I want "Products".
I would like to iterate Products and show a list of all items installed on the system.
So I thought... let me do a $wi.gettype().invokemember
Not really sure what to do $wi.gettype().invokemember("Products","InvokeMethod")
or something yields cannot find an overload...
But now I am lost. I have looked elsewhere but I don't want to create a whole XML file. I should be able to access the com objects methods.
If you are trying to get a list of installed programs in Windows, there is a native Powershell way, which is actually using WMI behind the scenes:
Get-WmiObject Win32_Product
Here's a related article from Microsoft Scripting Guys.
It appears that this approach has some issues, so better be avoided.
When you query this class, the way the provider works is that it
actually performs a Windows Installer “reconfiguration” on every MSI
package on the system as its performing the query!
I tried my best to find a solution that involves WindowsInstaller com object, but all of them point to an article that no longer exists. Here is one on stackoverflow.
An alternative solution is to give a try to psmsi on CodePlex.
Here my code-snippet for this:
cls
$msi = New-Object -ComObject WindowsInstaller.Installer
$prodList = foreach ($p in $msi.Products()) {
try {
$name = $msi.ProductInfo($p, 'ProductName')
$ver = $msi.ProductInfo($p, 'VersionString')
$guid = $p
[tuple]::Create($name, $ver, $guid)
} catch {}
}
$prodlist
I have a series of Word documents which link to templates which no longer exist. This is causing problems for users trying to open them. I can get a list of the documents, loop through each one, and set the tempalte to null. While this will solve the problem, I can't determine what the template was before I changed it.
In cases where the template is not available on open, Word will replace the attached template with Normal.dot(x). However, the template I'm trying find is located in the document's Tempaltes dialog. Both AttachedTempalte() and get_AttachedTemplate().Name return Normal.dot when I know the document in question has a different template listed in the Templates dialog in word.
I can access this in VBA, and it's fustrating to not be able to do this in PS. Can anyone see where I'm messing up?
$word = new-object -comobject "Word.Application"
$doc = $word.Documents.Open({document path})
$word.Dialogs(Microsoft.Office.Interop.Word.WdWordDialog.wdDialogToolsTemplates).Template()
Returns:
Missing ')' in method call.
At :line:1 char:15
+ $word.Dialogs(M <<<< icrosoft.Office.Interop.Word.WdWordDialog.wdDialogToolsTemplates).Template()
Working VBA:
Dim doc as Word.Document
Dim strTemplate as String
Set doc = Documents.Open(Filename:=filename, Visible:=False)
doc.Activate
strTemplate = Word.Dialogs(wdDialogsToolsTemplates).Template
After which I can see the template name and path I should see in strTemplate.
I checked the ps script and adding $doc.Activate doesn't seem to help. I also noticed that the interop and VBA do not use the same wdDialog. PS uses wdDialogToolsTemplates and VBA using wdDialogsToolsTemplates. I checked the assembly in PS with the following
[Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Interop.Word") | out-null
[Enum]::GetNames("Microsoft.Office.Interop.Word.WdDialogs")
and confirmed the correct option is wdDialogToolsTemplates.
In powershell you must use the [] brackets to specify types and then the :: to specify the type member, so your 3rd line of powershell code should look like this:
$word.Dialogs([Microsoft.Office.Interop.Word.WdWordDialog]::wdDialogToolsTemplates).Template()
See these blog posts about powershell enums: Jeffrey Snover or Richard Siddaway.
I am trying to do something similar and the main aim is not to store any code inside a Word document.
PowerShell
I made a tiny bit of progress with the PowerShell route but I can't find a way to extract the path from the dialog boxes.
$objWord = New-Object -ComObject "Word.Application"
$objWord.Visible = $True
$objDoc = $objWord.Documents.Open("C:\path\to\document.doc")
$objToolsTemplates = $objWord.Dialogs.Item([Microsoft.Office.Interop.Word.WdWordDialog]::wdDialogToolsTemplates)
$objDocStats = $objWord.Dialogs.Item([Microsoft.Office.Interop.Word.WdWordDialog]::wdDialogDocumentStatistics)
Now $objToolsTemplates.Show() and $objToolsTemplates.Show() both cause the GUI to display dialogs containing the path to the original (unavailable) template but I can't find any way to extract that information programatically. According to MSDN WdWordDialog Enumeration documentation both of the dialogs should have a Template property.
VBScript
In the end I had to go with VBS instead. The following code will give you the path of the original (unavailable) template. It's still a script at least.
Option Explicit
Const wdDialogToolsTemplates = 87
Const wdDoNotSaveChanges = 0
Dim objWordApp
Dim objDoc
Dim dlgTemplate
Set objWordApp = CreateObject("Word.Application")
objWordApp.Visible = False
Set objDoc = objWordApp.Documents.Open("C:\path\to\document.doc")
Set dlgTemplate = objWordApp.Dialogs(wdDialogToolsTemplates)
Wscript.Echo dlgTemplate.Template
objDoc.Close(wdDoNotSaveChanges)
objWordApp.Quit