Google
Showing posts with label Windows. Show all posts
Showing posts with label Windows. Show all posts

2013-08-13

Work around quirky Printui.dll on XP

In the previous post, I talked about issues I was seeing when using printui.dll to push per-machine connections on Windows XP. I did tons of testing and in the realized that XP's spooler service was too flaky for my purpose.

I spent some time diving deeper into what the commands are doing and if there was anything that I could do in the startup script that would make it more consistent...

Let's cut to the chase:

I concluded that the most reliable way to make sure printers will be pushed when users logged in was to make sure relevant connection entries are created by my script under HKLM during startup, instead of relying printui.dll /ga switch.

How did I arrive at this conclusion?

Well,  I used Process Monitor (aka Procmon) from SysInternals,
watched what happens if I issue commands like the ones below manually

  • rundll32 printui.dll,PrintUIEntry /ga /n\\PrintServer\Printer (add per-machine printer connection)
  • rundll32 printui.dll,PrintUIEntry /gd /n\\PrintServer\Printer (remove per-machine printer connection)

A Watched sequence of events during boot (Procmon > Options > Enable Boot-Logging)

Here are a few things to note that will help us understand what is going on :

  • Our Print Server is named PrintServer1 
  • Printers shared on it has sharenames Printer1.. PrinterN 
  • HKLM Connections key: HKLM\SYSTEM\CurrentControlSet\Control\Print\Connections
  • HKCU Connections key: HKCU\Printers\Connections
  • If there is no ",,PrintServer1,PrinterX" entry under HKLM Connections key, user does not get the PrinterX connection.

Q. What happens if we "add per-machine printer connection" in a user session?
A. Seemingly nothing, user does not see the new printer connections until Spooler service is started. In reality, a new registry key under HKLM Connections key is created.

Q. So, what happens if we re-start the spooler services?
A. Normally, user gets the printer connection. It does not show up immediately, spooler service takes its sweet time but unless something goes terribly wrong, new printers shows up in a minute or two. Here is how (shortened for relevancy):

Spooler Service checks the entries under HKLM Connections key and detects a new printer connection, which will be in the following form:
HKLM\SYSTEM\CurrentControlSet\Control\Print\Connections\,,PrintServer1,PrinterX
Server=\\PrintServer1 (RegSZ)
Provider=Win32spl.dll (RegSZ)
LocalConnection=0x0000001 (DWORD)

You probably noticed that \\PrintServer1\PrinterX has been transformed to ,,PrintServer1,PrinterX and became a key. Provider and LocalConnection always had the same value, "Server" was set to the name of the Print Server with '\\' prefix.

So far, so good. The rest is the tiring loop:

  • Spooler starts going through SIDS
  • Creates a new connection under HKCU\Printers\Connections,,PrinterServer1.PrinterX
  • Clones the 'Server', 'Provider', 'LocalConnection' settings from HKLM Connections key
  • Queries and creates a new port setting:

    HKLM\Software\Microsoft\Windows NT\CurrentVersion\Ports\NeXX: (Reg_SZ ),
    where Ne in my case went from Ne00 to Ne08
  • It then created an entry under devices that linked the port to printer:

    HKCU\Software\Microsoft\Windows NT\CurrentVersion\Devices\
    \\PrintServer1\PrinterX = Winspool,NeXX: (Reg_SZ)
  • In the next step, spooler creater a PrinterPort entry in the following form:

    HKCU\Software\Microsoft\Windows NT\CurrentVersion\PrinterPorts\
    \\PrintServer1\PrinterX = Winspool,NeXX:15,45 (Reg_SZ)
  • In the actual flow, HKCU was the last. Spooler actually started with SID S-1-5-19 (HKU\S-1-5-19\)and cycled through all SIDS under HKU but you get the point.
  • THEN, an important step comes: querying printer, getting a connection to it (I see a %windir%\System32\spool\PIPE file created), creating a registry key for printer and writing everything about that printer

    HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Providers\LanMan Print Services\Servers\PrinterServer1\Printers\PrinterX
So, that's gist of what spooler seems to be doing. 

What's the problem?

Well, the problem I identified was that during the startup Spooler service would something be slacking. The command I ran to add a per-machine printer connection would return 258. 
Whenever rundll32 returned 0, that process ended up creating an HKLM connection entry and printer showed up just fine for user.
If, however, rundll32 returned 258, no connection was created.

So, what does return code 258 mean? Answer comes from Alan Morris of Microsoft Windows Printing team:

