Warunkowo użyj odwołania 32/64 bitowego podczas kompilowania w programie Visual Studio

124

Mam projekt, który kompiluje się w wersji 32/64-bitowej i ma odpowiednie zależności 32/64-bitowe. Chcę mieć możliwość przełączania konfiguracji i używania odpowiedniego odwołania, ale nie wiem, jak powiedzieć programowi Visual Studio, aby używał zależności odpowiedniej dla architektury.

Może podchodzę do tego w niewłaściwy sposób, ale chcę mieć możliwość przełączania się między x86 i x64 w menu rozwijanym konfiguracji i mieć odpowiednią bitowość przywoływanej biblioteki DLL.

Jonathan Yee
źródło
Bardzo niejasne, jaki to język? Czy projekt DLL jest w rozwiązaniu?
Hans Passant
Przepraszamy, to jest .NET, piszę w C #.
Jonathan Yee
4
Ok, rozwiązałem to głupim rozwiązaniem: utworzyłem dodatkowy plik csproj, który odwołuje się tylko do biblioteki DLL x64 (i usunąłem konfigurację x86 z csproj). To działa, ale gdyby ktoś miał bardziej eleganckie rozwiązanie, które nie wymagało dodatkowego csproj, bardzo bym chciał to zobaczyć.
Jonathan Yee,

Odpowiedzi:

99

Oto, co zrobiłem w poprzednim projekcie, co będzie wymagało ręcznej edycji pliku (ów) .csproj. Potrzebujesz także oddzielnych katalogów dla różnych plików binarnych, najlepiej rodzeństwa innych i o tej samej nazwie co platforma, na którą kierujesz reklamy.

Po dodaniu referencji pojedynczej platformy do projektu otwórz plik .csproj w edytorze tekstu. Przed pierwszym <ItemGroup>elementem w <Project>elemencie dodaj następujący kod, który pomoże określić, na której platformie używasz (i budujesz).

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

Następnie dla odniesień specyficznych dla Twojej platformy wprowadź następujące zmiany:

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

Zwróć uwagę na wykorzystanie $(CurrentPlatform)właściwości, które zdefiniowaliśmy powyżej. Zamiast tego można użyć warunków warunkowych, dla których zestawy mają zostać uwzględnione dla danej platformy. Możesz również potrzebować:

  • Wymień $(PROCESSOR_ARCHITEW6432)i $(PROCESSOR_ARCHITECTURE)ze $(Platform)rozważyć TYLKO platformę docelową projektów
  • Zmień logikę określania platformy, aby była odpowiednia dla bieżącej maszyny, aby nie budować / odwoływać się do 64-bitowego pliku binarnego do wykonania na platformie 32-bitowej.

Napisałem to pierwotnie dla wewnętrznej Wiki w pracy, jednak zmodyfikowałem go i opublikowałem cały proces na moim blogu , jeśli jesteś zainteresowany szczegółowymi instrukcjami krok po kroku.

Hugo
źródło
1
Miły. Poszedłem z użyciem warunku na ItemGroup zgodnie z sugestią poniżej, ale używając $ (PROCESSOR_ARCHITEW6432) i $ (PROCESSOR_ARCHITECTURE) dla warunków jak tutaj. Uwaga: znalazłem $ (PROCESSOR_ARCHITECTURE) zwraca x86 na obu platformach 32 i 64-bitowych, ale $ (PROCESSOR_ARCHITEW6432) zwraca AMD64 tylko na 64-bitowym. Coś do zapamiętania, jeśli spróbujesz przetestować dla x86 (ponieważ AMD64 jest pochodną x86, jak zakładam).
tjmoore
Dzięki za te informacje @tjmoore. Na którym O / S to zauważyłeś? Właśnie ponownie sprawdziłem mój (Win7SP1) i powiedziałem AMD64 dla $ (PROCESSOR_ARCHITECTURE), ale zdecydowanie chciałbym mieć możliwie kompletne i dokładne informacje.
Hugo
7
Zabawne, moje wyszukiwanie prowadzi mnie tutaj i potrzebuję tego tylko dlatego, że używam również LeadTools ... +1
Ed S.
Rozwiązanie działa dla domyślnej konfiguracji, ale nie z moich testów, nie jeśli zmienisz konfigurację z konfiguracji z Visual Studio (w moim przypadku 2012) Lista rozwijana Konfiguracja rozwiązania.
Sarah Weinberger,
Zamiast używać $ (PROCESSOR_ARCHITEW6432) użyłem $ (Platform) z jakiegoś powodu $ (PROCESSOR_ARCHITEW6432) nie działał.
Dzyann
60

