Powershell 7 does not recognize Cells() method of Excel module

I have a PS script which has been functioning rather well under PS 5.1. When I started testing it under PS 7, it also worked. Suddenly and only today April 19, 2020, did I encounter a surprising message (repeated hundreds of times because it is inside a loop) when running in PS 7:

Invalid operation: C:\Users\myuserid\xxxx (details omitted)
$sheet.cells($row,2).Value = $_.FullName
Method invocation failed because [System._ComObject] does not contain a method named ‘Cells’.

I immediately re-ran the exact same script under PS 5.1 and everything worked without any error messages. What’s surprising to me is that a few (2 maybe 3) weeks earlier this script which was never changed was also running well under PS7. The database from which the data are being run are also identical.

Would be grateful for any advice, tips or guidance.

Thanking you all in advance.

 

Does it do the same when ran both with and without admin?

Yes, both were run as Administrator. (I have gotten into the habit of always running as Administrator, but before I sent this reply, I re-ran both again, as Administrator, just to be sure).

Much thanks for your response.

I was asking if it error in 7 on both admin and non admin. You answered my question, what I was thinking is if excel is running not as admin then powershell as admin can’t read it, in my experience. If you ran normal powershell as admin and it work, then testing 7 in admin is equivalent.

Yes, on PS 7, the same error message comes up whether running as Admin or as non-Admin.

In PS 5.1, the error never occurs whether running as Administrator or not.

Thanks again for your attention and kind consideration.

I suggested this to another person. If you’re going to be running it on windows and you just want to invoke it from 7, you could consider this.

PowerShell 7.0 marks a move a to .NET Core 3.1, enabling significantly more backwards compatibility with existing Windows PowerShell modules. This includes many modules on Windows that require GUI functionality like Out-GridView and Show-Command, as well as many role management modules that ship as part of Windows.

For Windows, a new switch parameter UseWindowsPowerShell is added to Import-Module. This switch creates a proxy module in PowerShell 7 that uses a local Windows PowerShell process to implicitly run any cmdlets contained in that module. For more information on Import-Module.


https://docs.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-70?view=powershell-7

Thank you Mr Maurer. much appreciated.

Hello Mr Maurer,

I executed the following command:

PS C:\Users\MYacct\Documents > import-module importexcel -UseWindowsPowershell | install-module

and got a warning, which did not lead to Install-module execution:

WARNING: Module importexcel is loaded in Windows PowerShell using WinPSCompatSession remoting session;
please note that all input and output of commands from this module will be deserialized objects.
If you want to load this module into PowerShell Core please use 'Import-Module -SkipEditionCheck' syntax.

Rather than do it right away as suggested in the warning: i.e., run with the ‘-SkipEditionCheck’ parameter, I thought it safer to check with you before proceeding. Would appreciate your comments and guidance.

Many thanks.

All that is saying is that the objects returned are going to be deserialized. The object is not a live object so I’m not sure you’ll be able to set properties, you’ll have to see. I noticed that importexcel imports into powershell 7 fine. Could you possibly share some of your script so I could play with it? As far as -skipeditioncheck I’m not sure it matters since it imports fine in 7.

This is the difference between deserialized and not.

With -UseWindowsPowershell

$excel = Open-ExcelPackage C:\temp\excel1.xlsx
$excel | gm

TypeName: Deserialized.OfficeOpenXml.ExcelPackage

