Apuntando tanto a 32 bits como a 64 bits con Visual Studio en la misma solución / proyecto

111

Tengo un pequeño dilema sobre cómo configurar las compilaciones de mi estudio visual para la orientación múltiple.

Antecedentes: c # .NET v2.0 con p / invocando en DLL de 32 bits de terceros, SQL compact v3.5 SP1, con un proyecto de instalación. En este momento, el destino de la plataforma está configurado en x86, por lo que se puede ejecutar en Windows x64.

La empresa de terceros acaba de lanzar versiones de 64 bits de sus DLL y quiero crear un programa dedicado de 64 bits.

Esto plantea algunas preguntas para las que aún no tengo respuesta. Quiero tener exactamente la misma base de código. Debo construir con referencias al conjunto de DLL de 32 bits o DLL de 64 bits. (Tanto de terceros como de SQL Server Compact)

¿Se puede solucionar esto con 2 nuevos conjuntos de configuraciones (Debug64 y Release64)?

¿Debo crear 2 proyectos de instalación separados (proyectos de estudio visual estándar, sin Wix o cualquier otra utilidad), o esto se puede resolver dentro del mismo .msi?

Cualquier idea y / o recomendación será bienvenida.

Magnus Johansson
fuente
@Magnus Johansson: puede usar dos configuraciones para lograr la mitad de su objetivo. El MSI es un poco más difícil.
user7116

Respuestas:

83

Sí, puede apuntar a x86 y x64 con la misma base de código en el mismo proyecto. En general, las cosas simplemente funcionarán si crea las configuraciones de solución correctas en VS.NET (aunque P / Invoke a DLL completamente no administradas probablemente requerirá algún código condicional): los elementos que encontré que requieren atención especial son:

  • Referencias a ensamblados administrados externos con el mismo nombre pero con su propio bitness específico (esto también se aplica a ensamblados de interoperabilidad COM)
  • El paquete MSI (que, como ya se ha señalado, deberá apuntar a x86 o x64)
  • Cualquier acción personalizada basada en la clase del instalador de .NET en su paquete MSI

El problema de la referencia de ensamblado no se puede resolver por completo dentro de VS.NET, ya que solo le permitirá agregar una referencia con un nombre dado a un proyecto una vez. Para solucionar esto, edite su archivo de proyecto manualmente (en VS, haga clic con el botón derecho en el archivo de su proyecto en el Explorador de soluciones, seleccione Descargar proyecto, luego haga clic con el botón derecho nuevamente y seleccione Editar). Después de agregar una referencia a, digamos, la versión x86 de un ensamblado, su archivo de proyecto contendrá algo como:

<Reference Include="Filename, ..., processorArchitecture=x86">
  <HintPath>C:\path\to\x86\DLL</HintPath>
</Reference>

Envuelva esa etiqueta de referencia dentro de una etiqueta ItemGroup que indique la configuración de la solución a la que se aplica, por ejemplo:

<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
   <Reference ...>....</Reference>
</ItemGroup>

Luego, copie y pegue la etiqueta ItemGroup completa y edítela para que contenga los detalles de su DLL de 64 bits, por ejemplo:

<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
  <Reference Include="Filename, ..., processorArchitecture=AMD64">
     <HintPath>C:\path\to\x64\DLL</HintPath>
   </Reference>
</ItemGroup>

Después de volver a cargar su proyecto en VS.NET, el cuadro de diálogo Referencia de ensamblado estará un poco confundido por estos cambios y es posible que encuentre algunas advertencias sobre ensamblajes con el procesador de destino incorrecto, pero todas sus compilaciones funcionarán bien.

Resolver el problema de MSI es el siguiente, y lamentablemente esto se requiere una herramienta non-VS.NET: Prefiero Caphyon de Advanced Installer para tal fin, ya que se quita el truco básico implicado (crear un MSI común, así como de 32 bits y MSI específicos de 64 bits, y use un iniciador de configuración .EXE para extraer la versión correcta y hacer las reparaciones necesarias en tiempo de ejecución) muy, muy bien.

