Kierowanie na wersje 32-bitowe i 64-bitowe w programie Visual Studio w tym samym rozwiązaniu / projekcie

111

Mam mały dylemat, jak skonfigurować moje kompilacje Visual Studio pod kątem multitargetingu.

Tło: C # .NET v2.0 z p / wywoływaniem do zewnętrznych 32-bitowych bibliotek DLL, SQL Compact v3.5 SP1, z projektem instalacji. Obecnie platforma docelowa jest ustawiona na x86, więc można ją uruchomić w systemie Windows x64.

Firma zewnętrzna właśnie wydała 64-bitowe wersje swoich bibliotek DLL i chcę zbudować dedykowany program 64-bitowy.

Pojawia się kilka pytań, na które nie mam jeszcze odpowiedzi. Chcę mieć dokładnie taką samą bazę kodu. Muszę budować z odniesieniami do 32-bitowego zestawu bibliotek DLL lub 64-bitowych bibliotek DLL. (Zarówno innych firm, jak i SQL Server Compact)

Czy można to rozwiązać za pomocą 2 nowych zestawów konfiguracji (Debug64 i Release64)?

Czy muszę utworzyć 2 oddzielne projekty konfiguracyjne (std. Projekty Visual Studio, bez Wix ani żadnego innego narzędzia), czy można to rozwiązać w tym samym pliku .msi?

Wszelkie pomysły i / lub zalecenia będą mile widziane.

Magnus Johansson
źródło
@Magnus Johansson: możesz użyć dwóch konfiguracji, aby osiągnąć połowę swojego celu. MSI jest nieco trudniejsze.
user7116

Odpowiedzi:

83

Tak, możesz kierować reklamy zarówno na x86, jak i x64 z tą samą bazą kodu w tym samym projekcie. Ogólnie rzecz biorąc, wszystko będzie po prostu działać, jeśli utworzysz odpowiednie konfiguracje rozwiązań w VS.NET (chociaż P / Invoke do całkowicie niezarządzanych bibliotek DLL najprawdopodobniej będzie wymagało kodu warunkowego): elementy, które według mnie wymagają szczególnej uwagi, to:

  • Odwołania do zewnętrznych zestawów zarządzanych o tej samej nazwie, ale ich własnej określonej bitowości (dotyczy to również zestawów międzyoperacyjnych COM)
  • Pakiet MSI (który, jak już wspomniano, będzie musiał być przeznaczony dla x86 lub x64)
  • Wszelkie niestandardowe akcje oparte na klasach Instalatora .NET w pakiecie MSI

Problemu z odwołaniem do zestawu nie można całkowicie rozwiązać w VS.NET, ponieważ pozwoli to tylko raz dodać odwołanie o danej nazwie do projektu. Aby obejść ten problem, ręcznie edytuj plik projektu (w programie VS, kliknij prawym przyciskiem myszy plik projektu w Eksploratorze rozwiązań, wybierz opcję Zwolnij projekt, a następnie kliknij ponownie prawym przyciskiem myszy i wybierz opcję Edytuj). Po dodaniu odniesienia do, powiedzmy, wersji zestawu x86, plik projektu będzie zawierał coś takiego:

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

Umieść ten tag Reference wewnątrz tagu ItemGroup wskazującego konfigurację rozwiązania, do którego ma zastosowanie, np .:

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

Następnie skopiuj i wklej cały znacznik ItemGroup i edytuj go, aby zawierał szczegóły 64-bitowej biblioteki DLL, np .:

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

Po ponownym załadowaniu projektu w VS.NET, okno dialogowe Odniesienie do zestawu będzie nieco zdezorientowane przez te zmiany i możesz napotkać ostrzeżenia dotyczące zestawów z niewłaściwym procesorem docelowym, ale wszystkie kompilacje będą działać dobrze.

Następnym krokiem jest rozwiązanie problemu z MSI i niestety będzie to wymagało narzędzia innego niż VS.NET: wolę do tego celu zaawansowany instalator Caphyon, ponieważ wykonuje on podstawową sztuczkę (stwórz wspólny plik MSI, a także 32-bitowy i 64-bitowe pliki MSI, a także użyj programu uruchamiającego konfigurację .EXE, aby wyodrębnić odpowiednią wersję i wykonać wymagane poprawki w czasie wykonywania) bardzo dobrze.

Prawdopodobnie możesz osiągnąć te same wyniki za pomocą innych narzędzi lub zestawu narzędzi Windows Installer XML (WiX) , ale Advanced Installer sprawia, że ​​wszystko jest tak proste (i jest na to dość przystępne), że nigdy tak naprawdę nie szukałem alternatyw.

Jedną rzeczą, do której nadal możesz potrzebować WiX, nawet podczas korzystania z Instalatora zaawansowanego, są niestandardowe akcje .NET Installer Class. Chociaż określenie pewnych akcji, które powinny być uruchamiane tylko na niektórych platformach (odpowiednio przy użyciu warunków wykonania VersionNT64 i NOT VersionNT64) jest trywialne, wbudowane niestandardowe akcje AI będą wykonywane przy użyciu 32-bitowej struktury, nawet na maszynach 64-bitowych .

Może to zostać naprawione w przyszłej wersji, ale na razie (lub gdy używasz innego narzędzia do tworzenia plików MSI, które ma ten sam problem), możesz użyć obsługi zarządzanych akcji niestandardowych przez WiX 3.0, aby utworzyć biblioteki DLL akcji o odpowiedniej bitowości, zostanie wykonany przy użyciu odpowiedniego Framework.


Edycja: od wersji 8.1.2 Advanced Installer poprawnie obsługuje 64-bitowe akcje niestandardowe. Od czasu mojej oryginalnej odpowiedzi jego cena niestety nieco wzrosła, mimo że nadal jest bardzo dobra w porównaniu z InstallShield i podobnymi ...


Edycja: jeśli biblioteki DLL są zarejestrowane w GAC, możesz również użyć standardowych tagów referencyjnych w ten sposób (na przykład SQLite):

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

Warunek jest również zredukowany do wszystkich typów kompilacji, wydania lub debugowania i określa tylko architekturę procesora.

mdb
źródło
W programie Visual Studio 2008 stwierdziłem, że nie można zagnieżdżać elementów <ItemGroup>. To rozwiązanie działało dobrze, gdy utworzyłem nowe <ItemGroup> poniżej grupy, pozostałe elementy <Reference>. Musiałem też zmienić x86 na AnyCPU, co prawdopodobnie odnosi się do historii mojego konkretnego projektu.
Oliver Bock
Ten zaawansowany instalator wygląda niesamowicie.
Pat
To może być głupie pytanie, ale jak dostać się do pliku, aby edytować go ręcznie?
hrh
1
Aby edytować plik w VS, kliknij prawym przyciskiem myszy projekt w Eksploratorze rozwiązań i znajdź „Zwolnij projekt”. Po wyładowaniu projektu kliknij go ponownie prawym przyciskiem myszy i kliknij „Edytuj <nazwa pliku projektu>”. Po edycji pliku projektu zapisz go i ponownie kliknij plik projektu prawym przyciskiem myszy i załaduj go. Jeśli nie ma literówek ani błędów, zostanie załadowany ponownie. Jeśli nie, VS powie ci prawie, na czym polega problem z plikiem. Mam nadzieję, że to pomoże!
John Baughman
Świetna odpowiedź! Dziękuję Ci!
John Baughman
27

Załóżmy, że masz biblioteki DLL kompilowane dla obu platform i znajdują się one w następującej lokalizacji:

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

Wystarczy edytować plik .csproj z tego:

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

Do tego:

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

Następnie powinieneś być w stanie skompilować projekt dla obu platform, a program MSBuild będzie szukał odpowiedniego katalogu dla wybranej platformy.

