I'm doing some scripting in PowerShell involving sending some mail automatically. I'm aware that the olMailItem object (on 2003 at least) has a couple of methods, Display() and Close() but is it possible to get the current visibility status?
If I run the following:
$Outlook = New-Object -ComObject Outlook.Application
$Mail1 = $Outlook.CreateItem(0)
$Mail1.To = $_.UserID
$Mail1.SentOnBehalfOfName = "me#mydomain.com"
$Mail1.Subject = $Subject1
$Mail1.Body = $BodyText1
$Mail1.Display()
$a = $Mail1
$Mail1.Close()
$b = $Mail1
I can't see any difference between $a and $b
What I was hoping for was a $Mail.IsVisible bool property or something.
Can it be done?
You can loop through the Application.Inspectors collection and compare each Inspector.CurrentItem.EntryId with MailItem.EntryId property of the item in question. If it is you who create the item and do not save it first, how can be opened in one of the Inspectors? You are the only one who can show it.
Related
Basically I'm trying to create a ContextMenu that shows a list of mounted network drive, so the user can click on one and access it.
The problem is : the list is not fix, but depend on a string list extracted from a DataGrid.
In my mind, I could just create the menu items by looping on that string list.
Here is the code sample :
$Main_Tool_Icon = New-Object System.Windows.Forms
[...]
# Add menu entries
$contextmenu = New-Object System.Windows.Forms.ContextMenu
$Main_Tool_Icon.ContextMenu = $contextmenu
foreach ($nameTag in $dataGrid[$DATAGRID_COLUMN_NAME]){
$menuEntry = New-Object System.Windows.Forms.MenuItem
$menuEntry.Text = $nameTag
$Main_Tool_Icon.ContextMenu.MenuItems.AddRange($menuEntry)
}
# Add separator
$Main_Tool_Icon.ContextMenu.MenuItems.Add('-')
# Last one to show main form
$displayMainform = New-Object System.Windows.Forms.MenuItem
$displayMainform.Text = "Edit"
$Main_Tool_Icon.ContextMenu.MenuItems.Add($displayMainform)
It appears that's wrong and only shows the separator and last "Edit" line.
Is there a way to populate a contextual menu with a list ?
Or I should look for a better way to do the job ?
EDIT #Mathias R. Jessen
You're right about AddRange(), it's just a mistake.
I can't show everything since it's quite big. I'll try to synthesize an answer properly.
The data grid is created/populated by my script and is just a table of strings from a csv file.
$DATAGRID_COLUMN_NAME = 'NAME' it's one of constants defined for my datagrid headers.
Here is the setup
# set datagrid
$dataGrid = New-Object System.Windows.Forms.DataGridView
[...]
$dataGrid.Columns.Add($DATAGRID_COLUMN_NAME,'Name')
$dataGrid.Columns[0].DataPropertyName = $dataGrid.Columns[0].Name
Then some databinding
# initiate the data table
$table = New-Object System.Data.DataTable
[...]loop on csv file[...]
# bind data table to data grid
$dataGrid.DataSource = $table
EDIT #SantiagoSquarzon
You were right for pointing out $dataGrid[$DATAGRID_COLUMN_NAME]. The syntax was bad and returned something null. Instead, I looped over the $dataGrid.Rows like so:
foreach ($row in $dataGrid.Rows){
$nameTag = $row.Cells[$DATAGRID_COLUMN_NAME].Value
if ($nameTag -ne $null){
$menuEntry = New-Object System.Windows.Forms.MenuItem
$menuEntry.Text = $nameTag
$Main_Tool_Icon.ContextMenu.MenuItems.AddRange($menuEntry)
}
}
Now I just have to figure out how to invoke there click event.
I have the following NotifyIcon running using PowerShell:
This is the context menu opened by right clicking the icon, it's just showing exit at the moment:
I would like to know how I can add two event handlers:
Run a function when there is a left click event on the icon
Add another option to the right click menu that also runs a function
I've been searching the web for over an hour and I have tried about 20 different variations using old code from 5 or 6 different websites (all showing wildly different examples). I have gained nothing but a headache. Can anyone offer any guidance/direction?
$ProgramDataPath = "$ENV:ProgramData\test"
$ProgramFilesPath = "${env:ProgramFiles(x86)}\test"
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$STForm = New-Object System.Windows.Forms.form
$NotifyIcon = New-Object System.Windows.Forms.NotifyIcon
$ContextMenu = New-Object System.Windows.Forms.ContextMenu
$MenuItem = New-Object System.Windows.Forms.MenuItem
$Timer = New-Object System.Windows.Forms.Timer
$HealthyIcon = New-Object System.Drawing.Icon("$ProgramFilesPath\icons\healthy.ico")
$UnhealthyIcon = New-Object System.Drawing.Icon("$ProgramFilesPath\icons\unhealthy.ico")
$STForm.ShowInTaskbar = $false
$STForm.WindowState = "minimized"
$NotifyIcon.Icon = $HealthyIcon
$NotifyIcon.ContextMenu = $ContextMenu
$NotifyIcon.ContextMenu.MenuItems.AddRange($MenuItem)
$NotifyIcon.Visible = $True
# We need to avoid using Start-Sleep as this freezes the GUI. Instead, we'll utilitse the .NET forms timer, it repeats a function at a set interval.
$Timer.Interval = 300000 # (5 min)
$Timer.add_Tick({ Load-Config })
$Timer.start()
# This will appear as a right click option on the system tray icon
$MenuItem.Index = 0
$MenuItem.Text = "Exit"
$MenuItem.add_Click({
$Timer.Stop()
$NotifyIcon.Visible = $False
$STForm.close()
})
function Load-Config
{
#Get-Content some Data from a file here
if ($warn)
{
$NotifyIcon.Icon = $UnhealthyIcon
$NotifyIcon.ShowBalloonTip(30000, "Attention!", "Some data from a file here...", [system.windows.forms.ToolTipIcon]"Warning")
Remove-Variable warn
}
else
{
$NotifyIcon.Icon = $HealthyIcon
}
}
Load-Config
[void][System.Windows.Forms.Application]::Run($STForm)
Lets talk about what you really need. It looks like you have alot of unneeded parts like a timer and so on. All you need is a runspace. A Open Form will keep the runspace open without the need for that timer. Make sure the $Form.ShowDialog() is the last thing run.
So lets move on to the NotifyIcon popup. The method that makes that popup happen is private, which means we will need to reach it through reflection. We will also need to set the event for the Notify Icon to run on MouseDown as well as get the button clicked $_.button
Make sure you set the $NotifyIcon.Icon to a Icon or else the Notify Icon wont show up.
Working Script
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object System.Windows.Forms.Form
$form.ShowInTaskbar = $true
$form.WindowState = [System.Windows.WindowState]::Normal
$MenuItemLeft = New-Object System.Windows.Forms.MenuItem
$MenuItemLeft.Text = "Left Exit"
$MenuItemLeft.add_Click({
$NotifyIcon.Visible = $False
$form.Close()
$NotifyIcon.Dispose()
})
$ContextMenuLeft = New-Object System.Windows.Forms.ContextMenu
$ContextMenuLeft.MenuItems.Add($MenuItemLeft)
$MenuItemRight = New-Object System.Windows.Forms.MenuItem
$MenuItemRight.Text = "Right Exit"
$MenuItemRight.add_Click({
$NotifyIcon.Visible = $False
$form.Close()
$NotifyIcon.Dispose()
})
$ContextMenuRight = New-Object System.Windows.Forms.ContextMenu
$ContextMenuRight.MenuItems.Add($MenuItemRight)
$NotifyIcon= New-Object System.Windows.Forms.NotifyIcon
$NotifyIcon.Icon = "C:\Test\Test.ico"
$NotifyIcon.ContextMenu = $ContextMenuRight
$NotifyIcon.add_MouseDown({
if ($_.Button -eq [System.Windows.Forms.MouseButtons]::left ) {
$NotifyIcon.contextMenu = $ContextMenuLeft
}else{
$NotifyIcon.contextMenu = $ContextMenuRight
}
$NotifyIcon.GetType().GetMethod("ShowContextMenu",[System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic).Invoke($NotifyIcon,$null)
})
$NotifyIcon.Visible = $True
$form.ShowDialog()
$NotifyIcon.Dispose()
I Reread your post so ill provide you with the most important parts
For Powershell to run your commands a runspace must be active. A Runspace takes powershell commands and turns them into real actions.
Since you went with powershell for this then the notifyicons actions are dependent on a runspace to interpret those actions.
A NotifyIcon is basically just a icon in the corner that can popup a balloon notification or Context Menu.
So when you look you will see $NotifyIcon.ContextMenu that is a property that holds a ContextMenu Object. A Context Menu Object contains Menu Items.
So just add MenuItems to a ContextMenu Object and then add that ContextMenu Object to $NotifyIcon.ContextMenu. Now you can change and add all the items you like.
Since powershell waits for the form to close before moving on to the next line of code the $Form.ShowDialog() will keep the runspace alive until the form is exited.
Lets look at this nasty mess :
$NotifyIcon.GetType().GetMethod("ShowContextMenu",[System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic).Invoke($NotifyIcon,$null)
This is called reflection. It allows you to interact with a class. In easier terms. ShowContextMenu method is private and cant be run normally from outside the internal workings of the class. Using reflection you can call it anyways.
So lets break it down just a little more since this is really what you asked about.
GetType() will get you what the object is. If i did "HEY".gettype() it would tell me that this object was a string. In this case $NotifyIcon.GetType() is telling me that this is a NotifyIcon. Whats happening its its bring me back a Type Class.
In this we see GetMethod("ShowContextMenu") but let me dig a little deeper here... How did we know there was a method called ShowContextMenu. Well what we can do is view all the members of this NotifyIcon class by using GetMembers(). Now GetMembers() is really just a search... by default only searches for public Members so we needs to search all Members. The parameters of what to search for is in an enum [System.Reflection.BindingFlags] and some Bitwise math.
$BitWise
[System.Reflection.BindingFlags].GetEnumNames() | %{
$BitWise = $BitWise -bor [System.Reflection.BindingFlags]$_
} | out-null
$NotifyIcon.GetType().GetMembers($BitWise) | ?{$_.Name -like "*Context*"} | select Name, MemberType
This says find all items that contain the word Context in its name and display its Full name and what Type it is. In response we get
Name MemberType
---- ----------
set_ContextMenu Method
get_ContextMenu Method
get_ContextMenuStrip Method
set_ContextMenuStrip Method
ShowContextMenu Method
ContextMenu Property
ContextMenuStrip Property
contextMenu Field
contextMenuStrip Field
We can see ShowContextMenu and we can also see its a method
So now we need to get that method directly. Here comes getMethod() which is just another search that brings back 1 item instead of all the items. So
GetMethod("ShowContextMenu",[System.Reflection.BindingFlags]::Instance -bor [System.Reflection.BindingFlags]::NonPublic)
Get Method ShowContextMenu it will be Private so NonPublic and a instance of the class has to be created before it can run so Instance.
.Invoke($NotifyIcon,$null)
Then we invoke the method by telling it which control has the method we want to run and pass any parameters which are none so $null.
And thats how you do it.
I've gathered some code from around the web to create Contacts and then Contact groups. However, if I update the contact after creation, the "relation" between the contact object inside the Contact group and the Contact is gone. The Contact group is not updated with the changes to the Contact.
If I manually create a Contact and Contact group, the relationship is just maintained as expected. Any ideas on what I could have missed?
Code for the Contact:
$olContactItem = 2
$o = new-object -comobject outlook.application
$c = $o.CreateItem($olContactItem)
$c.FullName = "Dummy Account"
$c.Email1Address = "aa#bb.com"
$a = $c.Save()
Code for the Contact group:
$outlook = new-object -com Outlook.Application
$contacts = $outlook.Session.GetDefaultFolder(10)
$session = $outlook.Session
$session.Logon("Outlook")
$namespace = $outlook.GetNamespace("MAPI")
$DL = $contacts.Items.Add("IPM.DistList")
$DL.DLName = "dummy2"
$recipient = $namespace.CreateRecipient("Dummy Account")
$recipient.Resolve()
$DL.AddMember($recipient)
$DL.Save()
Looks pretty straight forward to me. I checked the API, but that didn't get me much further.
https://msdn.microsoft.com/en-us/vba/outlook-vba/articles/recipients-object-outlook
Thanks in advance!
You add $recipient before it is initialized.
UPDATE: DistListItem.AddMember in OOM only adds one-off recipients, there is no way to add contacts. If using Redemption (I am its author) is an option, it exposes RDODistListItem.AddContact method that allows to pass either Outlook's ContactItem object or RDOContactItem object from Redemption. RDODistListItem also exposes AddMembers / AddMember / AddMemberEx methods.
I am currently trying to write a script that will go through a message queue and delete/send to another subqueue each message if a certain string is in the body.
In Pseudo code I am currently trying to implement
$queue = 'My Queue'
foreach($message in $queue)
{
if ($message.body.Contains("matchstring")
{
$message.delete OR $message.movequeue
}
}
I am currently using [Reflection.Assembly]::LoadWithPartialName("System.Messaging") in powershell but I am not sure if it contains all the functionality I need.
You can do this in PowerShell. Here's some simple code to get you started and connected to a queue:
$queuePath = ("Direct=OS:SERVER\private$\PRIVATEQUEUE")
$queue = New-Object System.Messaging.MessageQueue $queuePath
$queue.GetAllMessages()
Use $queue | gm and the MSDN to get you where you need to be.
I want to add values to a PropertyBag.
How is it possible to check if the value is already in the PropertyBag?
I know one can use an array, list, etc. But how can I use the $bag/$api object to do this check?
$api = New-Object -comObject “MOM.ScriptAPI”
$bag = $api.CreatePropertyBag()
$bag.AddValue("TestValue1","1234")
I'm searching for something like this:
if($bag -match "TestValue1")
{"In the Bag!"}
But, unfortunately, it's not working.
I do not have SCOM on a server I can access, but could you do the following to get the bag contents and check against it?
$api = New-Object -comObject “MOM.ScriptAPI”
$bag = $api.CreatePropertyBag()
$bagContents = $api.Return($bag)