0

I have a working powershell script to get the Windows product key from an machine in our network. The script runs well in powershell comand line but returns error when I try to run it from C#. I need this working to chart licenses in our corporation.

Powershell code:

function Get-WindowsKey {
$target = '$server'
$hklm = 2147483650
$regPath = "Software\Microsoft\Windows NT\CurrentVersion\DefaultProductKey"
$regValue = "DigitalProductId4"
$productKey = $null
$win32os = $null
$wmi = [WMIClass]"\\$target\root\default:stdRegProv"
$data = $wmi.GetBinaryValue($hklm,$regPath,$regValue)
$binArray = ($data.uValue)[52..66]
$charsArray = "B","C","D","F","G","H","J","K","M","P","Q","R","T","V","W","X","Y","2","3","4","6","7","8","9"
For ($i = 24; $i -ge 0; $i--) {
    $k = 0
    For ($j = 14; $j -ge 0; $j--) {
        $k = $k * 256 -bxor $binArray[$j]
        $binArray[$j] = [math]::truncate($k / 24)
        $k = $k % 24
    }
    $productKey = $charsArray[$k] + $productKey
    If (($i % 5 -eq 0) -and ($i -ne 0)) {
        $productKey = "-" + $productKey
    }
}
$productkey
}
Get-WindowsKey | Format-Table -AutoSize

Just replace $server with . or your IP

C# code that runs script:

private void RunWindowsKeyScript(string IP)
{
    try
    {
        Assembly keyAssembly = this.GetType().Assembly;
        ResourceManager keyResMan = new ResourceManager("LanMap.Resources.PS script", keyAssembly);
        string keyScript = keyResMan.GetString("WindowsKey");
        keyScript = keyScript.Replace("$server", IP);
        Runspace keyRunspace = RunspaceFactory.CreateRunspace();
        keyRunspace.Open();
        Pipeline keyPipeline = keyRunspace.CreatePipeline();
        keyPipeline.Commands.AddScript(keyScript);
        keyPipeline.Commands.Add("Out-String");
        Collection<psobject> keyResults = keyPipeline.Invoke();
        keyRunspace.Close();
        StringBuilder keyStringBuilder = new StringBuilder();
        foreach (PSObject obj in keyResults)
        {
            keyStringBuilder.AppendLine(obj.ToString());
        }
    }
    catch (Exception ex)
    {
        string s = "";
        s += " ";
    }
}

Please help if you can. I already know the error is that in C# GetBinaryValue returns null. I just can not make it work.

