• Blog
  • How to use ShellExecute?

How to use ShellExecute?

What is ShellExecute

Publish date:
Discover more of what matters to you

If you are asking yourself:

– How do I open this file with its default program without knowing where that program is installed?

– How do I launch common system actions through a simple interface without writing complex, low-level OS-specific code? 

– How do I perform basic file operations with minimal code? 

There is a solution you can use – it is called ShellExecute. 

What is ShellExecute?

ShellExecute is a Windows API function used to perform various operations related to file manipulation or system interactions by launching or executing a specified file or operation associated with the default action for the specified file or application. 

This function allows users to work with files, folders, and URLs in Delphi, and perform various actions associated with them, such as open, edit, print, explore, etc., from app’s menu, depending on the specified guidelines. In Delphi, it is used similarly to how it’s used in other programming languages for Windows.

In Delphi, it is used in the same way as in other Windows programming environments. It relies on Windows Shell associations to decide which application handles what file type.

Historical Context of the Windows ShellExecute API

The ShellExecute API has existed since Windows 95. There, it became the standard way for applications to ask the operating system to “do what the user expects” with a file or URL. It is one of the most stable API calls in the Windows Shell.

In Windows 11, it is still fully supported. However, Microsoft recommends to use wrappers or asynchronous patterns (ShellExecuteEx or IAsyncOperation APIs) for non-blocking operations.

It is a versatile function that serves several purposes:

  • Launching Applications: It can be used to start external applications. For example, you can use it to open Notepad, launch a web browser, or execute any executable file.
  • Opening Files: It can open files using the default program associated with their file types. For instance, it can open text files, documents, images, videos, etc., using their default associated applications.
  • Performing Actions on Files: It allows performing various actions associated with files, such as editing, printing, and exploring (opening a folder to view its contents), among others.
  • Handling URLs: It can open URLs in the default web browser. For instance, it can open a specific website link using the default web browser.

ShellExecute continues to be one of the simplest ways to initiate an action without manually handling:

  • path resolution
  • file associations
  • protocol handlers
  • user-default apps

Developers use this function in Windows programming to initiate these kinds of operations and interactions within their applications. It relies on the default associations set up in the Windows Shell interface, allowing applications to leverage the default applications and behaviors set by the user for different file types and protocols.

Its flexibility makes it a widely used function in programming scenarios where there is a need to trigger various file-related operations or launch applications associated with specific file types or URLs.

Legacy APIs often reveal deeper dependencies hidden in your Delphi codebase.
Our team performs code-level modernization audits to identify outdated deprecated components and unsafe integrations.
Request audit

Benefits of Using ShellExecute in Delphi Applications

ShellExecute offers multiple advantages for Delphi developers, especially to those working on large and multi-module applications.

  • Simplicity and minimal code. You can open a file or an app. You can execute a command using a single line of code. No need for complex WinAPI structures or manual path management.
  • Uses Windows defaults. It considers the user’s default applications and system preferences. For example, PDFs automatically open in the user’s chosen viewer.
  • Cross-application compatibility. Because the Windows Shell manages the association, your Delphi app doesn’t need to “know” anything about formats or registry settings.
  • Reduces code bloat. You avoid writing custom logic for file handling or URL opening. This simplifies maintenance and reduces bugs.
  • It works smoothly with Delphi VCL/FMX. You can easily bind ShellExecute to buttons, menus, toolbars, and context actions in both VCL and FMX applications.
  • Accelerates modernization and prototyping. For legacy Delphi apps, ShellExecute is an excellent bridge when migrating components, replacing old command handlers, and testing modern workflows. Many modernization teams use it to quickly replace deprecated or custom file-handling code paths during phased refactoring.

ShellExecute Parameters in Delphi

ShellExecute in Delphi uses six core parameters. Understanding each one helps ensure stable, predictable behavior, especially on modern 64-bit Windows builds. Below is a detailed, Delphi-specific explanation:

ParameterMeaningDelphi Notes (2025)
hWndHandle of the window that owns the launched process.Use Self.Handle or Application.Handle. Avoid using 0 unless you do not care about message routing.
lpOperationAction to perform – usually “open”, “print”, “explore”, “edit”.Verb must be a valid PChar/PWideChar. nil defaults to “open”.
lpFileFile, folder or URL to act on.Must be a Unicode string. Use PWideChar for Delphi XE2+ to avoid conversion issues.
lpParametersArguments passed to the application (optional).Often nil, but required when launching EXEs with parameters.
lpDirectoryWorking directory.Usually nil, but set it when launching command-line tools.
nShowCmdVisibility flag for the launched window.Common values: SW_SHOWNORMAL, SW_HIDE, SW_MAXIMIZE.

