2017-04-20 1 views
-1

32ビット版と64ビット版の管理対象アプリケーションを同じマシン上で実行する必要があり、同じ名前の両方のビット数のアンマネージDLLに依存する(つまり、 ICU 4.2)。WOW64プログラムファイルのリダイレクトが機能しない

だから、私は重い物を持ち上げる行うにはWOW64ファイルシステムのリダイレクトに頼って、そのマッチングプログラムファイルにICUの両方のバージョンを置くことにしました:C:\Program Files (x86)\ICUBinsC:\Program Files\ICUBinsにおける64ビット版と32ビット版を、前者は変数PATHに追加されました。

...それ以外は動作しません。どちらのバージョンのプログラムでも、アンマネージDLLの64ビット版を使用すると、C:\Program Files\ICUBinsを32ビットプロセスで使用しようとする試みがC:\Program Files (x86)\ICUBinsにリダイレクトされない理由を理解できません。

編集追加する:

をここでは、問題を再現し(ただしラッパーコードので代わりにDLLをロードする、それは単にそのbit化をチェックし、巨大であった)最小のコード例です。それは最初に.NET 2.0のために作成されたので、それはWOW64をチェックするために廃止された機能を使用していること 注:私はこのプログラムの32ビットバージョンを実行

using System; 

namespace SearchICU 
{ 
    /// <summary> 
    /// Methods for getting the version and date of the assembly that calls them. 
    /// </summary> 
    public static class BitnessHelper 
    { 
     public enum Machine : short 
     { 
      x86=0x14C, 
      Alpha=0x184, 
      ARM=0x1C0, 
      MIPS16R3000=0x162, 
      MIPS16R4000=0x166, 
      MIPS16R10000=0x168, 
      PowerPCLE=0x1F0, 
      PowerPCBE=0x1F2, 
      Itanium=0x200, 
      MIPS16=0x266, 
      Alpha64=0x284, 
      MIPSFPU=0x366, 
      MIPSFPU16=0x466, 
      x64=unchecked((short)0x8664), 
     } 

     public static Machine RetrieveMachine(string filePath) 
     { 
      if(string.IsNullOrEmpty(filePath)) { throw new ArgumentNullException("filePath"); } 

      const int c_PeHeaderOffsetOffset = 60; //Within whole file/IMAGE_DOS_HEADER structure. Equal to 0x003C 
      const int c_MachineOffset = 4; //Within IMAGE_NT_HEADERS 

      //Just read "enough" of the file: In modern PE files, the IMAGE_NT_HEADERS structure should never start past 2KiB anyway. 
      byte[] b = new byte[2048]; 
      System.IO.Stream s = null; 

      try 
      { 
       s = new System.IO.FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read); 
       s.Read(b, 0, 2048); 
      } 
      finally 
      { 
       if(s != null) 
       { 
        s.Close(); 
       } 
      } 

      //First check the MZ header (IMAGE_DOS_HEADER)'s magic. 
      short mzMagic = ReadInt16LE(b, 0); 
      if(mzMagic != 0x5A4D) //"MZ" in little-endian 
       throw new BadImageFormatException("File does not start with MZ header."); 

      //Get its "next header offset" value and check the PE header (IMAGE_NT_HEADERS)'s magic. 
      int peHeaderOffset = ReadInt32LE(b, c_PeHeaderOffsetOffset); 
      int peMagic = ReadInt32LE(b, peHeaderOffset); 
      if(peMagic != 0x00004550) //"PE\0\0" in little-endian 
       throw new BadImageFormatException("Header pointed by MZ header is not PE."); 

      //Read the machine from the PE header (IMAGE_NT_HEADERS). 
      //We're still in the bitness-agnostic part (the IMAGE_FILE_HEADER structure). 
      short machine = ReadInt16LE(b, peHeaderOffset + c_MachineOffset); 
      return (Machine)machine; 
     } 

