Bersyarat menggunakan referensi 32/64 bit saat membuat di Visual Studio

124

Saya memiliki proyek yang dibangun dalam 32/64-bit dan memiliki ketergantungan 32/64-bit yang sesuai. Saya ingin dapat mengganti konfigurasi dan menggunakan referensi yang benar, tetapi saya tidak tahu bagaimana cara memberitahu Visual Studio untuk menggunakan ketergantungan yang sesuai dengan arsitektur.

Mungkin saya melakukan ini dengan cara yang salah, tetapi saya ingin dapat beralih antara x86 dan x64 di menu dropdown konfigurasi, dan meminta DLL yang direferensikan menjadi bitness yang tepat.

Jonathan Yee
sumber
Sangat tidak jelas, bahasa apa ini? Apakah proyek DLL dalam solusinya?
Hans Passant
Maaf, ini .NET, saya menulis di C #.
Jonathan Yee
4
Ok, saya menyelesaikannya dengan solusi bodoh: Membuat file csproj tambahan yang hanya mereferensikan x64 DLL (dan menghapus konfigurasi x86 dari csproj). Ini berfungsi, tetapi jika ada yang memiliki solusi yang lebih elegan yang tidak melibatkan csproj tambahan, saya akan senang melihatnya.
Jonathan Yee

Jawaban:

99

Inilah yang telah saya lakukan di proyek sebelumnya, yang akan membutuhkan edisi manual dari file .csproj. Anda juga memerlukan direktori terpisah untuk biner yang berbeda, idealnya saudara kandung satu sama lain, dan dengan nama yang sama dengan platform yang Anda targetkan.

Setelah menambahkan referensi platform tunggal ke proyek, buka .csproj di editor teks. Sebelum <ItemGroup>elemen pertama dalam <Project>elemen, tambahkan kode berikut, yang akan membantu menentukan platform mana yang Anda jalankan (dan bangun).

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

Kemudian, untuk referensi khusus platform Anda, Anda membuat perubahan seperti berikut ini:

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

Perhatikan penggunaan $(CurrentPlatform)properti, yang kami definisikan di atas. Sebagai gantinya, Anda dapat menggunakan kondisional untuk rakitan mana yang akan disertakan untuk platform mana. Anda juga mungkin perlu:

  • Ganti $(PROCESSOR_ARCHITEW6432)dan $(PROCESSOR_ARCHITECTURE)dengan$(Platform) untuk mempertimbangkan HANYA platform target proyek
  • Ubah logika penentuan platform agar sesuai dengan mesin saat ini, sehingga Anda tidak membuat / mereferensikan biner 64 bit untuk dieksekusi pada platform 32 bit.

Saya awalnya menulis ini untuk Wiki internal di tempat kerja, namun, saya telah memodifikasinya dan memposting proses lengkapnya ke blog saya , jika Anda tertarik dengan petunjuk langkah demi langkah yang terperinci.

Hugo
sumber
1
Bagus. Saya pergi dengan menggunakan kondisi pada ItemGroup sesuai saran di bawah ini tetapi menggunakan $ (PROCESSOR_ARCHITEW6432) dan $ (PROCESSOR_ARCHITECTURE) untuk kondisi seperti di sini. Catatan saya menemukan $ (PROCESSOR_ARCHITECTURE) mengembalikan x86 pada platform 32 dan 64 bit tetapi $ (PROCESSOR_ARCHITEW6432) mengembalikan AMD64 hanya pada 64bit. Sesuatu yang perlu diperhatikan jika Anda mencoba menguji x86 (karena AMD64 adalah turunan dari x86 saya asumsikan).
tjmoore
Terima kasih atas informasi itu @tjmoore. Di O / S mana Anda memperhatikan ini? Saya baru saja memeriksa milik saya lagi (Win7SP1) dan mengatakan AMD64 untuk $ (PROCESSOR_ARCHITECTURE), tetapi pasti ingin memiliki informasi selengkap dan selengkap mungkin.
Hugo
7
Lucu, pencarian saya membawa saya ke sini, dan saya hanya perlu ini karena saya juga menggunakan LeadTools ... +1
Ed S.
Solusi bekerja untuk konfigurasi default, tetapi tidak dari pengujian saya tidak jika Anda mengubah konfigurasi dari konfigurasi dari daftar dropdown Konfigurasi Solusi Visual Studio (2012 dalam kasus saya).
Sarah Weinberger
Alih-alih menggunakan $ (PROCESSOR_ARCHITEW6432) saya menggunakan $ (Platform) karena beberapa alasan $ (PROCESSOR_ARCHITEW6432) tidak berfungsi.
Dzyann
60

AFAIK, jika proyek Anda memerlukan referensi yang spesifik 32-bit atau 64-bit (yaitu rakitan COM-interop), dan Anda tidak tertarik untuk mengedit file .csproj secara manual, maka Anda harus membuat 32-bit dan Proyek 64-bit.