Additional arguments can be simply passed through the lpParameters parameter when launching executable files.

Return Return Value (important for stability)

ShellExecute returns an HINSTANCE, but for error signaling:

  • > 32 → success
  • ≤ 32 → error code

Common error codes include:

  • 2 – File not found
  • 5 – Access denied
  • 31 – No association
  • 1155 – No application to open this file type

Developers can retrieve further information about a failure by checking the system error message or logging the returned code.

Unicode considerations

Since Delphi XE2, the RTL is Unicode by default. Use:

PWideChar(MyString)for 32/64-bit Windows compatibility. Incorrect casting (PChar instead of PWideChar) is one of the top causes of AVs, unexpected failures, and incorrect argument parsing
especially on Windows 11 ARM64.

How to use ShellExecute: Step-by-Step Guide

Our team has rich expertise in Delphi development services which allowed us to accumulate a lot of valuable insights related to this technology. So, to use this function in Delphi, follow these steps:

Step 1 – Add the Winapi.ShellAPI Unit

Include ShellAPI unit: Ensure that the ShellAPI (or Winapi.ShellAPI in current versions of Delphi) unit is included in your uses clause.

In modern Delphi versions (10.4 Sydney, 11 Alexandria, 12 Athens), you must explicitly include:

1234
uses
Winapi.ShellAPI,
Vcl.Forms,
Vcl.Controls;

Without Winapi.ShellAPI, the compiler cannot resolve ShellExecute and will mark it as undefined.

Note for legacy projects:

Delphi versions prior to XE2 used a flat namespace and the unit was named ShellAPI. When modernizing older Delphi applications, replace ShellAPI with Winapi.ShellAPI. This is required for Unicode and 64-bit compatibility.

Outdated ShellAPI calls break 64-bit builds and cause Unicode failures.
If you’re migrating an old Delphi app, schedule a quick audit.
Contact Us

Step 2 – Prepare the File Path or URL

You can work with:

12
Target := 'C:\Docs\report.pdf';
Target := 'https://www.softacom.com';

2025 Best Practice:

Always normalize the path before using it:

1
Target := ExpandFileName(Target);

On Windows 11, relative paths frequently lead to Error 2 – “File Not Found” because relative paths are resolved differently depending on execution context, elevation level, and working directory.

Step 3 – Launching Applications with ShellExecute

You can call the function like this:

12345678
ShellExecute(
Self.Handle, // Window owner — recommended instead of 0
'open', // Operation: open/print/explore/edit
PChar(Target), // File or URL
nil, // Parameters
nil, // Default directory
SW_SHOWNORMAL // Window mode
);

Common verbs you can use:

  • open – open file/URL using the default handler
  • print – send file to default printer
  • edit – open in edit mode
  • explore – open folder view in Explorer

Common SW_ window flags:

FlagMeaning
SW_SHOWNORMALNormal window
SW_HIDEHidden (no UI)
SW_MAXIMIZEStart maximized
SW_MINIMIZEStart minimized

Step 4 – Always Check the Result and Handle Errors

A successful call returns a value greater than 32. Anything else is an error.

For example:

1234567
var
ResultCode: HINST;
begin
ResultCode := ShellExecute(Self.Handle, 'open', PChar(Target), nil, nil, SW_SHOWNORMAL);
if ResultCode <= 32 then
ShowMessage('Error: ' + SysErrorMessage(GetLastError));
end;

Here’s the top ShellExecute Errors in 2025:

ErrorDescription
2File not found
5Access denied
31No association for file type
740Requires elevated privileges (UAC)
1155No application to handle the file

Reusable wrapper 

12345678
function SafeShellExecute(hWnd: HWND; Verb, FileName, Params, Dir: string): Boolean;
var
Code: HINST;
begin
Code := ShellExecute(hWnd, PChar(Verb), PChar(FileName),
PChar(Params), PChar(Dir), SW_SHOWNORMAL);
Result := Code > 32;
end;

Step 5 – Make Your Code Unicode- and 64-bit Safe

Delphi is Unicode by default since XE2, but many legacy codebases still use ANSI and PChar casts that break under modern conditions. Correct approach:

12345678
ShellExecute(
Handle,
'open',
PWideChar(Target), // Ensures correct Unicode handling
nil,
nil,
SW_SHOWNORMAL
);

Why this matters

  • Incorrect casting may cause Access Violations on Windows 11 ARM64.
  • 64-bit processes require proper Unicode pointer alignment.
  • ANSI-to-Unicode conversion fails for non-Latin paths.

Universal snippet (XE2 → Delphi 12)

12345
{$IFDEF UNICODE}
ShellExecute(Handle, 'open', PWideChar(Target), nil, nil, SW_SHOWNORMAL);
{$ELSE}
ShellExecute(Handle, 'open', PChar(Target), nil, nil, SW_SHOWNORMAL);
{$ENDIF}

This ensures the same code works on all modern Delphi versions

Delphi ShellExecute Example: Opening Files and URLs

Here’s an example demonstrating how to use this function to open a text file:

1234567
procedure TForm1.Button1Click(Sender: TObject);
var
FilePath: string;
begin
FilePath := 'C:\Path\To\Your\File.txt';
ShellExecute(0, 'open', PChar(FilePath), nil, nil, SW_SHOWNORMAL);
end;

This code uses these to open the file located at ‘C:\Path\To\Your\File.txt’ using the default program associated with text files in the system (‘open’ operation). You can change the operation or file path according to your specific use case.

Here’s how to open a URL:

1234567
procedure TForm1.Button1Click(Sender: TObject);
var
URL: string;
begin
URL := 'https://www.softacom.com/';
ShellExecute(Handle, 'open', PWideChar(URL), nil, nil, SW_SHOWNORMAL);
end;

This is an example of how to print a file:

1234567
procedure TForm1.Button2Click(Sender: TObject);
var
FileName: string;
begin
FileName := 'C:\Users\User\file.txt';
ShellExecute(Handle, 'print', PWideChar(FileName), nil, nil, SW_SHOWNORMAL);
End;

Here’s another example of how to launch an application with parameters:

12345678
ShellExecute(
Handle,
'open',
PWideChar('notepad.exe'),
PWideChar('C:\Temp\log.txt'),
nil,
SW_SHOWNORMAL
);

This opens Notepad and loads the target file automatically.

Error Handling and Best Practices for ShellExecute

ShellExecute is simple. But relying on default system associations means you must handle errors carefully. A successful result is any value above 32. Values 32 and below indicate a failure.

How to read the result correctly:

1234567
var
Code: HINST;
begin
Code := ShellExecute(Handle, 'open', PWideChar(Target), nil, nil, SW_SHOWNORMAL);
if Code <= 32 then
ShowMessage('ShellExecute failed: ' + SysErrorMessage(GetLastError));
end;

Why proper error handling matters:

Modern Windows versions introduce new failure modes:

  • UAC elevation requirements
  • Disabled protocols (e.g., FTP)
  • Missing app associations
  • Controlled folder access denying write/launch
  • ARM64 path resolution issues

Handling errors prevents silent failures that confuse users.

Best practices:

  • Always validate the path. Use FileExists, DirectoryExists, and ExpandFileName before calling ShellExecute.
  • Always check the result. Do not assume “open” works — file associations may not exist.
  • Prepare for UAC (error 740). If an operation requires elevation, show a clear user prompt.
  • Avoid blocking the UI. ShellExecute is synchronous. For long-running operations, prefer ShellExecuteEx or async wrappers.
  • Handle high-DPI scaling. When opening apps or windows from a high-DPI Delphi application, use SW_SHOWNORMAL to avoid incorrect scaling on multi-monitor setups.
  • Log failures during modernization audits. In legacy projects, failing ShellExecute calls often reveal missing dependencies or broken associations noted during migrations.

ShellExecute vs ShellExecuteEx: When to Choose What

ShellExecute is easy to use but limited. ShellExecuteEx is more powerful and better suited for modern Windows development.

Here’s a quick comparison table:

ShellExecuteShellExecuteEx
best for quick actionssynchronouscannot retrieve process handlesno extended controlideal for: open, print, explore, URL launchesasynchronous supportallows retrieving the process handlesupports extended invocation flagsenables waiting for process exitsupports more complex operations (verbs, working folders, elevation prompts)

When to upgrade in Delphi projects?

Use ShellExecuteEx instead of ShellExecute when:

  • You need to wait for a launched process to complete
  • You must detect whether an application truly started
  • You need elevation (runas) in a controlled way
  • You need more reliability in enterprise or multi-user environments
  • You are modernizing a legacy application that contains custom shell integrations