AFAIK, jeśli Twój projekt wymaga odniesień, które są specyficzne dla 32-bitowych lub 64-bitowych (np. Zespoły międzyoperacyjne COM) i nie jesteś zainteresowany ręczną edycją pliku .csproj, musisz utworzyć oddzielne 32-bitowe i Projekty 64-bitowe.

Powinienem zauważyć, że poniższe rozwiązanie nie zostało przetestowane, ale powinno działać. Jeśli chcesz ręcznie edytować plik .csproj, powinieneś być w stanie osiągnąć pożądany rezultat za pomocą jednego projektu. Plik .csproj to tylko skrypt programu MSBuild, więc pełne odniesienie można znaleźć tutaj . Po otwarciu pliku .csproj w edytorze zlokalizuj <Reference>elementy. Powinieneś móc podzielić te elementy na 3 odrębne grupy elementów : odwołania, które nie są specyficzne dla platformy, odwołania specyficzne dla x86 i odwołania specyficzne dla x64.

Oto przykład, który zakłada, że ​​Twój projekt jest skonfigurowany z platformami docelowymi o nazwach „x86” i „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>

Teraz, gdy ustawisz konfigurację kompilacji projektu / rozwiązania na platformę x86 lub x64, powinna ona zawierać odpowiednie odwołania w każdym przypadku. Oczywiście będziesz musiał bawić się <Reference>żywiołami. Możesz nawet skonfigurować fikcyjne projekty, w których dodajesz odwołania x86 i x64, a następnie po prostu kopiujesz niezbędne <Reference>elementy z tych fikcyjnych plików projektu do swojego „prawdziwego” pliku projektu.


Edytuj 1
Oto łącze do typowych elementów projektu MSBuild, które przypadkowo pominąłem w oryginalnym poście: http://msdn.microsoft.com/en-us/library/bb629388.aspx

Justin Holzer
źródło
Doskonała odpowiedź !! Uratowałem mój dzień! Wielkie dzięki.
hellodear,
20

Możesz użyć warunku dla ItemGroup dla odwołań dll w pliku projektu.
Spowoduje to, że program Visual Studio ponownie sprawdzi warunek 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
1
To świetnie, dziękuję! To zdecydowanie powinno być akceptowane rozwiązanie!
ManicBlowfish
Poważnie, ta odpowiedź jest znacznie lepsza i prostsza niż ta zaakceptowana.
Yandros
1
Czy po wykonaniu tej czynności zduplikowane wpisy w References są normalne?
natenho
7

Odwołuję się do bibliotek DLL x86, znajdujących się w np. \ Component \ v3_NET4, w moim projekcie. Specyficzne biblioteki DLL dla x86 / x64 znajdują się w podfolderach o nazwach „x86” i „x64”, odpowiednio.

Następnie używam skryptu przed kompilacją, który kopiuje odpowiednie biblioteki DLL (x86 / x64) do folderu, do którego się odwołuje, na podstawie $ (PlatformName).

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

Pracuje dla mnie.