Saya harus mencatat bahwa solusi berikut belum teruji, tetapi harus berfungsi. Jika Anda ingin mengedit file .csproj secara manual, Anda harus dapat mencapai hasil yang diinginkan dengan satu proyek. File .csproj hanyalah skrip MSBuild, jadi untuk referensi lengkap, lihat di sini . Setelah Anda membuka file .csproj di editor, cari <Reference>elemennya. Anda harus dapat membagi elemen ini menjadi 3 kelompok item yang berbeda : referensi yang tidak khusus platform, referensi khusus x86, dan referensi khusus x64.

Berikut adalah contoh yang mengasumsikan project Anda dikonfigurasi dengan platform target bernama "x86" dan "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>

Sekarang, saat Anda menyetel konfigurasi build project / solusi untuk menargetkan platform x86 atau x64, konfigurasi tersebut harus menyertakan referensi yang sesuai di setiap kasus. Tentu saja, Anda harus bermain-main dengan <Reference>elemennya. Anda bahkan dapat mengatur proyek tiruan di mana Anda menambahkan referensi x86 dan x64, dan kemudian menyalin <Reference>elemen yang diperlukan dari berkas proyek tiruan tersebut ke berkas proyek "asli" Anda.


Sunting 1
Berikut ini tautan ke item proyek MSBuild umum, yang secara tidak sengaja saya tinggalkan dari pos asli: http://msdn.microsoft.com/en-us/library/bb629388.aspx

Justin Holzer
sumber
Jawaban Luar Biasa !! Menyelamatkan hariku! Terima kasih banyak.
Hellodear
20

Anda dapat menggunakan kondisi ke ItemGroup untuk referensi dll di file proyek.
Ini akan menyebabkan studio visual memeriksa kembali kondisi dan referensi setiap kali Anda mengubah konfigurasi aktif.
Tambahkan saja kondisi untuk setiap konfigurasi.

Contoh:

 <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
sumber
1
Ini bagus, terima kasih! Ini pasti harus menjadi solusi yang diterima!
ManicBlowfish
Serius, jawaban ini jauh lebih baik dan lebih sederhana daripada yang diterima.
Yandros
1
Apakah normal untuk memiliki entri duplikat dalam Referensi setelah melakukan ini?
natenho
7

Saya mereferensikan DLL x86, yang terletak di misalnya \ component \ v3_NET4, dalam proyek saya. DLL tertentu untuk x86 / x64 terletak di sub-folder bernama "x86" dan "x64" resp.

Kemudian saya menggunakan skrip pra-bangun yang menyalin DLL yang sesuai (x86 / x64) ke dalam folder yang direferensikan, berdasarkan $ (PlatformName).

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

Bekerja untuk saya.

Micke
sumber
3

Satu .Net dibangun dengan Dependensi x86 / x64

Sementara semua jawaban lain memberi Anda solusi untuk membuat Build yang berbeda sesuai dengan platformnya, saya memberi Anda opsi untuk hanya memiliki konfigurasi "AnyCPU" dan membuat build yang berfungsi dengan dll x86 dan x64 Anda.

Resolusi x86 / x64-dll yang benar saat runtime

Langkah:

  1. Gunakan AnyCPU di csproj
  2. Putuskan apakah Anda hanya mereferensikan x86 atau x64 dll di csprojs Anda. Sesuaikan pengaturan UnitTests dengan pengaturan arsitektur yang Anda pilih. Ini penting untuk men-debug / menjalankan tes di dalam VisualStudio.
  3. Pada Referensi-Properti atur Salin Versi Lokal & Spesifik ke false
  4. Singkirkan peringatan arsitektur dengan menambahkan baris ini ke PropertyGroup pertama di semua file csproj tempat Anda mereferensikan x86 / x64: <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  5. Tambahkan skrip postbuild ini ke proyek startup Anda, gunakan dan ubah jalur skrip ini sehingga skrip ini menyalin semua dll x86 / x64 Anda di subfolder yang sesuai dari 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

    -> Ketika Anda akan memulai aplikasi sekarang, Anda mendapatkan pengecualian bahwa assembly tidak dapat ditemukan.

  6. Daftarkan acara AssemblyResolve tepat di awal titik masuk aplikasi Anda

    AppDomain.CurrentDomain.AssemblyResolve += TryResolveArchitectureDependency;

    dengan metode ini:

    /// <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. Jika Anda memiliki unit test, buatlah TestClass dengan Method yang memiliki AssemblyInitializeAttribute dan juga daftarkan TryResolveArchitectureDependency-Handler di atas di sana. (Terkadang ini tidak akan dijalankan jika Anda menjalankan pengujian tunggal di dalam visual studio, referensi akan diselesaikan bukan dari bin UnitTest. Oleh karena itu, keputusan di langkah 2 penting.)

Manfaat:

  • Satu Instalasi / Bangun untuk kedua platform

