Targeting sia a 32 bit che a 64 bit con Visual Studio nella stessa soluzione / progetto


111

Ho un piccolo dilemma su come impostare le build del mio studio visivo per il multi-targeting.

Sfondo: c # .NET v2.0 con p / invocazione in DLL a 32 bit di terze parti, SQL compact v3.5 SP1, con un progetto di installazione. Al momento, l'obiettivo della piattaforma è impostato su x86, quindi può essere eseguito su Windows x64.

L'azienda di terze parti ha appena rilasciato versioni a 64 bit delle loro DLL e desidero creare un programma a 64 bit dedicato.

Ciò solleva alcune domande a cui non ho ancora le risposte. Voglio avere la stessa identica base di codice. Devo compilare con riferimenti al set di DLL a 32 bit o alle DLL a 64 bit. (Sia di terze parti che SQL Server Compact)

Può essere risolto con 2 nuovi set di configurazioni (Debug64 e Release64)?

Devo creare 2 progetti di installazione separati (progetti di studio visivo standard, nessuna Wix o qualsiasi altra utilità), o questo può essere risolto all'interno dello stesso .msi?

Eventuali idee e / o raccomandazioni sarebbero ben accette.


@Magnus Johansson: puoi utilizzare due configurazioni per raggiungere metà del tuo obiettivo. L'MSI è un po 'più difficile.
user7116

Risposte:


83

Sì, puoi scegliere come target sia x86 che x64 con la stessa base di codice nello stesso progetto. In generale, le cose funzioneranno semplicemente se crei le giuste configurazioni della soluzione in VS.NET (anche se P / Invoke a DLL completamente non gestite molto probabilmente richiederà un codice condizionale): gli elementi che ho riscontrato richiedono particolare attenzione sono:

  • Riferimenti ad assembly gestiti esterni con lo stesso nome ma con il proprio bitness specifico (questo vale anche per gli assembly di interoperabilità COM)
  • Il pacchetto MSI (che, come è già stato notato, dovrà scegliere come target x86 o x64)
  • Qualsiasi azione personalizzata basata su classi .NET Installer nel pacchetto MSI

Il problema del riferimento all'assembly non può essere risolto interamente all'interno di VS.NET, poiché consentirà di aggiungere un riferimento con un determinato nome a un progetto solo una volta. Per ovviare a questo problema, modifica manualmente il file di progetto (in VS, fai clic con il pulsante destro del mouse sul file di progetto in Esplora soluzioni, seleziona Scarica progetto, quindi fai nuovamente clic con il pulsante destro del mouse e seleziona Modifica). Dopo aver aggiunto un riferimento, ad esempio, alla versione x86 di un assembly, il file di progetto conterrà qualcosa come:

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

Avvolgi il tag di riferimento all'interno di un tag ItemGroup che indica la configurazione della soluzione a cui si applica, ad esempio:

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

Quindi, copia e incolla l'intero tag ItemGroup e modificalo per contenere i dettagli della tua DLL a 64 bit, ad esempio:

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

Dopo aver ricaricato il progetto in VS.NET, la finestra di dialogo di riferimento dell'assembly sarà un po 'confusa da queste modifiche e potresti incontrare alcuni avvisi sugli assembly con il processore di destinazione sbagliato, ma tutte le tue build funzioneranno perfettamente.

Risolvere il problema MSI è accanto, e purtroppo questo sarà un apposito strumento non-VS.NET: preferisco di Caphyon Installer avanzata a tal fine, come si tira fuori il trucco di base coinvolti (creare un MSI comune, così come a 32 bit e MSI specifici a 64 bit e utilizzare un programma di avvio di installazione .EXE per estrarre la versione corretta ed eseguire le correzioni richieste in fase di esecuzione) molto, molto bene.

Probabilmente puoi ottenere gli stessi risultati utilizzando altri strumenti o il set di strumenti XML di Windows Installer (WiX) , ma Advanced Installer rende le cose così semplici (ed è abbastanza conveniente) che non ho mai veramente guardato alle alternative.

Una cosa per cui potresti comunque richiedere WiX, anche quando usi Advanced Installer, è per le tue azioni personalizzate di .NET Installer Class. Sebbene sia banale specificare determinate azioni che dovrebbero essere eseguite solo su determinate piattaforme (utilizzando rispettivamente le condizioni di esecuzione VersionNT64 e NOT VersionNT64), le azioni personalizzate AI integrate verranno eseguite utilizzando il Framework a 32 bit, anche su macchine a 64 bit .

