Utilizzare in modo condizionale il riferimento a 32/64 bit durante la creazione in Visual Studio


124

Ho un progetto che si sviluppa a 32/64 bit e ha dipendenze a 32/64 bit corrispondenti. Voglio essere in grado di cambiare configurazione e utilizzare il riferimento corretto, ma non so come dire a Visual Studio di utilizzare la dipendenza appropriata dall'architettura.

Forse sto andando in questo modo nel modo sbagliato, ma voglio essere in grado di passare tra x86 e x64 nel menu a discesa della configurazione e fare in modo che la DLL di riferimento sia il giusto testimone.


Molto poco chiaro, che lingua è questa? Il progetto DLL è nella soluzione?
Hans Passant,

Siamo spiacenti, questo è .NET, sto scrivendo in C #.
Jonathan Yee,

4
Ok, l'ho risolto con una soluzione stupida: ho creato un file csproj aggiuntivo che fa riferimento solo alla DLL x64 (e ho rimosso la configurazione x86 da csproj). Funziona, ma se qualcuno avesse una soluzione più elegante che non comportasse un ulteriore csproj, mi piacerebbe vederlo.
Jonathan Yee,

Risposte:


99

Ecco cosa ho fatto in un precedente progetto, che richiederà l'edizione manuale dei file .csproj. Hai anche bisogno di directory separate per i diversi binari, idealmente fratelli l'uno dell'altro e con lo stesso nome della piattaforma che stai prendendo di mira.

Dopo aver aggiunto i riferimenti di una singola piattaforma al progetto, apri .csproj in un editor di testo. Prima del primo <ItemGroup>elemento all'interno <Project>dell'elemento, aggiungi il seguente codice, che ti aiuterà a determinare su quale piattaforma stai eseguendo (e costruendo).

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

Quindi, per i riferimenti specifici della tua piattaforma, apporti modifiche come le seguenti:

<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>

Nota l'uso della $(CurrentPlatform)proprietà, che abbiamo definito sopra. È possibile, invece, utilizzare condizionali per i quali assiemi includere per quale piattaforma. Potrebbe anche essere necessario:

  • Sostituire $(PROCESSOR_ARCHITEW6432)e $(PROCESSOR_ARCHITECTURE)con $(Platform)considerare SOLO la piattaforma di destinazione dei progetti
  • Modificare la logica di determinazione della piattaforma per essere appropriata alla macchina corrente, in modo da non creare / fare riferimento a un binario a 64 bit da eseguire su una piattaforma a 32 bit.

L'ho fatto scrivere originariamente per un Wiki interno al lavoro, tuttavia, l'ho modificato e pubblicato l' intero processo sul mio blog , se sei interessato alle istruzioni dettagliate dettagliate.


1
Bello. Sono andato con l'uso di un condizionale sul ItemGroup secondo il suggerimento seguente ma usando $ (PROCESSOR_ARCHITEW6432) e $ (PROCESSOR_ARCHITECTURE) per le condizioni come qui. Una nota è che ho trovato $ (PROCESSOR_ARCHITECTURE) restituisce x86 su entrambe le piattaforme a 32 e 64 bit ma $ (PROCESSOR_ARCHITEW6432) restituisce AMD64 solo su 64 bit. Qualcosa da notare se provi a provare x86 (perché AMD64 è una derivata di x86 suppongo).
tjmoore,

Grazie per queste informazioni @tjmoore. Su quale O / S hai notato questo? Ho appena ricontrollato il mio (Win7SP1) e ho detto AMD64 per $ (PROCESSOR_ARCHITECTURE), ma vorrei sicuramente avere le informazioni complete e complete possibili.
Hugo

7
Divertente, la mia ricerca mi porta qui e ne ho bisogno solo perché sto usando anche LeadTools ... +1
Ed S.

La soluzione funziona per la configurazione predefinita, ma non dal mio test non se si modifica la configurazione dalla configurazione dall'elenco a discesa Configurazione soluzione di Visual Studio (2012 nel mio caso).
Sarah Weinberger,

Invece di usare $ (PROCESSOR_ARCHITEW6432) ho usato $ (piattaforma) per qualche motivo $ (PROCESSOR_ARCHITEW6432) non funzionava.
Dzyann,

60

AFAIK, se il tuo progetto richiede riferimenti a 32 o 64 bit specifici (ad es. Assiemi di interoperabilità COM) e non hai interesse a modificare manualmente il file .csproj, dovrai creare un file a 32 bit separato e Progetti a 64 bit.

