Repository: pyinstaller/pyinstaller

Issue:
LoadIconMetric/Ordinal 380 errors when console is False
## Description of the issue

EDIT: Update, this issue seems to be with the default bootloader as well, but in a different manner. See first comment.

When I use pyinstaller on a script with a gcc compiled bootloader (in this case using MSYS2 and Mingw-64 gcc) on Windows 10. The program only launches without error if there is a console enabled. If I set console to False, I get an error.


### Context information (for bug reports)

* Output of `pyinstaller --version`: ```4.5.1```
* Version of Python: 3.6.7
* Platform: Windows 10 20H2
* How you installed Python: python.org (installer)
* Did you also try this on another platform? Does it work there? Only Windows

### A minimal example program which shows the error
I am using the following run_application.py to generate a simple Window, so that I can still see something without a console.

```
import pyglet
window = pyglet.window.Window()
pyglet.app.run()
```

EXE is created using: `pyinstaller .\run_application.py --noconfirm --clean --noconsole`

Bootloader compiled via `python ./waf --gcc all --target-arch=32bit`

### Stacktrace / full error message


Below message shows up in a win32 error dialog message:
```
---------------------------
run_application.exe - Entry Point Not Found
---------------------------
The procedure entry point LoadIconMetric could not be located in the dynamic link library 
```

EDIT: I will note, this works without a console with the default bootloader.



Discussion:

Resolving patch:
diff --git a/PyInstaller/building/api.py b/PyInstaller/building/api.py
--- a/PyInstaller/building/api.py
+++ b/PyInstaller/building/api.py
@@ -338,6 +338,11 @@ def __init__(self, *args, **kwargs):
                 Windows only. Setting to True creates a Manifest with will request elevation upon application start.
             uac_uiaccess
                 Windows only. Setting to True allows an elevated application to work with Remote Desktop.
+            embed_manifest
+                Windows only. Setting to True (the default) embeds the manifest into the executable. Setting to False
+                generates an external .exe.manifest file. Applicable only in onedir mode (exclude_binaries=True); in
+                onefile mode (exclude_binaries=False), the manifest is always embedded in the executable, regardless
+                of this option.
             target_arch
                 macOS only. Used to explicitly specify the target architecture; either single-arch ('x86_64' or 'arm64')
                 or 'universal2'. Used in checks that the collected binaries contain the requires arch slice(s) and/or
@@ -363,6 +368,7 @@ def __init__(self, *args, **kwargs):
         self.icon = kwargs.get('icon', None)
         self.versrsrc = kwargs.get('version', None)
         self.manifest = kwargs.get('manifest', None)
+        self.embed_manifest = kwargs.get('embed_manifest', True)
         self.resources = kwargs.get('resources', [])
         self.strip = kwargs.get('strip', False)
         self.upx_exclude = kwargs.get("upx_exclude", [])
@@ -446,6 +452,11 @@ def __init__(self, *args, **kwargs):
             self.toc.append(("pyi-disable-windowed-traceback", "", "OPTION"))
 
         if is_win:
+            if not self.exclude_binaries:
+                # onefile mode forces embed_manifest=True
+                if not self.embed_manifest:
+                    logger.warning("Ignoring embed_manifest=False setting in onefile mode!")
+                self.embed_manifest = True
             if not self.icon:
                 # --icon not specified; use default from bootloader folder
                 if self.console:
@@ -460,11 +471,11 @@ def __init__(self, *args, **kwargs):
 
             manifest_filename = os.path.basename(self.name) + ".manifest"
 
-            self.toc.append((manifest_filename, filename, 'BINARY'))
-            if not self.exclude_binaries:
-                # Onefile mode: manifest file is explicitly loaded. Store name of manifest file as bootloader option.
-                # Allows the exe to be renamed.
-                self.toc.append(("pyi-windows-manifest-filename " + manifest_filename, "", "OPTION"))
+            # If external manifest file is requested (supported only in onedir mode), add the file to the TOC in order
+            # for it to be collected as an external manifest file. Otherwise, the assembly pipeline will embed the
+            # manifest into the executable later on.
+            if not self.embed_manifest:
+                self.toc.append((manifest_filename, filename, 'BINARY'))
 
             if self.versrsrc:
                 if not isinstance(self.versrsrc, versioninfo.VSVersionInfo) and not os.path.isabs(self.versrsrc):
@@ -500,6 +511,7 @@ def __init__(self, *args, **kwargs):
         ('uac_admin', _check_guts_eq),
         ('uac_uiaccess', _check_guts_eq),
         ('manifest', _check_guts_eq),
+        ('embed_manifest', _check_guts_eq),
         ('append_pkg', _check_guts_eq),
         ('target_arch', _check_guts_eq),
         ('codesign_identity', _check_guts_eq),
@@ -636,7 +648,8 @@ def assemble(self):
                             resfile,
                             exc_info=1
                         )
-            if self.manifest and not self.exclude_binaries:
+            # Embed the manifest into the executable.
+            if self.embed_manifest:
                 self.manifest.update_resources(tmpnm, [1])
             trash.append(tmpnm)
             exe = tmpnm
diff --git a/PyInstaller/building/makespec.py b/PyInstaller/building/makespec.py
--- a/PyInstaller/building/makespec.py
+++ b/PyInstaller/building/makespec.py
@@ -479,6 +479,13 @@ def __add_options(parser):
         metavar="<FILE or XML>",
         help="Add manifest FILE or XML to the exe.",
     )
+    g.add_argument(
+        "--no-embed-manifest",
+        dest="embed_manifest",
+        action="store_false",
+        help="Generate an external .exe.manifest file instead of embedding the manifest into the exe. Applicable only "
+        "to onedir mode; in onefile mode, the manifest is always embedded, regardless of this option.",
+    )
     g.add_argument(
         "-r",
         "--resource",
@@ -601,6 +608,7 @@ def main(
     binaries=None,
     icon_file=None,
     manifest=None,
+    embed_manifest=True,
     resources=None,
     bundle_identifier=None,
     hiddenimports=None,
@@ -677,6 +685,8 @@ def main(
         else:
             # Assume filename
             exe_options = "%s, manifest='%s'" % (exe_options, quote_win_filepath(manifest))
+    if not embed_manifest:
+        exe_options = "%s, embed_manifest=%s" % (exe_options, 'False')
     if resources:
         resources = list(map(quote_win_filepath, resources))
         exe_options = "%s, resources=%s" % (exe_options, repr(resources))


Test patch: