[Bug 47075] 32-bit LAVFilters 0.74.x installer fails to register 64-bit AX codec/ filters in Wow64 environment ( 32-bit regsvr32.exe needs to support 64-bit dll registration and vice versa by re-exec with proper bitness )

wine-bugs at winehq.org wine-bugs at winehq.org
Fri Apr 26 19:32:33 CDT 2019


https://bugs.winehq.org/show_bug.cgi?id=47075

Anastasius Focht <focht at gmx.net> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |focht at gmx.net
          Component|-unknown                    |programs
                URL|https://softpedia-secure-do |https://github.com/Nevcairi
                   |wnload.com/dl/fbaa35edc239e |el/LAVFilters/releases/down
                   |424392b469e93e40a24/5cc2b1b |load/0.74.1/LAVFilters-0.74
                   |2/100186359/software/multim |.1-Installer.exe
                   |edia/video/LAVFilters-0.74. |
                   |1-Installer.exe             |
            Summary|Cannot install LAVFilters,  |32-bit LAVFilters 0.74.x
                   |fails at registering DLLs   |installer fails to register
                   |                            |64-bit AX codec/filters in
                   |                            |Wow64 environment (32-bit
                   |                            |regsvr32.exe needs to
                   |                            |support 64-bit dll
                   |                            |registration and vice versa
                   |                            |by re-exec with proper
                   |                            |bitness)
           Hardware|x86                         |x86-64

--- Comment #5 from Anastasius Focht <focht at gmx.net> ---
Hello folks,

confirming.

The installer is 32-bit and tries to register 32-bit _and_ 64-bit dlls (AX
codec/filters).