Name               MemberType   Definition
----               ----------   ----------
GetType            Method       type GetType()
ToString           Method       string ToString(), string ToString(string format, System.IFormatProvider formatProvide…
PSComputerName     NoteProperty string PSComputerName=localhost
PSShowComputerName NoteProperty bool PSShowComputerName=False
RunspaceId         NoteProperty guid RunspaceId=bb5b43b9-249c-4b35-977c-9c8bf5bce5a6
Sheet1             NoteProperty Deserialized.OfficeOpenXml.ExcelWorksheet Sheet1=Sheet1
Compatibility      Property     System.String {get;set;}
Compression        Property     System.String {get;set;}
DoAdjustDrawings   Property     System.Boolean {get;set;}
Encryption         Property     System.String {get;set;}
File               Property     System.String {get;set;}
Package            Property     System.String {get;set;}
Stream             Property     System.String {get;set;}
Workbook           Property     System.String {get;set;}

And without it.

$excel = Open-ExcelPackage C:\temp\excel.xlsx
$excel | gm

TypeName: OfficeOpenXml.ExcelPackage

Name             MemberType     Definition
----             ----------     ----------
Dispose          Method         void Dispose(), void IDisposable.Dispose()
Equals           Method         bool Equals(System.Object obj)
GetAsByteArray   Method         byte[] GetAsByteArray(), byte[] GetAsByteArray(string password)
GetHashCode      Method         int GetHashCode()
GetType          Method         type GetType()
Load             Method         void Load(System.IO.Stream input), void Load(System.IO.Stream input, string Password)
Save             Method         void Save(), void Save(string password)
SaveAs           Method         void SaveAs(System.IO.FileInfo file), void SaveAs(System.IO.FileInfo file, string pass…
ToString         Method         string ToString()
Compatibility    Property       OfficeOpenXml.Compatibility.CompatibilitySettings Compatibility {get;}
Compression      Property       OfficeOpenXml.CompressionLevel Compression {get;set;}
DoAdjustDrawings Property       bool DoAdjustDrawings {get;set;}
Encryption       Property       OfficeOpenXml.ExcelEncryption Encryption {get;}
File             Property       System.IO.FileInfo File {get;set;}
Package          Property       OfficeOpenXml.Packaging.ZipPackage Package {get;}
Stream           Property       System.IO.Stream Stream {get;}
Workbook         Property       OfficeOpenXml.ExcelWorkbook Workbook {get;}
Sheet1           ScriptProperty System.Object Sheet1 {get=$this.workbook.Worksheets["Sheet1"];}

So if you need to use one of those missing methods then this will not work. I’m not sure if there is an easy way to “rehydrate” the object in this case. Hopefully someone else has more info on that. In the meantime if I get a chance I’ll see if I can reproduce your error.

I have simplified the original script to its “bare essentials” so your efforts will not be wasted. Insofar as the error message I am getting is concerned, the following stripped down version works in PS 5.1 but fails on PS 7. I fully understand your most recent explanation, there is no way this script can work or be made to work in PS 7 because the Object Type returned in PS5.1 is not the same as that in PS7. I suppose this is due to .Net Core in PS7.

There is an Excel template file that is part of the script that the script reads in. I cannot see a mechanism in this forum to attach a file. Thanking you for your assistance and kind consideration. The code (about 55 lines) :

<#
Sample script involving EXCEL ComObject in PS 7.
#>
# ------------------------------------------------------------
# Location of Excel template file
#
$InpExcel = 'C:\Users\xxt35\Documents\Excel_Template.xlsx'

# ------------------------------------------------------------
# Preparations for Excel output file
# (Worksheet is password protected, pw='abc')
#
$Excel = New-Object -Comobject Excel.Application
$Excel.Visible = $True
$Excel.DisplayAlerts = $False
$book = $Excel.Workbooks.Open($InpExcel)
$sheet = $book.Worksheets.Item('PPWKS')
$sheet.Unprotect('abc')

# -------------------------------------------------
# Blank out target data rows & columns
#
$sheet.Range("B3:B3").ClearContents | Out-Null
$sheet.Range("B4:B4").ClearContents | Out-Null
$sheet.Range("B6:B10").ClearContents | Out-Null
$sheet.Range("C6:C10").ClearContents | Out-Null
$sheet.Range("D6:D10").ClearContents | Out-Null
$sheet.Range("E6:E10").ClearContents | Out-Null

# -------------------------------------------------
# Data to be inserted into Excel file
#
$List_of_Employees = @{"John"=10; "Bob"=20; "Tom"=30; "Roy"=40}

# -------------------------------------------------
# Insert the data into the Excel template file
# One employee per row, starting in Row 6.
#
$row = 6
foreach ($employee in $List_of_Employees.Keys) {
    $sheet.Cells($row,2).Value = $employee
    $sheet.Cells($row,4).Value = $List_of_Employees.Item($employee)
    $row++
}

# -------------------------------------------------
# Save result to a new output file

$SaveTo = 'C:\Users\xxt35\Documents\Excel_WithData.xlsx'
If (Test-path -path $SaveTo) {Remove-item $SaveTo -force}
$sheet.Protect('abc')
$sheet.SaveAs($SaveTo,51)
$Excel.Workbooks.Close()
$Excel.Quit()

 

Hello Mr Maurer,

I have a stripped down version of the script that you asked for, in order that your efforts are not wasted.

I fully understand your most recent reply, and know fully well that in PS7 which uses .Net Core, my stated problem cannot be solved, if only because PS 5.1 returns a COM Object Type different from the one returned under PS 7.

As a layman/novice I cannot understand why Microsoft would want to issue a new Powershell that is “less than” the functionality of an older version. I am sure there will be profound explanations in the various blogs out there, if I’m lucky enough to find them. I am clearly missing something fundamental.

The script that reproduces that same error is shown below. The script reads an EXCEL template file (.xlsx). I cannot find a way to attach this file so my reply/post. It seems the PS Forum does not have such a feature.

#
# Sample script involving EXCEL ComObject in PS 7.
#
# ------------------------------------------------------------
# Location of Excel template file
#
$InpExcel = 'C:\Users\xxt35\Documents\Excel_Template.xlsx'

# ------------------------------------------------------------
# Preparations for Excel output file
# (Worksheet is password protected, pw='abc')
#
$Excel = New-Object -Comobject Excel.Application
$Excel.Visible = $True
$Excel.DisplayAlerts = $False
$book = $Excel.Workbooks.Open($InpExcel)
$sheet = $book.Worksheets.Item('PPWKS')
$sheet.Unprotect('abc')

# -------------------------------------------------
#    Blank out target data rows & columns
#
$sheet.Range("B3:B3").ClearContents | Out-Null
$sheet.Range("B4:B4").ClearContents | Out-Null
$sheet.Range("B6:B10").ClearContents | Out-Null
$sheet.Range("C6:C10").ClearContents | Out-Null
$sheet.Range("D6:D10").ClearContents | Out-Null
$sheet.Range("E6:E10").ClearContents | Out-Null

# -------------------------------------------------
#   Data to be inserted into Excel file
#
$List_of_Employees = @{"John"=10; "Bob"=20; "Tom"=30; "Roy"=40}

# -------------------------------------------------
#   Insert the data into the Excel template file
#   One employee per row, starting in Row 6.
#
$row = 6
foreach ($employee in $List_of_Employees.Keys) {
    $sheet.Cells($row,2).Value = $employee
    $sheet.Cells($row,4).Value = $List_of_Employees.Item($employee)
    $row++
}

# -------------------------------------------------
# Save result to a new output file

$SaveTo = 'C:\Users\xxt35\Documents\Excel_WithData.xlsx'
If (Test-path -path $SaveTo) {Remove-item $SaveTo -force}
$sheet.Protect('abc')
$sheet.SaveAs($SaveTo,51)
$Excel.Workbooks.Close()
$Excel.Quit()

 

Could you try adding the excel assembly before the rest of the script?

[Reflection.Assembly]::LoadFile('C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.Excel\15.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Excel.dll')

Please note you may need to locate where your Microsoft.Interop.Excel. This code should find it for you.

$exceldll = Get-ChildItem -path c:\windows\assembly -Include microsoft.office.interop.excel.dll -Recurse -ErrorAction SilentlyContinue | select -First 1

[Reflection.Assembly]::LoadFile($exceldll)

I hope this helps.

Yeah that didn’t work. For some reason PS7 is considering Cells() a method instead of a property. Even though it shows it as a property with get-member

Cells Property Range Cells () {get}

In regular powershell you can run these

$sheet.Cells($row,4).Value | gm

TypeName: System.Management.Automation.PSParameterizedProperty

$sheet.Cells($row,4).Value.issettable
True

In PS7

$Sheet.Cells($row,4) | gm
InvalidOperation: Method invocation failed because [System.__ComObject] does not contain a method named 'Cells'.

$Sheet.Cells | gm

In fact the second command caused PS7 to hang, requiring ctrl + c. Could you possibly use range?

I’m not sure if you could use range or not, but at least it’s working in PS7 as your clearcontents command and the following output shows.

$Sheet.Range('a1','a4').value

IsSettable : True
IsGettable : True
OverloadDefinitions : {Variant Value (Variant RangeValueDataType)}
TypeNameOfValue : System.Object
MemberType : ParameterizedProperty
Value : Variant Value (Variant RangeValueDataType) {get} {set}
Name : Value
IsInstance : True

Dear Mr Maurer,

Many thanks for your responses. I am right now overseas (and will be for a while due to Covid19) and the Internet and telephone infrastructure is very poor. I saw your messages briefly by email and couldn’t even scroll down to read the rest. I’ve attempted half a dozen Logins in the last 48 hours and always ended up failing in various degrees. Now (12 AM, Manila time, April22) I finally got in safely and haven’t been cut off yet. SO I am quickly printed all your messages before I get cut off. I will revert with feedback after I try all your suggestions. My sincerest gratitude for your inputs and kind consideration.

 

Well when you get this message, I’m not sure you’re going to be able to run this script in PS7 yet. Hopefully someone else has better information. Stay safe and good luck.

Thanks Mr Maurer.

(1) Range() fails when I put in the ($row,$column).Value line of code – the error message suggests I am performing an invalid operation on a ParametrizedProperty.

(2) The following link suggests using the Add-Type -AssemblyName approach:

https://stackoverflow.com/questions/59045043/powershell-6-doesnt-find-excel-interop-powershell-5-1-does

I tried it various times, first with -AssemblyName, then with -Path and -LiteralPath, but it didn;t work.

In any case, my “solution” (if it can be called one!) is to just stay with PS 5.1 since it works there.

Thanks again for your tips and guidance.