Utilice condicionalmente la referencia de 32/64 bits al compilar en Visual Studio

124

Tengo un proyecto que se construye en 32/64 bits y tiene las correspondientes dependencias de 32/64 bits. Quiero poder cambiar las configuraciones y usar la referencia correcta, pero no sé cómo decirle a Visual Studio que use la dependencia apropiada para la arquitectura.

Tal vez estoy haciendo esto de la manera incorrecta, pero quiero poder cambiar entre x86 y x64 en el menú desplegable de configuración, y hacer que la DLL referenciada sea la bitness correcta.

Jonathan Yee
fuente
Muy poco claro, ¿qué idioma es este? ¿Está el proyecto DLL en la solución?
Hans Passant
Lo siento, esto es .NET, estoy escribiendo en C #.
Jonathan Yee
44
Ok, lo resolví con una solución tonta: creé un archivo csproj adicional que solo hace referencia a la DLL x64 (y eliminé la configuración x86 de csproj). Funciona, pero si alguien tuviera una solución más elegante que no involucrara un csproj adicional, me encantaría verla.
Jonathan Yee

Respuestas:

99

Esto es lo que hice en un proyecto anterior, que requerirá la edición manual de los archivos .csproj. También necesita directorios separados para los diferentes binarios, idealmente hermanos entre sí, y con el mismo nombre que la plataforma a la que se dirige.

Después de agregar referencias de una sola plataforma al proyecto, abra el archivo .csproj en un editor de texto. Antes del primer <ItemGroup>elemento dentro del <Project>elemento, agregue el siguiente código, que ayudará a determinar en qué plataforma está ejecutando (y construyendo).

<!-- Properties group for Determining 64bit Architecture -->
<PropertyGroup>
  <CurrentPlatform>x86</CurrentPlatform>
  <CurrentPlatform Condition="'$(PROCESSOR_ARCHITECTURE)'=='AMD64' or '$(PROCESSOR_ARCHITEW6432)'=='AMD64'">AMD64</CurrentPlatform>
</PropertyGroup>

Luego, para las referencias específicas de su plataforma, realice cambios como los siguientes:

<ItemGroup>
  <Reference Include="Leadtools, Version=16.5.0.0, Culture=neutral, PublicKeyToken=9cf889f53ea9b907, processorArchitecture=x86">
    <SpecificVersion>False</SpecificVersion>
    <HintPath>..\..\Lib\Leadtools\$(CurrentPlatform)\Leadtools.dll</HintPath>
  </Reference>
  <Reference Include="Leadtools.Codecs, Version=16.5.0.0, Culture=neutral, PublicKeyToken=9cf889f53ea9b907, processorArchitecture=x86">
    <SpecificVersion>False</SpecificVersion>
    <HintPath>..\..\Lib\Leadtools\$(CurrentPlatform)\Leadtools.Codecs.dll</HintPath>
  </Reference>
  <Reference Include="Leadtools.ImageProcessing.Core, Version=16.5.0.0, Culture=neutral, PublicKeyToken=9cf889f53ea9b907, processorArchitecture=x86">
    <SpecificVersion>False</SpecificVersion>
    <HintPath>..\..\Lib\Leadtools\$(CurrentPlatform)\Leadtools.ImageProcessing.Core.dll</HintPath>
  </Reference>
  <Reference Include="System" />
  <Reference Include="System.Core" />
  <Reference Include="System.Data.Entity" />
  <!--  Other project references -->
</ItemGroup>

Tenga en cuenta el uso de la $(CurrentPlatform)propiedad, que definimos anteriormente. En su lugar, podría usar condicionales para qué conjuntos incluir para qué plataforma. También podría necesitar:

  • Reemplace la $(PROCESSOR_ARCHITEW6432) y $(PROCESSOR_ARCHITECTURE)con $(Platform)tener en cuenta sólo la plataforma de destino de los proyectos
  • Modifique la lógica de determinación de la plataforma para que sea apropiada para la máquina actual, de modo que no esté construyendo / haciendo referencia a un binario de 64 bits para ejecutar en una plataforma de 32 bits.

Tenía esto escrito originalmente para un Wiki interno en el trabajo, sin embargo, lo modifiqué y publiqué el proceso completo en mi blog , si está interesado en las instrucciones detalladas paso a paso.

