I need to open several objects: two sockets each with a Stream and a Writer, one pipe with...
Can I store them in hashes so that I just call a function with a hash and in that function I can access the specific socket, stream, or writer?
How do I predefine this hash-entry?
$hash = #{
...
"Socket" = ???
"Stream" = ???
"Writer" = ???
...
}
function connect {
param([hashtable] $h)
try {
$Enc = [System.Text.Encoding]::ASCII
$h.Socket = New-Object System.Net.Sockets.TcpClient $h.IP, $h.PORT
$h.Stream = $h.Socket.GetStream() # Or better: $($h.Socket).GetStream()
$h.Writer = New-Object System.IO.StreamWriter($h.Stream, $Enc)
...
}
catch { ...}
...
}
And the use of all that:
function WriteMsg {
param([hashtable]]$h, [string]]$m)
try {
$h.Writer.Write($m) #
}
catch {
Write-Host $_ #Write-Debug $_
}
}
Would that be saved to use?
PS:
For those who might be interested, this seems to work - so far:
$Conn1 = New-Object psobject -Property #{
"ID" = "Conn1"
"State" = "ok"
"IP" = "127.0.0.1"
"PORT" = 4711
"Socket" = $Null
"Stream" = $Null
"Writer" = $Null
}
function conn { # Usage: conn $Conn1
param (
[parameter(Mandatory=$true)]
[PSObject] $h
)
try {
$e = [System.Text.Encoding]::ASCII
$h.Socket = $(New-Object System.Net.Sockets.TcpClient $h.IP, $h.PORT)
if ($h.Socket.Connected) {
$h.Stream = $($h.Socket.GetStream())
$h.Writer = $(New-Object System.IO.StreamWriter( $h.Stream, $e))
$h.Writer.AutoFlush = $true
}
}
catch {
Write-Host "$($h.ID) Connection Failed $_" #Write-Debug $_
}
finally {
return
}
}
Even a re-new of the connection won't fail due to a double entry.
Related
I already have a working snippet for stamping PDF's.
But, now a request has reached me for adding an additional page to the same document.
I have managed to get it working, but I'd like to verify that it is the best way to do it.
Edit: It turns out it is not working as expected.
The fields get malformatted with a big "x" instead of the text as per below image. If I click the field I can see that the text has been input though.
Old code
$FieldsHash = ConvertFrom-StringData -StringData $Fields
$FieldsHash | Export-Clixml C:\Temp\fieldhash.xml
$reader = New-Object iTextSharp.text.pdf.PdfReader -ArgumentList $InputPdfFilePath
$stamper = New-Object iTextSharp.text.pdf.PdfStamper($reader, [System.IO.File]::Create($OutputPdfFilePath))
foreach ($j in $FieldsHash.GetEnumerator())
{
try {
$formattedDate = Get-Date $j.Value -Format $DatePattern
$null = $stamper.AcroFields.SetField($j.Key, $formattedDate)
}
catch {
try {
$null = $stamper.AcroFields.SetField($j.Key, $j.Value)
} catch {
"Could not find $($j.Key)"
}
}
}
$stamper.Close()
Modified code:
$fileStream = New-Object System.IO.FileStream($OutputPdfFilePath, [System.IO.FileMode]::OpenOrCreate)
$document = New-Object iTextSharp.text.Document
$pdfCopy = New-Object iTextSharp.text.pdf.PdfCopy($document, $fileStream)
$mainreader = New-Object iTextSharp.text.pdf.PdfReader($InputPdfFilePath)
$stamper = New-Object iTextSharp.text.pdf.PdfStamper($mainreader, $fileStream)
$document.Open()
$FieldsHash = ConvertFrom-StringData -StringData $Fields
$FieldsHash | Export-Clixml C:\Temp\pdf\fieldhash.xml
foreach ($j in $FieldsHash.GetEnumerator())
{
try {
$formattedDate = Get-Date $j.Value -Format $DatePattern
$null = $stamper.AcroFields.SetField($j.Key, $formattedDate)
} catch {
try {
$null = $stamper.AcroFields.SetField($j.Key, $j.Value)
} catch {
"Could not find $($j.Key)"
}
}
}
$pdfCopy.AddDocument($mainreader)
$mainreader.Dispose()
$attreader = New-Object iTextSharp.text.pdf.PdfReader($att)
$pdfCopy.AddDocument($attreader)
$attreader.Dispose()
$pdfCopy.Dispose()
$document.Dispose()
$fileStream.Dispose()
Solution, user stamper methods instead:
$reader = New-Object iTextSharp.text.pdf.PdfReader -ArgumentList $InputPdfFilePath
$stamper = New-Object iTextSharp.text.pdf.PdfStamper($reader, [System.IO.File]::Create($OutputPdfFilePath))
foreach ($j in $FieldsHash.GetEnumerator())
{
try {
$formattedDate = Get-Date $j.Value -Format $DatePattern
$null = $stamper.AcroFields.SetField($j.Key, $formattedDate)
} catch {
try {
$null = $stamper.AcroFields.SetField($j.Key, $j.Value)
} catch {
"Could not find $($j.Key)"
}
}
}
if(Test-Path $attpath) {
$attreader = New-Object iTextSharp.text.pdf.PdfReader($attpath)
$stamper.InsertPage(3,$attreader.GetPageSize(1))
$stamper.replacePage($attreader, 1, 3)
$stamper.Close()
$reader.Close()
$attreader.Close()
}
else {
$stamper.Close()
$reader.Close()
}
I have a third party function which creates a profile for a server. When I create an array and assign to hash that is required by third party function it is working fine, but when I dynamically create an array and assign it I am getting error.
Have tried working with simple variable that has all values and have created and array also of these values statically. But when I dynamically create it fails.
Function New-Disk
{
Param (
[parameter(Mandatory = $false)]
[Array] $XXX_drivedata
)
if ($XXX_drivedata[3] -ieq "yes")
{
$boot_data = $TRUE;
}
else
{
$boot_data = $FALSE;
}
if ($XXX_drivedata[9] -ieq "yes")
{
$erase_data = $TRUE;
}
else
{
$erase_data = $FALSE;
}
$params1 = #{
name = $XXX_drivedata[0];
RAID = $XXX_drivedata[1];
numberofDrives = $XXX_drivedata[2];
driveType = $XXX_drivedata[5];
driveSelectionBy = $XXX_drivedata[6];
minDriveSize = $XXX_drivedata[7];
maxDriveSize = $XXX_drivedata[8];
eraseDataOnDelete = $erase_data;
bootable = $boot_data;
accelerator = $XXX_drivedata[4];
storageLocation = $XXX_drivedata[10]
}
$params = $params1.Clone()
foreach($item in $params1.GetEnumerator())
{
#if ([string]::IsNullOrWhiteSpace($item.Value) -or ($item.Value -ieq "null"))
if (!$item.value)
{
$params.Remove($item.Key)
}
}
try {
$logical_disk_create = New-<Function for disk> #params
if ($logical_disk_create)
{
$XXX_disk_create_status = "pass"
return $SCID_disk_create_status,$logical_disk_create.SasLogicalJBOD
}
}
catch {
Write-Error $_
$XXX_disk_create_status = "fail"
return $XXX_disk_create_status,$logical_disk_create
continue
}
}
#------------------------------------------------
#Attach local disk and JBOD to controller
#------------------------------------------------
Function New-Controller
{
Param (
[parameter(Mandatory = $true)]
[Array] $SCID_controller_detail,
[parameter(Mandatory = $true)]
[Array] $SCID_logicaldisk_detail
)
if ($SCID_controller_detail[1] -ieq "yes")
{
$initialize_data = $TRUE;
}
else
{
$initialize_data = $FALSE;
}
$params1 = #{controllerID = $XXX_controller_detail[0];initialize = $initialize_data;writeCache = $XXX_controller_detail[2];logicalDisk = $XXX_logicaldisk_detail}
$params = $params1.Clone()
foreach($item in $params1.GetEnumerator())
{
if ($item.key -ne "logicalDisk")
{
#if ([string]::IsNullOrWhiteSpace($item.Value))
if (!$item.value)
{
$params.Remove($item.Key)
}
}
}
try {
$logicaldisk_controller_create = New-<Function for disk controller> #params
if ($logicaldisk_controller_create)
{
$SCID_disk_create_status = "pass"
return $SCID_disk_create_status,$logicaldisk_controller_create
}
}
catch {
Write-Error $_
$SCID_disk_create_status = "fail"
return $SCID_disk_create_status,$logicaldisk_controller_create
continue
}
}
#--------------------------------------------------
#Create Server Profile
#--------------------------------------------------
Function New-ServerProfile
{
.......
#------------------------------------------------------
#Read local disk and JBOD details
#------------------------------------------------------
$SP_logical_disk_list = #()
$SP_logical_disk_list_controller = #()
$XXX_controllerdata = #("$($serverprofile.localStorages.integratedStorageController.controllerID)", "$($serverprofile.localStorages.integratedStorageController.reinitialize)", "$($serverprofile.localStorages.integratedStorageController.writeCache)")
if ($serverprofile.localStorages.integratedStorageController.logicalDrive)
{
foreach ($logicaldrive in $($serverprofile.localStorages.integratedStorageController.logicalDrive))
{
$XXX_drivedata = #("$($logicaldrive.name)", "$($logicaldrive.raidLevel)", "$($logicaldrive.physicalDrives)", "$($logicaldrive.boot)", "$($logicaldrive.accelarator)", "$($logicaldrive.driveTechnology)")
$logicaldisk_create = New-Disk -XXX_drivedata $XXX_drivedata
if ($logicaldisk_create[0] -ne "fail")
{
$SP_logical_disk_list += $logicaldisk_create[1]
$XXX_drivedata.Clear()
}
}
$logdisk_controller = New-Controller -XXX_controller_detail $SCID_controllerdata -XXX_logicaldisk_detail $SP_logical_disk_list
if ($logdisk_controller[0] -ne "fail")
{
$SP_logical_disk_list_controller += $logdisk_controller[1]
}
}
...........................
$LogicalDisk = New-<Fuctionfordisk> -Name "MyDisk" | New-<Function for disk controller> -Initialize
$LogicalDisks = New-<Fuctionfordisk> -Name "MyDisk" | New-<Function for disk controller> -Initialize
$logcaldr = #($LogicalDisk, $LogicalDisks)
$params1 = #{
....................
other parameters
logicalDisk = $SP_logical_disk_list_controller
}
$params = $params1.Clone()
foreach($item in $params1.GetEnumerator())
{
if ($item.key -ne "LogicalDisk|localStorage")
{
#if ([string]::IsNullOrWhiteSpace($item.Value) -or ($item.Value -ieq "null"))
if (!$item.value)
{
$params.Remove($item.Key)
}
}
}
$task = New-<Server Profile> #params | Wait-<task>
When I use localdr it is working fine but when I use $SP_logical_disk_list_controller for Storage I am getting error
The JSON sent in the request contained a unknown type where a different unknown type was required on line 1 near column 746. Correct the content of the JSON and try the request again.
I have even tried using $logdisk_controller[1] but still same error comes.
I don't know much about PowerShell but have inherited a script from someone who is no longer available for assistance. This script imports AD Group Info and memberships related to Users and Computers. It works fine when run on a machine with PS 2.0 but it crashes if executed on PS 3.0 or newer.
I have not been able to figure out what needs to be modified but it seems the errors start occurring in the "Computer" membership import step and there are hundreds of errors that all say:
Command failed while processing computers: , Exception of type 'System.OutOfMemoryException' was thrown
Then at some point it looks like the script just stops and it never even gets to the 3rd step / function.
Any advice?
[Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices") | Out-Null
$DBServer = "DBSERVER"
$DBName = "DBNAME"
$TableUsers = "[$DBName].[dbo].[AD_GroupToClient]"
$TableComps = "[$DBName].[dbo].[AD_GroupToDevice]"
$TableGroups = "[$DBName].[dbo].[AD_Group_Info]"
$sqldateformat = "yyyy/MM/dd HH:mm:ss:fff"
[system.Data.SqlClient.SqlConnection]$global:SqlConnection = $null
function Get-ScriptPath { $Invocation = (Get-Variable MyInvocation -Scope 1).Value; Split-Path $Invocation.MyCommand.Path }
$ScriptPath = Get-ScriptPath
$Logfile = "$ScriptPath\OutLog.log"
function Write-Logfile {
param($logtext)
[string](Get-Date -format $sqldateformat) + "`t$logtext" | Out-File $Logfile -Encoding ascii -Append
}
function Open-Database {
$global:SqlConnection = New-Object system.Data.SqlClient.SqlConnection
try {
$global:SqlConnection.ConnectionString = "Server=$DBServer;Database=$DBName;Integrated Security=True"
$global:SqlConnection.Open() | Out-Null
Write-Logfile "OK`tDatabase opened"
} catch {
Write-Host "Error Opening SQL Database`t$($_.Exception.Message)"
Write-Logfile "Error`tDatabase open failed, $($_.exception.message)"
exit
}
}
function Close-Database {
$global:SqlConnection.Close()
Write-Logfile "OK`tDatabase closed"
}
function Esc-Quote {
param($str)
if ($str) { $str.Replace("'","''") }
}
function Run-DBCommand {
param($SqlCommands, [switch]$getnumrows)
if ($SqlCommands.Count -ge 1) {
$SqlCommandText = [string]::Join(";", $SqlCommands)
try {
$SqlCmd = New-Object Data.SqlClient.SqlCommand($SqlCommandText, $SqlConnection)
$returnvalue = $SqlCmd.ExecuteNonQuery()
if ($getnumrows) { return $returnvalue }
} catch {
Write-Logfile "Error`tSQL Command failed, $($_.exception.message)"
}
}
}
function Run-GroupMemberExport {
param($exportmode)
switch ($exportmode) {
"users" {
$dom = [ADSI]"LDAP://OU=Clients123,DC=test1,DC=test2,DC=test3"
$query = "(&(objectClass=user)(objectCategory=person)(samaccountname=*))"
$table = $TableUsers
$namecolumn = "AD_Group_Member_Name"
$attribs = #("samaccountname")
}
"computers" {
$dom = [ADSI]"LDAP://DC=test1,DC=test2,DC=test3"
$query = "(&(objectClass=computer)(samaccountname=*))"
$table = $TableComps
$namecolumn = "AD_Group_Member_Device"
$attribs = #("samaccountname", "whencreated")
}
}
$starttime = (Get-Date).ToUniversalTime().ToString($sqldateformat)
$srch = New-Object DirectoryServices.DirectorySearcher($dom, $query, $attribs)
$srch.PageSize = 1000
$srch.Sort = New-Object DirectoryServices.SortOption("sAMAccountName", [DirectoryServices.SortDirection]::Ascending)
$results = $srch.FindAll()
$count = 0
$numaccounts = $results.Count
foreach ($res in $results) {
try {
$objAccount = $res.GetDirectoryEntry()
$samaccountname = $objAccount.properties["samaccountname"][0]
$whencreated = ""
if ($exportmode -eq "computers") { $whencreated = Get-Date ([datetime]$objAccount.properties["whencreated"][0]) -Format $sqldateformat }
$count++
Write-Progress "Querying accounts" $samaccountname -PercentComplete ($count * 100.0 / $numaccounts)
$objAccount.psbase.RefreshCache("tokenGroups")
$SIDs = $objAccount.psbase.Properties.Item("tokenGroups")
$groups = #()
ForEach ($Value In $SIDs) {
$SID = New-Object System.Security.Principal.SecurityIdentifier $Value, 0
try {
$Group = $SID.Translate([System.Security.Principal.NTAccount]).Value
} catch {
$Group = $SID.Translate([System.Security.Principal.SecurityIdentifier]).Value
}
if ($groups -notcontains $Group -and $Group.Split("\")[1] -ne $samaccountname) { $groups += $Group }
}
Run-DBCommand #("DELETE FROM $table WHERE [$namecolumn] = '$(Esc-Quote $samaccountname)'")
$sqlcommands = #()
$currenttime = (Get-Date).ToUniversalTime().ToString($sqldateformat)
if ($groups) {
$groups | sort | foreach {
if ($exportmode -eq "users") {
$sqlcommands += "INSERT INTO $table ([$namecolumn], [AD_Group_Name], [Last_Update]) VALUES ('$(Esc-Quote $samaccountname)', '$(Esc-Quote $_)', '$currenttime')"
} else {
$sqlcommands += "INSERT INTO $table ([$namecolumn], [AD_Group_Name], [Last_Update], [Record_Created]) VALUES ('$(Esc-Quote $samaccountname)', '$(Esc-Quote $_)', '$currenttime', '$whencreated')"
}
if ($sqlcommands.count -ge 50) { Run-DBCommand $sqlcommands; $sqlcommands = #() }
}
} else {
if ($exportmode -eq "users") {
$sqlcommands += "INSERT INTO $table ([$namecolumn], [AD_Group_Name], [Last_Update]) VALUES ('$(Esc-Quote $samaccountname)', 'ERROR: Unable to retrieve groups', '$currenttime')"
} else {
$sqlcommands += "INSERT INTO $table ([$namecolumn], [AD_Group_Name], [Last_Update], [Record_Created]) VALUES ('$(Esc-Quote $samaccountname)', 'ERROR: Unable to retrieve groups', '$currenttime', '$whencreated')"
}
}
Run-DBCommand $sqlcommands
} catch {
Write-Logfile "Error`tCommand failed while processing $exportmode`: $($objAccount.name), $($_.exception.message)"
}
}
Write-Progress " " " " -Completed
if ($count -eq $numaccounts) {
$numdeleted = Run-DBCommand #("DELETE FROM $table WHERE [Last_Update] < '$starttime' OR [Last_Update] IS NULL") -getnumrows
Write-Logfile "OK`tUpdates for $exportmode completed, $numdeleted old records deleted."
}
}
function Run-GroupDescriptionExport {
$dom = [ADSI]"LDAP://DC=test1,DC=test2,DC=test3"
$query = "(&(objectClass=group)(samaccountname=*))"
$table = $TableGroups
$attribs = #("samaccountname", "displayname", "description", "whencreated", "managedby", "grouptype","distinguishedname","whenchanged")
$srch = New-Object DirectoryServices.DirectorySearcher($dom, $query, $attribs)
$srch.PageSize = 1000
$srch.Sort = New-Object DirectoryServices.SortOption("sAMAccountName", [DirectoryServices.SortDirection]::Ascending)
$results = $srch.FindAll()
$count = 0
$numgroups = $results.Count
$sqlcommands = #()
$starttime = [datetime]::Now.ToUniversalTime().ToString($sqldateformat)
foreach ($res in $results) {
$count++
$samaccountname = $res.properties["samaccountname"][0]
Write-Progress "Querying accounts, $count/$numgroups" $samaccountname -PercentComplete ($count * 100.0 / $numgroups)
$displayName = ""; if ($res.properties.contains("displayname")) { $displayName = $res.properties["displayname"][0] }
$description = ""; if ($res.properties.contains("description")) { $description = $res.properties["description"][0] }
$managedby = ""; if ($res.properties.contains("managedby")) { $managedby = $res.properties["managedby"][0] }
$grouptype = ""; if ($res.properties.contains("grouptype")) { $grouptype = $res.properties["grouptype"][0] }
$distinguishedname = ""; if ($res.properties.contains("distinguishedname")) { $distinguishedname = $res.properties["distinguishedname"][0] }
$whencreated = ""; if ($res.properties.contains("whencreated")) { $whencreated = ([datetime]$res.properties["whencreated"][0]).ToString($sqldateformat) }
$whenchanged = ""; if ($res.properties.contains("whenchanged")) { $whenchanged = ([datetime]$res.properties["whenchanged"][0]).ToString($sqldateformat) }
$lastupdated = [datetime]::Now.ToUniversalTime().ToString($sqldateformat)
$sqlcommand = "DELETE FROM $table WHERE [AD_Group_Name] = '$(Esc-Quote $samaccountname)'; "
$sqlcommand += "INSERT INTO $table ([AD_Group_Name], [AD_Group_DisplayName], [AD_Group_Description], [Last_Update], [Managed_By],[Distinguished_Name],[Group_Category],[Created_On], AD_Last_Modified]) VALUES ('$(Esc-Quote $samaccountname)', '$(Esc-Quote $displayName)', '$(Esc-Quote $description)', '$lastupdated', '$(Esc-Quote $managedby)', '$(Esc-Quote $distinguishedname)', '$grouptype', '$whencreated','$whenchanged')"
$sqlcommands += $sqlcommand
if ($sqlcommands.count -ge 100) { Run-DBCommand $sqlcommands; $sqlcommands = #()
}
}
Run-DBCommand $sqlcommands
if ($numgroups -eq $count) {
Run-DBCommand #("DELETE FROM $table WHERE [Last_Update] <= '$starttime'")
}
Write-Progress " " " " -Completed
}
Open-Database
Run-GroupMemberExport "users"
Run-GroupMemberExport "computers"
Run-GroupDescriptionExport
Close-Database
This doesn't have anything to do with the PowerShell version. You're just plain running out of memory. You're pulling in a lot of data, so you need to be more conscious of getting rid of that data when you're done with it.
There are a couple things you can do to clean up memory:
First, the documentation for DirectorySearcher.FindAll() says:
Due to implementation restrictions, the SearchResultCollection class cannot release all of its unmanaged resources when it is garbage collected. To prevent a memory leak, you must call the Dispose method when the SearchResultCollection object is no longer needed.
So whenever you do:
$results = $srch.FindAll()
Make sure you call $results.Dispose() when you're done with it (at the end of the function).
Second, when you loop through the results in your Run-GroupMemberExport function, you're calling $res.GetDirectoryEntry(). Usually you can just let the garbage collector clean up DirectoryEntry objects, but when you're creating so many in a loop like that, the GC doesn't have time to run. This has happened to me when I've run a loop over thousands of accounts.
To solve this, you can call Dispose() on the DirectoryEntry objects yourself. Since you already have a try/catch block there, I would suggest adding a finally block to make sure it happens even if an error is thrown:
try {
...
} catch {
Write-Logfile "Error`tCommand failed while processing $exportmode`: $($objAccount.name), $($_.exception.message)"
} finally {
$objAccount.Dispose()
}
Actually, you could probably just not use GetDirectoryEntry() at all. Just ask the DirectorySearcher to return the other attributes you need. But if you want to still use it, then make sure you call RefreshCache for every attribute you need (you can put them all in one call to RefreshCache). If you access the Properties collection and ask for a value that it does not already have in cache, then it will ask AD for every attribute with a value - that's a lot of unnecessary data.
I am trying to write a routine in powershell which will listen on a UDP port, then exit when a key is pressed. The problem I have is that the program will only exit after a datagram has been read.
I.e. It will read n values, user will hit F12, program will wait until it gets the n+1th value, then exit.
What should happen is: reads n values, user will hit F12, program should shut down.
$endpoint = New-Object System.Net.IPEndPoint ([IPAddress]::Any, $port)
$continue = $true
while($continue)
{
if ([console]::KeyAvailable)
{
echo "Exit with F12";
$x = [System.Console]::ReadKey()
switch ( $x.key)
{
F12 { $continue = $false }
}
}
else
{
$socket = New-Object System.Net.Sockets.UdpClient $port
$content = $socket.Receive([ref]$endpoint)
$socket.Close()
[Text.Encoding]::ASCII.GetString($content)
}
}
I'm completely new to Powershell, so maybe this isn't possible. The rest of the code has been stolen from other answers here.
A solution that uses the ReceiveTimeout property, which is mentioned in the #bluuf's comment:
$p = 17042
$e = New-Object System.Net.IPEndPoint ([System.Net.IPAddress]::Any, $p)
$u = New-Object System.Net.Sockets.UdpClient $p
$u.Client.ReceiveTimeout = 100
try
{
for()
{
try
{
$b = $u.Receive([ref]$e)
$s = [System.Text.Encoding]::ASCII.GetString($b)
Write-Host $s
}
catch [System.Net.Sockets.SocketException]
{
if ( $_.Exception.SocketErrorCode -ne 'TimedOut' )
{
throw
}
}
if ( [System.Console]::KeyAvailable )
{
$x = [System.Console]::ReadKey($true)
if ( $x.key -eq [System.ConsoleKey]::F12 )
{
Write-Host 'Exit with F12'
break
}
}
}
}
finally
{
$u.Close()
}
The asynchronous version might look like this:
if( -not('CallbackEventBridge' -as [type]) )
{
Add-Type #'
using System;
public sealed class CallbackEventBridge
{
public event AsyncCallback CallbackComplete = delegate { };
private CallbackEventBridge() {}
private void CallbackInternal(IAsyncResult result)
{
CallbackComplete(result);
}
public AsyncCallback Callback
{
get { return new AsyncCallback(CallbackInternal); }
}
public static CallbackEventBridge Create()
{
return new CallbackEventBridge();
}
}
'#
}
$sb = {
param($ar)
$e = $ar.AsyncState.e
$u = $ar.AsyncState.u
$b = $u.EndReceive($ar, [ref]$e)
$s = [System.Text.Encoding]::ASCII.GetString($b)
Write-Host $s
$ar.AsyncState.completed = $true
}
$bridge = [CallbackEventBridge]::Create()
Register-ObjectEvent -InputObject $bridge -EventName CallbackComplete -Action $sb > $null
$p = 17042
$e = New-Object System.Net.IPEndPoint ([System.Net.IPAddress]::Any, $p)
$u = New-Object System.Net.Sockets.UdpClient $p
$state = #{e = $e; u = $u; completed = $true}
try
{
for()
{
if( $state.completed )
{
$state.completed = $false
[void]$u.BeginReceive($bridge.Callback, $state)
}
if ( [System.Console]::KeyAvailable )
{
$x = [System.Console]::ReadKey($true)
if ( $x.key -eq [System.ConsoleKey]::F12 )
{
Write-Host 'Exit with F12'
break
}
}
[System.Threading.Thread]::Sleep(100)
}
}
finally
{
$u.Close()
}
Learn more about the CallbackEventouBridge class in the PowerShell 2.0 – Asynchronous Callbacks from .NET article by Oisin Grehan.
Both versions can be tested with the following code snippet:
$p = 17042
$u = New-Object System.Net.Sockets.UdpClient
$b = [System.Text.Encoding]::ASCII.GetBytes('Is anybody there')
$u.Connect('localhost', $p)
[void]$u.Send($b, $b.Length)
$u.Close()
I need to query printer over JetDirect protocol (Tcp-IP Port 9100)
I already write the code to connect and disconnect, but for put and read data i have some problem :(
'printer.local:9100' | Connect-TcpHost | Disconnect-TcpHost
result
TcpDestNodes IsOpen Latency Query
------------ ------ ------- -----
printer.local:9100 True 0,7065 {}
My code
function Connect-TcpHost (
[Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]$Dest,
$TCPtimeout=250
) {
Begin {
} Process {
($HostName, $port) = $Dest.split(':')
Write-Verbose "$HostName : $port"
$tcpClient = New-Object System.Net.Sockets.TCPClient
$connect = $tcpClient.BeginConnect($HostName,$port,$null,$null)
Write-Verbose "Connecting..."
$timeMs = (Measure-Command {
$wait = $connect.AsyncWaitHandle.WaitOne($TCPtimeout,$false)
Write-Verbose "Connecting 2..."
}).TotalMilliseconds
If (!$wait) {
Write-error "$HostName : $Port"
Write-Verbose "Close connections..."
$tcpClient.Close()
$tcpClient.Dispose()
return;
}
[pscustomobject][ordered]#{
TcpDestNodes = $dest
tcpClient = $tcpClient
connect = $connect
IsOpen = $true
Latency = $timeMs
Query = #()
}
} End {
}
}
function Disconnect-TcpHost (
[Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]$ObjTcp
) {
Begin {
} Process {
Write-Verbose "Disconnecting..."
$ObjTcp.tcpClient.Close()
$ObjTcp.tcpClient.Dispose()
[pscustomobject][ordered]#{
TcpDestNodes = $ObjTcp.TcpDestNodes
IsOpen = $ObjTcp.IsOpen
Latency = $ObjTcp.Latency
Query = $ObjTcp.Query
}
} End {
}
}
I Write 2 new function Put-TcpHost() and Read-TcpHost()
'printer.local:9100' | Connect-TcpHost -verbose | Put-TcpHost -query '#PJL INFO ID' -verbose | Read-TcpHost -verbose | Disconnect-TcpHost -verbose
return
TcpDestNodes : 10.48.5.102:9100
IsOpen : True
Latency : 0,8351
Query : {#{Type=Query; Data=System.Object[]}, #{Type=Answer; Data=System.Object[]}}
my full Tcp-Tools.ps1
function Connect-TcpHost (
[Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]$Dest,
$TCPtimeout=250
) {
Begin {
} Process {
($HostName, $port) = $Dest.split(':')
Write-Verbose "$HostName : $port"
$tcpClient = New-Object System.Net.Sockets.TCPClient
$connect = $tcpClient.BeginConnect($HostName,$port,$null,$null)
Write-Verbose "Connecting..."
$timeMs = (Measure-Command {
$wait = $connect.AsyncWaitHandle.WaitOne($TCPtimeout,$false)
}).TotalMilliseconds
If (!$wait) {
Write-error "$HostName : $Port"
Write-Verbose "Echec, Close socket..."
$tcpClient.Close()
$tcpClient.Dispose()
return;
}
Write-Verbose "Connection Available"
[pscustomobject][ordered]#{
TcpDestNodes = $dest
tcpClient = $tcpClient
connect = $connect
IsOpen = $true
Latency = $timeMs
Query = #()
}
} End {
}
}
function Put-TcpHost (
[Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]$ObjTcp,[string]$query
) {
Begin {
} Process {
if ($ObjTcp.tcpClient.Connected) {
Write-Verbose "Send > $query"
$ObjTcp.query += [pscustomobject][ordered]#{
Type = 'Query'
Data = #($query)
}
$data = [System.Text.Encoding]::ASCII.GetBytes("$query`n")
$stream = $ObjTcp.TcpClient.GetStream()
$stream.Write($data, 0, $data.Length)
} else {
Write-error "N'est plus connecte !"
$ObjTcp.tcpClient.Close()
$ObjTcp.tcpClient.Dispose()
return;
}
$ObjTcp
} End {
}
}
function Read-TcpHost (
[Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]$ObjTcp,
$timeout=2500
) {
Begin {
$step = 25
} Process {
$i=$timeout/$step
Write-Verbose "> Waiting Answer"
While ([int64]$ObjTcp.tcpClient.Available -le 0 -and $i--) {
Start-Sleep -Milliseconds $step
}
Write-Verbose "Reply $($timeout-$i*$step)ms > Bytes available: $($ObjTcp.tcpClient.Available)"
If ([int64]$ObjTcp.tcpClient.Available -gt 0) {
$stringBuilder = New-Object Text.StringBuilder
try {
$stream = $ObjTcp.TcpClient.GetStream()
$bindResponseBuffer = New-Object Byte[] -ArgumentList $ObjTcp.tcpClient.Available
[Int]$response = $stream.Read($bindResponseBuffer, 0, $bindResponseBuffer.count)
$Null = $stringBuilder.Append(($bindResponseBuffer | ForEach {[char][int]$_}) -join '')
Write-Verbose "Read > $(#($stringBuilder.Tostring() -split("`n"))[1])"
$ObjTcp.query += [pscustomobject][ordered]#{
Type = 'Answer'
Data = #($stringBuilder.Tostring() -split("`n"))
}
} catch {
Write-error "Probleme !"
$ObjTcp.tcpClient.Close()
$ObjTcp.tcpClient.Dispose()
return;
}
}
$ObjTcp
} End {
}
}
function Disconnect-TcpHost (
[Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]$ObjTcp
) {
Begin {
} Process {
Write-Verbose "Disconnecting..."
$ObjTcp.tcpClient.Close()
$ObjTcp.tcpClient.Dispose()
[pscustomobject][ordered]#{
TcpDestNodes = $ObjTcp.TcpDestNodes
IsOpen = $ObjTcp.IsOpen
Latency = $ObjTcp.Latency
Query = $ObjTcp.Query
}
} End {
}
}