Send-vmkb

Dec 30, 2009 at 11:56 PM
Edited Dec 31, 2009 at 12:08 AM

First, a bit of background.  I use PS, Hyper-V and your module regularly in a major test environment.  One of the recurring admin functions is the creation of fresh Windows XP images, to which I use a 2008 SP2 host to create my images.  Due to our image deployment process, much of the provisioning is manual.  My goal is to automate as much of this as possible.

While looking over the msvm input classes, I saw that no code had been written to take advantage of this in powershell. 

I plan to expand this with a hashtable for all TypeKey codes and work through guest image builds through IC/software installation to image capture. 

So, here's my first attempt at coding this function:

 

 

Function Send-vmkb
{# .ExternalHelp  MAML-VM.XML
 #.SYNOPSIS
 #        Send text to a VM.
 #.Description
 #        Sends text to active element in a VM, including CTRL-ALT-DEL and Carriage Return (CR).
    [CmdletBinding()]
    Param(
      [parameter(Position=0 , Mandatory = $true, ValueFromPipeline = $true)] $VM, 
      [String]$Server=".", 
      [string]$text,
      [Switch]$CAD,
      [SWITCH]$CR,
      $PSC
    )
    process {
        if ($psc -eq $null)  {$psc = $pscmdlet} ; if (-not $PSBoundParameters.psc) {$PSBoundParameters.add("psc",$psc)}
        if ($VM -is [String]) {$VM=(Get-VM -Name $VM -Server $Server) }
        if ($VM -is [System.Management.ManagementObject]) {
            If (($text) -or ($CAD ) ) {
                $VSMKB=($VM.getRelated("Msvm_Keyboard") | select-object)   # get-wmiobject  -computername $vm.__SERVER -namespace $HyperVNamespace  -Query "associators of {$vm} where resultclass=Msvm_Keyboard")
                if ($text ) {
                    $result = $VSMKB.TypeText($text)
                        if ($result.returnvalue -eq 0) {write-host "'$text' was successfully sent to $vm on $server"}
                }
                if ($CR ){$VSMKB.Typekey(0x0D)}
                if ($CAD ) {$VSMKB.TypeCtrlAltDel() }
            }
        }
    }
}#

 

 

 

 

 

 

 

 

 

Coordinator
Jan 8, 2010 at 5:12 PM

Hi

You've probably worked out that the hash-table is the bigger piece of work : if you are developing that it would be a useful addition to the library.

Thanks

James

 

Jan 9, 2010 at 12:18 AM

I have that much done already and am now working on modifier keys.  The plan is to use short notation for keys, ie L_WIN for left windows, CTRL, F1, etc and parse the parameter to accept any modifier combination, WINDOWS+R for instance.  The parameter should also accept any hex codes input to allow reserved and unspecified keys. 

Once I have worked out the key modifier parameters, I'll update the code.  Feel free to use anything you think would help. 

 

Some more details about the project I am working on:

Using either SQL or an XML config, pull OS properties and initiate a run of OS builds against an imaging host's VMs through this module. 

Build the OS, configure it via script for prerequisites, then capture the image through our own imaging iso. 

The next part of the automation package will utilize an OCR scan of the VM's thumbnails for status updates.  MODI is an option, but as it was deprecated in Office 2010, not viable in the long term. 

The whole process will be wrapped inside a job list that I intend to get a WPK framework built for starting jobs, monitoring, etc. 

Here's the major hurdle I haven't yet vaulted:

In our environment, we test across 22 languages, which has added a layer of complexity, that being how to type a setup script with full UNC paths without writing language specific commands.  I have not been successful researching this yet, and although it is possible to control the mouse via code to change it via the language bar, that's quite likely going to be time consuming to get reproduceable results or downright impossible.

 

 

 

 

 

 

 

 

 

 

 

 

Jan 12, 2010 at 6:03 PM

I have updated the code.  It now supports key commands using common names and can accept multiple character input, ie WIN+R, WIN-R, ALT-TAB, etc.  All OEM keys are supported via direct Hex code '0xE0', '0xF1', etc.  See hashtable for full list.  VK code listings are provided as a reference, but not implementated as of yet.

