Sunday, October 24, 2010

Estrarre il contenuto di un WSP via PowerShell

Lavorando per un progettino che penso nel giro di qualche settimana verrà pubblicato su CodePlex (per ora non posso dire di più) avevo la necessità di estrarre il contenuto di un WSP su file system. Come sappiamo i WSP sono dei CAB rinominati e l'estrazione del contenuto non è cosi semplice se effettuata tramite C# o VB.NET forse ancora meno com PowerShell. Queste almeno erano le sensazioni che avevo prima di approcciare il problema.
Ho fatto qualche ricerca ottenendo questi risultati:
  • molti suggerivano di farlo a mano da file system
    [grazie ottimo suggerimento! :X]
  • lavorare direttamente in C++ o di sfruttare l'SDK per interagire con questo formato. [soluzione un pò lunga, preferirei trovare qualcosa di più veloce]
  • alcuni hanno realizzato delle librerie managed da usare come facilitatori vedi http://www.codeproject.com/KB/files/CABCompressExtract.aspx
    [interessante esempio, ma serve portare dentro tutta sta libreria per solamente estrarre dei file da un CAB? Magari no]
  • creare un progetto VS e aggiungere una reference a Shell Automation COM component. Come suggerito sul questo thread http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/58d9cf34-9a43-4d58-96c7-44bff69ca5d4
    [devo comunque compilare con VS. Tra l'altro lo snippet segnalato non espande il CAB tenendo in considerazione la struttura folder/subfolder originaria)
  • usare un powershell che si occupa di estrarre il contenuto del CAB su un path generando la struttura a subfolder in esso contenuta. Ho trovato lo snippet powershell sul KB 2292741 "How to manually update scan engines in Microsoft Forefront Protection for Exchange Server or Microsoft Forefront Protection for SharePoint" (non fatevi inflenzare dal titolo del KB :-)
    [finalmente! era quello che stavo cercando! tempo risparmiato: un'enormità!]

Per semplicità riporto la funzione PowerShell che si occupa di estrarre il contenuto di un CAB (WSP rinominato) su file system.

# Use the Shell.Application COM object to extract the
# contents of the sourceCabPath and put the contents into
# the destinationDirectory. Support is included for cab
# files with sub directory hierarchies.
function ExtractCab($sourceCabPath, $destinationDirectory)
{
    # Determine if we can call the expand.exe utility
    # if so use it, otherwise, use the Shell.Application
    # COM object to perform the expansion of the CAB   
    & "expand.exe"
    
    if($?)
    {
        
        & "expand.exe" "-R" $sourceCabPath "-F:*" $destinationDirectory
    }
    else
    {
    
        $shell = new-object -comobject $ShellProgId

        if(!$?)
        {
            $(throw "unable to create $ShellProgId object")
        }

        $source = $shell.Namespace($sourceCabPath).items()

        $destination = $shell.Namespace($destinationDirectory)

        $flags = $DoNotDisplayProgress + $YesAll + $NoConfirmDirectory + $NoUI
        $itemCount = $source.Count
        $cabNameLength = $sourceCabPath.Length
        
        $cachedDestDir = ""    
        $relativeDest = ""
        
        # Process each item in the cab. Determine if the destination
        # is a sub directory and create if necessary.
        for($i=0; $i -lt $itemCount; $i++)
        {
            $lastPathIndex = $source.item($i).Path.LastIndexOf("\");
            
            # If the file inside the zip file should be extracted
            # to a subfolder, then we need to reset the destination
            if ($lastPathIndex -gt $cabNameLength)
            {
                $relativePath = $source.item($i).Path.SubString(($cabNameLength + 1), ($lastPathIndex - $cabNameLength))
                $relativeDestDir = $destinationDirectory + $relativePath
                
                if ($relativeDestDir -ne $cachedDestDir)
                {
                    $relativeDest = $shell.Namespace($relativeDestDir)
                    $cachedDestDir = $relativeDestDir
                }
                
                $relativeDest.CopyHere($source.item($i), $flags)
            }
            else
            {
                $destination.CopyHere($source.item($i), $flags)
            }
        }
    }
}

1 comment:

  1. Se il progettino é quello che penso io sei un grande!! ;-)

    ReplyDelete