Questo potrebbe essere risolto in una versione futura, ma per ora (o quando si utilizza uno strumento diverso per creare i propri MSI con lo stesso problema), è possibile utilizzare il supporto di azioni personalizzate gestite di WiX 3.0 per creare DLL di azioni con il bit corretto che verrà eseguito utilizzando il Framework corrispondente.


Modifica: a partire dalla versione 8.1.2, Advanced Installer supporta correttamente le azioni personalizzate a 64 bit. Dalla mia risposta originale, il suo prezzo è aumentato un po ', sfortunatamente, anche se è ancora estremamente conveniente rispetto a InstallShield e simili ...


Modifica: se le tue DLL sono registrate nella GAC, puoi anche utilizzare i tag di riferimento standard in questo modo (SQLite come esempio):

<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 condizione è ridotta anche a tutti i tipi di build, release o debug e specifica solo l'architettura del processore.


In Visual Studio 2008, ho scoperto che <ItemGroup> non poteva essere annidato. Questa soluzione ha funzionato bene una volta che ho creato i nuovi <ItemGroup> sotto il gruppo nel resto dei <Reference>. Ho anche dovuto cambiare x86 in AnyCPU, che probabilmente si riferisce alla storia del mio particolare progetto.
Oliver Bock

Quel programma di installazione avanzato sembra davvero fantastico.
Pat

Questa potrebbe essere una domanda stupida, ma come si arriva al file per modificarlo manualmente?
hrh

1
Per modificare il file in VS, fare clic con il pulsante destro del mouse sul progetto in Esplora soluzioni e trovare "Unload Project". Una volta scaricato il progetto, fare nuovamente clic con il pulsante destro del mouse e fare clic su "Modifica <nome file progetto>". Dopo aver modificato il file di progetto, salvarlo e fare nuovamente clic con il pulsante destro del mouse sul file di progetto e caricarlo. Se non ci sono refusi o errori, verrà caricato di nuovo. In caso contrario, VS ti dirà praticamente qual è il problema con il file. Spero che aiuti!
John Baughman

Bella risposta! Grazie!
John Baughman

27

Supponiamo che tu abbia le DLL compilate per entrambe le piattaforme e si trovano nella seguente posizione:

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

Devi semplicemente modificare il tuo file .csproj da questo:

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

A questa:

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

Dovresti quindi essere in grado di creare il tuo progetto mirando a entrambe le piattaforme e MSBuild cercherà nella directory corretta per la piattaforma scelta.


Sarebbe fantastico se funzionasse, ma non è così. Almeno non per me.
John Sheehan,

10
Non dovrebbe essere: <HintPath> C: \ qualunque \ $ (Piattaforma) \ qualunque.dll </HintPath>
Andreas

Ha funzionato bene su Visual Studio 2008 per me, ma non ha copiato automaticamente la DLL nella directory di destinazione della build, come fa un normale <Reference>. La soluzione di mdb ha funzionato meglio per me.
Oliver Bock

1

Non sono sicuro della risposta totale alla tua domanda - ma ho pensato di segnalare un commento nella sezione Informazioni aggiuntive della pagina di download di SQL Compact 3.5 SP1 visto che stai guardando x64 - spero che aiuti.

A causa delle modifiche in SQL Server Compact SP1 e del supporto aggiuntivo della versione a 64 bit, gli ambienti installati centralmente e in modalità mista della versione a 32 bit di SQL Server Compact 3.5 e della versione a 64 bit di SQL Server Compact 3.5 SP1 possono creare ciò che sembra essere intermittente i problemi. Per ridurre al minimo il potenziale di conflitti e per abilitare la distribuzione indipendente dalla piattaforma delle applicazioni client gestite, l'installazione centralizzata della versione a 64 bit di SQL Server Compact 3.5 SP1 utilizzando il file Windows Installer (MSI) richiede anche l'installazione della versione a 32 bit di SQL Server File MSI Compact 3.5 SP1. Per le applicazioni che richiedono solo 64 bit nativi, è possibile utilizzare la distribuzione privata della versione a 64 bit di SQL Server Compact 3.5 SP1.

Ho letto questo come "include i file SQLCE a 32 bit così come i file a 64 bit" se si distribuisce per client a 64 bit.

