Someone asked in the #PowerShell channel on FreeNode for a way to register dll’s (you know, old COM libraries for use in scripts or apps) ... specifically, they wanted to create a script that could register a bunch of Dll files, and know which ones passed or failed.

It turned out to be an interesting problem, because even though the source code on the MSDN Library has a /C parameter to cause the application to write to stdout, the one which comes with windows doesn’t, so the only obvious way to get output is in a MessageBox. However, there is also an exit code, but the exit code doesn’t seem to end up in $LASTEXITCODE or $? at all (maybe because the app is actually not a console app? I don’t get it, really). So the real problem became: how do I capture the exit code of an application in PowerShell?

[new] Someone sent me an email today (Jan 7 2010) mentioning that if you nest your call to regsvr32 inside a call to cmd.exe … it does set the $LastExitCode:

cmd /c "regsvr32 /s 'C:\Path\To\Some.dll'"
$LastExitCode ## Will be set correctly.

Well, the simplest thing would be to just go around that — to compile and distribute the MSDN sample with the /c console output option so I don’t need the exit code. Of course, they are very clear that we shouldn’t do that because the sample isn’t “safe,” and distributing it afterward would probably be a problem with licensing. Well, I don’t know about that, but I figured the best thing would be to just use the exit code from the existing RegSvr32.exe rather than asking people to use what they would perceive as a 3rd party RegSvr32.

And if we play around a bit with the .Net Process and ProcessStartInfo classes, it’s not to hard to get the exit code …


#### Get-ExitCode.ps1
##########################################################
# initialize things
$p = new-object System.Diagnostics.Process
$si = new-object System.Diagnostics.ProcessStartInfo
$exit = $false

# split off the first argument (the command) from any others
$si.FileName, $si.Arguments = $args

$p.StartInfo = $si
if( $p.Start() ) {
  $p.WaitForExit()
  $exit = $p.ExitCode
}
$p.Dispose()
$exit
 

After a bit more experimentation with the return values, I came up with a function called RegisterDllServer which appears below. The function has a -Verbose switch and a -Debug switch which turn on some helpful messages (the Debug switch actually allows the RegSvr32 message boxes to show up, so you shouldn’t use that if you’re calling it from within a script).

So, to answer the original question, to use this to register all the dll’s in a folder you could just call it like ls *.dll | RegSvr32 using the alias that the script sets up at the end, or to be more complete, call it like: ls *.dll | % {$r=$(RegSvr $_); if($r -gt 0){ "FAILED: $_" } else { "SUCCEEDED: $_" }} … nice, eh?


#### RegisterDllServer.ps1
################################################################################
#### If you'd rather use it as a script:
#### * Comment out the first two and last two lines of script
#### * Uncomment the next line instead!
# param([string]$DllPath, [switch]$Uninstall = $false, [switch]$Verbose = $false, [switch]$Debug = $false)

function RegisterDllServer([string]$DllPath, [switch]$Uninstall = $false, [switch]$Verbose = $false, [switch]$Debug = $false)
{
  begin
  {
    $Write = &{if($Verbose -or ($VerbosePreference -ne "SilentlyContinue")) { "Write-Output" } else { "Write-Verbose" }}
  }
  process
  {
    if($_) { $DllPath = $_ }
   
    if(Test-Path $DllPath) {
      $p = new-object System.Diagnostics.Process;
      $si = new-object System.Diagnostics.ProcessStartInfo;

      $si.FileName = "regsvr32.exe";
      $si.Arguments = "$DllPath";
      if(!$Debug) {
        $si.Arguments = "/s " + $si.Arguments;
      }
      if($Uninstall) {
        $si.Arguments = "/u " + $si.Arguments;
      }

      $p.StartInfo = $si;
      $result = $p.Start();

      $p.WaitForExit();
      [int]$exit = $p.ExitCode;

      $p.Dispose();

      switch( $exit ) {
        0 { &$Write "$DllPath Registered Successfully" }
        1 { &$Write "Bad arguments to RegSvr32" }
        2 { &$Write "OLE initilization failed for $DllPath" }
        3 { &$Write "Failed to load the module ($DllPath), you may need to check for problems with dependencies." }
        4 { if($Uninstall) { &$Write "Can't find DllUnregisterServer entry point in the file ($DllPath), maybe it's not a .DLL or .OCX?" } else { &$Write "Can't find DllRegisterServer entry point in the file ($DllPath), maybe it's not a .DLL or .OCX?" } }
        5 {  &$Write "The assembly ($DllPath) was loaded, but the call to DllRegisterServer failed."
            if(!$Debug) {
              &$Write "Call RegisterDllServer again with the -Debug switch to see more information in a MessageBox."
            }
          }
        default { &$Write "Something went wrong, with Exit Code $exit!" }
      }
      return $exit;  
    } else {
      &$Write "Failed to find the file ($DllPath), please check the path."
      return 3;
    }
  }
}
Set-Alias RegSvr32 RegisterDllServer
 

5 Responses to “Register DLL – Calling RegSvr32 without MessageBoxes in PowerShell”

  • Nic Bedford says:

    I’m just getting into PowerShell, but this seems like a lot of work compared with the old cmd.exe way of achieving the same thing

    C:\> for %i in (*.dll) do regsvr32 /s %i

  • Calling regsvr32 with the /s silent switch does not do anywhere near the same thing — it just hides the message box entirely — you would have no idea if it worked or not. You could, of course, do that in PowerShell:

    ls *.dll|%{regsvr32 /s $_}

    The script I provided doesn’t have any pop-up MessageBoxes, but it does provide the results, including the appropriate error messages as console output, so you would know whether the registration succeeded or not, and if not, why.

  • Nic Bedford says:

    Thanks for the clarification Joel, I guessed I was missing something, but didn’t appreciate the subtlety of actually being able to access the return code :(

  • Peter Nimmo says:

    Ah, but you can test the return code of regsvr32, for example it returns 3 if the file doesn’t exist

  • Peter: what do mean? Yes, of course you can test the return code — that’s the whole point of this script…