Probablemente pueda lograr los mismos resultados utilizando otras herramientas o el conjunto de herramientas Windows Installer XML (WiX) , pero Advanced Installer hace las cosas tan fáciles (y es bastante asequible) que nunca he buscado alternativas.

Sin embargo, una cosa para la que aún puede necesitar WiX, incluso cuando usa el Instalador avanzado, es para las acciones personalizadas de la Clase de instalador .NET. Aunque es trivial especificar ciertas acciones que solo deberían ejecutarse en ciertas plataformas (usando las condiciones de ejecución VersionNT64 y NO VersionNT64, respectivamente), las acciones personalizadas de IA incorporadas se ejecutarán usando el Framework de 32 bits, incluso en máquinas de 64 bits .

Esto puede corregirse en una versión futura, pero por ahora (o cuando use una herramienta diferente para crear sus MSI que tenga el mismo problema), puede usar el soporte de acciones personalizadas administradas de WiX 3.0 para crear archivos DLL de acción con el bitness adecuado que se ejecutará utilizando el Framework correspondiente.


Editar: a partir de la versión 8.1.2, Advanced Installer admite correctamente acciones personalizadas de 64 bits. Desde mi respuesta original, su precio ha aumentado bastante, desafortunadamente, aunque sigue siendo un valor extremadamente bueno en comparación con InstallShield y sus similares ...


Editar: si sus DLL están registradas en el GAC, también puede usar las etiquetas de referencia estándar de esta manera (SQLite como ejemplo):

<ItemGroup Condition="'$(Platform)' == 'x86'">
    <Reference Include="System.Data.SQLite, Version=1.0.80.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86" />
</ItemGroup>
<ItemGroup Condition="'$(Platform)' == 'x64'">
    <Reference Include="System.Data.SQLite, Version=1.0.80.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=AMD64" />
</ItemGroup>

La condición también se reduce a todos los tipos de compilación, lanzamiento o depuración, y solo especifica la arquitectura del procesador.

mdb
fuente
En Visual Studio 2008, encontré que <ItemGroup> s no se podían anidar. Esta solución funcionó bien una vez que hice los nuevos <ItemGroup> s debajo del grupo del resto de las <Reference>. También tuve que cambiar x86 a AnyCPU, lo que probablemente se relaciona con la historia de mi proyecto en particular.
Oliver Bock
Ese instalador avanzado se ve bastante impresionante.
Pat
Esta puede ser una pregunta tonta, pero ¿cómo llegar al archivo para editarlo manualmente?
hr
1
Para editar el archivo dentro de VS, haga clic con el botón derecho en el proyecto en el Explorador de soluciones y busque "Descargar proyecto". Una vez descargado el proyecto, haz clic derecho sobre él nuevamente y haz clic en "Editar <nombre de archivo del proyecto>". Después de haber editado el archivo del proyecto, guárdelo y haga clic derecho en el archivo del proyecto nuevamente y cárguelo. Si no hay errores tipográficos o errores, se cargará nuevamente. Si no es así, VS le dirá prácticamente cuál es el problema con el archivo. ¡Espero que ayude!
John Baughman
¡Gran respuesta! ¡Gracias!
John Baughman
27

Supongamos que tiene las DLL compiladas para ambas plataformas y se encuentran en la siguiente ubicación:

C:\whatever\x86\whatever.dll
C:\whatever\x64\whatever.dll

Simplemente necesita editar su archivo .csproj desde esto:

<HintPath>C:\whatever\x86\whatever.dll</HintPath>

A esto:

<HintPath>C:\whatever\$(Platform)\whatever.dll</HintPath>

Entonces debería poder construir su proyecto dirigido a ambas plataformas, y MSBuild buscará en el directorio correcto para la plataforma elegida.

Tim Booker
fuente
Sería genial si funcionara, pero no funciona. Al menos no para mi.
John Sheehan
10
¿No se supone que es: <HintPath> C: \ cualquier \ $ (Plataforma) \ cualquier.dll </HintPath>
Andreas
Funcionó bien en Visual Studio 2008 para mí, pero no copié automáticamente la DLL en el directorio de destino de la compilación, como lo hace una <Referencia> normal. La solución de mdb funcionó mejor para mí.
Oliver Bock
1