"this is the error
C:\>winerror 258
   258 WAIT_TIMEOUT <--> No NTSTATUS matched
printui does not validate the data that you are adding to the registry."
He goes on to explain:

"I'd check the number of printers and drivers as well as the drivers installed on a machine where this is failing.  The spooler may not be fully online and printui does call into spooler for these methods." 

Well, all good, but I had seen services.exe kicking off spooler service a full minute before any commands were issued to it. Additionally, spooler service would almost always map the first printer but the number of printers it connected was varying each time.

I was using the same XP VM and got considerably different results (sometimes tons of return code 258 errors, sometimes just 1 or two).

Techs in the field were reporting a similar pattern and a quick  Google search also got many hits in terms of people complaining about success rate of using these commands in the startup scripts.

What do we do now?

Well, as I said in the beginning, my approach was to help the spooler service a bit by creating the HKLM connections registry keys for it. In my tests, it worked like a charm (YMMV) every single time.

Below is modified sample script


on error resume next
Const ForReading = 1, ForWriting = 2, ForAppending = 8, CanCreate=True
ScriptVersion="2013-08-13-02"

Set oShell = CreateObject("WScript.Shell")
oShell.LogEvent 0, "WinXP_WSStartup.vbs: Script Version: " & ScriptVersion

'Get group membership for the machine
Set objSysInfo = CreateObject("ADSystemInfo")
Set objComputer = GetObject("LDAP://" & objSysInfo.ComputerName)

Groups = objComputer.GetEx("MemberOf")
' If Above Command does not find any groups for computer, it will error
if  Err.Number <> 0 Then  
 oShell.LogEvent 0, "WinXP_WSStartup.vbs: Computer is not member of any AD groups. Exiting."
 Wscript.Quit
end if

isFirst=vbTrue
Count=0
For Each group In Groups
  group=Replace(group,"/","\/")   ' escape groups with a slash in the object name   
  Set objGroup = GetObject("LDAP://" & group)
  If ( Instr(LCase(objGroup.CN),"grp.prn." ) > 0 ) then          
  If isFirst Then
   PrinterGroups = objGroup.CN
   isFirst=vbFalse
   Count=1
  Else
   PrinterGroups = PrinterGroups & ";" & objGroup.CN
   Count=Count+1
  End If   
  End if 
  'AllGroups = AllGroups & ";" & objGroup.CN
Next 

if (Count > 0) Then
   
 'Take care of setting up Reg File
 Set oFSO = CreateObject("Scripting.FileSystemObject") 
 sRegFile = oShell.ExpandEnvironmentStrings("%TEMP%")  & "\WinXP_WSStartup_PrinterConnections.reg" 
 If oFSO.FileExists(sRegFile) Then
  oFSO.DeleteFile(sRegFile)
 End If 
  
 Set oRegFile = oFSO.OpenTextFile(sRegFile, ForAppending, CanCreate)
 oRegFile.WriteLine "Windows Registry Editor Version 5.00" & vbCrLF & vbCrLF
  
 ' Now let's process each printer grou[
 
 arrPrinterGroups=Split(PrinterGroups,";") 
 for each PrinterGroup in arrPrinterGroups 
  arrGroup = Split(PrinterGroup,".")
  ' skipping first 2 grp and prn as groups are named grp.prn.ServerName.PrinterName
  Server=arrGroup(2)
  Printer=arrGroup(3)
  
  '' Update registry file
   oRegFile.WriteLine("[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Connections\,," & Server & "," & Printer & "]" )
   oRegFile.WriteLine(chr(34) & "Server" & chr(34) & "=" & chr(34) & "\\\\" & Server & chr(34))
   oRegFile.WriteLine(chr(34) & "Provider" & chr(34) & "=" & chr(34) & "win32spl.dll" & chr(34))
   oRegFile.WriteLine(chr(34) & "LocalConnection" & chr(34) & "=dword:0000001" & vbCrLf)

  ''
'  cmdConnectPrinter="%comspec% /c rundll32 printui.dll,PrintUIEntry /ga /n\\" & Server & "\" & Printer
  oShell.LogEvent 0, "WinXP_WSStartup.vbs: Connecting to Printer: " & Printer & " on Server: " & Server
'  oShell.LogEvent 0, "WinXP_WSStartup.vbs: Running " & cmdConnectPrinter
  
  ' Hide Window and return immediately without waiting
'  oShell.Run cmdConnectPrinter,0,false
   
 Next 
 
 oRegFile.close
 
 ' Import Printer Connection file silently
 oShell.Run "regedit /s " & Chr(34) & sRegFile & Chr(34), 0, True 
 
End If

oShell.LogEvent 0, "WinXP_WSStartup.vbs: Nothing else to do. Exiting" 
Set oShell=Nothing

2011-09-14

What's coming in Server 8

In his latest newsletter, Mark Minasi has a wonderful summary of features coming in Windows Server 8 from BUILD event that took place this week.

Windows 8 Dynamic Access Control (DAC) seems quite interesting and is a clear indication that Microsoft is trying to respond to an every day problem of 'permissions and auditing' in large enterprises. To be honest, I am not sure tagging is the answer to it, mainly because it's an attempt to use some of the unused attributes in AD and in file tags in NTFS, which may prove to be limited once enterprises starts being creative in employing the technology. However, the simple fact that it will be possible to use Regular Expressions on file ACLs is a welcome  news.

We will have to wait and see the implementation details. In the meantime here is an article published today at Windows IT Pro by Sean Deuby that explains the DAC in more details.

PowerShell is of course getting a bigger pie in server management (e.g. Active Directory Admin Center a.k.a. ADAC) with Version 3. Number of cmdlets are going from ~300 to 2300!

Speaking of AD, there does not seem to be much news other than making it Virtualization friendly. If you are still waiting for a SQL/Database driven directory, don't! It's not coming yet!

2011-09-12

Writing Binary Data to Registry

Uh, oh! I found yet another post in drafts from 2007. I do not recall the events but posting it for common good :)

