powershelldependenciesopenxmlnuget-package.net-assembly

OpenXml Dependency Loading issue


Loading all these package dependencies with repetitive try/ catch blocks:

$libPath = ".\packages\DocumentFormat.OpenXml.3.3.0\lib\netstandard2.0"
$libFPath = ".\packages\DocumentFormat.OpenXml.Framework.3.3.0\lib\netstandard2.0"
$libSPath = ".\packages\System.IO.Packaging.8.0.0\lib\netstandard2.0"
$libMPath = ".\packages\System.Memory.4.5.4\lib\netstandard2.0"
$libBPath = ".\packages\System.Buffers.4.5.1\lib\netstandard2.0\System.Buffers.dll"
$libVPath = ".\packages\System.Numerics.Vectors.4.5.0\lib\netstandard2.0\System.Numerics.Vectors.dll"
$libCSPath = ".\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll"


#[System.Reflection.Assembly]::LoadFrom("$memoryPath\System.Memory.dll") | Out-Null
#Write-Host "Loaded: System.Memory.dll"
### Load all DLLs in the folder via reflection
Get-ChildItem -Filter "*.dll" -Path $libBPath | ForEach-Object {
    try {
        [System.Reflection.Assembly]::LoadFrom($_.FullName) | Out-Null
        Write-Host "Loaded: $($_.Name)"
    }
    catch {
        Write-Warning "Failed to load $($_.Name): $($_.Exception.Message)"
    }
}

Then I try to open an excel file doing the following:

### Load path to excel file
try {
    $excelFile = (Resolve-Path "./excel.xlsx").Path
    Write-Host "Path resolved: $excelFile"
}
catch {
    Write-Error "The file doesn't exist!"
}

### Load/Open excel file
try{
    $doc = [DocumentFormat.OpenXml.Packaging.SpreadsheetDocument]::Open($excelFile, $false)
    Write-Host "File opened successfully"
    $doc.Close()
}
catch{

    Write-Host "Error during excel file processing: $_"
    Write-Host "Error during excel file processing .Message : $($_.Exception.Message)"
}

And receiving these exceptions:

Exception calling "Open" with "2" argument(s): "You cannot call a method on a null-valued expression." Error during excel file processing.
Message : Exception calling "Open" with "2" argument(s): "You cannot call a method on a null-valued expression."

Any ideas? Pretty much new to PowerShell.


Solution

  • The error implies you haven't loaded the OpenXml library or is not correctly loaded. The likable reason is that the dependencies package versions aren't compatible, the most reliable way to determine this is to use the dotnet CLI to download all dependencies, as shown in this answer.

    Using the CLI on a project I've created, these are the required packages with the corresponding resolved versions. As you may see, some packages have a different resolved version than the ones you have.

    PS \> dotnet list package --include-transitive
    
    # Project 'DocumentFormat.OpenXml.Package' has the following package references
    #    [netstandard2.0]:
    #    Top-level Package                Requested   Resolved
    #    > DocumentFormat.OpenXml         3.3.0       3.3.0
    #    > NETStandard.Library      (A)   [2.0.3, )   2.0.3
    #
    #    Transitive Package                            Resolved
    #    > DocumentFormat.OpenXml.Framework            3.3.0
    #    > Microsoft.NETCore.Platforms                 1.1.0
    #    > System.Buffers                              4.5.1
    #    > System.IO.Packaging                         8.0.1
    #    > System.Memory                               4.5.5
    #    > System.Numerics.Vectors                     4.4.0
    #    > System.Runtime.CompilerServices.Unsafe      4.5.3
    #
    # (A) : Auto-referenced package.
    

    Based on the above, the first step I'd recommend is to re-download all packages with the exact version the CLI is giving us. For that you can use below code that will place all required assemblies in a folder named DocumentFormat.OpenXml in your current directory:

    $ProgressPreference = 'SilentlyContinue'
    
    New-Item DocumentFormat.OpenXml -ItemType Directory | Push-Location
    
    $package = @(
        'DocumentFormat.OpenXml.3.3.0'
        'DocumentFormat.OpenXml.Framework.3.3.0'
        'System.Buffers.4.5.1'
        'System.IO.Packaging.8.0.1'
        'System.Memory.4.5.5'
        'System.Numerics.Vectors.4.4.0'
        'System.Runtime.CompilerServices.Unsafe.4.5.3'
    )
    
    $uri = 'https://api.nuget.org/v3/flatcontainer/{0}/{1}/{2}.nupkg'
    foreach ($pkg in $package) {
        $name, $version = $pkg -split '\.(?=\d)', 2
        $zip = $name + '.zip'
    
        $invokeWebRequestSplat = @{
            OutFile = $zip
            Uri     = $uri -f $name, $version, $pkg
        }
        # download the package
        Invoke-WebRequest @invokeWebRequestSplat
        # extract it
        Expand-Archive $zip
        # move dlls to the current directory
        Move-Item "$name\lib\netstandard2.0\*" -Destination . -EA 0
        # remove the zip file and the extracted zip folder
        Remove-Item $zip, $name -Recurse
    }
    
    Pop-Location
    

    Then you can try loading them all and testing with OpenXml.Packaging.SpreadsheetDocument, note here after testing, the class doesn't have a .Close() method, use .Dispose() instead:

    # load all assemblies
    Add-Type -Path .\DocumentFormat.OpenXml\*.dll
    # resolve the path to the excel file
    $excelFile = (Resolve-Path './test.xlsx' -ErrorAction Stop).Path
    # load the document
    $pkg = [DocumentFormat.OpenXml.Packaging.SpreadsheetDocument]::Open($excelFile, $false)
    # dispose it
    $pkg.Dispose()