2025, Nov 11 21:00

How to Determine a Windows Update’s Target OS (Windows 10 vs 11) Without Relying on Filesystem Metadata

Learn why MSU, MSI, CAB and MSP files don’t reveal Windows 10 or 11 targets via filesystem metadata, and how to detect the right OS using content inspection.

When you need to tell whether a Windows update package targets Windows 10 or Windows 11, it’s tempting to scan the file metadata and be done. This comes up often when automating checks for downloads from the Windows Update Catalog and you want to avoid brittle filename parsing. The question is whether the answer lives in filesystem metadata for .msu, .msi, .cab, or .msp files. The short version: it doesn’t.

Problem statement

The goal is to detect the intended Windows version for update files fetched from the Windows Update Catalog without relying on their names. The initial instinct is to read whatever “version” might be exposed by the filesystem metadata in Python.

Example of the failed approach

The following Python snippet shows the shape of a typical attempt. It looks at the file’s metadata and basic attributes to infer the target OS version. Unsurprisingly, it yields nothing useful because the intended Windows version isn’t encoded at the filesystem level.

from pathlib import Path
from typing import Optional

# Attempts to guess target OS version from filesystem metadata.
# This does not work for .msu, .msi, .cab, .msp.
def infer_target_platform_from_fs(path_str: str) -> Optional[str]:
    obj = Path(path_str)
    if not obj.exists():
        raise FileNotFoundError(f"Missing file: {obj}")

    # Basic file system attributes
    info = obj.stat()
    size = info.st_size
    modified_ts = info.st_mtime
    suffix = obj.suffix.lower()

    # None of these attributes contain Windows target version
    # Returning None to reflect the absence of a reliable signal
    return None

# Illustrative calls; all will return None
print(infer_target_platform_from_fs("example.cab"))
print(infer_target_platform_from_fs("example.msi"))
print(infer_target_platform_from_fs("example.msu"))
print(infer_target_platform_from_fs("example.msp"))

Why this doesn’t work

From the filesystem metadata? No.

That’s the core issue. CAB, MSI, MSU, and MSP files do not expose the intended Windows version via filesystem metadata. A CAB is an archive, similar in spirit to a ZIP, that bundles multiple compressed files. The OS targeting, if present at all, is located inside the payload. A single CAB can even hold sets of files for different OS versions, which means there might not be a single answer for the archive as a whole.

MSI files are not just containers; they are databases that describe how to install or update a product. In some cases, one could derive prerequisites by inspecting the appropriate table, but in the general case that’s not reliable because MSIs can include or reference custom scripts or DLLs that implement ad‑hoc prerequisite checks. That logic won’t surface in basic metadata and won’t be captured by a simple query.

For MSU and MSP, there is no reason to expect the intended Windows version to appear in filesystem metadata either.

The practical path forward

If you want to know which Windows version an update is meant for, don’t rely on filesystem metadata. For a CAB, you would have to look into the archive’s contents and find the relevant information inside the files. Tools like extract.exe can unpack a cabinet, and the determining data may live in one of the unpacked files. For an MSI, you would need to access the MSI database and, when present, identify prerequisites there, keeping in mind that the effective checks could be implemented through custom code. For MSU and MSP, it is not reasonable to expect a filesystem attribute to give you the answer.

The code below reflects that approach. It does not attempt to manufacture a result from metadata and instead makes it explicit that the answer lies inside the package content or installer logic.

from pathlib import Path

class TargetDetectionUnavailable(Exception):
    pass

# Dispatcher that refuses to infer OS targeting from filesystem metadata
# and directs the caller toward content-level inspection.
def determine_target_os(pkg_path: str) -> str:
    p = Path(pkg_path)
    if not p.exists():
        raise FileNotFoundError(f"Missing file: {p}")

    ext = p.suffix.lower()

    if ext == ".cab":
        raise TargetDetectionUnavailable(
            "CAB archives do not carry OS targeting in filesystem metadata; "
            "inspect files inside the cabinet to find relevant information."
        )

    if ext == ".msi":
        raise TargetDetectionUnavailable(
            "MSI targeting is defined in the MSI database and possibly custom scripts or DLLs; "
            "do not rely on filesystem metadata."
        )

    if ext in (".msu", ".msp"):
        raise TargetDetectionUnavailable(
            "Do not expect filesystem metadata to reveal intended Windows version for MSU/MSP; "
            "analyze the package content if needed."
        )

    raise TargetDetectionUnavailable(
        "Unknown package type; no filesystem metadata can provide the target OS."
    )

Why it matters

Automation that assumes the target Windows version can be derived from a timestamp, size, or any basic attribute will silently do the wrong thing. CABs may mix content for Windows 10 and Windows 11, so even a package‑level label would be misleading. MSIs might gate installation with logic that never appears in a straightforward field. Treating filenames as ground truth is equally fragile because naming conventions are not guaranteed.

Takeaways

Don’t expect filesystem metadata to tell you whether a .msu, .msi, .cab, or .msp targets Windows 10 or Windows 11. For cabinets, look inside the payload if you need certainty. For MSIs, the answer is embedded in the database and potentially enforced by custom scripts or DLLs. For MSU and MSP, do not rely on attributes exposed by the filesystem. Build your tooling around content inspection or external authoritative sources, and assume that a single package can span multiple Windows versions.

The article is based on a question from StackOverflow by PawnShopChessboard and an answer by Adrian McCarthy.