Hugo
fuente
1
Agradable. Seguí usando un condicional en el ItemGroup según la sugerencia a continuación, pero usando $ (PROCESSOR_ARCHITEW6432) y $ (PROCESSOR_ARCHITECTURE) para las condiciones como aquí. Una nota es que encontré que $ (PROCESSOR_ARCHITECTURE) devuelve x86 en las plataformas de 32 y 64 bits, pero $ (PROCESSOR_ARCHITEW6432) devuelve AMD64 solo en 64 bits. Algo a tener en cuenta si intenta probar x86 (porque supongo que AMD64 es un derivado de x86).
tjmoore
Gracias por esa información @tjmoore. ¿En qué O / S notaste esto? Acabo de revisar el mío nuevamente (Win7SP1) y dice AMD64 para $ (PROCESSOR_ARCHITECTURE), pero definitivamente me gustaría tener la información más completa y completa posible.
Hugo
77
Es curioso, mi búsqueda me trae aquí, y solo necesito esto porque también estoy usando LeadTools ... +1
Ed S.
La solución funciona para la configuración predeterminada, pero no desde mi prueba, no si cambia la configuración desde la configuración desde la lista desplegable Configuración de la solución de Visual Studio (2012 en mi caso).
Sarah Weinberger
En lugar de usar $ (PROCESSOR_ARCHITEW6432) usé $ (Platform) por alguna razón $ (PROCESSOR_ARCHITEW6432) no estaba funcionando.
Dzyann
60

AFAIK, si su proyecto requiere referencias que sean específicas de 32 bits o 64 bits (es decir, ensamblados de interoperabilidad COM), y no tiene interés en editar manualmente el archivo .csproj, entonces tendrá que crear archivos de 32 bits y Proyectos de 64 bits.

Debo señalar que la siguiente solución no se ha probado, pero debería funcionar. Si está dispuesto a editar manualmente el archivo .csproj, entonces debería poder lograr el resultado deseado con un solo proyecto. El archivo .csproj es solo un script de MSBuild, así que para una referencia completa, mira aquí . Una vez que abra el archivo .csproj en un editor, ubique los <Reference>elementos. Debería poder dividir estos elementos en 3 grupos de elementos distintos : referencias que no son específicas de la plataforma, referencias específicas de x86 y referencias específicas de x64.

Aquí hay un ejemplo que asume que su proyecto está configurado con plataformas de destino llamadas "x86" y "x64"

<!-- this group contains references that are not platform specific -->
<ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <!-- any other references that aren't platform specific -->
</ItemGroup>

<!-- x86 specific references -->
<ItemGroup Condition=" '$(Platform)' == 'x86' ">
    <Reference Include="MyComAssembly.Interop">
        <HintPath>..\..\lib\x86\MyComAssembly.Interop.dll</HintPath>
    </Reference>

    <!-- any additional x86 specific references -->
</ItemGroup>

<!-- x64 specific referneces -->
<ItemGroup Condition=" '$(Platform)' == 'x64' ">
    <Reference Include="MyComAssembly.Interop">
        <HintPath>..\..\lib\x64\MyComAssembly.Interop.dll</HintPath>
    </Reference>

    <!-- any additional x64 specific references -->
</ItemGroup>

Ahora, cuando configura la configuración de construcción de su proyecto / solución para apuntar a la plataforma x86 o x64, debe incluir las referencias adecuadas en cada caso. Por supuesto, deberás jugar con los <Reference>elementos. Incluso podría configurar proyectos ficticios donde agregue las referencias x86 y x64, y luego simplemente copie los <Reference>elementos necesarios de esos archivos ficticios del proyecto a su archivo de proyecto "real".


Editar 1
Aquí hay un enlace a los elementos comunes del proyecto MSBuild, que accidentalmente omití de la publicación original: http://msdn.microsoft.com/en-us/library/bb629388.aspx

Justin Holzer
fuente
Excelente respuesta !! Me salvó el día! Muchas gracias.
hellodear
20

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
1
¡Esto es genial, gracias! ¡Esta definitivamente debería ser la solución aceptada!
ManicBlowfish el
En serio, esta respuesta es mucho mejor y más simple que la aceptada.
Yandros
1
¿Es normal tener entradas duplicadas en Referencias después de hacer esto?
natenho
7

Estoy haciendo referencia a las DLL x86, ubicadas en, por ejemplo, \ component \ v3_NET4, en mi proyecto. Las DLL específicas para x86 / x64 se encuentran en subcarpetas denominadas "x86" y "x64" resp.

Luego estoy usando una secuencia de comandos previa a la compilación que copia las DLL apropiadas (x86 / x64) en la carpeta referenciada, basada en $ (PlatformName).

xcopy /s /e /y "$(SolutionDir)..\component\v3_NET4\$(PlatformName)\*" "$(SolutionDir)..\component\v3_NET4"

Funciona para mi.

Micke
fuente
3