Devo notare che la seguente soluzione non è testata, ma dovrebbe funzionare. Se desideri modificare manualmente il file .csproj, dovresti essere in grado di ottenere il risultato desiderato con un singolo progetto. Il file .csproj è solo uno script MSBuild, quindi per un riferimento completo, guarda qui . Dopo aver aperto il file .csproj in un editor, individuare gli <Reference>elementi. Dovresti essere in grado di suddividere questi elementi in 3 gruppi di articoli distinti : riferimenti che non sono specifici della piattaforma, riferimenti specifici per x86 e riferimenti specifici per x64.

Ecco un esempio che presuppone che il progetto sia configurato con piattaforme di destinazione denominate "x86" e "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>

Ora, quando imposti la configurazione del tuo progetto / soluzione per indirizzare la piattaforma x86 o x64, dovrebbe includere i riferimenti corretti in ciascun caso. Ovviamente, dovrai giocare con gli <Reference>elementi. È anche possibile impostare progetti fittizi in cui si aggiungono i riferimenti x86 e x64 e quindi copiare gli <Reference>elementi necessari da quei file di progetto fittizi nel file di progetto "reale".


Modifica 1
Ecco un link agli elementi comuni del progetto MSBuild, che ho accidentalmente escluso dal post originale: http://msdn.microsoft.com/en-us/library/bb629388.aspx


Risposta eccellente !! Mi hai salvato la giornata! Molte grazie.
hellodear il

20

È possibile utilizzare una condizione per un ItemGroup per i riferimenti dll nel file di progetto.
Ciò farà sì che Visual Studio ricontrollerà la condizione e i riferimenti ogni volta che cambi la configurazione attiva.
Aggiungi una condizione per ogni configurazione.

Esempio:

 <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>

1
Questo è fantastico grazie! Questa dovrebbe essere sicuramente la soluzione accettata!
ManicBlowfish

Scherzi a parte, questa risposta è molto migliore e più semplice di quella accettata.
Yandros,

1
È normale avere voci duplicate nei riferimenti dopo aver fatto questo?
natenho,

7

Sto facendo riferimento alle DLL x86, che si trovano in eg \ component \ v3_NET4, nel mio progetto. DLL specifiche per x86 / x64 si trovano in sottocartelle denominate "x86" e "x64" rispettivamente.

Quindi sto usando uno script pre-build che copia le DLL appropriate (x86 / x64) nella cartella referenziata, basato su $ (PlatformName).

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

Per me va bene.


3

Una build .Net con dipendenze x86 / x64

Mentre tutte le altre risposte ti offrono una soluzione per creare build diverse in base alla piattaforma, ti offro un'opzione per avere solo la configurazione "AnyCPU" e creare una build che funzioni con le tue dll x86 e x64.

Risoluzione di x86 / x64-dlls corretti in fase di esecuzione

passi:

  1. Usa AnyCPU in csproj
  2. Decidi se fai riferimento solo alle dll x86 o x64 nei tuoi csprojs. Adatta le impostazioni di UnitTest alle impostazioni dell'architettura che hai scelto. È importante per il debug / esecuzione dei test all'interno di VisualStudio.
  3. In Riferimento-Proprietà impostare Copia versione locale e specifica su false
  4. Elimina gli avvisi di architettura aggiungendo questa riga al primo gruppo di proprietà in tutti i tuoi file csproj in cui fai riferimento a x86 / x64: <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  5. Aggiungi questo script postbuild al tuo progetto di avvio, usa e modifica i percorsi di questo script in modo che copi tutte le tue dll x86 / x64 nelle corrispondenti sottocartelle del tuo bin di compilazione \ 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

    -> Quando avvii l'applicazione adesso, ricevi un'eccezione che non è stato possibile trovare l'assemblaggio.

  6. Registrare l'evento AssemblyResolve proprio all'inizio del punto di ingresso dell'applicazione

    AppDomain.CurrentDomain.AssemblyResolve += TryResolveArchitectureDependency;

    con questo metodo:

    /// <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. Se disponi di unit test, crea una TestClass con un metodo che ha un AssemblyInitializeAttribute e lì registra anche il gestore TryResolveArchitectureDependency-Handler sopra riportato. (Questo non verrà eseguito a volte se si eseguono test singoli all'interno di Visual Studio, i riferimenti non verranno risolti dal cestino UnitTest. Pertanto, la decisione nel passaggio 2 è importante.)