No estoy seguro de la respuesta total a su pregunta, pero pensé en señalar un comentario en la sección de Información adicional de la página de descarga de SQL Compact 3.5 SP1 al ver que está viendo x64, espero que ayude.

Debido a los cambios en SQL Server Compact SP1 y la compatibilidad adicional con la versión de 64 bits, los entornos de modo mixto e instalados de forma centralizada de la versión de 32 bits de SQL Server Compact 3.5 y la versión de 64 bits de SQL Server Compact 3.5 SP1 pueden crear lo que parece ser intermitente problemas. Para minimizar la posibilidad de conflictos y permitir la implementación de aplicaciones cliente administradas en una plataforma neutral, la instalación centralizada de la versión de 64 bits de SQL Server Compact 3.5 SP1 con el archivo Windows Installer (MSI) también requiere la instalación de la versión de 32 bits de SQL Server. Archivo MSI compacto 3.5 SP1. Para aplicaciones que solo requieren 64 bits nativos, se puede utilizar la implementación privada de la versión de 64 bits de SQL Server Compact 3.5 SP1.

Leí esto como "incluir los archivos SQLCE de 32 bits , así como los archivos de 64 bits" si se distribuye para clientes de 64 bits.

Hace la vida interesante, supongo ... debo decir que me encanta la línea "lo que parecen ser problemas intermitentes" ... suena un poco como "estás imaginando cosas, pero por si acaso, haz esto ..."

brillar
fuente
1

Una compilación .Net con dependencias x86 / x64

Si bien todas las demás respuestas le brindan una solución para hacer diferentes Builds según la plataforma, le doy la opción de tener solo la configuración "AnyCPU" y hacer un build que funcione con sus dlls x86 y x64.

Tienes que escribir un código de plomería para esto.

Resolución de x86 / x64-dlls correctos en tiempo de ejecución

Pasos:

  1. Utilice AnyCPU en csproj
  2. Decida si solo hace referencia a las dlls x86 o x64 en sus csprojs. Adapte la configuración de UnitTests a la configuración de arquitectura que haya elegido. Es importante para depurar / ejecutar las pruebas dentro de VisualStudio.
  3. En las propiedades de referencia, establezca Copiar versión local y específica en falso
  4. Deshágase de las advertencias de arquitectura agregando esta línea al primer PropertyGroup en todos sus archivos csproj donde hace referencia a x86 / x64: <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  5. Agregue este script postbuild a su proyecto de inicio, use y modifique las rutas de este script sp que copia todos sus dlls x86 / x64 en las subcarpetas correspondientes de su build bin \ x86 \ bin \ x64 \

    xcopy /E /H /R /Y /I /D $(SolutionDir)\YourPathToX86Dlls $(TargetDir)\x86 xcopy /E /H /R /Y /I /D $(SolutionDir)\YourPathToX64Dlls $(TargetDir)\x64

    -> Cuando inicia la aplicación ahora, obtiene una excepción de que no se pudo encontrar el ensamblaje.

  6. Registre el evento AssemblyResolve justo al comienzo del punto de entrada de su aplicación

    AppDomain.CurrentDomain.AssemblyResolve += TryResolveArchitectureDependency;

    con este método:

    /// <summary>
    /// Event Handler for AppDomain.CurrentDomain.AssemblyResolve
    /// </summary>
    /// <param name="sender">The app domain</param>
    /// <param name="resolveEventArgs">The resolve event args</param>
    /// <returns>The architecture dependent assembly</returns>
    public static Assembly TryResolveArchitectureDependency(object sender, ResolveEventArgs resolveEventArgs)
    {
        var dllName = resolveEventArgs.Name.Substring(0, resolveEventArgs.Name.IndexOf(","));
    
        var anyCpuAssemblyPath = $".\\{dllName}.dll";
    
        var architectureName = System.Environment.Is64BitProcess ? "x64" : "x86";
    
        var assemblyPath = $".\\{architectureName}\\{dllName}.dll";
    
        if (File.Exists(assemblyPath))
        {
            return Assembly.LoadFrom(assemblyPath);
        }
    
        return null;
    }
  7. Si tiene pruebas unitarias, haga un TestClass con un método que tenga un AssemblyInitializeAttribute y también registre el TryResolveArchitectureDependency-Handler anterior allí. (Esto no se ejecutará a veces si ejecuta pruebas individuales dentro de Visual Studio, las referencias no se resolverán desde el contenedor UnitTest. Por lo tanto, la decisión del paso 2 es importante).

