Custom PowerShell Snapin: custom format doesn't work - c#

I used this article to write my first own Powershell Cmdlet and Snapin. And it works fine.
But I return a set of objects from my own data class, which has four properties and I want Powershell to display just one of these properties by default. So I used this part of the article to create this format file:
<?xml version="1.0" encoding="utf-8" ?>
<Configuration>
<ViewDefinitions>
<View>
<Name>RemoteFile</Name>
<ViewSelectedBy>
<TypeName>MyFullNamespace.RemoteFileData</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader />
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem>
<PropertyName>Filename</PropertyName>
</TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
</ViewDefinitions>
</Configuration>
and link it in the Snapin:
public override string[] Formats
{
get { return new string[] { "MyFormatFilename.ps1xml" }; }
}
But when I install the Snapin with installutil, use Add-PSSnapin and call my Cmdlet, all Properties of the objects are displayed.
What am I doing wrong?

Everything looks correct except that I'm not sure how it behaves with no column header label defined. Try adding this node instead of your empty one:
<TableColumnHeader>
<Label>FileName</Label>
</TableColumnHeader>
Also make sure the file MyFormatFilename.ps1xml is in the same dir with the snapin when it is being loaded via Add-PSSnapin. Also, probably a duh, but double check for typos in the type name specified in the <TypeName> element.
Update: I tried your XML as listed above and it works for me. I copied it into Notepad2 and saved it to C:\temp\test.ps1xml then executed:
1# $obj = new-object psobject
2# $obj.psobject.TypeNames.Insert(0, 'MyFullNamespace.RemoteFileData')
3# Add-Member -InputObject $obj -MemberType NoteProperty -Name Filename `
-Value 'some-remotefile.txt'
4# Add-Member -InputObject $obj -MemberType NoteProperty -Name Dummy `
-Value 'dummy prop'
5# $obj.psobject.TypeNames
MyFullNamespace.RemoteFileData
System.Management.Automation.PSCustomObject
System.Object
6# $obj
Filename Dummy
-------- -----
some-remotefile.txt dummy prop
7# Update-FormatData C:\temp\test.ps1xml
8# $obj
Filename
--------
some-remotefile.txt
I would double check the full typename instance.GetType().FullName and also double check the contents of the format file. Make sure it is in the same dir that you registered the snapin from.

Related

When creating a virtual directory and converting it to a WebApplication, why does the output show two entries

I created a PowerShell script to create a Virtual Directory and convert it to a WebApplication, with an existing physical path.
It works but I find it strange that the output shows two entries, when converting it to a WebApplication.
Name PhysicalPath
---- ------------
v2 E:\inetpub\MyWebsite\v2
v2 E:\inetpub\MyWebsite\v2
I tried different ways of creating the Virtual Folder and the WebApplication.
function Create-VirtualDirectory-With-AppPool {
Param (
[string] $WebSiteName,
[string] $VirtualFolderName,
[string] $AppPoolName
)
$PhysicalPathWebSite = (Get-Website -Name $WebSiteName | Select-Object).PhysicalPath
$PhysicalPathVirtDir = "$PhysicalPathWebSite\$VirtualFolderName"
Write-Host "The following will be created:"
Write-Host "- Virtual folder: $WebSiteName\$VirtualFolderName with physical path: $PhysicalPathVirtDir"
Write-Host "- Application: IIS:\Sites\$WebSiteName\$VirtualFolderName"
Read-Host "Press enter to continue (Ctrl-c to abort)"
New-WebVirtualDirectory -Site $WebSiteName -Name $VirtualFolderName -PhysicalPath $PhysicalPathVirtDir
# Other attempt, same result:
#New-Item "IIS:\Sites\$WebSiteName\$VirtualFolderName" -type VirtualDirectory -physicalPath $PhysicalPathVirtDir
ConvertTo-WebApplication "IIS:\Sites\$WebSiteName\$VirtualFolderName"
# Other attempt, same result:
#New-Item "IIS:\Sites\$WebSiteName\$VirtualFolderName" -type Application -physicalPath $PhysicalPathVirtDir
# Set AppPool
Set-ItemProperty "IIS:\Sites\$WebSiteName\$VirtualFolderName" -name applicationPool -value $AppPoolName
Write-Host "Done."
}
Am I doing something wrong?
Is there some caching?
It is indeed as Mathias mentioned.
When playing around with piping to | Out-Null for both the Virtual directory and Converting to a WebApplication, I saw the separate outputs.
So, there are two options: discard the output or place the output in a variable and add some text. I choose the last one.
function Create-VirtualDirectory-With-AppPool {
Param (
[string] $WebSiteName,
[string] $VirtualFolderName,
[string] $AppPoolName
)
$PhysicalPathWebSite = (Get-Website -Name $WebSiteName | Select-Object).PhysicalPath
$PhysicalPathVirtDir = "$PhysicalPathWebSite\$VirtualFolderName"
Write-Host "The following will be created:"
Write-Host "- Virtual folder: $WebSiteName\$VirtualFolderName with physical path: $PhysicalPathVirtDir"
Write-Host "- Application: IIS:\Sites\$WebSiteName\$VirtualFolderName"
Read-Host "Press enter to continue (Ctrl-c to abort)"
$OutputNewvDir = New-WebVirtualDirectory -Site $WebSiteName -Name $VirtualFolderName -PhysicalPath $PhysicalPathVirtDir
$OutputConvertApp = ConvertTo-WebApplication "IIS:\Sites\$WebSiteName\$VirtualFolderName"
# Set AppPool
Set-ItemProperty "IIS:\Sites\$WebSiteName\$VirtualFolderName" -name applicationPool -value $AppPoolName
Write-Host "New Virtual directory: $($OutputNewvDir | Format-List | Out-String)"
Write-Host "Convert to Application: $($OutputConvertApp | Format-List | Out-String)"
Write-Host "Done."
}
New Virtual directory:
Name : V2
PhysicalPath : C:\inetpub\wwwroot\MyWebsite\V2
Convert to Application:
Path : /V2
ApplicationPool : DefaultAppPool
EnabledProtocols : http
PhysicalPath : C:\inetpub\wwwroot\MyWebsite\V2
One strange thing is that both commands have a different number of parameters, but some are shown when output together (as shown in my original question).
But I can live with that, will research that later.