Kekurangan: - Tidak ada kesalahan pada waktu kompilasi ketika x86 / x64 dll tidak cocok. - Anda masih harus menjalankan pengujian di kedua mode!

Secara opsional buat executable kedua yang eksklusif untuk arsitektur x64 dengan Corflags.exe dalam skrip postbuild

Varian lain untuk dicoba: - Anda tidak memerlukan event handler AssemblyResolve jika Anda memastikan sebaliknya bahwa dll disalin di folder biner Anda saat mulai (Evaluasi arsitektur Proses -> pindahkan dll yang sesuai dari x64 / x86 ke folder bin dan sebaliknya.) - Dalam Installer, evaluasi arsitektur dan hapus binari untuk arsitektur yang salah dan pindahkan yang benar ke folder bin.

Felix Keil
sumber
2

Saya menghadapi masalah yang sama dan menghabiskan cukup banyak waktu untuk mencari solusi yang layak. Kebanyakan orang menawarkan pengeditan manual dari file solusi Visual Studio, yang cukup membosankan, rawan kesalahan dan membingungkan saat menjelajahi file yang diedit ini di Visual Studio GUI sesudahnya. Ketika saya sudah menyerah, solusinya muncul dengan sendirinya. Ini sangat mirip dengan apa yang direkomendasikan Micke dalam jawabannya di atas.

Di pengelola akun, saya membuat dua target build terpisah untuk platform x86 dan x64, seperti biasa. Selanjutnya, saya menambahkan referensi ke perakitan x86 ke proyek saya. Pada titik ini, saya percaya bahwa proyek dikonfigurasi untuk x86 build saja dan tidak akan pernah membangun untuk konfigurasi x64, kecuali saya akan melakukan pengeditan manual seperti yang disarankan oleh Hugo di atas.

Setelah beberapa saat, saya akhirnya lupa batasannya dan secara tidak sengaja memulai pembuatan x64. Tentu saja, build tersebut gagal. Tapi yang penting adalah pesan kesalahan yang saya terima. Pesan kesalahan memberi tahu bahwa assembly bernama persis seperti assembly x86 yang direferensikan hilang dalam folder yang dimaksudkan sebagai target build x64 untuk solusi saya.

Setelah memperhatikan ini, saya telah menyalin perakitan x64 yang tepat secara manual ke dalam direktori ini. Kemuliaan! Build x64 saya secara ajaib berhasil dengan perakitan yang tepat ditemukan dan ditautkan secara implisit. Hanya dalam hitungan menit untuk mengubah solusi saya untuk menetapkan direktori target build untuk perakitan x64 ke folder ini. Setelah langkah-langkah ini, solusi dibangun secara otomatis untuk x86 dan x64 tanpa pengeditan manual file MSBuild.

Untuk menyimpulkan:

  1. Buat target x86 dan x64 dalam satu proyek
  2. Tambahkan semua referensi proyek yang tepat ke rakitan x86
  3. Tetapkan satu direktori target build umum untuk semua rakitan x64
  4. Jika Anda memiliki rakitan x64 siap, cukup salin sekali ke direktori target build x64 Anda

Setelah menyelesaikan langkah-langkah ini, solusi Anda akan dibuat dengan benar untuk konfigurasi x86 dan x64.

Ini bekerja untuk saya pada proyek Visual Studio 2010 .NET 4.0 C #. Jelas, ini adalah semacam perilaku internal Visual Studio yang tidak terdokumentasi, yang mungkin dapat berubah pada versi 2012, 2013 dan 2015. Jika seseorang akan mencoba versi lain, silakan bagikan pengalaman Anda.

Boris Zinchenko
sumber
-1

Saya akhirnya menggunakan apa yang saya anggap sebagai solusi yang lebih mudah yang semacam pembalikan dari Micke. Proyek ini adalah aplikasi formulir C #, Visual Studio 2015, dengan target x86 dan x64. Saya mereferensikan salah satu rakitan .NET, saya menggunakan yang 32 bit. Di properti referensi, saya menetapkan "Salin Lokal" ke false. Kemudian saya hanya secara manual meletakkan perakitan .Net yang sesuai (32 atau 64 bit) di setiap direktori target. Bitness referensi sebenarnya tidak relevan, dengan asumsi mereka memiliki kemampuan yang sama, karena ini hanya mendefinisikan antarmuka eksternal. Anda juga bisa meletakkan langkah copy post build jika Anda ingin menjadi mewah. Perhatikan proyek ini juga memiliki referensi COM, hal yang sama berfungsi. Referensi mendefinisikan objek / antarmuka sehingga bitness DLL referensi tidak relevan. Jika kedua 32 bit dan 64 bit COM DLL terdaftar, aplikasi akan mencari di tempat yang sesuai di registri dan membuat objek COM 32 atau 64 bit yang benar. Bekerja untuk saya!

Jeff H.
sumber