Richard
  • 106,783
  • 21
  • 203
  • 265
  • After fixing some compilation problems and using a different method of obtaining the `ResourceManager`, I found that your script/code executes just fine on my machine. What is the error that you are seeing? – Tung Jan 29 '13 at 08:58
  • For me $wmi.GetBinaryValue($hklm,$regPath,$regValue) returns null and the result is that the I get an exception "Can not insert into null array" or the result is "BBBBB-BBBBB-...BB" – Domocos Dorel Jan 29 '13 at 10:43
  • 1. What is the state of the pipeline `keyPipeline` after `Invoke`? (Specifically is `keyPipeline.PipelineStateInfo` `Failed` or `Stopped`?). 2. What does `keyStringBuilder` contain at the end of the `try` block? – Richard Jan 29 '13 at 10:56
  • Right now just after the Invoke it raises an error (goest to Catch) "Cannot index into a null array." – Domocos Dorel Jan 29 '13 at 11:02
  • If you say that is working fine for you can you help me find some way to get what is stoping it from running. As I sad in powershell commandline it is working but not from C#. Is thare anithing that may block the query of registry in my case (c# powershell). I suspect it may bee some network setting. – Domocos Dorel Jan 29 '13 at 11:10
  • What line does the exception throw on? (It should contain details of which statement and where in that statement: look at the other properties of the exception.) – Richard Jan 29 '13 at 11:25
  • Did you already checked following [page](http://www.codeproject.com/Articles/18229/How-to-run-PowerShell-scripts-from-C) or maybe this [page](http://stackoverflow.com/questions/11120452/run-powershell-script-from-c-sharp-application?rq=1)? – TimVK Jan 29 '13 at 08:11
  • Yes. Tried this also and no result. – Domocos Dorel Jan 29 '13 at 09:18
  • @Richard: 1. Keypipeline.PipelineStateInfo.State = Failed 2. result string is null. – Domocos Dorel Jan 29 '13 at 14:23
  • That makes sense if an exception is thrown: put in some proper logging of the exception to tell you where in the PSH script the error is occurring. – Richard Jan 29 '13 at 21:24
  • As I already told in the initial submit the error is generated by GetBinaryValue(). This is null when running in C# and I do not know why. – Domocos Dorel Jan 30 '13 at 06:18
  • Sorry missed that: please update the question with these details so the information is presented in a cohesive way. Next step: forget remoting for the moment: use `Get-ItemProperty` to directly read the registry (ie. bypass WMI). – Richard Jan 30 '13 at 08:28
  • Important question: How are you running the .NET code? (eg. console app from PowerShell console.) – Richard Jan 30 '13 at 08:50

2 Answers2

0

I'm not sure how you got this to work in script form with this:

$target = '$server'

Single quoted strings will not expand variables inside them. This should work just fine:

$target = "$server"

This would normally be sufficient $target = $server but since you're doing a string replace, it is best to stick with the double quotes.

Another possibility is that you're C# code is running as x86 and you're reading the WOW64 registry instead of the 64-bit registry. That might explain why you see different results between running the script and running via your C# app.

Keith Hill
  • 194,368
  • 42
  • 353
  • 369
  • That's not it, note the C# code does a string replace on `"$server"` to some parameter of the C# function. (But I think the root cause is some variable inherited from the PSH session when using the console that won't be present from C#.) – Richard Jan 29 '13 at 21:23
  • Yeah that's a bit of odd code - doing a string replace. Seems like it would be better to just make $server a parameter to the function & script and pass it through. – Keith Hill Jan 29 '13 at 23:20
  • Even with no replace, if all the text is inputed as string with static IP the error is thrown. – Domocos Dorel Jan 30 '13 at 06:16
0

Update: See bottom for theory.

Testing the PowerShell code from ISE: looks like GetBinaryValue is failing:

$target = 'localhost'
$hklm = 2147483650
$regPath = "Software\Microsoft\Windows NT\CurrentVersion\DefaultProductKey"
$regValue = "DigitalProductId4"
$productKey = $null
$win32os = $null
$wmi = [WMIClass]"\\$target\root\default:stdRegProv"
$data = $wmi.GetBinaryValue($hklm,$regPath,$regValue)

if ($data -eq $null) { Write-DEbug "Data is NULL"; return "STOP 2" }

write-debug '$Data'
write-debug ("$($data): " + ($data | gm | out-string))
write-debug ("$($data): " + ($data | fl * | out-string))

if ($data.uValue -eq $null) { Write-Debug "Data.uValue is NULL"; return "STOP 3" }
# rest cut for testing

results in:

DEBUG: $Data
DEBUG: System.Management.ManagementBaseObject: 

   TypeName: System.Management.ManagementBaseObject#\__PARAMETERS

Name             MemberType    Definition                      
----             ----------    ----------                      
PSComputerName   AliasProperty PSComputerName = __SERVER       
ReturnValue      Property      uint32 ReturnValue {get;set;}   
uValue           Property      byte[] uValue {get;set;}        
__CLASS          Property      string __CLASS {get;set;}       
__DERIVATION     Property      string[] __DERIVATION {get;set;}
__DYNASTY        Property      string __DYNASTY {get;set;}     
__GENUS          Property      int __GENUS {get;set;}          
__NAMESPACE      Property      string __NAMESPACE {get;set;}   
__PATH           Property      string __PATH {get;set;}        
__PROPERTY_COUNT Property      int __PROPERTY_COUNT {get;set;} 
__RELPATH        Property      string __RELPATH {get;set;}     
__SERVER         Property      string __SERVER {get;set;}      
__SUPERCLASS     Property      string __SUPERCLASS {get;set;}  



DEBUG: System.Management.ManagementBaseObject: 

PSComputerName   : 
__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     : 
__DYNASTY        : __PARAMETERS
__RELPATH        : 
__PROPERTY_COUNT : 2
__DERIVATION     : {}
__SERVER         : 
__NAMESPACE      : 
__PATH           : 
ReturnValue      : 2
uValue           : 
Properties       : {ReturnValue, uValue}
SystemProperties : {__GENUS, __CLASS, __SUPERCLASS, __DYNASTY...}
Qualifiers       : {}
ClassPath        : __PARAMETERS
Site             : 
Container        : 




DEBUG: Data.uValue is NULL
STOP 3

That ReturnValue: 2 corresponds to ERROR_FILE_NOT_FOUND (assuming the ReturnValue member is reflecting the return value of GetBinaryValue where 0 is success, otherwise standard Windows API error returns).

Update:

Under by ISE instance even get this:

PS [32] C:\ #50> gp "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DefaultProductKey"
gp : Cannot find path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DefaultProductKey' because it does not exist.
At line:1 char:1
+ gp "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DefaultProductKey"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (HKLM:\SOFTWARE\...faultProductKey:String) [Get-ItemProperty], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetItemPropertyCommand
 

But I can see the key... however a 64 bit instance of ISE and it works.

The key is only available (directly) to 64bit processes, the default in recent Visual Studio versions is for most applications to be built as 32bit (most applications don't need the extra address space), a 32bit .NET application will load the 32bit PowerShell runtime assemblies.

Have you tried building your application for 64bit?

PS. This question might help: Reading 64bit Registry from a 32bit application

Community
  • 1
  • 1
Richard
  • 106,783
  • 21
  • 203
  • 265