Rende la vita interessante, immagino .. devo dire che adoro la frase "quelli che sembrano problemi intermittenti" ... suona un po 'come "stai immaginando cose, ma per ogni evenienza, fallo ..."


1

Una build .Net con dipendenze x86 / x64

Mentre tutte le altre risposte ti danno una soluzione per creare build diverse a seconda della piattaforma, ti do un'opzione per avere solo la configurazione "AnyCPU" e creare una build che funzioni con le tue dll x86 e x64.

Devi scrivere del codice idraulico per questo.

Risoluzione delle dll x86 / x64 corrette in fase di esecuzione

passi:

  1. Usa AnyCPU in csproj
  2. Decidi se fai riferimento solo alle dll x86 o x64 nel tuo csprojs. Adatta le impostazioni di UnitTests alle impostazioni dell'architettura che hai scelto. È importante per il debug / l'esecuzione dei test all'interno di VisualStudio.
  3. In Proprietà di riferimento impostare Copia versione locale e specifica su false
  4. Sbarazzati degli avvertimenti sull'architettura aggiungendo questa riga al primo PropertyGroup in tutti i tuoi file csproj dove 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 sp che copia tutte le tue dll x86 / x64 nelle corrispondenti sottocartelle del tuo 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

    -> Quando avvii l'applicazione ora, ottieni un'eccezione che non è stato possibile trovare l'assembly.

  6. Registra l'evento AssemblyResolve 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 si dispone di unit test, creare una TestClass con un metodo che ha un AssemblyInitializeAttribute e registrare anche il gestore TryResolveArchitectureDependency sopra. (Questo a volte non verrà eseguito se esegui test singoli all'interno di visual studio, i riferimenti verranno risolti non dal contenitore UnitTest. Pertanto la decisione nel passaggio 2 è importante.)

Benefici:

  • Un'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 è necessario il gestore di eventi AssemblyResolve se si assicura che le DLL corrette siano copiate nella cartella binaria all'avvio (Valuta l'architettura del processo -> sposta le DLL corrispondenti da x64 / x86 alla cartella bin e viceversa. ) - In Installer valuta l'architettura ed elimina i binari per l'architettura sbagliata e sposta quelli giusti nella cartella bin.


0

Per quanto riguarda la tua ultima domanda. Molto probabilmente non puoi risolverlo all'interno di un singolo MSI. Se si utilizzano cartelle di registro / di sistema o qualsiasi altra cosa correlata, lo stesso MSI deve esserne consapevole e è necessario preparare un MSI a 64 bit per installarlo correttamente su una macchina a 32 bit.

C'è la possibilità che tu possa far installare il tuo prodotto come un'applicazione 32 IT ed essere comunque in grado di farlo funzionare come 64 bit, ma penso che possa essere un po 'difficile da ottenere.

detto questo, penso che dovresti essere in grado di mantenere un'unica base di codice per tutto. Nel mio attuale posto di lavoro siamo riusciti a farlo. (ma ci è voluto un po 'di giocoleria per far suonare tutto insieme)

Spero che questo ti aiuti. Ecco un collegamento ad alcune informazioni relative a problemi a 32/64 bit: http://blog.typemock.com/2008/07/registry-on-windows-64-bit-double-your.html


0

Se utilizzi azioni personalizzate scritte in .NET come parte del programma di installazione MSI, hai un altro problema.

Lo "shim" che esegue queste azioni personalizzate è sempre a 32 bit, quindi anche la tua azione personalizzata verrà eseguita a 32 bit, indipendentemente dal target specificato.

Maggiori informazioni e alcune mosse ninja per spostarsi (sostanzialmente cambia l'MSI per utilizzare la versione a 64 bit di questo shim)

Creazione di un file MSI in Visual Studio 2005/2008 per lavorare su SharePoint 64

Azioni personalizzate gestite a 64 bit con Visual Studio


0

Puoi generare due soluzioni in modo diverso e unirle in seguito! L'ho fatto per VS 2010. e funziona. Avevo 2 diverse soluzioni generate da CMake e le ho unite


0

È possibile utilizzare una condizione per un ItemGroup per i riferimenti dll nel file di progetto.
Ciò farà sì che Visual Studio ricontrolli la condizione e i riferimenti ogni volta che si modifica la configurazione attiva.
Basta aggiungere 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>
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.