     /// <summary>Reads a 16-bit integer as little-endian from a byte array.</summary> 
     /// <remarks>Because BitConverter depends on the platform's endianness, and here I need an "always little-endian" code. 
     /// Made public because some other code has a need for this.</remarks> 
     public static short ReadInt16LE(byte[] bytes, int offset) 
     { 
      if(bytes==null) { throw new ArgumentNullException("bytes"); } 

      ushort ret = 0; 
      for(int i=1 ; i>=0 ; i--) { ret <<= 8; ret |= bytes[offset+i]; } 
      return unchecked((short)ret); 
     } 
     /// <summary>Reads a 32-bit integer as little-endian from a byte array.</summary> 
     /// <remarks>Because BitConverter depends on the platform's endianness, and here I need an "always little-endian" code. 
     /// Made public because some other code has a need for this.</remarks> 
     public static int ReadInt32LE(byte[] bytes, int offset) 
     { 
      if(bytes==null) { throw new ArgumentNullException("bytes"); } 

      uint ret = 0; 
      for(int i=3 ; i>=0 ; i--) { ret <<= 8; ret |= bytes[offset+i]; } 
      return unchecked((int)ret); 
     } 

     #region Win64/WOW64 methods 
     /// <summary> 
     /// Win32 function <c>IsWow64Process</c>: Determines whether the specified process is running under WOW64. 
     /// </summary> 
     /// <param name="hProcess">[in] Process handle with enough access rights.</param> 
     /// <param name="Wow64Process">[out] set to <c>true</c> if running under WOW64, <c>false</c> for Win32 and Win64.</param> 
     /// <returns><c>true</c> if succeeded.</returns> 
     [System.Runtime.InteropServices.DllImport("Kernel32.dll", SetLastError=true)] 
     extern static bool IsWow64Process(IntPtr hProcess, ref bool Wow64Process); 

     /// <summary> 
     /// Wrapper for <c>IsWow64Process</c>, so the calling function may throw <c>SecurityException</c> when calling it 
     /// rather than be completely prevented from running by <c>LinkDemand</c>. 
     /// </summary> 
     static bool CallIsWow64Process(IntPtr hProcess, ref bool Wow64Process) 
     { 
      //P/Invoke has a LinkDemand for full trust, so this function won't even start 
      //if partially trusted. 
      return IsWow64Process(hProcess, ref Wow64Process); 
     } 

     /// <summary> 
     /// Wrapper for <c>Process.GetCurrentProcess</c>, so the calling function may throw <c>SecurityException</c> when calling it 
     /// rather than be completely prevented from running by <c>LinkDemand</c>. 
     /// </summary> 
     static IntPtr GetProcessHandle() 
     { 
      //GetCurrentProcess() has a LinkDemand for full trust, so this function won't even start 
      //if partially trusted. 
      return System.Diagnostics.Process.GetCurrentProcess().Handle; 
     } 

     /// <summary> 
     /// Wrapper for <c>Marshal.GetLastWin32Error</c>, so the calling function may throw <c>SecurityException</c> when calling it 
     /// rather than be completely prevented from running by <c>LinkDemand</c>. 
     /// </summary> 
     static int CallGetLastWin32Error() 
     { 
      //GetLastWin32Error() has a LinkDemand for UnmanagedCode, so this function won't even start 
      //if partially trusted. 
      return System.Runtime.InteropServices.Marshal.GetLastWin32Error(); 
     } 