Unable to register dll gac using powershell or without gacutil in windows server 2012

Please can you any help me ,why powershell keeps throwing below error message
PS C:\Users\Administrator\Desktop> .\Register_dll.cmd
C:\Users\Administrator\Desktop>[System.Reflection.Assembly]::Load("System.EnterpriseServices, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=7f48c3199e5dff41")
The filename, directory name, or volume label syntax is incorrect.
C:\Users\Administrator\Desktop>$publish = New-Object System.EnterpriseServices.Internal.Publish
'$publish' is not recognized as an internal or external command,
operable program or batch file.
C:\Users\Administrator\Desktop>$publish.GacInstall("C:\Users\Administrator\Desktop\Mydll.dll")
'$publish.GacInstall' is not recognized as an internal or external command,
operable program or batch file.
Below is the batch file i have ran in Powershell as Administrator and i have verified the version and publictoken .I guess that is not the issue here.
[System.Reflection.Assembly]::Load("System.EnterpriseServices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7f48c3199e5dff41")
$publish = New-Object System.EnterpriseServices.Internal.Publish
$publish.GacInstall("C:\Users\Administrator\Desktop\MyDll.dll")
Your script needs to be saved as .ps1. It currently has the extention of .cmd which is making the PowerShell call the old Command shell to execute it which then doesn't recognise the PowerShell commands.
I wrote this a while ago to allow me to remote install GAC assemblies. Save this script as a module (.psm1) or a script (.ps1), import it, and run Install-GACAssembly:
Set-Variable GAC_VS2015_ASSEMBLY_REGKEY -option ReadOnly -value "HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx"
[System.Reflection.Assembly]::Load("System.EnterpriseServices, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") | Out-Null
function Install-GACAssembly {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNull()]
[ValidateScript({ Test-Path $_ -PathType Container })]
[string] $AssemblyDir,
[Parameter()]
[switch] $DevMode,
[Parameter()]
[string] $DevRegKeyName,
[Parameter()]
[switch] $uninstall
)
$ErrorActionPreference = "Stop"
if($DevMode -and [string]::IsNullOrWhiteSpace($DevRegKeyName)) {
throw "If `$DevMode is true, a unique folder name must be specified in `$DevRegKeyName i.e. 'MyClient'"
}
if($DevMode) {
New-Item -Path $GAC_VS2015_ASSEMBLY_REGKEY -Name $DevRegKeyName -Value $AssemblyDir -Force | Out-Null
}
$installText = "$(if ($uninstall) { "un" })install"
Write-Verbose "Searching '$AssemblyDir' for GAC .dll files to $installText"
$publisher = New-Object System.EnterpriseServices.Internal.Publish
$assemblies = Get-ChildItem $AssemblyDir -Filter "*.dll"
Write-Verbose #"
Found $($assemblies.Count) .dll files:
$($assemblies.FullName)
"#
$assemblies | % {
if(!$uninstall) {
Write-Verbose " Installing $($_.FullName) into the GAC"
$output = $publisher.GacInstall($_.FullName)
} else {
Write-Verbose " Removing $($_.FullName) from the GAC"
$output = $publisher.GacRemove($_.FullName)
}
}
Write-Verbose "GAC $installText finished"
Write-Verbose "===================================="
}
If you create a module (.psm1) you have to import it into your powershell script / session using Import-Module:
PS D:\> Import-Module -Name "D:\Path-to-module\ModuleFileName.psm1" -Force
Install-GACAssembly -AssemblyDir "D:\Path-to-your-assembly\assembly-name.dll"
You can also use it to uninstall:
PS D:\> Import-Module -Name "D:\Path-to-module\ModuleFileName.psm1" -Force
Install-GACAssembly -AssemblyDir "D:\Path-to-your-assembly\assembly-name.dll" -uninstall
You can see verbose output by using the -Verbose flag on either command.
I found the solution myself for the question.
Firstly as Mark stated we need to run have file extension as .PS1 because it is a PowerShell script.
And then run below command to to successfully register the DLL.
[Reflection.Assembly]::LoadWithPartialName("System.EnterpriseServices") | Out-Null
[System.EnterpriseServices.Internal.Publish] $publish = new-object System.EnterpriseServices.Internal.Publish
$publish.GacInstall("MYdllFile.dll")

Retrieving the values of a nested Powershell object via properties in C#

C# Newbie here. I am not sure if what I'm going for is even possible.
Basically, I'm trying to read the properties of a system object array within a Powershell object in C#.
For simplicity's sake, here is a basic Powershell code that has a similar output:
$object = New-Object -TypeName PSObject
$object | Add-Member -MemberType NoteProperty -Name "Name" -Value "Yad"
$object | Add-Member -MemberType NoteProperty -Name "Title" -Value "C# Noob"
$object | Add-Member -MemberType NotePropety -Name "Machine Services" -Value (Get-Service)
return $object
On the C# side of things, I am able to fetch the results by mapping the properties as below:
var shell = PowerShell.Create();
shell.Commands.AddScript("C:\\Scripts\\Powershell\\TestScript.ps1 -user " + userName);
Collection<PSObject> results = shell.Invoke();
Profile userProfile = new Profile(); //Profile class is declared prior this line
foreach (PSObject psObject in results)
{
userProfile.Name = Convert.ToString(psObject.Properties["Name"].Value);
userProfile.Title= Convert.ToString(psObject.Properties["Title"].Value);
userProfile.MachineServices= psObject.Properties["Machine Services"].Value;
}
With this, the 'userProfile' object is equivalent to the Powershell output.
Now, the 'MachineServices' property is an object that contains its own set of properties (status, name, and displayname in Powershell). Is it possible to call these properties and retrieve their values?
I tried something similar to below but, of course, I get an error as the Powershell script is completely separate and the properties are unknown to the C# code prior runtime.
userProfile.MachineServices.Status
Any ideas?
If you investigate Get-Service in PowerShell with .GetType() you can find out that it returns an Object array, where the members are of type ServiceController.
I haven't tried it in C# but you should be able to declare Profile.MachineServices as System.ServiceProcess.ServiceController[] and then use the assignment
userProfile.MachineServices= (System.ServiceProcess.ServiceController[])(psObject.Properties["Machine Services"].Value);
Once C# knows the correct object type, you can access the properties and methods, get IntelliSense and so on.

Adding bookmark to web browser from external applications

Does anyone know if it is possible to add bookmark to web browsers (Safari, IE, FF, Chrome, Opera) from external applications ?
For IE :
You need to create a link file here :
c#
Environment.GetFolderPath(Environment.SpecialFolder.Favorites)
Powershell
[Environment]::GetFolderPath( [System.Environment+SpecialFolder]::Favorites)
Chrome:
You need to add entry in json format file bookmarks (with no extension):
on Win7 is
C:\Users\<YOURUSERNAME>\AppData\Local\Google\Chrome\User Data\Default\
Firefox:
The bookmarks are stored in a SQLite:
../Application Data/Mozilla/Firefox/Profiles/{your firefox profile}/places.sqlite
Using System.Data.SQLite you can try to add link, but I can't help you more.
Can't help you for Safari and Opera
In Powershell V2 ISE (x86), this code will list all the Special Folders on the system or even this -
$objShell = New-Object -com "Wscript.Shell"
$objShell.SpecialFolders | WHERE {$_.ToString() -match "Fav"}
You can then access & manipulate the C:\Users\username\Favorites folder. I don't know if this will extend to all browsers [except IE]
Here is the solution I came up with for adding bookmarks to Chrome from PowerShell:
$fileBookmarks="$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Bookmarks"
$dataBookmarks=Get-Content $fileBookmarks -Encoding UTF8| Out-String |ConvertFrom-Json
function createNewChromeBookmark ($Bookmarks, [string]$BookmarkName, [string]$BookmarkURL) {
function getBookmarkIDs($object){
$object | ForEach-Object{
"{0:0000}" -f [int]($_.id);
if([bool]($_.PSobject.Properties.name -match "children")){
GetBookmarkIDs($_.children);
}
}
};
$nextBookmarkID = [int](getBookmarkIDs -object $Bookmarks.roots.bookmark_bar|Sort-Object -Descending|Select-Object -First 1) + 1
$currentChomeTime=[System.DateTimeOffset]::Now.ToUnixTimeMilliseconds()*10000;
$newBookmark= [PSCustomObject]#{
date_added=$currentChomeTime
guid=[guid]::NewGuid()
id=$nextBookMarkID
name="$BookmarkName"
type="url"
url="$BookmarkURL"
}
return $newBookmark;
}
$newBookmark = createNewChromeBookmark -Bookmarks $dataBookmarks -BookmarkName "Your Bookmark Name" -BookmarkURL "https://[Your URL Here]";
$dataBookmarks.roots.bookmark_bar.children += $newBookmark;
$dataBookmarksJSON = ConvertTo-Json -InputObject $dataBookmarks -Depth 200
Set-Content -Path $fileBookmarks -Value $dataBookmarksJSON -Encoding UTF8

