bdist_msi

This command is implemented by cx_Freeze to handle installing executables and their dependencies creating Windows installer packages.

For Python 3.13+ cx_Freeze is using python-msilib with support for Windows x64 and also for Windows ARM64.

cxfreeze bdist_msi
python setup.py bdist_msi

The following options are available for the command:

add-to-path

add the target directory to the PATH environment variable [default: False]

all-users

install for all users or just for the installing user [default: False]

data

dictionary of arbitrary MSI data indexed by table name; for each table, a list of tuples should be provided, representing the rows that should be added to the table. For binary values (e.g. Icon.Data), pass the path to the file containing the data.

directories

list of directories that should be created during installation.

environment-variables

list of environment variables that should be added to the system during installation.

extensions

list of dictionaries specifying the extensions that the installed program handles. Each extension needs to specify at least the extension, a verb, and an executable. Additional allowed keys are argument to specify the invocation of the executable, mime for the extension’s mime type, and context for the context menu text.

initial-target-dir

defines the initial target directory supplied to the user during installation; to specify a target directory of “XYZ” in the default program directory use “[ProgramFiles64Folder]XYZ” or “[ProgramFilesFolder]XYZ” (for the default 64-bit or non-64-bit locations, respectively).

install-icon

path of icon to use for the add/remove programs window that pops up during installation.

launch-on-finish

boolean flag that includes a “Launch the installed app on finish?” checkbox to the final step of the installer [default: False]

license-file

path to a rtf formmated file to be used as the license agreement; refer to Windows Installer Scrollable Text .

output-name

specifies the name of the file that is to be created [default: metadata name-version-platform.msi]

product-code

define the product code for the package that is created

product-name

define the product name for the package that is created [default: metadata name]

product-version

define the product version for the package that is created [default: metadata version if available]

summary-data

dictionary of data to include in MSI summary information stream (allowable keys are “author”, “comments”, and “keywords”).

upgrade-code

define the GUID of the upgrade code for the package that is created; this is used to force the removal of any packages created with the same upgrade code before the installation of this one; the valid format for a GUID is {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} where X is a hex digit. Refer to Windows GUID .

Added in version 6.7: extensions option.

Added in version 7.2: license-file option.

Added in version 8.2: launch-on-finish option.

Added in version 8.6: output-name, product-name, and product-version options.

Removed in version 8.6: target-name option.

For example:

[project]
name = "hello"
version = "0.1.2.3"
description = "Sample cx_Freeze script to test MSI arbitrary data stream"

[[tool.cxfreeze.executables]]
script = "hello.py"
base = "gui"
copyright = "Copyright (C) 2026 cx_Freeze"
icon = "icon.ico"
shortcut-name = "My Program Name"
shortcut-dir = "MyProgramMenu"

[tool.cxfreeze.build_exe]
excludes = ["tkinter", "unittest"]
include-msvcr = true

[tool.cxfreeze.bdist_msi]
add-to-path = true
environment-variables = [
    ["E_MYAPP_VAR", "=-*MYAPP_VAR", "1", "TARGETDIR"]
]
product-name = "Hello cx_Freeze"
# use a different upgrade-code for your project
upgrade-code = "{6B29FC40-CA47-1067-B31D-00DD010662DA}"

[tool.cxfreeze.bdist_msi.data]
Directory = [
    ["ProgramMenuFolder", "TARGETDIR", "."],
    ["MyProgramMenu", "ProgramMenuFolder", "MYPROG~1|My Program"]
]
ProgId = [
    ["Prog.Id", 0, 0, "This is a description", "IconId", 0]
]
Icon = [
    ["IconId", "icon.ico"]
]
from cx_Freeze import Executable, setup

directory_table = [
    ("ProgramMenuFolder", "TARGETDIR", "."),
    ("MyProgramMenu", "ProgramMenuFolder", "MYPROG~1|My Program"),
]

msi_data = {
    "Directory": directory_table,
    "ProgId": [
        ("Prog.Id", None, None, "This is a description", "IconId", None),
    ],
    "Icon": [
        ("IconId", "icon.ico"),
    ],
}

bdist_msi_options = {
    "add_to_path": True,
    "data": msi_data,
    "environment_variables": [
        ("E_MYAPP_VAR", "=-*MYAPP_VAR", "1", "TARGETDIR")
    ],
    "upgrade_code": "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}",
}

build_exe_options = {"excludes": ["tkinter"], "include_msvcr": True}

executables = [
    Executable(
        "hello.py",
        base="gui",
        copyright="Copyright (C) 2026 cx_Freeze",
        icon="icon.ico",
        shortcut_name="My Program Name",
        shortcut_dir="MyProgramMenu",
    )
]

setup(
    name="hello",
    version="0.1",
    description="Sample cx_Freeze script to test MSI arbitrary data stream",
    executables=executables,
    options={
        "build_exe": build_exe_options,
        "bdist_msi": bdist_msi_options,
    },
)

Samples: There are more examples in the samples directory.