     /// <summary> 
     /// Test whether the current process is running under Win32, Win64 or WOW64. 
     /// </summary> 
     /// <param name="message">[out] Human-readable message describing the situation.</param> 
     /// <returns><c>true</c> if succeeded, <c>false</c> if couldn't determine (security or IsWow64Process failure).</returns> 
     /// <exception cref="Exception">For any other error with the P/Invoke call.</exception> 
     public static bool TestWow64(out string message) 
     { 
      //Note on exceptions: Apparently, on a full .Net Framework P/Invoke can throw EntryPointNotFoundException, 
      //ExecutionEngineException (if incorrectly declared) or DllNotFoundException. 
      //(the former two replaced with MissingMethodException and NotSupportedException on the Compact Framework). 
      //Since we're hitting Kernel32.dll, using the correct declaration, and not planning for an embedded version, 
      //only EntryPointNotFoundException will be handled here. 
      try 
      { 
       bool isWow64 = false; 
       //Call wrapper functions to avoid security exceptions being thrown before the try block. 
       if(CallIsWow64Process(GetProcessHandle(), ref isWow64)) 
       { 
        if(isWow64) 
         message = "Running as a 32-bit process on a Win64 machine."; 
        else if(IntPtr.Size==4) 
         message = "Running on Win32."; 
        else if(IntPtr.Size==8) 
         message = "Running on Win64."; 
        else 
         message = string.Format("Something weird: Not WOW64, but pointer size is {0}.", IntPtr.Size); 
        return true; 
       } 
       else 
       { 
        message = string.Format("IsWow64Process was correctly called, but failed with error {0}", CallGetLastWin32Error()); 
        return false; 
       } 
      } 
      catch(EntryPointNotFoundException) 
      { 
       message = "Running on Win32, WOW64 not supported."; 
       return true; 
      } 
      catch(System.Security.SecurityException) 
      { 
       message = "Running in a sandbox, process information inaccessible."; 
       return false; 
      } 
      //catch(Exception e) 
      //{ 
      // log.Warn("IsWow64Process call failed:", e); //test 
      //} 
     } 

     /// <summary> 
     /// Wrapper method for determining whether the current process is 64-bit. 
     /// Useful for determining which version of a library to load. 
     /// </summary> 
     public static bool IsWin64 
     { 
      get { return IntPtr.Size==8; } //In V10, use Environment.Is64BitProcess 
     } 
     #endregion 
    } 
} 

using System; 
using System.IO; 

namespace SearchICU 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string bitness; 
      if(BitnessHelper.TestWow64(out bitness)) { Console.WriteLine(bitness); } 
      string icuDir = FindDirInPath(new string[] { "icudt42.dll", "icuuc42.dll", "icuin42.dll" }); 
      if(icuDir==null) 
      { 
       Console.WriteLine("ICU DLLs not found in PATH."); 
       return; 
      } 

      Console.WriteLine("ICU DLLs found in PATH:{1}\t{0}", icuDir, Environment.NewLine); 

      string dllPath = Path.Combine(icuDir, "icuin42.dll"); 
      BitnessHelper.Machine machine = BitnessHelper.RetrieveMachine(dllPath); 
      switch(machine) 
      { 
      case BitnessHelper.Machine.x86: 
       Console.WriteLine("DLL in path is 32-bit DLL."); 
       break; 
      case BitnessHelper.Machine.x64: 
       Console.WriteLine("DLL in path is 64-bit DLL."); 
       break; 
      default: 
       Console.WriteLine("DLL in path is unknown (machine={0}).", machine); 
       break; 
      } 
     } 

     public static string FindDirInPath(string[] filesToFind) 
     { 
      if(filesToFind==null || filesToFind.Length==0) 
       throw new ArgumentException("filesToFind must be a non-empty array of file names.", "filesToFind"); 

      string pathEnvVariable = Environment.GetEnvironmentVariable("PATH"); 
      if(!string.IsNullOrEmpty(pathEnvVariable)) 
      { 
       foreach(string pathDirectory in pathEnvVariable.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) 
       { 
        bool allFound = true; 
        foreach(string fileName in filesToFind) 
        { 
         string filePath = Path.Combine(pathDirectory, fileName); 
         if(!File.Exists(filePath)) 
         { 
          allFound = false; 
          break; 
         } 
        } 
        if(allFound) 
         return pathDirectory; 
       } 
      } 
      return null; 
     } 
    } 
} 

、出力はこうです:

Running as a 32-bit process on a Win64 machine. 
ICU DLLs found in PATH: 
     C:\Program Files\ICUBins 
DLL in path is 64-bit DLL. 
+2

これは私が何を言うことができる...です。私は、どこかに隠されたWow64DisableWow64FsRedirection()が大声で叫ぶだけです。これをしないでください。PATHはすでにそれほど醜いので、必要なDLLをクライアントアプリケーションと同じディレクトリに展開してください。 –

答えて

1

私は問題を見つけたと私は問題です。

もっと正確に言えば、最初にProgram Filesリダイレクトがあったとします。しかし、thedocumentationの何も実際にはそうだと思われます.WOW64はプログラムファイルをリダイレクトせず、単に%ProgramFiles%が指し示すものを変更します。傷害に侮辱を加えるために、I can't use %ProgramFiles% in the system PATHこれを修正する。

したがって、ユーザーごとに環境変数を設定する必要があります(またはアプリケーションプールごとにウェブサイトやアプリケーションプールの場合)、またはプログラム自体を変更して、PATHに正しくマッサージする必要があります。 DLL。

これをしないでください、PATH自体が過度に非常に醜いすでに、あなたはクライアントアプリケーションと同じディレクトリに

感謝を必要とするDLLを展開し、悲しいかな、私の衝動に削減することがあります私が「本当の」プログラムとASP.Netの両方のWebサイトを持っていることを言及することは控えました(後者の場合、アプリケーションのディレクトリにDLLを展開することはできません)。また、「実世界」コードはC++/CLIラッパーアセンブリを使用し、ICU DLL(C++/CLIラッパーに動的にリンクされた「静的」)ではなく、動的にロードされるアセンブリです。

編集:最後には、私は私のコードにこれを追加しました:

/// <summary> 
/// Enumerates the paths in the <c>PATH</c> environment variable, looking for <paramref name="filesToFind"/>, 
/// then checks their bitness and if it's a conflicting bitness in one of the Program Files directories, 
/// looks into the other Program File directory and updates <c>PATH</c> in consequence when successful. 
/// </summary> 
/// <param name="filesToFind">[in] File names.</param> 
/// <param name="oldPath">[out] Old value of <c>PATH</c>. This parameter is always set.</param> 
/// <param name="foundPath">[out] Directory in which the files were found, or <c>null</c> if they weren't.</param> 
/// <returns><c>true</c> if the <c>PATH</c> environment variable was modified.</returns> 
public static bool FindDirInPathAndUpdatePathWithBitness(string[] filesToFind, out string oldPath, out string foundPath) 
{ 
    if(filesToFind==null || filesToFind.Length==0) 
     throw new ArgumentException("filesToFind must be a non-empty array of file names.", "filesToFind"); 

    string pathEnvVariable = Environment.GetEnvironmentVariable("PATH"); 
    oldPath = pathEnvVariable; 
    foundPath = null; 

    if(!string.IsNullOrEmpty(pathEnvVariable)) 
    { 
     string[] pathArray = pathEnvVariable.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); 
     for(int iPath=0 ; iPath<pathArray.Length ; iPath++) 
     { 
      string pathDirectory = pathArray[iPath]; 
      bool allFound = true; 
      foreach(string fileName in filesToFind) 
      { 
       string filePath = Path.Combine(pathDirectory, fileName); 
       if(!File.Exists(filePath)) 
       { 
        allFound = false; 
        break; 
       } 
      } 

      //If all files were found in this directory 
      if(allFound) 
      { 
       //Now the fun begins 
       try 
       { 
        string firstFilePath = Path.Combine(pathDirectory, filesToFind[0]); 
        bool runningWin64 = BitnessHelper.IsWin64; 
        var fileBitness = BitnessHelper.RetrieveMachine(firstFilePath); 
        if(runningWin64 != (fileBitness==BitnessHelper.Machine.x64)) 
        { 
         //Bitness conflict detected. Is this directory in %ProgramFiles%? 
         bool bHandled = HandleBitnessConflict(ref pathDirectory, filesToFind[0], runningWin64, fileBitness); 
         if(bHandled) 
         { 
          pathArray[iPath] = pathDirectory; 
          string newPath = string.Join(";", pathArray); 
          Environment.SetEnvironmentVariable("PATH", newPath); 
          return true; 
         } 
         //Otherwise, several possible scenarios: 
         //Remove the path from PATH and keep searching (which requires some bookkeeping), 
         //or just return foundPath as if the check hadn't happened, letting subsequent code throw a BadImageFormatException. 
         //We'll just do the latter, at least for now. 
        } 
       } 
       catch { } 

       foundPath = pathArray[iPath]; 
       return false; 
      } 
     } 
    } 
    return false; 
} 

