Menargetkan 32bit dan 64bit dengan Visual Studio dalam solusi / proyek yang sama

111

Saya memiliki sedikit dilema tentang cara menyiapkan studio visual saya untuk multi-penargetan.

Latar belakang: c # .NET v2.0 dengan p / pemanggilan ke pihak ketiga DLL 32 bit, SQL compact v3.5 SP1, dengan proyek Setup. Saat ini, target platform disetel ke x86 sehingga dapat dijalankan di Windows x64.

Perusahaan pihak ke-3 baru saja merilis versi 64 bit DLL mereka dan saya ingin membangun program 64bit khusus.

Hal ini menimbulkan beberapa pertanyaan yang belum saya jawab. Saya ingin memiliki basis kode yang sama persis. Saya harus membangun dengan referensi ke salah satu set 32bit DLL atau 64bit DLL. (Pihak ketiga dan SQL Server Compact)

Bisakah ini diselesaikan dengan 2 set konfigurasi baru (Debug64 dan Release64)?

Haruskah saya membuat 2 proyek penyiapan terpisah (proyek studio visual standar, tanpa Wix atau utilitas lain), atau dapatkah ini diselesaikan dalam .msi yang sama?

Setiap ide dan / atau rekomendasi akan diterima.

Magnus Johansson
sumber
@ Magnus Johansson: Anda dapat menggunakan dua konfigurasi untuk mencapai setengah tujuan Anda. MSI sedikit lebih sulit.
pengguna7116

Jawaban:

83

Ya, Anda dapat menargetkan x86 dan x64 dengan basis kode yang sama dalam proyek yang sama. Secara umum, semuanya akan Berfungsi jika Anda membuat konfigurasi solusi yang tepat di VS.NET (meskipun P / Invoke ke DLL yang sepenuhnya tidak dikelola kemungkinan besar akan memerlukan beberapa kode bersyarat): item yang menurut saya memerlukan perhatian khusus adalah:

  • Referensi ke rakitan yang dikelola di luar dengan nama yang sama tetapi bitness spesifiknya sendiri (ini juga berlaku untuk rakitan interop COM)
  • Paket MSI (yang, seperti yang telah disebutkan, perlu menargetkan x86 atau x64)
  • Tindakan berbasis Kelas Penginstal .NET kustom apa pun dalam paket MSI Anda

Masalah referensi perakitan tidak dapat diselesaikan sepenuhnya dalam VS.NET, karena ini hanya akan memungkinkan Anda untuk menambahkan referensi dengan nama tertentu ke proyek satu kali. Untuk mengatasi ini, edit file proyek Anda secara manual (di VS, klik kanan file proyek Anda di Solution Explorer, pilih Unload Project, lalu klik kanan lagi dan pilih Edit). Setelah menambahkan referensi ke, katakanlah, versi x86 dari sebuah rakitan, file proyek Anda akan berisi sesuatu seperti:

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

Bungkus tag Referensi itu di dalam tag ItemGroup yang menunjukkan konfigurasi solusi yang diterapkan padanya, misalnya:

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

Kemudian, salin dan tempel seluruh tag ItemGroup, dan edit untuk memuat detail DLL 64-bit Anda, misalnya:

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

Setelah memuat ulang proyek Anda di VS.NET, dialog Referensi Perakitan akan sedikit bingung dengan perubahan ini, dan Anda mungkin menemukan beberapa peringatan tentang rakitan dengan prosesor target yang salah, tetapi semua build Anda akan berfungsi dengan baik.

Memecahkan masalah MSI adalah selanjutnya, dan sayangnya ini akan membutuhkan alat non-VS.NET: Saya lebih suka Caphyon's Advanced Installer untuk tujuan itu, karena ini melakukan trik dasar yang terlibat (buat MSI umum, serta 32-bit dan MSI khusus 64-bit, serta menggunakan peluncur penyiapan .EXE untuk mengekstrak versi yang tepat dan melakukan perbaikan yang diperlukan saat runtime) dengan sangat, sangat baik.