Tim Booker
źródło
Byłoby wspaniale, gdyby zadziałało, ale tak nie jest. Przynajmniej nie dla mnie.
John Sheehan,
10
Czy to nie powinno być: <HintPath> C: \ cokolwiek \ $ (Platform) \ cokolwiek.dll </HintPath>
Andreas
Działało dobrze w programie Visual Studio 2008, ale nie skopiowałem automatycznie biblioteki DLL do katalogu docelowego kompilacji, jak to zwykle robi <Reference>. Rozwiązanie mdb działało lepiej dla mnie.
Oliver Bock
1

Nie jestem pewien całkowitej odpowiedzi na twoje pytanie - ale pomyślałem, że wskazałbym komentarz w sekcji Dodatkowe informacje na stronie pobierania SQL Compact 3.5 SP1, widząc, że patrzysz na x64 - mam nadzieję, że to pomoże.

Ze względu na zmiany w SQL Server Compact SP1 i dodatkowej obsłudze 64-bitowej wersji, centralnie instalowane i mieszane środowiska 32-bitowej wersji SQL Server Compact 3.5 i 64-bitowej wersji SQL Server Compact 3.5 SP1 mogą tworzyć coś, co wydaje się sporadyczne problemy. Aby zminimalizować potencjalne konflikty i umożliwić niezależne od platformy wdrażanie zarządzanych aplikacji klienckich, centralne zainstalowanie 64-bitowej wersji programu SQL Server Compact 3.5 SP1 przy użyciu pliku Instalatora Windows (MSI) wymaga również zainstalowania 32-bitowej wersji programu SQL Server Kompaktowy plik MSI 3.5 SP1. W przypadku aplikacji, które wymagają tylko natywnej wersji 64-bitowej, można zastosować prywatne wdrożenie 64-bitowej wersji programu SQL Server Compact 3.5 z dodatkiem SP1.

Odczytuję to jako „dołącz 32-bitowe pliki SQLCE, jak również pliki 64-bitowe” w przypadku dystrybucji dla klientów 64-bitowych.

Chyba sprawia, że ​​życie jest interesujące .. Muszę powiedzieć, że uwielbiam wiersz „co wydaje się być sporadycznymi problemami”… brzmi trochę jak „Wyobrażasz sobie różne rzeczy, ale na wszelki wypadek zrób to…”

gleng
źródło
1

Jedna kompilacja .Net z zależnościami x86 / x64

Podczas gdy wszystkie inne odpowiedzi dają rozwiązanie umożliwiające tworzenie różnych kompilacji w zależności od platformy, daję ci opcję posiadania tylko konfiguracji „AnyCPU” i stworzenia kompilacji, która będzie działać z twoimi bibliotekami dll x86 i x64.

Musisz w tym celu napisać jakiś kod hydrauliczny.

Rozdzielczość poprawnych bibliotek dll x86 / x64 w czasie wykonywania

Kroki:

  1. Użyj AnyCPU w csproj
  2. Zdecyduj, czy odwołujesz się tylko do bibliotek dll x86 czy x64 w csprojs. Dostosuj ustawienia UnitTests do wybranych ustawień architektury. Jest to ważne dla debugowania / uruchamiania testów wewnątrz VisualStudio.
  3. We właściwościach odniesienia ustaw opcję Kopiuj lokalną i określoną wersję na wartość false
  4. Pozbądź się ostrzeżeń dotyczących architektury, dodając tę ​​linię do pierwszej PropertyGroup we wszystkich plikach csproj, w których odwołujesz się do x86 / x64: <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  5. Dodaj ten skrypt postbuild do swojego projektu startowego, użyj i zmodyfikuj ścieżki tego skryptu, aby kopiował wszystkie pliki dll x86 / x64 w odpowiednich podfolderach bin budowania \ 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

    -> Gdy chcesz teraz uruchomić aplikację, pojawi się wyjątek, że nie można znaleźć zestawu.

  6. Zarejestruj zdarzenie AssemblyResolve bezpośrednio na początku punktu wejścia aplikacji

    AppDomain.CurrentDomain.AssemblyResolve += TryResolveArchitectureDependency;

    tą metodą:

    /// <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. Jeśli masz testy jednostkowe, utwórz TestClass z Method, który ma AssemblyInitializeAttribute, a także zarejestruj tam powyższą procedurę TryResolveArchitectureDependency-Handler. (Czasami nie zostanie to wykonane, jeśli uruchomisz pojedyncze testy w programie Visual Studio, odwołania zostaną rozwiązane nie z pojemnika UnitTest. Dlatego ważna jest decyzja podjęta w kroku 2).