Beneficios:

  • Una instalación / compilación para ambas plataformas

Inconvenientes: - No hay errores en el tiempo de compilación cuando las dlls x86 / x64 no coinciden. - ¡Aún debe ejecutar la prueba en ambos modos!

Opcionalmente, cree un segundo ejecutable que sea exclusivo para la arquitectura x64 con Corflags.exe en el script postbuild

Otras variantes para probar: - No necesita el controlador de eventos AssemblyResolve si se asegura de que las dlls correctas se copien en su carpeta binaria al inicio (Evalúe la arquitectura del proceso -> mueva las DLL correspondientes de x64 / x86 a la carpeta bin y viceversa. ) - En el instalador evalúe la arquitectura y elimine los binarios de la arquitectura incorrecta y mueva los correctos a la carpeta bin.

Felix Keil
fuente
0

Respecto a tu última pregunta. Lo más probable es que no pueda resolver esto dentro de un solo MSI. Si está utilizando carpetas de registro / sistema o cualquier cosa relacionada, el propio MSI debe ser consciente de esto y debe preparar un MSI de 64 bits para instalarlo correctamente en una máquina de 32 bits.

Existe la posibilidad de que pueda hacer que su producto se instale como una aplicación de 32 it y aún así poder ejecutarlo como una de 64 bits, pero creo que eso puede ser algo difícil de lograr.

Dicho esto, creo que debería poder mantener una única base de código para todo. En mi lugar de trabajo actual lo hemos logrado. (pero se necesitaron algunos malabarismos para que todo funcionara en conjunto)

Espero que esto ayude. Aquí hay un enlace a información relacionada con problemas de 32/64 bits: http://blog.typemock.com/2008/07/registry-on-windows-64-bit-double-your.html

Lior Friedman
fuente
0

Si usa acciones personalizadas escritas en .NET como parte de su instalador MSI, entonces tiene otro problema.

El 'shim' que ejecuta estas acciones personalizadas es siempre de 32 bits, entonces su acción personalizada también se ejecutará en 32 bits, independientemente del objetivo que especifique.

Más información y algunos movimientos ninja para moverse (básicamente cambie el MSI para usar la versión de 64 bits de esta cuña)

Construyendo un MSI en Visual Studio 2005/2008 para trabajar en un SharePoint 64

Acciones personalizadas administradas de 64 bits con Visual Studio

Ryan
fuente
0

¡Puede generar dos soluciones de manera diferente y fusionarlas después! Hice esto para VS 2010. y funciona. Tenía 2 soluciones diferentes generadas por CMake y las fusioné

voidMainReturn
fuente
0

Puede usar una condición para un ItemGroup para las referencias dll en el archivo del proyecto.
Esto hará que Visual Studio vuelva a verificar la condición y las referencias cada vez que cambie la configuración activa.
Simplemente agregue una condición para cada configuración.

Ejemplo:

 <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
    <Reference Include="DLLName">
      <HintPath>..\DLLName.dll</HintPath>
    </Reference>
    <ProjectReference Include="..\MyOtherProject.vcxproj">
      <Project>{AAAAAA-000000-BBBB-CCCC-TTTTTTTTTT}</Project>
      <Name>MyOtherProject</Name>
    </ProjectReference>
  </ItemGroup>
Yochai Timmer
fuente