Calling COM and .NET from PowerShell — Widening What Your Scripts Can Reach
· Go Komura · PowerShell, Windows, COM, .NET, Automation, Script, Legacy Asset Reuse, Business Efficiency
“I looked for a cmdlet, but there isn’t one for what I want to do.” — use PowerShell for a while and you will hit this wall without fail. Creating shortcuts in bulk, inspecting the contents of a ZIP without extracting it, manipulating windows, reading and writing Excel files. Requests that the standard cmdlets can’t reach come up constantly from in-house IT and operations staff at small and mid-sized companies.
The thing is, the other side of that wall is PowerShell’s home ground. Because PowerShell is built on top of .NET, you can call types from the .NET class library directly, with no extra installation. Beyond that, Add-Type lets you embed C# code or Win32 APIs (P/Invoke) on the spot, and New-Object -ComObject lets you drive COM objects such as WScript.Shell and Excel. Effectively, the entire “Windows automation” toolbox that used to be written in VBScript or VBA is available to you almost as-is.
That power comes with a duty to clean up afterwards, though. Excel COM automation in particular is the breeding ground for the classic “EXCEL.EXE is still running after the script finished” problem — and Microsoft doesn’t recommend automating Office in unattended runs at all. This article lays out the practical patterns and pitfalls of calling .NET, using Add-Type, and driving COM from PowerShell, and finishes with how to decide “how far do I push the script, and when do I turn it into a C# tool?”
1. The Bottom Line First
- PowerShell sits on top of .NET. Windows PowerShell 5.1 is built on the .NET Framework and PowerShell 7 on .NET (formerly .NET Core), and you can call .NET static methods directly, such as
[System.IO.Path]::GetFileNameWithoutExtension().1 - Create instances with
New-Object, or with[Type]::new()on PowerShell 5.0 and later. Type[Type]::new(without parentheses) and you get a list of constructors, so you can work out the arguments as you write.2 Add-Typecompiles C# source code on the spot and adds it to the session. Pass a DllImport signature and you can make P/Invoke calls into Win32 APIs. The added type can’t be removed from the session, and a type of the same name can’t be redefined.3- COM objects are created with
New-Object -ComObject <ProgId>. VBScript’sCreateObject("Shell.Application")maps directly ontoNew-Object -ComObject Shell.Application, so WSH-era tools such as creating shortcuts with WScript.Shell are available from PowerShell.45 - COM object lifetime is managed by reference counting, and on the .NET side the Runtime Callable Wrapper (RCW) holds those references. While a reference remains, processes such as Excel don’t exit. You can release explicitly with
Marshal.ReleaseComObject, but overusing it invites a different class of accident, so the official guidance is “only when you really have to”.67 - Microsoft states plainly that it “does not recommend and does not support” Office automation in unattended runs. Driving Excel through COM from a service, from Task Scheduler, or server-side is an unsupported configuration even when it appears to work. Replace unattended processing with Open XML libraries or CSV integration.8
- The types and methods available differ between 5.1 and 7. There are cases like the String.Split overload difference where the same code behaves differently, and in 5.1 you sometimes have to load GAC assemblies explicitly with
Add-Type.13 - When direct .NET calls start piling up, that’s the line at which to consider turning it into a C# tool. Once Add-Type’s inability to redefine types, or distribution problems, start getting in the way, the script is nearing its limits.
2. PowerShell Sits on Top of .NET — [Type]::Method and New-Object
PowerShell’s cmdlets are “parts of the .NET class library wrapped for convenient operational work”. The unwrapped functionality is also callable directly — just write the type name in square brackets. Static methods use ::; instance methods and properties use ..
# Call .NET static methods directly. No cmdlet needed, no extra installation
[System.IO.Path]::GetFileNameWithoutExtension('C:\data\orders_20260718.csv') # -> orders_20260718
[System.IO.Path]::Combine('C:\data', 'archive', '2026-07') # -> C:\data\archive\2026-07
[System.Math]::Round(123.456, 1) # -> 123.5
# If you need an instance, create it with New-Object or [Type]::new()
$list = [System.Collections.Generic.List[string]]::new()
$list.Add('server01')
# Typing ::new without parentheses returns the list of constructors (handy for finding arguments)
[System.IO.StreamWriter]::new
[Type]::new() is a syntax added in PowerShell 5.0. It is faster than New-Object and, as in the example above, lets you check the constructor overloads on the spot.2 There is a caveat, though. Objects returned by cmdlets such as Get-Item sometimes have additional properties (NoteProperty) attached by PowerShell, so their members may not match an object of the same type created directly with ::new().2 If you find yourself confused because “the property that was there when I got it via the cmdlet is missing”, remember this mechanism.
Here’s one practical recipe. For ZIP work there are Compress-Archive and Expand-Archive, but the ZipArchive API underneath them has a 2 GB file size limit9, and finer operations such as “just list the contents without extracting” have no cmdlet at all. That’s where .NET’s ZipFile class comes in.10
# On Windows PowerShell 5.1 you have to load the GAC assembly explicitly
# (on PowerShell 7 bundled assemblies load automatically on demand, so this line is optional)
Add-Type -AssemblyName System.IO.Compression.FileSystem
# Reading first — inspect the ZIP's contents without extracting
$archive = [System.IO.Compression.ZipFile]::OpenRead('C:\deploy\release.zip')
try {
$archive.Entries | Select-Object FullName, Length, LastWriteTime
}
finally {
$archive.Dispose() # make sure the file handle is returned
}
# If the contents look fine, extract. If a file of the same name exists at the destination,
# this two-argument overload throws part-way through extraction (it does not overwrite).
# Avoid extracting straight into an existing folder; extract into a fresh folder each time
# and then switch over — that's the safe pattern
$dest = "C:\apps\web_$(Get-Date -Format yyyyMMddHHmmss)"
[System.IO.Compression.ZipFile]::ExtractToDirectory('C:\deploy\release.zip', $dest)
On the .NET Framework, using the ZipFile class requires a reference to the System.IO.Compression.FileSystem assembly, and the same applies on PowerShell 5.1.10 On 7 the bundled assembly loads automatically on demand so it works without Add-Type, but writing it means the script works on both, so being explicit is the safer choice for shared scripts.3
3. Embedding Your Own C# and Win32 APIs (P/Invoke) with Add-Type
After “things that exist in .NET” come “things that don’t”. Pass C# source code to Add-Type and it is compiled on the spot, adding the type to your session. Go further and pass a DllImport signature to -MemberDefinition and you can call Win32 APIs through P/Invoke. This is a legitimate technique that appears as an example in the official documentation.3
# Notify me with a dialog when a long-running job I started by hand finishes
# (calls MessageBoxW in user32.dll through P/Invoke)
$signature = @'
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int MessageBoxW(IntPtr hWnd, string text, string caption, uint type);
'@
$native = Add-Type -MemberDefinition $signature -Name 'NativeMethods' `
-Namespace 'Win32' -PassThru
# Display it with MB_ICONINFORMATION (0x40)
[void]$native::MessageBoxW([IntPtr]::Zero, 'The backup has completed.', 'Long-running job', 0x40)
One warning. Modal dialogs are strictly for interactive runs where you are sitting in front of the desktop. In a non-interactive session such as an unattended Task Scheduler run or a Windows service, processing stops at a dialog nobody can close and never comes back. For unattended jobs, use notification mechanisms that don’t wait for a response — log files, the event log, sending email.
Add-Type has three operationally important constraints.3
- The added type is limited to that session. Another session, or a remote one, needs
Add-Typeagain. - A type of the same name can’t be redefined. If you got a signature wrong and want to fix it, change the name or start a new session. Do your experimenting in a throwaway console.
- On PowerShell 7, if a type of the same name already exists, compilation itself is skipped. Suspect this when “the code I supposedly fixed isn’t taking effect”.
One more thing, common to all P/Invoke work. Get the signature wrong — argument types, character set, calling convention — and instead of an exception you can take the whole process down. The pitfalls of marshalling strings and handles are covered in detail in “Safely Calling Win32 APIs from C# — A Practical P/Invoke Guide (DllImport / LibraryImport / CsWin32)”, which is worth reading before you start using Win32 APIs seriously through Add-Type.
4. Calling COM — New-Object -ComObject and the WSH Toolbox
COM is the family of components that has been shipped with Windows since long before .NET. You create them with New-Object -ComObject <ProgId>, and VBScript’s Set objShell = CreateObject("Shell.Application") maps directly onto $objShell = New-Object -ComObject Shell.Application.4 Objects originating in WSH (Windows Script Host), such as WScript.Shell, WScript.Network and Scripting.FileSystemObject, are available in the same way.5 For the underlying question of what COM actually is, see “What Are COM / ActiveX / OCX? - The Differences and Relationships Explained”.
A representative practical recipe is creating shortcuts in bulk. There is no cmdlet for creating a shortcut (.lnk), and the official documentation itself says that “some tasks, such as creating a shortcut, are easier using WSH classes” and gives a WScript.Shell example.5
# Scenario: distributing shortcuts to a tool on a shared folder to every machine's public desktop
$shortcuts = Import-Csv 'C:\deploy\shortcuts.csv' # three columns: Name, Target, Args
$wsh = New-Object -ComObject WScript.Shell
foreach ($item in $shortcuts) {
$lnkPath = Join-Path 'C:\Users\Public\Desktop' "$($item.Name).lnk"
$lnk = $wsh.CreateShortcut($lnkPath) # an existing .lnk will be overwritten
$lnk.TargetPath = $item.Target
$lnk.Arguments = $item.Args
$lnk.Save()
Write-Host "Created: $lnkPath"
}
You can inspect a COM object’s members with $wsh | Get-Member.5 Drafting email in Outlook, working with special folders through Shell.Application — the range of applications is enormous. But ActiveX executables such as Excel.Application (COM servers that launch in a separate process) come with the cleanup problem covered in the next section. The official documentation also cautions that whether the process exits when you release your reference depends on the application in question, and that you should test the exit behaviour before you rely on it.5
5. Cleaning Up After COM — Leftover EXCEL.EXE and “Unattended Office Is Unsupported”
5.1. Why the Process Stays Alive
COM object lifetime is managed by reference counting. When you touch COM from .NET (that is, from PowerShell), a proxy called an RCW (Runtime Callable Wrapper) is created for each COM object, and while the RCW is alive the reference on the COM side is not released. Reclaiming the RCW is left to garbage collection.6 In other words, even if you call $excel.Quit(), EXCEL.EXE won’t exit while a reference remains anywhere — including the RCW of an intermediate object you never assigned to a variable, as in $excel.Workbooks.Open(...).
$excel = New-Object -ComObject Excel.Application
try {
$excel.DisplayAlerts = $false
$books = $excel.Workbooks # assign intermediate objects too, so they can be released later
$book = $books.Open('C:\work\monthly-sales.xlsx')
$sheet = $book.Worksheets.Item(1)
$sheet.Cells.Item(1, 1).Value2 = "Updated: $(Get-Date -Format 'yyyy-MM-dd')"
$book.Save()
}
finally {
# The path where Close fails is exactly where EXCEL.EXE tends to survive,
# so use nested finally blocks to guarantee Quit and release
try {
if ($book) { $book.Close($false) }
}
finally {
# Even if Quit fails (Excel not responding, COM server disconnected...), always release the RCWs
try {
if ($excel) { $excel.Quit() }
}
finally {
# Explicitly decrement the RCW reference counts (children before parents)
foreach ($obj in @($sheet, $book, $books, $excel)) {
if ($obj) { [void][System.Runtime.InteropServices.Marshal]::ReleaseComObject($obj) }
}
# Insurance: let the GC reclaim any RCWs we failed to capture in a variable
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
[System.GC]::Collect()
}
}
}
Marshal.ReleaseComObject decrements the RCW’s reference count and releases the reference on the COM side when it reaches zero. The official documentation warns strongly, however, that accessing an already-released RCW causes exceptions or access violations, so it should be used “only when absolutely necessary”.7 The competing schools of release patterns (the ReleaseComObject camp and the GC camp), the two-dot rule trap, and how to identify processes that survive anyway are covered in detail in “Why EXCEL.EXE Processes Remain After C# Excel COM Automation — Reference Release Patterns and the Replacement Decision”. That article is about C#, but the RCW mechanism is identical in PowerShell.
5.2. Better Still: Don’t Use Office in Unattended Runs at All
There is a more fundamental decision to make. Microsoft states officially that it “does not currently recommend, and does not support, automation of Office from any unattended, non-interactive client application or component (including ASP, ASP.NET, DCOM, and NT services)”. Office is designed around interactive use, and in unattended environments it can behave unpredictably or deadlock.8 Concrete problems are listed too: processing stopping at an unexpected dialog, and the STA-based design not standing up to concurrent execution.8
So a configuration that drives Excel COM from a nightly Task Scheduler job or from a service is unsupported from the moment it starts working. For unattended processing, the recommendation is to switch to editing files directly with Open XML, or CSV integration — anything that doesn’t launch the Office application itself.8 Concrete PowerShell alternatives are collected in “Automating Excel and CSV Work with PowerShell — Practical Recipes for Aggregation, Reconciliation, and Report Output”, published alongside this article. Draw the line at using Excel COM only for “assisting a person’s work, on a desktop where a person is logged on”.
6. The .NET You Can Use Differs Between 5.1 and 7
Windows PowerShell 5.1 is built on the .NET Framework 4.5 line, and PowerShell 7 on .NET (formerly .NET Core).1 As long as you stick to cmdlets there are few occasions to notice the difference, but start calling .NET directly as in this article and the difference surfaces.
| Point | Windows PowerShell 5.1 | PowerShell 7 |
|---|---|---|
| Foundation | .NET Framework 4.5 line1 | .NET (7.4 is on .NET 8.0, updated per version)1 |
| Method overloads | Fewer (e.g. String.Split has 6)1 | More (the docs show the same Split('pq') producing different results)1 |
| Assembly loading | GAC assemblies often need explicit loading with Add-Type3 |
Bundled assemblies load automatically on demand3 |
| Things no longer available | — | Some Windows-only cmdlets such as *-EventLog have been removed1 |
Both “it worked on 5.1 but not on 7” and the reverse can happen. Any script that has to support both must be tested in both environments. The overall picture for migration decisions is covered in “The Differences Between Windows PowerShell 5.1 and PowerShell 7 — A Practical Guide to Migrating In-House Scripts”, published alongside this article.
7. Practical Rules of Thumb (Decision Table)
| Point | Options | How to judge |
|---|---|---|
| No cmdlet exists for what you want | Give up / Call the .NET class directly | Look for [Type]::Method first. System.IO, System.Text and System.IO.Compression fill most of the “so close” gaps1 |
| Creating an instance | New-Object / [Type]::new() | If you only support 5.0 and later, use ::new(). You get the constructor list, and it’s faster. COM is New-Object -ComObject only24 |
| You need a Win32 API | Do it by hand instead / P/Invoke via Add-Type | For a handful of APIs, Add-Type is plenty. A wrong signature takes the whole process down, so verify in a throwaway session3 |
| WSH-derived operations such as creating shortcuts | COM (WScript.Shell) | COM is still current where no cmdlet exists. The official sample uses this approach too5 |
| Excel work that assists a person | COM + rigorous cleanup | Quit, ReleaseComObject and GC as a set in finally. Assign intermediate objects to variables too76 |
| Excel/Office processing in an unattended job | COM / A method that doesn’t launch Office | Unattended Office automation is unsupported. Replace it with Open XML or CSV8 |
| The script has outgrown itself | Push on in PowerShell / Turn it into a C# tool | Once the Add-Type C# runs to hundreds of lines, the no-redefinition constraint gets in the way of development, or you don’t want a compile on every target machine — that’s the signal to move |
That last row is this article’s closing judgement. Once the C# you’re embedding with Add-Type starts to bloat, what you have is “a C# program wrapped in a PowerShell skin”. Moving it to a C# project — with Visual Studio’s type checking and debugger, NuGet packages, and unit tests — makes both development and maintenance faster. And since there are ways to call back into your PowerShell assets from C#, migrating doesn’t mean rewriting everything. “How to Run PowerShell from C# (CSharp) and Receive the Results as Objects” covers the bridging patterns.
8. Summary
- PowerShell is built on top of .NET, and you can call the .NET class library directly with
[Type]::StaticMethod,New-Objectand[Type]::new(). When there’s no cmdlet, .NET is the first place to look. Add-Typecompiles C# code on the spot, and passing a DllImport signature lets you call Win32 APIs through P/Invoke. Types are session-scoped, and a type of the same name can’t be redefined.- COM is driven with
New-Object -ComObject. WSH-derived tools such as shortcut creation are still current. - COM lifetime is managed with reference counting plus RCWs, and leftover references leave processes like EXCEL.EXE running. Write cleanup that covers Quit, ReleaseComObject and the GC in a finally block.
- Microsoft states plainly that unattended Office automation is neither recommended nor supported. Replace nightly jobs with methods that never launch Office.
- 5.1 and 7 sit on different .NET foundations, with different available types and overloads. Scripts supporting both must be tested on both.
- A bloated Add-Type block is the signal to turn the script into a C# tool. There are established types for bridging between a script and an executable.
Related Articles
- Why EXCEL.EXE Processes Remain After C# Excel COM Automation — Reference Release Patterns and the Replacement Decision
- Safely Calling Win32 APIs from C# — A Practical P/Invoke Guide (DllImport / LibraryImport / CsWin32)
- What Are COM / ActiveX / OCX? - The Differences and Relationships Explained
- How to Run PowerShell from C# (CSharp) and Receive the Results as Objects
- Automating Excel and CSV Work with PowerShell — Practical Recipes for Aggregation, Reconciliation, and Report Output
- The Differences Between Windows PowerShell 5.1 and PowerShell 7 — A Practical Guide to Migrating In-House Scripts
Related Consulting Areas
KomuraSoft LLC handles automating internal business processes with PowerShell, designing and reworking scripts that involve COM assets (Excel integration, legacy components), and turning scripts that have “outgrown themselves” into C# tools. We also take on bug investigations such as leftover EXCEL.EXE processes.
- Windows App Development
- Legacy Asset Reuse & Migration Support
- Bug Investigation & Root Cause Analysis
- Technical Consulting & Design Review
- Contact
References
-
Microsoft Learn, Differences between Windows PowerShell 5.1 and PowerShell 7.x. On how Windows PowerShell 5.1 is built on .NET Framework 4.5 and PowerShell 6.0 and later on .NET Core (now .NET), the list of .NET versions underlying each PowerShell version, the example where String.Split overload differences change the result of identical code, and the cmdlets removed in 7. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9
-
Microsoft Learn, about_Object_Creation. On the static new() method added to all .NET types in PowerShell 5.0, how typing ::new lists the constructor overloads, and how objects obtained through cmdlets have NoteProperties added by PowerShell so their members may not match objects created with ::new(). ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Add-Type. On how Add-Type compiles C# source and adds types to the session, the official P/Invoke sample using MemberDefinition (ShowWindowAsync in user32.dll), how added types are session-scoped and a type of the same name can’t be redefined, how PowerShell 7 skips compilation when a type of the same name exists, and how 5.1 needs Add-Type to load GAC assemblies while 6 and later load them automatically. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
Microsoft Learn, New-Object. On how New-Object creates instances of .NET or COM objects, how the -ComObject parameter takes a ProgId, and how VBScript’s CreateObject(“Shell.Application”) corresponds to New-Object -ComObject “Shell.Application”. ↩ ↩2 ↩3
-
Microsoft Learn, Creating .NET and COM objects (New-Object). On creating WSH objects such as WScript.Shell, the statement that shortcut creation is easier using WSH classes together with a worked CreateShortcut example, applying Get-Member to COM objects, how the exit behaviour of ActiveX executables is application-dependent and needs testing in advance, and how New-Object uses .NET’s RCW. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Runtime Callable Wrapper. On how .NET exposes COM objects through a proxy called an RCW, how exactly one RCW is created per COM object, and how the RCW releases its reference to the COM object when it is reclaimed by garbage collection. ↩ ↩2 ↩3
-
Microsoft Learn, Marshal.ReleaseComObject(Object) Method. On how ReleaseComObject decrements the RCW’s reference count and releases the underlying COM object at zero, how using an RCW after release causes exceptions or access violations, the official warning to “use only when absolutely necessary”, and its relationship to FinalReleaseComObject. ↩ ↩2 ↩3
-
Microsoft Learn, Considerations for unattended automation of Office in the Microsoft 365 for unattended RPA environment. On how Microsoft neither recommends nor supports Office automation from unattended, non-interactive client applications or components (including ASP, ASP.NET, DCOM and NT services), the possibility of unstable behaviour and deadlocks in unattended environments, concrete problems such as stopping at dialogs and STA-derived concurrency constraints, and how editing Open XML file formats directly is the recommended alternative. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Expand-Archive. On how Expand-Archive uses the System.IO.Compression.ZipArchive API, and how that API has a maximum file size limit of 2 GB. ↩
-
Microsoft Learn, ZipFile Class. On how the ZipFile class provides static methods for creating, extracting and opening ZIP files, how using it on the .NET Framework requires a reference to the System.IO.Compression.FileSystem assembly, and the purposes of CreateFromDirectory, ExtractToDirectory and OpenRead. ↩ ↩2
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
Parameter Design and Modularization for PowerShell Scripts — From a Script That Works to a Script You Can Hand Over
A step-by-step procedure for raising a PowerShell script to a quality you can hand to someone else. Covers the param block and [CmdletBin...
Automating Excel and CSV Work with PowerShell — Practical Recipes for Aggregation, Reconciliation, and Report Output
Practical recipes for automating CSV aggregation, reconciliation, and Excel report output with PowerShell. Covers the default encodings o...
The Differences Between Windows PowerShell 5.1 and PowerShell 7 — A Practical Guide to Migrating In-House Scripts
The relationship between Windows PowerShell 5.1 and PowerShell 7 (side-by-side coexistence and pwsh.exe), Microsoft's official position t...
How to Run PowerShell from C# (CSharp) and Receive the Results as Objects
How to launch PowerShell from C# and receive results as PSObject rather than strings — a practical walkthrough of the PowerShell SDK, Add...
Practical PowerShell Command Recipes — Growing the Small Tools You Use Every Day
A practical roundup of PowerShell commands for everyday work, covering where to use Measure-Object, Group-Object, Select-String, Compare-...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
ActiveX Migration
Topic page for staged decisions around keeping, wrapping, or replacing COM / ActiveX / OCX assets.
Where This Topic Connects
This article connects naturally to the following service pages.
Windows App Development
We support Windows desktop applications that involve resident processing, device integration, operational logging, and maintainable structure.
Legacy Asset Reuse & Migration Support
We help plan staged migration while continuing to reuse COM / ActiveX / OCX assets, native code, and 32-bit dependencies.
Frequently Asked Questions
Common questions about the topic of this article.
- Can I call .NET classes directly from PowerShell?
- You can. PowerShell is built on top of .NET, so you write the type name in square brackets and call a static method with ::, as in [System.IO.Path]::GetFileNameWithoutExtension(). If you need an instance, create one with New-Object or with [Type]::new(), available since PowerShell 5.0. Even where no cmdlet exists for what you want, if it is in the .NET class library you can use it from a script with no extra installation.
- Should I use New-Object or [Type]::new()?
- Either will create the object, but [Type]::new() typed without parentheses lists that type's constructors, which makes it good for working out the arguments as you write. It also performs better. On the other hand, COM objects can only be created with New-Object -ComObject. Use New-Object as well if you need to support environments older than PowerShell 5.0.
- Why does EXCEL.EXE stay running after I drive Excel through COM from PowerShell?
- COM object lifetime is managed by reference counting, and on the .NET side a Runtime Callable Wrapper (RCW) holds that reference. Write something like $excel.Workbooks.Open() and an invisible RCW is also created for the intermediate Workbooks object, leaving a reference you never assigned to a variable. Call Quit() while references remain and the process doesn't exit. When you're finished you need to clean up explicitly — either release with Marshal.ReleaseComObject, or null the variables and let GC.Collect() and WaitForPendingFinalizers() reclaim them.
- Is it acceptable to drive Excel through COM on a server or in a nightly batch job?
- It isn't recommended. Microsoft states officially that it does not recommend and does not support automation of Office applications from unattended, non-interactive client applications or components. Office is designed around interactive desktop use, and in unattended runs it can behave unpredictably or deadlock. For unattended processing, the standard approach is to switch to methods that never launch the Office application itself — Open XML libraries, CSV integration and so on.
- Can I call Win32 APIs with Add-Type?
- You can. Pass a C# signature with DllImport to Add-Type's -MemberDefinition and it is compiled on the spot, letting you call Win32 APIs through P/Invoke. The official documentation even includes an example calling ShowWindowAsync in user32.dll. Note that the added type stays in the session and a type of the same name can't be redefined, so re-run in a fresh session while you're experimenting. Getting a signature wrong can take the whole process down, so it is safest to verify in a throwaway console.
- Are there differences in calling .NET between Windows PowerShell 5.1 and PowerShell 7?
- Yes. 5.1 is built on the .NET Framework 4.5 line while PowerShell 7 is built on .NET (formerly .NET Core), so the available types and method overloads differ. String.Split, for example, has more overloads in 7, and the official documentation shows an example where the same code produces different results. In 5.1 you also frequently have to load GAC assemblies with Add-Type, whereas 7 loads bundled assemblies automatically on demand. Test any script that has to run on both in both environments.
Author Profile
Profile page for the article author.
Go Komura
Representative of KomuraSoft LLC
Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.
Public links