For everyday file opening and simple tasks, ShellExecute is still sufficient.

Common Use Cases for ShellExecute in Modern Delphi Projects

ShellExecute remains extremely relevant in 2026 because many workflows still depend on default Windows associations. Common tasks include:

  • Opening documents 
  • Previewing exports
  • Email composition
  • Displaying reports
  • Exploring folders
  • Cross-platform notes

This makes ShellExecute an important part of “conditional platform” code.

Troubleshooting ShellExecute Issues in Delphi

When ShellExecute fails, it typically returns a value ≤ 32. Here are the most common problems and their fixes:

#1 Error 2 – File Not Found

It returns this error when the path is not normalized, the file extension is missing, or the current working directory is incorrect. 

Fix: Use ExpandFileName and check FileExists.

#2 Error 5 – Access Denied

This error is shown when folder is protected by UAC or attempt to open files in Program Files or restricted directories.  

Fix: Request elevation or move files to user space.

#3 Error 31 – No Association

The reason for it is the user has no default app for this file type.

Fix: Prompt user to choose a default application.

#4 Error 1155 – No application registered

This happens often with proprietary extensions.

Fix: Register or map handler during installation.

#5 Windows 11 specific issues

Reasons: Controlled Folder Access, missing legacy handlers (FTP, mail clients), or ARM64 architecture mismatches.

Fix: Add explicit error messaging and compatibility checks.

Integrating ShellExecute in Legacy Delphi Modernization

ShellExecute appears simple, but in large legacy Delphi systems it often reveals deeper architectural issues:

  • custom file handlers
  • deprecated ShellAPI units
  • ANSI string usage
  • broken associations
  • hard-coded paths from the early 2000s
  • silent failures hidden under try/except blocks

During modernization projects, we frequently replace dozens of ShellExecute calls with safer abstractions or with ShellExecuteEx for better process control. We also clean up outdated patterns such as ShellExecute(0, …) or ANSI PChar casts that break 64-bit builds.

A modernization audit helps identify:

  • unsafe ShellExecute calls
  • missing Unicode handling
  • broken UAC elevation paths
  • deprecated Windows Shell usage
If your legacy Delphi application relies on ShellExecute or ShellAPI, our team can review the integration and recommend modern, stable alternatives. Request a code audit.
Contact Us

Alternatives to ShellExecute in Delphi

While ShellExecute is convenient, some scenarios require more control or cross-platform compatibility.

CreateProcess

  • Full control over process creation
  • Lets you capture output, set environment variables, manage handles
  • More complex but essential for advanced tasks

TProcess (FPC/Lazarus-style)

  • Used when building cross-platform code
  • Offers a higher-level wrapper
  • Works on Windows, Linux, macOS

Choose these alternatives when you need deeper process integration than ShellExecute provides.

Conclusion

ShellExecute remains one of the simplest and most reliable ways to interact with Windows from a Delphi application. It helps open documents, launch applications, explore folders, or trigger system actions. It offers an easy-to-use interface with minimal code and maximum flexibility. You should ensure proper Unicode handling and error checking. Then, this API works smoothly across modern Windows versions.

If you’re modernizing or maintaining a legacy Delphi project, reviewing ShellExecute usage is a quick win for improving stability.

Want more Delphi guides? Subscribe to our newsletter or follow Softacom for updates.

Even small API functions can become blockers when your system evolves.
Our team helps product companies modernize Delphi applications without rewriting everything from scratch.
Work with us

Subscribe to our newsletter and get amazing content right in your inbox.

This field is required
This field is required Invalid email address
By submitting data, I agree to the Privacy Policy

Thank you for subscribing!
See you soon... in your inbox!

confirm your subscription, make sure to check your promotions/spam folder

Get in touch
Our benefits
  • 18+ years of expertise in legacy software modernization
  • AI Migration Tool:
    faster timelines, lower costs, better accuracy (99.9%)
  • Accelerate release cycles by 30–50% compared to manual migration
  • 1–2 business day turnaround for detailed estimates
  • Trusted by clients across the USA, UK, Germany, and other European countries
Review
Thanks to Softacom's efforts, the solutions they delivered are already in use and have increased revenue streams.
  • Niels Thomassen
  • Microcom A/S
This field is required
This field is required Invalid email address Invalid business email address
This field is required
By submitting data, I agree to the Privacy Policy
We’ll reply within 24 hours — no sales talk, no spam