Korzyści:

  • Jedna instalacja / kompilacja dla obu platform

Wady: - Brak błędów w czasie kompilacji, gdy biblioteki DLL x86 / x64 nie są zgodne. - Nadal powinieneś uruchomić test w obu trybach!

Opcjonalnie utwórz drugi plik wykonywalny, który jest przeznaczony wyłącznie dla architektury x64, za pomocą narzędzia Corflags.exe w skrypcie po kompilacji

Inne warianty do wypróbowania: - Nie potrzebujesz obsługi zdarzeń AssemblyResolve, jeśli zapewniasz, że odpowiednie biblioteki dll są kopiowane do folderu binarnego na początku (Oceń architekturę procesu -> przenieś odpowiednie biblioteki dll z x64 / x86 do folderu bin iz powrotem. ) - W Instalatorze oceń architekturę i usuń pliki binarne pod kątem niewłaściwej architektury i przenieś właściwe pliki do folderu bin.

Felix Keil
źródło
0

Odnośnie twojego ostatniego pytania. Najprawdopodobniej nie można tego rozwiązać w pojedynczym pliku MSI. Jeśli używasz folderów rejestru / systemowych lub czegokolwiek z tym związanego, sam MSI musi być tego świadomy i musisz przygotować 64-bitowy plik MSI, aby poprawnie zainstalować na komputerze 32-bitowym.

Istnieje możliwość, że możesz sprawić, że Twój produkt będzie zainstalowany jako aplikacja 32 IT i nadal będzie działał jako 64-bitowy, ale myślę, że może to być trudne do osiągnięcia.

Biorąc to pod uwagę, myślę, że powinieneś być w stanie zachować jedną bazę kodu dla wszystkiego. W moim obecnym miejscu pracy udało się to zrobić. (ale zajęło trochę żonglowania, aby wszystko grało razem)

Mam nadzieję że to pomoże. Oto link do informacji związanych z problemami 32/64 bit: http://blog.typemock.com/2008/07/registry-on-windows-64-bit-double-your.html

Lior Friedman
źródło
0

Jeśli używasz akcji niestandardowych napisanych w .NET jako części instalatora MSI, masz inny problem.

`` Podkładka '', która uruchamia te niestandardowe akcje, jest zawsze 32-bitowa, wtedy Twoja niestandardowa akcja będzie również działać 32-bitowa, niezależnie od określonego celu.

Więcej informacji i trochę ruchów ninja do obejścia (w zasadzie zmień MSI, aby używać 64-bitowej wersji tej podkładki)

Tworzenie MSI w Visual Studio 2005/2008 do pracy na SharePoint 64

64-bitowe zarządzane akcje niestandardowe w programie Visual Studio

Ryan
źródło
0

Możesz wygenerować dwa rozwiązania w inny sposób, a następnie połączyć je! Zrobiłem to dla VS 2010. i działa. Miałem 2 różne rozwiązania wygenerowane przez CMake i połączyłem je

voidMainReturn
źródło
0

Możesz użyć warunku dla ItemGroup dla odwołań dll w pliku projektu.
Spowoduje to, że program Visual Studio ponownie sprawdzi stan i odwołania po każdej zmianie aktywnej konfiguracji.
Po prostu dodaj warunek dla każdej konfiguracji.

Przykład:

 <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
źródło