Why do Powershell return as null when called from C# app code?

I'm using this as a reference: http://msdn.microsoft.com/en-us/library/dd182449(v=VS.85).aspx
So the implementation is very similar. I'm executing the following PowerShell command to retrieve process information from the intended computer through the 1-liner command below:
$computer = "Remote.Computer.Here"; Get-Process -computer $computer | Sort-Object WorkingSet -desc | Select-Object -first 10 | Format-Table -property name, ID, #{Expression= {$_.WorkingSet/1mb};Label="MemoryLoad";} -auto
The command above executes perfectly in PS window. However, when called from C# app code, I get no returns. Especially so, when I access it through the following:
PowerShell shell = PowerShell.Create();
shell.AddScript(CmdletMap[PSVocab.OsProcLoad]);
Collection<PSObject> obj = shell.Invoke();
DataTable dt = new DataTable();
dt.Columns.Add("ProcessName");
dt.Columns.Add("ID");
dt.Columns.Add("MemoryLoad");
DataRow row;
foreach (PSObject resultObject in obj)
{
row = dt.NewRow();
row["ProcessName"] = resultObject.Members["name"].Value;
row["ID"] = resultObject.Members["id"].Value;
row["MemoryCol"] = resultObject.Members["MemoryLoad"].Value;
dt.Rows.Add(row);
}
Doing a quick-watch of resultObject.Members[].Value would simply return null.
Any help?
Thanks.
Inspect shell.Streams.Error to see what error is happening with your invocation of the script.
In PowerShell, the default for a failing operation is to return nothing. PowerShell has several well-known streams, and your error is either lying in the error stream ([PowerShell].Streams.Error) or it is a terminating error ([Powershell].InvocationStateInfo.Reason).
Hope this helps,
Use two slightly different commands: one for C# (and console) and another for console only.
For invoking from C# and console:
$computer = "."
Get-Process -computer $computer | Sort-Object WorkingSet -desc | Select-Object -first 10 |
Select-Object -property name, ID, #{Expression= {$_.WorkingSet/1mb};Label="MemoryLoad"}
For interactive host (i.e. console, ISE, etc.) with prettier look:
$computer = "."
Get-Process -computer $computer | Sort-Object WorkingSet -desc | Select-Object -first 10 |
Select-Object -property name, ID, #{Expression= {$_.WorkingSet/1mb};Label="MemoryLoad"} |
Format-Table -AutoSize
It is the Format-Table that makes problems in C#. Do not use it in C#. As for console, it should be the last command in the pipeline, it produces objects for printing, not for further use. Example: the first command shows two columns name and ID but the second command does not get any name properties:
Get-Process | Format-Table -property name, ID
Get-Process | Format-Table -property name, ID | Select-Object name
According to the Technet, your syntax is wrong. . .
http://technet.microsoft.com/en-us/library/dd347630.aspx
Syntax
Get-Process [[-Name] ] [-ComputerName ]
[-FileVersionInfo] [-Module] []
Get-Process -Id [-ComputerName ]
[-FileVersionInfo] [-Module] []
Get-Process -InputObject [-ComputerName ]
[-FileVersionInfo] [-Module] []
Specifically, you need to use -computername, and not computer. And I have no idea what "Remote.Computer.Here" is. . .. you can use localhost.
edit
Nm, my coworker is an idiot. I just had to swap Remote.Computer.here with . and it looks all fine and dandy. See if that works.

Categories

Resources