**************************************************************

Yesterday, a friend from work showed me an interesting script he was working on. His script was reading a reg_binary type registry key, modifying its value and was 'attempting' to write it back to registry.

There was an issue with 'writing back to registry'. He was using SetBinaryValue method to write an array, which had modified values, back to registry but vbscript kept on complaining there was a "type mismatch" for this line:

Return = oReg.SetBinaryValue(HKEY_LOCAL_MACHINE, strKeyPath & "\" & subKey, strValueName, arrValues

if he set the arrValues to a static array like this

arrValues=Array(1,2,3,4,5,6)

script worked without any issues.

I took the code and tried to figure out what was wrong with it. I would like to write down a couple of key points for those people who are trying to do something similar.

* When we are talking about Binary data in Registry, we are actually referring to Hexadecimal values, because that's the Registry-speak (1984 anyone?). We can use GetBinaryValue Method of WMI's StdRegProv class. Output is "an array of binary bytes"

* However, binary bytes (hex values) are not meaningful to us, so if we are reading it to, let's say, modify a value, we will probably want to convert it to string using CHR function, which returns a character associated with the specified ANSI character code. I.e. a decimal value between 0..127 (see ascii table).

Also, although registry speaks in hex as far as binary data is concerned, "SetBinaryValue" method does not understand Hex

Consider the following Reg Key/Value (pasting from exported .reg):

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Adobe\Adobe Acrobat\7.0\FeatureLockDown\cDefaultLaunchURLPerms]"

sSchemePerms2"=hex:76,65,72,73,69,6f,6e,3a,31,7c,73,68,65,6c,6c,3a,33,7c,68,\ 63,70,3a,33,7c,6d,73,2d,68,65,6c,70,3a,33,7c,6d,73,2d,69,74,73,3a,33,7c,6d,\ 73,2d,69,74,73,73,3a,33,7c,69,74,73,3a,33,7c,6d,6b,3a,33,7c,6d,68,74,6d,6c,\ 3a,33,7c,68,65,6c,70,3a,33,7c,64,69,73,6b,3a,33,7c,61,66,70,3a,33,7c,64,69,\ 73,6b,73,3a,33,7c,74,65,6c,6e,65,74,3a,33,7c,73,73,68,3a,33,7c,6a,61,76,61,\ 73,63,72,69,70,74,3a,31,7c,76,62,73,63,72,69,70,74,3a,31,7c,61,63,72,6f,62,\ 61,74,3a,32,7c,6d,61,69,6c,74,6f,3a,32,7c,66,69,6c,65,3a,32,00

If we convert it to string, we get something like

version:1shell:3hcp:3ms-help:3ms-its:3
ms-itss:3its:3mk:3mhtml:3help:3disk:3
afp:3disks:3telnet:3ssh:3javascript:1
vbscript:1acrobat:2mailto:2 file:2

Then, you will need to change
mailto:2
 to
mailto:3
 with vbscript's REPLACE funtion

sNewValue = replace(sOldValue,"mailto:2","mailto:3", 1, -1 , 1)