Anda mungkin dapat mencapai hasil yang sama dengan menggunakan alat lain atau toolset Windows Installer XML (WiX) , tetapi Advanced Installer membuat segalanya menjadi sangat mudah (dan cukup terjangkau) sehingga saya tidak pernah benar-benar melihat alternatif.

Satu hal yang mungkin masih Anda perlukan untuk WiX, bahkan saat menggunakan Penginstal Lanjutan, adalah untuk tindakan kustom Kelas Penginstal .NET. Meskipun sepele untuk menentukan tindakan tertentu yang hanya boleh dijalankan pada platform tertentu (masing-masing menggunakan kondisi eksekusi VersionNT64 dan BUKAN VersionNT64), tindakan kustom AI bawaan akan dijalankan menggunakan Framework 32-bit, bahkan pada mesin 64-bit. .

Ini mungkin diperbaiki di rilis mendatang, tetapi untuk saat ini (atau saat menggunakan alat yang berbeda untuk membuat MSI Anda yang memiliki masalah yang sama), Anda dapat menggunakan dukungan tindakan kustom terkelola WiX 3.0 untuk membuat tindakan DLL dengan bitness yang tepat yang akan dieksekusi menggunakan Kerangka yang sesuai.


Sunting: mulai versi 8.1.2, Penginstal Lanjutan dengan benar mendukung tindakan kustom 64-bit. Sejak jawaban asli saya, harganya naik cukup sedikit, sayangnya, meskipun nilainya masih sangat bagus jika dibandingkan dengan InstallShield dan sejenisnya ...


Edit: Jika DLL Anda terdaftar di GAC, Anda juga dapat menggunakan tag referensi standar dengan cara ini (SQLite sebagai contoh):

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

Kondisi tersebut juga dikurangi menjadi semua jenis build, rilis atau debug, dan hanya menentukan arsitektur prosesor.

mdb
sumber
Dalam Visual Studio 2008, saya menemukan bahwa <ItemGroup> tidak dapat disarangkan. Solusi ini bekerja dengan baik setelah saya membuat <ItemGroup> baru di bawah grup sisa <Reference>. Saya juga harus mengubah x86 ke AnyCPU, yang mungkin terkait dengan riwayat proyek khusus saya.
Oliver Bock
Penginstal Tingkat Lanjut itu terlihat sangat luar biasa.
Tepuk
Ini mungkin pertanyaan bodoh, tetapi bagaimana Anda mendapatkan file untuk mengeditnya secara manual?
hrh
1
Untuk mengedit file dalam VS, klik kanan pada proyek di Solution Explorer dan temukan "Unload Project". Setelah proyek dibongkar, klik kanan padanya lagi dan klik "Edit <nama file proyek>". Setelah Anda mengedit file proyek, simpan dan klik kanan pada file proyek lagi dan muat. Jika tidak ada kesalahan ketik atau kesalahan, itu akan memuat lagi. Jika tidak, VS akan memberi tahu Anda apa masalahnya dengan file tersebut. Semoga membantu!
John Baughman
Jawaban yang bagus! Terima kasih!
John Baughman
27

Katakanlah Anda memiliki DLL yang dibangun untuk kedua platform, dan mereka berada di lokasi berikut:

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

Anda hanya perlu mengedit file .csproj Anda dari ini:

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

Untuk ini:

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

Anda kemudian harus dapat membangun proyek Anda yang menargetkan kedua platform, dan MSBuild akan mencari di direktori yang benar untuk platform yang dipilih.

Tim Booker
sumber
Ini akan menjadi brilian jika berhasil, tetapi tidak. Setidaknya tidak untukku.
John Sheehan
10
Bukankah itu seharusnya: <HintPath> C: \ anything \ $ (Platform) \ anything.dll </HintPath>
Andreas
Bekerja dengan baik pada Visual Studio 2008 untuk saya, tetapi tidak secara otomatis menyalin DLL ke direktori membangun target, seperti <Referensi> normal tidak. solusi mdb bekerja lebih baik untuk saya.
Oliver Bock
1