Micke
źródło
3

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 tworzenia kompilacji, która będzie działać z twoimi bibliotekami dll x86 i x64.

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 po kompilacji 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ę, otrzymujesz 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 za pomocą metody, która 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 zapewnisz, że w przeciwnym razie pliki dll zostaną skopiowane do folderu binarnego na początku (Oceń architekturę procesu -> przenieś odpowiednie pliki 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
2

Zmierzyłem się z tym samym problemem i spędziłem trochę czasu na poszukiwaniu przyzwoitego rozwiązania. Większość ludzi oferuje ręczną edycję plików rozwiązań Visual Studio, co jest dość żmudne, podatne na błędy i mylące podczas późniejszego eksplorowania tych edytowanych plików w interfejsie GUI programu Visual Studio. Kiedy już się poddałem, rozwiązanie przyszło samo. Jest to bardzo podobne do tego, co Micke zaleca w swojej odpowiedzi powyżej.

W menadżerze kont, jak zwykle, stworzyłem dwa osobne cele kompilacji dla platform x86 i x64. Następnie dodałem odwołanie do zestawu x86 do mojego projektu. W tym miejscu wierzyłem, że projekt jest skonfigurowany tylko do kompilacji x86 i nigdy nie będzie budował dla konfiguracji x64, chyba że dokonam ręcznej edycji, jak sugerował Hugo powyżej.

Po jakimś czasie w końcu zapomniałem o ograniczeniu i przypadkowo uruchomiłem kompilację x64. Oczywiście budowa się nie powiodła. Ale ważny był komunikat o błędzie, który otrzymałem. Komunikat o błędzie informował, że w folderze przeznaczonym jako cel kompilacji x64 dla mojego rozwiązania brakuje zestawu nazwanego dokładnie tak, jak przywoływany zestaw x86.

Zauważywszy to, ręcznie skopiowałem odpowiedni zestaw x64 do tego katalogu. Chwała! Moja kompilacja x64 cudownie się powiodła, gdy znaleziono właściwy zestaw i niejawnie połączony. Zmodyfikowanie mojego rozwiązania, aby ustawić katalog docelowy kompilacji dla zestawu x64 na ten folder było kwestią minut. Po wykonaniu tych kroków rozwiązanie jest kompilowane automatycznie zarówno dla x86, jak i x64 bez ręcznej edycji plików MSBuild.

Podsumowując:

  1. Utwórz cele x86 i x64 w jednym projekcie
  2. Dodaj wszystkie odpowiednie odwołania do projektów do zestawów x86
  3. Ustaw jeden wspólny katalog docelowy kompilacji dla wszystkich zestawów x64
  4. Jeśli masz gotowe zestawy x64, po prostu skopiuj je raz do katalogu docelowego kompilacji x64

Po wykonaniu tych czynności rozwiązanie zostanie poprawnie skompilowane dla konfiguracji x86 i x64.

To zadziałało dla mnie w projekcie Visual Studio 2010 .NET 4.0 C #. Najwyraźniej jest to rodzaj nieudokumentowanego wewnętrznego zachowania programu Visual Studio, które może ulec zmianie w wersjach 2012, 2013 i 2015. Jeśli ktoś spróbuje innych wersji, podziel się swoimi doświadczeniami.

Boris Zinchenko
źródło
-1

Skończyło się na tym, co uważam za łatwiejsze rozwiązanie, czyli coś w rodzaju inwersji rozwiązania Mickiego. Projekt jest aplikacją C # formularzy, Visual Studio 2015, z elementami docelowymi x86 i x64. Odwołałem się do jednego z zestawów .NET, użyłem 32-bitowego. We właściwościach odniesienia ustawiłem „Copy Local” na false. Następnie po prostu ręcznie umieszczam odpowiedni (32- lub 64-bitowy) zestaw .Net w każdym katalogu docelowym. Rzeczywista bitowość odniesienia jest nieistotna, zakładając, że mają te same możliwości, ponieważ definiuje tylko interfejs zewnętrzny. Możesz również umieścić krok po kompilacji, jeśli chcesz mieć ochotę. Zauważ, że ten projekt również miał odniesienie COM, to samo działa. Odwołanie definiuje obiekty / interfejsy, więc bitowość referencyjnej biblioteki DLL nie ma znaczenia. Jeśli zarejestrowane są zarówno 32-bitowe, jak i 64-bitowe biblioteki DLL COM, aplikacja wyszuka w odpowiednim miejscu w rejestrze i utworzy poprawny 32- lub 64-bitowy obiekt COM. Pracuje dla mnie!

Jeff H.
źródło