private static bool HandleBitnessConflict(ref string pathDirectory, string firstFileName, bool runningWin64, BitnessHelper.Machine fileBitness) 
{ 
    //Bitness conflict detected. Is this directory in %ProgramFiles%? 

    //Bitness-dependent Program Files 
    string programFiles = Environment.GetEnvironmentVariable("ProgramFiles"); 
    //Always points to 32-bit version, if a 64-bit Windows. 
    string programFilesX86 = Environment.GetEnvironmentVariable("ProgramFiles(x86)"); 
    //Always points to 64-bit version, if a 64-bit Windows 7 or greater. 
    string programW6432 = Environment.GetEnvironmentVariable("ProgramW6432"); 
    char[] directoryChars = new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; 

    if(string.IsNullOrEmpty(programFilesX86)) 
     return false; //Not Win64, so there won't be two Program Files directories anyway. 
    if(string.IsNullOrEmpty(programW6432)) 
    { 
     //Pre-7 Windows version: Try an heuristic. 
     int ix = programFilesX86.IndexOf(" (x86)", StringComparison.OrdinalIgnoreCase); 
     if(ix < 0) { return false; } //Heuristic failed. 
     string exactSubstring = programFilesX86.Substring(ix, " (x86)".Length); 
     programW6432 = programFilesX86.Replace(exactSubstring, ""); 
     if(!Directory.Exists(programW6432)) { return false; } //Heuristic failed. 
    } 

    if(pathDirectory.StartsWith(programFilesX86) && fileBitness==BitnessHelper.Machine.x86) 
    { 
     //The file is a 32-bit file in the 32-bit directory in the path; 
     //Since there's a conflict, this must mean the current process is 64-bit 
     if(!runningWin64) { return false; } //No conflict, no handling. 

     string directory64 = Path.Combine(programW6432, pathDirectory.Substring(programFilesX86.Length).TrimStart(directoryChars)); 
     string filePath64 = Path.Combine(directory64, firstFileName); 
     if(Directory.Exists(directory64) && File.Exists(filePath64)) 
     { 
      if(BitnessHelper.RetrieveMachine(filePath64) == BitnessHelper.Machine.x64) 
      { 
       pathDirectory = directory64; 
       return true; 
      } 
     } 
    } 
    else if(pathDirectory.StartsWith(programW6432) && fileBitness==BitnessHelper.Machine.x64) 
    { 
     //The file is a 64-bit file in the 64-bit directory in the path; 
     //Since there's a conflict, this must mean the current process is 32-bit 
     if(runningWin64) { return false; } //No conflict, no handling. 

     string directory32 = Path.Combine(programFilesX86, pathDirectory.Substring(programW6432.Length).TrimStart(directoryChars)); 
     string filePath32 = Path.Combine(directory32, firstFileName); 
     if(Directory.Exists(directory32) && File.Exists(filePath32)) 
     { 
      if(BitnessHelper.RetrieveMachine(filePath32) == BitnessHelper.Machine.x86) 
      { 
       pathDirectory = directory32; 
       return true; 
      } 
     } 
    } 

    return false; 
} 
関連する問題