Tidak yakin dengan jawaban total untuk pertanyaan Anda - tetapi saya pikir saya akan menunjukkan komentar di bagian Informasi Tambahan dari halaman unduhan SQL Compact 3.5 SP1 melihat Anda melihat x64 - semoga ini membantu.

Karena perubahan dalam SQL Server Compact SP1 dan dukungan versi 64-bit tambahan, lingkungan mode campuran dan terpusat yang diinstal secara terpusat dari versi 32-bit SQL Server Compact 3.5 dan versi 64-bit dari SQL Server Compact 3.5 SP1 dapat membuat apa yang tampak terputus-putus masalah. Untuk meminimalkan potensi konflik, dan untuk mengaktifkan penyebaran netral platform aplikasi klien terkelola, menginstal terpusat versi 64-bit dari SQL Server Compact 3.5 SP1 menggunakan berkas Penginstal Windows (MSI) juga memerlukan penginstalan versi 32-bit dari SQL Server File MSI kompak 3.5 SP1. Untuk aplikasi yang hanya memerlukan 64-bit asli, penyebaran pribadi dari versi 64-bit dari SQL Server Compact 3.5 SP1 dapat digunakan.

Saya membaca ini sebagai "sertakan file SQLCE 32bit serta file 64bit" jika mendistribusikan untuk klien 64bit.

Membuat hidup menarik kurasa .. harus mengatakan bahwa aku menyukai baris "apa yang tampaknya menjadi masalah yang terputus-putus" ... terdengar agak seperti "kamu membayangkan sesuatu, tapi untuk berjaga-jaga, lakukan ini ..."

gleng
sumber
1

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.

Anda harus menulis beberapa kode pipa untuk ini.

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 sp ini yang 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 tes unit, buatlah TestClass dengan Metode 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 bahwa dll yang benar disalin ke folder biner Anda di awal (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
0

Mengenai pertanyaan terakhir Anda. Kemungkinan besar Anda tidak dapat menyelesaikan ini di dalam satu MSI. Jika Anda menggunakan folder registri / sistem atau apa pun yang terkait, MSI sendiri harus mengetahui hal ini dan Anda harus menyiapkan MSI 64bit untuk menginstal dengan benar pada mesin 32 bit.

Ada kemungkinan bahwa Anda dapat membuat produk Anda terinstal sebagai aplikasi 32 it dan masih dapat menjalankannya sebagai 64 bit, tapi saya pikir itu mungkin agak sulit untuk dicapai.

yang sedang berkata saya pikir Anda harus dapat menyimpan basis kode tunggal untuk semuanya. Di tempat kerja saya saat ini, kami telah berhasil melakukannya. (tapi butuh beberapa juggling untuk membuat semuanya bermain bersama)

Semoga ini membantu. Berikut ini tautan ke beberapa info yang terkait dengan masalah 32/64 bit: http://blog.typemock.com/2008/07/registry-on-windows-64-bit-double-your.html

Lior Friedman
sumber
0

Jika Anda menggunakan Tindakan Kustom yang ditulis dalam .NET sebagai bagian dari pemasang MSI Anda, maka Anda memiliki masalah lain.

'Shim' yang menjalankan tindakan kustom ini selalu 32bit, maka tindakan kustom Anda juga akan berjalan 32bit, terlepas dari target yang Anda tentukan.

Info lebih lanjut & beberapa gerakan ninja untuk berkeliling (pada dasarnya ubah MSI untuk menggunakan versi 64 bit shim ini)

Membangun MSI di Visual Studio 2005/2008 untuk bekerja di SharePoint 64

Tindakan Kustom Terkelola 64-bit dengan Visual Studio

Ryan
sumber
0

Anda dapat membuat dua solusi secara berbeda dan menggabungkannya setelahnya! Saya melakukan ini untuk VS 2010. dan berhasil. Saya memiliki 2 solusi berbeda yang dihasilkan oleh CMake dan saya menggabungkannya

voidMainReturn
sumber
0

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