Benefici:

  • Una installazione / build per entrambe le piattaforme

Svantaggi: - Nessun errore in fase di compilazione quando le dll x86 / x64 non corrispondono. - Dovresti comunque eseguire il test in entrambe le modalità!

Facoltativamente, creare un secondo eseguibile esclusivo per l'architettura x64 con Corflags.exe nello script postbuild

Altre varianti da provare: - Non avresti bisogno del gestore di eventi AssemblyResolve se assicurassi altrimenti che le dll vengano copiate nella tua cartella binaria all'avvio (Valuta l'architettura del processo -> sposta le dll corrispondenti da x64 / x86 nella cartella bin e viceversa). - Nell'Installer valuta l'architettura ed elimina i binari per un'architettura errata e sposta quelli giusti nella cartella bin.


2

Ho affrontato lo stesso problema e ho trascorso parecchio tempo a cercare una soluzione decente. La maggior parte delle persone offre la modifica manuale dei file della soluzione di Visual Studio, che è piuttosto noiosa, soggetta a errori e confusa durante l'esplorazione di questi file modificati nella GUI di Visual Studio in seguito. Quando ho già rinunciato, la soluzione è arrivata da sola. È molto simile a quello che Micke raccomanda nella sua risposta sopra.

In account manager ho creato due target di build separati per piattaforme x86 e x64, come al solito. Successivamente, ho aggiunto un riferimento all'assembly x86 al mio progetto. Su questo punto, ho creduto che il progetto fosse configurato solo per la compilazione x86 e non costruirà mai per la configurazione x64, a meno che non lo realizzerò manualmente come suggerito da Hugo sopra.

Dopo un po ', alla fine ho dimenticato la limitazione e ho accidentalmente avviato la build x64. Ovviamente, la build non è riuscita. Ma è stato importante il messaggio di errore che ho ricevuto. Messaggio di errore che indica che l'assembly chiamato esattamente come il mio assembly x86 di riferimento è mancante nella cartella intesa come destinazione build x64 per la mia soluzione.

Avendo notato questo, ho copiato manualmente l'assembly x64 corretto in questa directory. Gloria! La mia build x64 è miracolosamente riuscita con il corretto assemblaggio trovato e collegato implicitamente. È stata questione di minuti per modificare la mia soluzione per impostare una directory di destinazione build per l'assembly x64 in questa cartella. Dopo questi passaggi la soluzione viene compilata automaticamente per entrambi x86 e x64 senza alcuna modifica manuale dei file MSBuild.

Per riassumere:

  1. Crea target x86 e x64 in un singolo progetto
  2. Aggiungere tutti i riferimenti di progetto appropriati agli assembly x86
  3. Impostare una directory di destinazione build comune per tutti gli assembly x64
  4. Nel caso in cui si disponga di assembly x64 pronti, copiarli una sola volta nella directory di destinazione della build x64

Dopo aver completato questi passaggi, la soluzione verrà compilata correttamente per le configurazioni x86 e x64.

Questo ha funzionato per me nel progetto Visual Studio 2010 .NET 4.0 C #. Evidentemente, si tratta di una sorta di comportamento interno non documentato di Visual Studio, che potrebbe essere soggetto a modifiche nelle versioni 2012, 2013 e 2015. Se qualcuno proverà su altre versioni, condividi la tua esperienza.


-1

Ho finito per usare quella che considero una soluzione più semplice che è una specie di inversione di Micke. Il progetto è un'app di moduli C #, Visual Studio 2015, con target x86 e x64. Ho fatto riferimento a uno degli assembly .NET, ho usato quello a 32 bit. Nelle proprietà di riferimento, ho impostato "Copia locale" su false. Quindi inserisco manualmente l'assembly .Net appropriato (32 o 64 bit) in ogni directory di destinazione. L'attuale testimone di riferimento è irrilevante, supponendo che abbiano le stesse capacità, poiché sta semplicemente definendo l'interfaccia esterna. Potresti anche mettere una fase di copia post build se vuoi essere sofisticato. Nota che questo progetto aveva anche un riferimento COM, la stessa cosa funziona. Il riferimento definisce gli oggetti / le interfacce, quindi il testimone della DLL di riferimento è irrilevante. Se sono registrate DLL COM a 32 e 64 bit, l'app cercherà nella posizione appropriata nel registro e creerà l'oggetto COM corretto a 32 o 64 bit. Per me va bene!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.