Una compilación .Net con dependencias x86 / x64

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

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

Pasos:

  1. Use AnyCPU en csproj
  2. Decide si solo haces referencia a los dlls x86 o x64 en tus 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 Propiedades de referencia, configure 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 haga 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 para que copie todas 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 iniciaría la aplicación ahora, obtendrá una excepción de que no se pudo encontrar el ensamblado.

  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 una 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 se resolverán no desde el bin UnitTest. Por lo tanto, la decisión en el paso 2 es importante).

Beneficios:

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

Inconvenientes: - No hay errores en el momento de la compilación cuando x86 / x64 dlls no coinciden. - ¡Aún deberías 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 necesitaría el controlador de eventos AssemblyResolve si se asegura de lo contrario que los archivos DLL se copian en su carpeta binaria al inicio (Evalúe la arquitectura del proceso -> mueva los archivos DLL correspondientes de x64 / x86 a la carpeta bin y viceversa). - En el instalador, evalúe la arquitectura y elimine los archivos binarios para la arquitectura incorrecta y mueva los correctos a la carpeta bin.

Felix Keil
fuente
2

Me enfrenté al mismo problema y pasé bastante tiempo buscando una solución decente. La mayoría de las personas ofrecen edición manual de los archivos de la solución de Visual Studio, lo cual es bastante tedioso, propenso a errores y confuso al explorar estos archivos editados en la GUI de Visual Studio después. Cuando ya me rendí, la solución surgió por sí misma. Es muy similar a lo que Micke recomienda en su respuesta anterior.

En el administrador de cuentas, creé dos objetivos de compilación separados para plataformas x86 y x64, como de costumbre. A continuación, agregué una referencia al ensamblaje x86 a mi proyecto. En este punto, creí que el proyecto está configurado solo para la compilación x86 y nunca se compilará para la configuración x64, a menos que haga una edición manual como lo sugirió Hugo anteriormente.

Después de un tiempo, finalmente olvidé la limitación y accidentalmente comencé la compilación x64. Por supuesto, la construcción falló. Pero importante fue el mensaje de error que recibí. El mensaje de error decía que faltaba el ensamblado nombrado exactamente como mi ensamblado x86 referenciado en la carpeta destinada como destino de compilación x64 para mi solución.

Al notar esto, he copiado manualmente el ensamblado x64 adecuado en este directorio. ¡Gloria! Mi compilación x64 tuvo éxito milagrosamente con un ensamblaje adecuado encontrado y vinculado implícitamente. Fue cuestión de minutos modificar mi solución para establecer un directorio de destino de compilación para el ensamblado x64 en esta carpeta. Después de estos pasos, la solución se construye automáticamente para x86 y x64 sin edición manual de los archivos MSBuild.

Para resumir:

  1. Crea objetivos x86 y x64 en un solo proyecto
  2. Agregue todas las referencias de proyecto adecuadas a ensamblajes x86
  3. Establecer un directorio de destino de compilación común para todos los ensamblados x64
  4. En caso de que tenga ensamblados x64 listos, simplemente cópielos una vez en su directorio de destino de compilación x64

Después de completar estos pasos, su solución se compilará correctamente para las configuraciones x86 y x64.

Esto funcionó para mí en el proyecto Visual Studio 2010 .NET 4.0 C #. Evidentemente, este es un tipo de comportamiento interno no documentado de Visual Studio, que podría estar sujeto a cambios en las versiones 2012, 2013 y 2015. Si alguien prueba otras versiones, comparta su experiencia.

Boris Zinchenko
fuente
-1

Terminé usando lo que considero una solución más fácil que es una especie de inversión de Micke. El proyecto es una aplicación de formularios C #, Visual Studio 2015, con objetivos x86 y x64. Hice referencia a uno de los ensamblados .NET, utilicé el de 32 bits. En las propiedades de referencia, configuro "Copiar local" en falso. Luego, simplemente coloco manualmente el ensamblado .Net apropiado (32 o 64 bits) en cada directorio de destino. El bit de referencia real es irrelevante, suponiendo que tengan las mismas capacidades, ya que solo define la interfaz externa. También podría poner un paso de copia de compilación posterior si quisiera hacerse elegante. Tenga en cuenta que este proyecto también tenía una referencia COM, lo mismo funciona. La referencia define los objetos / interfaces por lo que el bitness de la DLL de referencia es irrelevante. Si se registran DLL COM de 32 bits y 64 bits, la aplicación buscará en el lugar apropiado del registro y creará el objeto COM correcto de 32 o 64 bits. ¡Funciona para mi!

Jeff H
fuente