--- snip ---
$ WINEDEBUG=+seh,+relay,loaddll,+module wine ./LAVFilters-0.74.1-Installer.exe
>>log.txt 2>&1
...
002a:Call KERNEL32.CreateProcessA(00000000,00420474
"\"C:\\users\\focht\\Temp\\is-0VI6K.tmp\\LAVFilters-0.74.1-Installer.tmp\"
/SL5=\"$1006E,12293661,58368,Z:\\home\\focht\\Downloads\\LAVFilters-0.74.1-Installer.exe\"
",00000000,00000000,00000000,00000000,00000000,00000000,0034fe04,0034fdf4)
ret=00409f39
...
002c:Call KERNEL32.__wine_kernel_init() ret=7bc67e16
002c:Ret  KERNEL32.__wine_kernel_init() retval=7b472c54 ret=7bc67e16
002a:Ret  KERNEL32.CreateProcessA() retval=00000001 ret=00409f39 
...
002c:Call KERNEL32.CreateProcessA(00000000,005612c8
"\"C:\\windows\\system32\\regsvr32.exe\" /s \"C:\\Program Files (x86)\\LAV
Filters\\x86\\LAVAudio.ax\"",00000000,00000000,00000000,04000000,00000000,004f02e8
"C:\\windows\\system32",0032e9cc,0032e9bc) ret=00453019 
...
002c:Ret  KERNEL32.CreateProcessA() retval=00000001 ret=00453019
...
002e:Call KERNEL32.LoadLibraryExW(00113018 L"C:\\Program Files (x86)\\LAV
Filters\\x86\\LAVAudio.ax",00000000,00000008) ret=7efeebf9 
...
002e:trace:module:process_attach (L"LAVAudio.ax",(nil)) - END
002e:Ret  KERNEL32.LoadLibraryExW() retval=10000000 ret=7efeebf9
002e:Call KERNEL32.GetProcAddress(10000000,7efef337 "DllRegisterServer")
ret=7efeec0a
002e:Ret  KERNEL32.GetProcAddress() retval=10002930 ret=7efeec0a 
...
002e:Call KERNEL32.ExitProcess(00000000) ret=7efef1dd 
--- snip ---

The broken one, where it tries to register 32-bit AX. Still same parent process
-> 32-bitness is inherited:

--- snip ---
002c:Call KERNEL32.CreateProcessA(00000000,005612c8
"\"C:\\windows\\system32\\regsvr32.exe\" /s \"C:\\Program Files (x86)\\LAV
Filters\\x64\\LAVAudio.ax\"",00000000,00000000,00000000,04000000,00000000,004f02e8
"C:\\windows\\system32",0032e9cc,0032e9bc) ret=00453019 
...
0034:Ret  KERNEL32.__wine_kernel_init() retval=7b472c54 ret=7bc67e16
002c:Ret  KERNEL32.CreateProcessA() retval=00000001 ret=00453019
...
0034:Call KERNEL32.LoadLibraryExW(00113018 L"C:\\Program Files (x86)\\LAV
Filters\\x64\\LAVAudio.ax",00000000,00000008) ret=7efeebf9 
...
0034:trace:module:open_dll_file L"\\??\\C:\\Program Files (x86)\\LAV
Filters\\x64\\LAVAudio.ax" is for arch 8664, continuing search
0034:warn:module:load_dll Failed to load module L"C:\\Program Files (x86)\\LAV
Filters\\x64\\LAVAudio.ax"; status=c000007b
0034:Ret  KERNEL32.LoadLibraryExW() retval=00000000 ret=7efeebf9
0034:Call KERNEL32.ExitProcess(00000003) ret=7efeec5b 
...
002c:Call KERNEL32.GetExitCodeProcess(0000008c,0032e984) ret=004588de
002c:Ret  KERNEL32.GetExitCodeProcess() retval=00000001 ret=004588de
002c:Call KERNEL32.CloseHandle(0000008c) ret=00458904
002c:Ret  KERNEL32.CloseHandle() retval=00000001 ret=00458904
002c:Call KERNEL32.RaiseException(0eedface,00000001,00000007,0032e978)
ret=00458ac8
002c:trace:seh:raise_exception code=eedface flags=1 addr=0x7b44493b ip=7b44493b
tid=002c
002c:trace:seh:raise_exception  info[0]=00458ac8
002c:trace:seh:raise_exception  info[1]=00561328
002c:trace:seh:raise_exception  info[2]=00000003
002c:trace:seh:raise_exception  info[3]=00560c4c
002c:trace:seh:raise_exception  info[4]=00552404
002c:trace:seh:raise_exception  info[5]=0032ea1c
002c:trace:seh:raise_exception  info[6]=0032e994
002c:trace:seh:raise_exception  eax=7b42e1b9 ebx=00000018 ecx=0032e8c4
edx=0032e978 esi=0032ea1c edi=0032e94
...
002c:Call user32.MessageBoxA(000100a0,00561428 "C:\\Program Files (x86)\\LAV
Filters\\x64\\LAVAudio.ax\r\n\r\nUnable to register the DLL/OCX: RegSvr32
failed with exit code 0x3.\r\n\r\nClick Retry to try again, Ignore to proceed
anyway (not recommended), or Abort to cancel installation.",004f3168
"Error",00000032) ret=0042f878 
--- snip ---

This obviously can't work as you can't load a 64-bit code dll into a 32-bit
Wow64 process.

The project is on Github:

https://github.com/Nevcairiel/LAVFilters

https://github.com/Nevcairiel/LAVFilters/releases/tag/0.74.1

@Louis:

Hint: When providing download links, double check it's not user session
generated URI like the previous one from Softpedia. I've replaced it with
Github now.

Anyway, the installer is Inno Setup based which fortunately is open sauce.

https://github.com/jrsoftware/issrc

I spent some time digging through the sources of the installer. It seems to
have all the code paths to handle the registration properly for 32-bit and
64-bit in Wow64 environment. I suspect the authoring/packaging (install script)
doing a questionable thing wrt 32-bit vs. 64-bit dlls.

https://github.com/jrsoftware/issrc/blob/e7d8563af77ba49700b6615649d4b3c6f8b62af7/Projects/RegDLL.pas

--- snip ---
procedure RegisterServerUsingRegSvr32(const AUnregister: Boolean;
  const AIs64Bit: Boolean; const Filename: String);
var
  SysDir, CmdLine: String;
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
  ExitCode: DWORD;
begin
  SysDir := GetSystemDir;
  CmdLine := '"' + AddBackslash(SysDir) + 'regsvr32.exe"';
  if AUnregister then
    CmdLine := CmdLine + ' /u';
  CmdLine := CmdLine + ' /s "' + Filename + '"';
  if AIs64Bit then
    Log('Spawning 64-bit RegSvr32: ' + CmdLine)
  else
    Log('Spawning 32-bit RegSvr32: ' + CmdLine);

  FillChar(StartupInfo, SizeOf(StartupInfo), 0);
  StartupInfo.cb := SizeOf(StartupInfo);
  if not CreateProcessRedir(AIs64Bit, nil, PChar(CmdLine), nil, nil, False,
     CREATE_DEFAULT_ERROR_MODE, nil, PChar(SysDir), StartupInfo,
     ProcessInfo) then
    Win32ErrorMsg('CreateProcess');
  CloseHandle(ProcessInfo.hThread);
  ExitCode := WaitForAndCloseProcessHandle(ProcessInfo.hProcess);
  if ExitCode <> 0 then
    raise Exception.Create(FmtSetupMessage1(msgErrorRegSvr32Failed,
      Format('0x%x', [ExitCode])));
end;

...

procedure RegisterServer(const AUnregister: Boolean; const AIs64Bit: Boolean;
  const Filename: String; const AFailCriticalErrors: Boolean);
var
  WindowDisabler: TWindowDisabler;
begin
  if AIs64Bit and not IsWin64 then
    InternalError('Cannot register 64-bit DLLs on this version of Windows');

  { Disable windows so the user can't utilize our UI while the child process
    is running }
  WindowDisabler := TWindowDisabler.Create;
  try
    { On Windows Vista, to get the "WRP Mitigation" compatibility hack which
      a lot of DLLs a require, we must use regsvr32.exe to handle the
      (un)registration.
      On Windows 2000/XP/2003, use regsvr32.exe as well for behavioral &
      error message consistency. }
    RegisterServerUsingRegSvr32(AUnregister, AIs64Bit, Filename);
  finally
    WindowDisabler.Free;
  end;
end;
--- snip ---

https://github.com/jrsoftware/issrc/blob/32e8b217216a71d46b8dae815be919219fe88dee/Projects/Install.pas#L2601

--- snip ---
    procedure RegisterSvr(const Is64Bit: Boolean; const Filename: String;
      const NoErrorMessages: Boolean);
    var
      NeedToRetry: Boolean;
    begin
      repeat
        if Is64Bit then
          LogFmt('Registering 64-bit DLL/OCX: %s', [Filename])
        else
          LogFmt('Registering 32-bit DLL/OCX: %s', [Filename]);
        NeedToRetry := False;
        try
          RegisterServer(False, Is64Bit, Filename, NoErrorMessages);
          Log('Registration successful.');
        except
          Log('Registration failed:' + SNewLine + GetExceptMessage);
          if not NoErrorMessages then
            if not AbortRetryIgnoreTaskDialogMsgBox(
                     Filename + SNewLine2 +
FmtSetupMessage1(msgErrorRegisterServer, GetExceptMessage),
                     [SetupMessages[msgAbortRetryIgnoreRetry],
SetupMessages[msgFileAbortRetryIgnoreIgnoreNotRecommended],
SetupMessages[msgAbortRetryIgnoreCancel]]) then
              NeedToRetry := True;
        end;
      until not NeedToRetry;
end;
--- snip ---

https://github.com/jrsoftware/issrc/blob/32e8b217216a71d46b8dae815be919219fe88dee/Projects/Install.pas#L2654

--- snip ---
  var
    I: Integer;
  begin
    if not NeedsRestart then
      for I := 0 to RegisterFilesList.Count-1 do begin
        with PRegisterFilesListRec(RegisterFilesList[I])^ do
          if not TypeLib then
            RegisterSvr(Is64Bit, Filename, NoErrorMessages)
          else
            RegisterTLib(Is64Bit, Filename, NoErrorMessages);
      end
    else begin
      { When a restart is needed, all "regserver" & "regtypelib" files will get
        registered on the next logon }
      Log('Delaying registration of all files until the next logon since a
restart is needed.');
      try
        RegisterServersOnRestart;
      except
        Application.HandleException(nil);
      end;
    end;
end;
--- snip ---

https://github.com/jrsoftware/issrc/blob/32e8b217216a71d46b8dae815be919219fe88dee/Projects/Install.pas#L1549

--- snip ---
        { If foRegisterServer or foRegisterTypeLib is in Options, add the
          file to RegisterFilesList for registering later.
          Don't attempt to register if the file doesn't exist (which can
          happen if the foOnlyIfDestFileExists flag is used). }
        if ((foRegisterServer in CurFile^.Options) or
            (foRegisterTypeLib in CurFile^.Options)) and
           NewFileExistsRedir(DisableFsRedir, DestFile) then begin
          LastOperation := '';
          if foRegisterTypeLib in CurFile^.Options then
            Log('Will register the file (a type library) later.')
          else
            Log('Will register the file (a DLL/OCX) later.');
          New(RegisterRec);
          RegisterRec^.Filename := DestFile;
          RegisterRec^.Is64Bit := DisableFsRedir;
          RegisterRec^.TypeLib := foRegisterTypeLib in CurFile^.Options;
          RegisterRec^.NoErrorMessages := foNoRegError in CurFile^.Options;
          RegisterFilesList.Add(RegisterRec);
--- snip ---

https://github.com/jrsoftware/issrc/blob/32e8b217216a71d46b8dae815be919219fe88dee/Projects/Install.pas#L1772

--- snip ---
  var
    FileLocationFilenames: TStringList;
    I: Integer;
    CurFileNumber: Integer;
    CurFile: PSetupFileEntry;
    ExternalSize: Integer64;
    SourceWildcard: String;
    ProgressBefore, ExpectedBytesLeft: Integer64;
    DisableFsRedir, FoundFiles: Boolean;
  begin
    FileLocationFilenames := TStringList.Create;
    try
      for I := 0 to Entries[seFileLocation].Count-1 do
        FileLocationFilenames.Add('');
      for CurFileNumber := 0 to Entries[seFile].Count-1 do begin
        CurFile := PSetupFileEntry(Entries[seFile][CurFileNumber]);
        if ((CurFile^.FileType <> ftUninstExe) or Uninstallable) and
           ShouldProcessFileEntry(WizardComponents, WizardTasks, CurFile,
False) then begin
          DebugNotifyEntry(seFile, CurFileNumber);
          NotifyBeforeInstallFileEntry(CurFile);

          DisableFsRedir := InstallDefaultDisableFsRedir;
          if fo32Bit in CurFile^.Options then
            DisableFsRedir := False;
          if fo64Bit in CurFile^.Options then begin
            if not IsWin64 then
              InternalError('Cannot install files to 64-bit locations on this
version of Windows');
            DisableFsRedir := True;
          end;

          if CurFile^.LocationEntry <> -1 then begin
            ExternalSize.Hi := 0;  { not used... }
            ExternalSize.Lo := 0;
            ProcessFileEntry(CurFile, DisableFsRedir, '', '',
FileLocationFilenames, ExternalSize);
          end
          else begin
            { File is an 'external' file }
            if CurFile^.FileType = ftUninstExe then begin
              { This is the file entry for the uninstaller program }
              SourceWildcard := NewParamStr(0);
              DisableFsRedir := False;
            end
else

--- snip ---

The inner installer 'LAVFilters-0.74.1-Installer.tmp' is created in TEMP folder
and can be run manually for easier debugging, along with verbose/log option. 

--- snip ---
$ wine ./LAVFilters-0.74.1-Installer.tmp
/SL5="\$1006E,12293661,58368,Z:\\home\\focht\\Downloads\\LAVFilters-0.74.1-Installer.exe"
/LOG
--- snip ---

Which produces a log file 'Setup Log <date> #001.txt' in TEMP folder:

--- snip ---
2019-04-27 00:28:52.015   Log opened. (Time zone: UTC+02:00)
2019-04-27 00:28:52.015   Setup version: Inno Setup version 5.6.1 (a)
2019-04-27 00:28:52.015   Original Setup EXE:
Z:\home\focht\Downloads\LAVFilters-0.74.1-Installer.exe
2019-04-27 00:28:52.015   Setup command line:
/SL5=$1006E,12293661,58368,Z:\home\focht\Downloads\LAVFilters-0.74.1-Installer.exe
/LOG
2019-04-27 00:28:52.015   Windows version: 6.1.7601 SP1  (NT platform: Yes)
2019-04-27 00:28:52.015   64-bit Windows: Yes
2019-04-27 00:28:52.015   Processor architecture: x64
2019-04-27 00:28:52.015   User privileges: Administrative
2019-04-27 00:28:52.016   64-bit install mode: No
2019-04-27 00:28:52.016   Created temporary directory:
C:\users\focht\Temp\is-KU7T7.tmp
...
2019-04-27 00:29:30.548   Registering 32-bit DLL/OCX: C:\Program Files
(x86)\LAV Filters\x86\LAVAudio.ax
2019-04-27 00:29:30.551   Spawning 32-bit RegSvr32:
"C:\windows\system32\regsvr32.exe" /s "C:\Program Files (x86)\LAV
Filters\x86\LAVAudio.ax"
2019-04-27 00:29:30.694   Registration successful.
2019-04-27 00:29:30.694   Registering 32-bit DLL/OCX: C:\Program Files
(x86)\LAV Filters\x86\LAVSplitter.ax
2019-04-27 00:29:30.697   Spawning 32-bit RegSvr32:
"C:\windows\system32\regsvr32.exe" /s "C:\Program Files (x86)\LAV
Filters\x86\LAVSplitter.ax"
2019-04-27 00:29:30.842   Registration successful.
2019-04-27 00:29:30.842   Registering 32-bit DLL/OCX: C:\Program Files
(x86)\LAV Filters\x86\LAVVideo.ax
2019-04-27 00:29:30.845   Spawning 32-bit RegSvr32:
"C:\windows\system32\regsvr32.exe" /s "C:\Program Files (x86)\LAV
Filters\x86\LAVVideo.ax"
2019-04-27 00:29:30.983   Registration successful.
2019-04-27 00:29:30.983   Registering 32-bit DLL/OCX: C:\Program Files
(x86)\LAV Filters\x64\LAVAudio.ax
2019-04-27 00:29:30.985   Spawning 32-bit RegSvr32:
"C:\windows\system32\regsvr32.exe" /s "C:\Program Files (x86)\LAV
Filters\x64\LAVAudio.ax"
2019-04-27 00:29:31.075   Registration failed:
                          RegSvr32 failed with exit code 0x3.
2019-04-27 00:29:31.075   Message box (Abort/Retry/Ignore):
                          C:\Program Files (x86)\LAV Filters\x64\LAVAudio.ax

                          Unable to register the DLL/OCX: RegSvr32 failed with
exit code 0x3.

                          Click Retry to try again, Ignore to proceed anyway
(not recommended), or Abort to cancel installation.
--- snip ---

'64-bit install mode: No' (!)

And yes, it really spawns 32-bit regsvr32.exe for the 64-bit Codec/Filter dll.

@Louis

--- quote ---
This looks like a regression. I tried manually register in wine-4.0 and in
current git.
--- quote ---

No, Wine behaves more correctly now. It was fixed with
https://source.winehq.org/git/wine.git/commitdiff/9839bb7691a1b1c57a4ca501d03825420c1609d7
and friends.

It seems MS implemented a workaround for 'regsvr32.exe' to "help" all those
broken/brain damaged installers to still succeed in such Wow64 scenarios.

Even Wine respawns processes with proper bitness to cope with Wow64 environment
process/dll bitness requirements. The latest case was Wine uninstaller
(https://source.winehq.org/git/wine.git/commitdiff/88580a3ad6e6afea25716b8717a33d20184154e5).

I found this blog entry which supports my analysis:

http://blog.differentpla.net/blog/2008/10/25/things-i-learnt-this-week-regsvr32exe-on-windows-x64/

Internet Archive link in case it goes away:

https://web.archive.org/web/20150912153210/http://blog.differentpla.net/blog/2008/10/25/things-i-learnt-this-week-regsvr32exe-on-windows-x64/

--- quote ---
Things I learnt this week: RegSvr32.EXE on Windows x64
2008-10-25 16:17:29 +0000

On Windows, 64-bit processes cannot load 32-bit DLLs, and 32-bit processes
cannot load 64-bit DLLs. How does REGSVR32.EXE manage to successfully register
both 32-bit and 64-bit COM DLLs?

On Windows x64 (I’ve checked on Windows Vista and Windows 2003, and I assume
it’s the same for Windows XP and Windows 2008) there are two copies of
REGSVR32.EXE. One of them is in C:\Windows\System32, and is 64-bit; the other
is in C:\Windows\SysWOW64, and is 32-bit.

If you attempt to use the 64-bit version of REGSVR32.EXE to register a 32-bit
DLL, it spots this and spawns the 32-bit version to do the registration.
Similarly, if you attempt to use the 32-bit version of REGSVR32.EXE to register
a 64-bit DLL, it spawns the 64-bit version.
--- quote ---

Wine source:

https://source.winehq.org/git/wine.git/blob/HEAD:/programs/regsvr32/regsvr32.c#l91

--- snip ---
  91 /**
  92  * Loads procedure.
  93  *
  94  * Parameters:
  95  * strDll - name of the dll.
  96  * procName - name of the procedure to load from the dll.
  97  * DllHandle - a variable that receives the handle of the loaded dll.
  98  */
  99 static VOID *LoadProc(const WCHAR* strDll, const char* procName, HMODULE*
DllHandle)
 100 {
 101     VOID* (*proc)(void);
 102 
 103     *DllHandle = LoadLibraryExW(strDll, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
 104     if(!*DllHandle)
 105     {
 106         output_write(STRING_DLL_LOAD_FAILED, strDll);
 107         ExitProcess(LOADLIBRARY_FAILED);
 108     }
 109     proc = (VOID *) GetProcAddress(*DllHandle, procName);
 110     if(!proc)
 111     {
 112         output_write(STRING_PROC_NOT_IMPLEMENTED, procName, strDll);
 113         FreeLibrary(*DllHandle);
 114         return NULL;
 115     }
 116     return proc;
 117 }
--- snip ---

So you might want to check for 'ERROR_BAD_EXE_FORMAT' lasterror in case of
'LoadLibraryExW()' failure and respawn with proper bitness (either direction).

$ sha1sum LAVFilters-0.74.1-Installer.exe
ab9f0c04ea67088ea4f52b310565172427a567d6  LAVFilters-0.74.1-Installer.exe

$ du -sh LAVFilters-0.74.1-Installer.exe
12M    LAVFilters-0.74.1-Installer.exe

$ wine --version
wine-4.7

Regards

-- 
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.


More information about the wine-bugs mailing list