Examples:

  • send-vmkb -server fooserver -vm foo -t "notepad.exe c:\text.txt" -cr (sends the enclosed text and a Carriage return. )
  • send-vmkb -server fooserver -vm foo -vkc ALT+TAB or WIN+R or ALT-F4  both + and - are accepted separators.
  • send-vmkb -server fooserver -vm foo -cad (in essence, an alias of -vkc CTRL-ALT-DEL)

 

Limitations:

  • Once I work out why it's failing, all hex codes will be accepted directly. 
  • Mouse specific commands present in the key code list are not supported yet.

Up next:

  • Support for multiple targets via array or through a get-vm command. 
  • Support for Scankeycodes method in R2. 
  • Basic mouse support (more for detailed scripting as it would be impossible to code complex actions).  
  • Support for a -convert switch to convert text strings into Unicode characters.

 

$LHash_VirtualKeyCode = @{
	LMB	  	 	= 	'0x01' 	<#VK_LBUTTON#>	        ;	RMB	      	 	= 	'0x02' 	<#VK_RBUTTON#>	 	;		
	BREAK	  	= 	'0x03' 	<#VK_CANCEL#>	        ;	MMB	      	 	= 	'0x04' 	<#VK_MBUTTON#>	 	;		
	X1_MB	  	= 	'0x05' 	<#VK_XBUTTON1#>	 	 	;	X2_MB	  	 	= 	'0x06' 	<#VK_XBUTTON2#>	 	;		
	BACKSPACE 	= 	'0x08' 	<#VK_BACK#>	         	;	TAB	      	 	= 	'0x09' 	<#VK_TAB#>	 	 	;		
	CLEAR	  	= 	'0x0C' 	<#VK_CLEAR#>	        ;	ENTER	  	 	= 	'0x0D' 	<#VK_RETURN#>	 	;		
	SHIFT 	 	= 	'0x10' 	<#VK_SHIFT#>	        ;	CTRL	  	 	= 	'0x11' 	<#VK_CONTROL#>	 	;		
	ALT	        = 	'0x12' 	<#VK_MENU#>	 	 	 	;	PAUSE	     	= 	'0x13' 	<#VK_PAUSE#>	 	;		
	CAPS_LOCK	= 	'0x14' 	<#VK_CAPITAL#>	 	 	;	IME_Kana	 	= 	'0x15' 	<#VK_KANA#>	  	 	;		
	IME_Hangul	= 	'0x15' 	<#VK_HANGUL#>	 	 	;	IME_Junja	 	= 	'0x17' 	<#VK_JUNJA#>	 	;		
	IME_final	= 	'0x18' 	<#VK_FINAL#>	 	 	;	IME_Hanja	 	= 	'0x19' 	<#VK_HANJA#>	 	;		
	IME_Kanji	= 	'0x19' 	<#VK_KANJI#>	 	 	;	ESC	  	 	 	= 	'0x1B' 	<#VK_ESCAPE#>	 	;		
	IME_convert	= 	'0x1C' 	<#VK_CONVERT#>	 	 	;	IME_nonconvert	= 	'0x1D' 	<#VK_NONCONVERT#>	;		
	IME_accept	= 	'0x1E' 	<#VK_ACCEPT#>	 	 	;	IME_mode_change = 	'0x1F' 	<#VK_MODECHANGE#>	;		
	SPACEBAR	= 	'0x20' 	<#VK_SPACE#>	 	 	;	PAGE_UP	  	 	= 	'0x21' 	<#VK_PRIOR#>	 	;		
	PAGE_DOWN	= 	'0x22' 	<#VK_NEXT#>	 	 	 	;	END	  	 	 	= 	'0x23' 	<#VK_END#>	 	 	;		
	HOME	  	= 	'0x24' 	<#VK_HOME#>	 	 	 	;	LEFT_ARROW	 	= 	'0x25' 	<#VK_LEFT#>	 	 	;		
	UP_ARROW	= 	'0x26' 	<#VK_UP#>	 	 	 	;	RIGHT_ARROW	 	= 	'0x27' 	<#VK_RIGHT#>	 	;		
	DOWN_ARROW	= 	'0x28' 	<#VK_DOWN#>	 	 	 	;	SELECT	  	 	= 	'0x29' 	<#VK_SELECT#> 		;		
	PRINT	  	= 	'0x2A' 	<#VK_PRINT#>	 	 	;	EXECUTE	  	 	= 	'0x2B' 	<#VK_EXECUTE#> 		;		
	PRINT_SCREEN= 	'0x2C' 	<#VK_SNAPSHOT#>	 	 	;	INS	 	 	 	= 	'0x2D' 	<#VK_INSERT#>	 	;		
    PRTSCN      = 	'0x2C' 	<#VK_SNAPSHOT#>	 	 	;	INSERT	 	 	= 	'0x2D' 	<#VK_INSERT#>	 	;		
	DEL	  	 	= 	'0x2E' 	<#VK_DELETE#> 	 		;	HELP	  	 	= 	'0x2F' 	<#VK_HELP#> 		;		
	0	 = 	'0x30' 	;	1	 = 	'0x31' 	;   2	 = 	'0x32' 	;	3	 = 	'0x33' 	;
	4	 = 	'0x34' 	;	5	 = 	'0x35' 	;	6	 = 	'0x36' 	;	7	 = 	'0x37' 	;
	8	 = 	'0x38' 	;	9	 = 	'0x39' 	;	A	 = 	'0x41' 	;	B	 = 	'0x42' 	;
	C	 = 	'0x43' 	;	D	 = 	'0x44' 	;	E	 = 	'0x45' 	;	F	 = 	'0x46' 	;
	G	 = 	'0x47' 	;	H	 = 	'0x48' 	;	I	 = 	'0x49' 	;   J	 = 	'0x4A' 	;
	K	 = 	'0x4B' 	;	L	 = 	'0x4C' 	;	M	 = 	'0x4D' 	;	N	 = 	'0x4E' 	;
	O	 = 	'0x4F' 	;	P	 = 	'0x50' 	;	Q	 = 	'0x51' 	;	R	 = 	'0x52' 	;
	S	 = 	'0x53' 	;	T	 = 	'0x54' 	;	U	 = 	'0x55' 	;	V	 = 	'0x56' 	;
	W	 = 	'0x57' 	;	X	 = 	'0x58' 	;	Y	 = 	'0x59' 	;	Z	 = 	'0x5A' 	;
	L_Win	  	 	= 	'0x5B' 	<#VK_LWIN#>	 	;		R_Win	 	 = 	'0x5C' 	<#VK_RWIN#>	 	;		
    Win	  	 		= 	'0x5B' 	<#VK_LWIN#>	 	;		Windows	 	 = 	'0x5C' 	<#VK_RWIN#>	 	;		
	Applications	= 	'0x5D' 	<#VK_APPS#>	 	;	 	Sleep	 	 = 	'0x5F' 	<#VK_SLEEP#>	;		
	NK0	 = 	'0x60' 	<#VK_NUMPAD0#>	;		NK1	 = 	'0x61' 	<#VK_NUMPAD1#>	;		
	NK2	 = 	'0x62' 	<#VK_NUMPAD2#>	;		NK3	 = 	'0x63' 	<#VK_NUMPAD3#>	;		
	NK4	 = 	'0x64' 	<#VK_NUMPAD4#>	;		NK5	 = 	'0x65' 	<#VK_NUMPAD5#>	;		
	NK6	 = 	'0x66' 	<#VK_NUMPAD6#>	;		NK7	 = 	'0x67' 	<#VK_NUMPAD7#>	;		
	NK8	 = 	'0x68' 	<#VK_NUMPAD8#>	;		NK9	 = 	'0x69' 	<#VK_NUMPAD9#>	;		
	NK_Multiply	 = 	'0x6A' 	<#VK_MULTIPLY#>	 	;	NK_PLUS	 = 	'0x6B' 	<#	VK_ADD 	#>	;		
	Separator	 = 	'0x6C' 	<#VK_SEPARATOR#>	;	NK_MINUS	 = 	'0x6D' 	<#VK_SUBTRACT#>	;		
	NK_DECIMAL	 = 	'0x6E' 	<#VK_DECIMAL#>	 	;	NK_DIVIDE	 = 	'0x6F' 	<#VK_DIVIDE#>	;		
	F1	 = 	'0x70' 	<#VK_F1#>	;	F2	 = 	'0x71' 	<#VK_F2#>	;		
	F3	 = 	'0x72' 	<#VK_F3#>	;	F4	 = 	'0x73' 	<#VK_F4#>	;		
	F5	 = 	'0x74' 	<#VK_F5#>	;	F6	 = 	'0x75' 	<#VK_F6#>	;		
	F7	 = 	'0x76' 	<#VK_F7#>	;	F8	 = 	'0x77' 	<#VK_F8#>	;		
	F9	 = 	'0x78' 	<#VK_F9#>	;	F10	 = 	'0x79' 	<#VK_F10#>	;		
	F11	 = 	'0x7A' 	<#VK_F11#>	;	F12	 = 	'0x7B' 	<#VK_F12#>	;		
	F13	 = 	'0x7C' 	<#VK_F13#>	;	F14	 = 	'0x7D' 	<#VK_F14#>	;		
	F15	 = 	'0x7E' 	<#VK_F15#>	;	F16	 = 	'0x7F' 	<#VK_F16#>	;		
	F17	 = 	'0x80H' <#VK_F17#>	;	F18	 = 	'0x81H' <#VK_F18#>	;		
	F19	 = 	'0x82H' <#VK_F19#>	;	F20	 = 	'0x83H' <#VK_F20#>	;		
	F21	 = 	'0x84H' <#VK_F21#>	;	F22	 = 	'0x85H' <#VK_F22#>	;		
	F23	 = 	'0x86H' <#VK_F23#>	;	F24	 = 	'0x87H' <#VK_F24#>	;		
	NUM_LOCK	 = 	'0x90' 	<#VK_NUMLOCK#>	;	SCROLL_LOCK	 = 	'0x91' 	<#VK_SCROLL#>	;		
	Left_SHIFT	 = 	'0xA0' 	<#VK_LSHIFT#>	;	Right_SHIFT	 = 	'0xA1' 	<#VK_RSHIFT#>	;		
	Left_CONTROL = 	'0xA2'	<#VK_LCONTROL#>	;	Right_CONTROL= 	'0xA3' 	<#VK_RCONTROL#>	;		
	Left_MENU	 = 	'0xA4' 	<#VK_LMENU#>	;	Right_MENU	 = 	'0xA5' 	<#	VK_RMENU#>	;
    ';:'		 = 	'0xBA' 	<#VK_OEM_1#>		;	'+'	 		= 	'0xBB' 	<#VK_OEM_PLUS#>		;		
	','	 		 = 	'0xBC' 	<#VK_OEM_COMMA#>	;	'-'	 		= 	'0xBD' 	<#VK_OEM_MINUS#>	;		
	'.'	 	  	 = 	'0xBE' 	<#VK_OEM_PERIOD#>	;	'/?'		= 	'0xBF' 	<#VK_OEM_2#>		;		
	'`~'		 = 	'0xC0' 	<#VK_OEM_3#>		;	'[{'	 	= 	'0xDB' 	<#VK_OEM_4#>		;		
	'\|' 	 	 = 	'0xDC' 	<#VK_OEM_5#>		;	']}' 	 	= 	'0xDD' 	<#VK_OEM_6#>		;		
	'sQT/dQT' 	 = 	'0xDE' 	<#VK_OEM_7#>		;	'0xDF' 	 	= 	'0xDF' 	<#VK_OEM_8#>		;		
	'0xE0'	 = 	'0xE0' 	<#	- 	#>		;		'0xE1'	 	 = 	'0xE1' 	<#	 	#>	;		
	'0xE2' 	 = 	'0xE2' 	<#VK_OEM_102#>	;		'0xE3'	 	 = 	'0xE3' 	<#	 	#>	;	
    '0xE4'	 = 	'0xE4' 	<#OEM#>			;		IME_PROCESS	 = 	'0xE5' 	<#VK_PROCESSKEY#>	;		
	'0xE6'	 = 	'0xE6' 	<#OEM#>			;		Unicode	 	 = 	'0xE7' 	<#VK_PACKET#>	;		
	'0xE8'	 = 	'0xE8' 	<#	- 	#>		;		'0xE9'	 	 = 	'0xE9' 	<#	 	#>		;	
	'0xEA'	 = 	'0xEA' 	<#	 	#>		;    	'0xEB'		 = 	'0xEB' 	<#	- 	#>		;
    '0xEC'	 = 	'0xEC' 	<#	 	#>		;		'0xED'		 = 	'0xED' 	<#	- 	#>		;
    '0xEE'	 = 	'0xEE' 	<#	 	#>		;	    '0xEF'	 	 = 	'0xEF' 	<#	- 	#>		;
	'0xF0'	 = 	'0xF0' 	<#	- 	#>		;		'0xF1'	 	 = 	'0xF1' 	<#	 	#>		;
    '0xF2'	 = 	'0xF2' 	<#	- 	#>		;		'0xF3'	 	 = 	'0xF3' 	<#	 	#>		;
    '0xF4'	 = 	'0xF4' 	<#	- 	#>		;		'0xF5'	 	 = 	'0xF5' 	<#	 	#>		;
	Attn	 = 	'0xF6' 	<#VK_ATTN#>		;		CrSel	 	 = 	'0xF7' 	<#VK_CRSEL#>	;		
	ExSel	 = 	'0xF8' 	<#VK_EXSEL#>	;		Erase_EOF	 = 	'0xF9' 	<#VK_EREOF#>	;		
	Play	 = 	'0xFA' 	<#VK_PLAY#>		;		Zoom	 	 = 	'0xFB' 	<#VK_ZOOM#>		;		
	'0xFC'	 = 	'0xFC' 	<#VK_NONAME#>	;		PA1		 	 = 	'0xFD' 	<#VK_PA1#>		;		
	OEM_Clear= 	'0xFE' 	<#VK_OEM_CLEAR#>			 
    } #End VirtualKeyCode

Function Send-vmkb
{# 
 #.SYNOPSIS
 #        Send text to a VM.
 #.Description
 #        Sends text to active element in a VM. Includes modifier keys using 'key1+key2', CTRL-ALT-DEL and Carriage Return (CR).
 #.Example
 #        send-vmkb -server fooserver -vm foo -t "notepad.exe c:\text.txt" -cr 
 #        send-vmkb -vkc ALT+TAB or WIN+R or ALT-F4  both + and - are accepted separators.
 #        send-vmkb -cad 
 #.Reference
 #        -VKC accepts common button names for most standard english virtual key codes.  
 #        Expand hash table $LHash_VirtualKeyCode for all current supported codes.  
 #        VK codes provided as reference.  
    [CmdletBinding()]
    Param(
      [parameter(Position=0 , Mandatory = $true, ValueFromPipeline = $true)] $VM, 
      [String]$Server=".", 
      [string]$text,
      [Switch]$CAD,
      [SWITCH]$CR,
      $VKC,
      $PSC
    )
    process {
        if ($psc -eq $null)  {$psc = $pscmdlet} ; if (-not $PSBoundParameters.psc) {$PSBoundParameters.add("psc",$psc)}
        if ($VM -is [String]) {$VM=(Get-VM -Name $VM -Server $Server) }
        if ($VM -is [System.Management.ManagementObject]) {
            $VSMKB=($VM.getRelated("Msvm_Keyboard") | select-object)   # get-wmiobject  -computername $vm.__SERVER -namespace $HyperVNamespace  -Query "associators of {$vm} where resultclass=Msvm_Keyboard")
                if ($text ) {
                    $result = $VSMKB.TypeText($text)
                        if ($result.returnvalue -eq 0) {write-host "'$text' was successfully sent to $vm on $server"}
                }
                
                if ($CAD ) {$VSMKB.TypeCtrlAltDel() }

                if ($vkc -is [string]) {$VSMKB.Typekey($LHash_VirtualKeyCode.$vkc)}
                if ($vkc -is [Array])  { 
                    $Vkc | Foreach-object {
                        foreach ($vk in $VKC) {$VSMKB.presskey($LHash_VirtualKeyCode.$vk)}
                        foreach ($vk in $VKC) {$VSMKB.releasekey($LHash_VirtualKeyCode.$vk)}
                    }
                }
                
                #if ($vkc.contains('0x')) {$VSMKB.Typekey($vkc)} # 'invalid method parameters' when called
                
				if ($VKC.contains('+') ) {
                    $VKC = @($VKC.split('+'))
                    foreach ($vk in $VKC) {$VSMKB.presskey($LHash_VirtualKeyCode.$vk)}
                    foreach ($vk in $VKC) {$VSMKB.releasekey($LHash_VirtualKeyCode.$vk)}
			    }
				if ($VKC.contains('-') ) {
                    $VKC = @($VKC.split('-'))
                    foreach ($vk in $VKC) {$VSMKB.presskey($LHash_VirtualKeyCode.$vk)}
                    foreach ($vk in $VKC) {$VSMKB.releasekey($LHash_VirtualKeyCode.$vk)}
			    }
                
                if ($CR ){$VSMKB.Typekey(0x0D)}
            
			}
		}
	}