* SetBinaryValue method is used to write "an array of binary data values" to registry. What is misleading here, and this was the key to solving our issue, is that method actually needs a variant or you will get type mismatch.

So this code works :

'Assumes objRegistry is a valid StdRegProv object.On Error Resume Next
Const HKEY_LOCAL_MACHINE As Long = &H80000002
Dim lRC As Long
Dim sPath As String
Dim uBinary() As Variant
sPath = "SOFTWARE\MyKey"
uBinary = Array(1,2,3,4,5,6,7,8)

lRC = objRegistry.SetBinaryValue(HKEY_LOCAL_MACHINE, sPath, "MyBinaryNamedValue", uBinaryData)

If (lRC = 0) And (Err.Number = 0)  Then   

  'Do something

Else    

  'An error occurred

End If 
* Pay attention to Array function, which returns a Variant containing an array as mentioned in MS documentation:

"A variable that is not declared as an array can still contain an array. Although a Variant variable containing an array is conceptually different from an array variable containing Variant elements, the array elements are accessed in the same way."

2010-12-24

Screenshot Capturing App

Just wanted to drop a note about a nifty little app called "Greenshot"... Recently, I was writing an article and needed to take some screenshot and do minimal editing to make instructions clearer.
Of course, Snag-it, from TechSmith is the leader and hands down the best tool for such things but it's not free and I could not justify buying it to myself as I rarely need to use it at home.

Greenshot is a free, open-source alternative, that did the job well. When you launch it, you will notice a new tray icon ( menu is shows on the left). It then detects when you hit "Print Screen" key in Windows, and allows you to specify the area you would like to capture. Once done, you can put annotations like some text, arrows, and shapes (circle some text, etc.) and save it as .png or .jpg (is anything else being used these days?)

That's it. If you are looking for an alternative on Windows, give it a shot.

2009-08-07

The 40 most popular SA tools

Sunbelt's NTSYSADMIN was one of my favorite lists for years. These folks gathered a list of 40 most popular SA tools. Take a look.

2006-12-13

Utility: Process Tamer

Windows only: Freeware system tray app ProcessTamer is a lightweight CPU usage monitoring tool that watches your computer for CPU hogs and "tames" them before they freeze up your computer.

Read full article here.

2006-11-30

Vista Aero at last!


When I installed Vista, I was disappointed to see that my Windows Experience Index was 1.0 I was disappointed because I have a pretty decent machine:
  • HP XW6000 Workstation
  • 2 x 2.8 GHz P4 processor
  • 1 GB DDR 266Mhz RAM
  • 2 x 750GB SATA drives
  • nVidia 980XGL video card with 128 MB ram & dual DVI output
So, what was wrong in this picture? Why was I getting such a bad score? Well, apparently, Windows Experience Index base score is determined by the lowest number. And what was that? Yep, you probably guesses it right. It was my video card.

It looks like I fell behind times in video cards department and I felt that this was a good time to catch up. So, I did some research. As always, Tom's Hardware Guide on graphic cards was pretty useful.

I ended up ordering an AGP 8x, ATI Radeon GDDR3 X800 XT with 256 MB ram from TigerDirect.com for $168 (I checked again now and they are not selling it anymore, maybe I got the last one :). It's all-in-one card, although I am not currently planning to use its TV capture capabilities.

I received in just two days. When I installed it though, my machine did not boot, all I was hearing was 2 long beeps. I put back the old card and got on internet to talk to HP. They simply told me that
- "ATI Radeon X800 XT card is not tested on xw6000 and is not supported".

In fact, HP guy told me that there was a problem between it and the motherboard but I kept on asking questions and he confessed that he was not sure of that and suggested I try the *newest* BIOS which was 2 years old and I was already running that.

I started tinkering with BIOS settings and one setting got my eye "AGP Aperture Size". It was set to 64MB. I did not know what it was for but when I saw someone mentioning it to fix a video card issue (not ATI), I realized that I had to change it to 256MB = what my new card had & voila!

To my surprise, Vista detected the card and installed the driver for it. Then, I checked my Windows Experience Index again: 4.3

This time, it was my 1Gig memory that was taking me down but hey, I have all the bells and whistles of Aero now :) I am quite disappointed with Doom 3 though, it looks better but not way better...

BTW, to see your Windows Experience index, do the followings:
Start > Run
control.exe system

Lastly, I wrote about BlackBerry on Vista yesterday. I checked the device in the morning and as expected it was fully charged.