Kesadaran DPI - Tidak Sadar dalam satu Rilis, Sistem Sadar di Yang Lain [duplikat]
Jadi kami memiliki masalah yang sangat aneh ini. Aplikasi kita adalah aplikasi C # / WinForms. Dalam rilis 6.0 kami, aplikasi kami tidak menyadari DPI. Dalam rilis 6.1 kami, DPI tiba-tiba menjadi sadar.
Dalam rilis 6.0, jika Anda menjalankannya dalam DPI tinggi, itu menggunakan skala bitmap Windows, yang bagus, karena itu tidak mempengaruhi tata letak layar. Dalam rilis 6.1, karena untuk beberapa alasan menjadi sadar DPI, antarmuka pengguna menjadi rusak.
Kami tidak dalam posisi untuk memperbaikinya sekarang. Kami memiliki ratusan layar, jadi membuat semuanya berfungsi dengan baik dalam mode sadar DPI akan membutuhkan banyak waktu.
Kami telah mengonfirmasi ini menggunakan SysInternals Process Explorer. Dalam rilis 6.0 kami, ini menunjukkan Unaware , tetapi dalam rilis 6.1 kami, awalnya menampilkan Unaware , tetapi kemudian berubah menjadi System Aware .
Yang terakhir terjadi ketika kode masuk dari EXE ke dalam perakitan DLL kami yang berisi semua kode antarmuka pengguna kami (EXE kami pada dasarnya adalah cangkang yang sangat tipis; yang sebenarnya dilakukannya hanyalah memanggil kelas Pengontrol pada rakitan lapisan presentasi kami.)
Kami telah mengkonfirmasi hal berikut:
- Kedua versi dibangun dalam Mode Rilis menggunakan VSS 2017.
- Kedua versi menargetkan sama .NET Framework (4.5)
- Kedua versi menggunakan versi DevExpress yang sama.
- Kedua versi memiliki manifest aplikasi yang sama, yang tidak tidak memiliki pengaturan kesadaran DPI diaktifkan.
- Tidak ada versi yang memiliki panggilan ke API Windows terkait DPI.
- Dengan menggunakan Internal Sys dan beberapa kotak pesan, kami menentukan pada titik mana rilis 6.1 menjadi sadar (titik masuk ke perakitan Presentasi) dan DLL apa yang dimuat pada saat itu (milik kami, DevExpress, dependensi lain), dan kemudian kami membuat aplikasi dummy kecil yang mereferensikan DLL yang sama, dan memastikan bahwa ini dimuat. Aplikasi tiruan itu tidak menyadari DPI.
- Kami telah membandingkan file csproj utama antara kedua rilis dan tidak ada perbedaan yang berarti.
- Tidak ada referensi rilis apa pun dari WPF.
Kami tidak mengerti mengapa rilis 6.1 kami tiba-tiba menjadi sadar DPI. Kami tidak tahu apa lagi yang harus dilihat dan kami memerlukan perbaikan yang mengembalikan rilis ini ke mode tidak sadar DPI. Itu menahan pembebasan kami. Akan sangat menghargai petunjuk apa pun. Kami bersedia mencoba apa pun saat ini.
Jawaban
Tentang masalah yang dilaporkan dalam Pertanyaan ini :
Sebuah aplikasi, yang desainnya tidak disadari DPI, mengandalkan virtualisasi Windows untuk menskalakan konten UI-nya, secara tiba-tiba (meskipun setelah beberapa modifikasi, mengarah ke pembaruan rilis kecil) - dan tampaknya tanpa alasan yang dapat diamati - menjadi DPI-Aware (System Aware).
Aplikasi ini juga bergantung pada interpretasi
app.manifest<windowsSettings>, di mana tidak adanya definisi kesadaran DPI, default (untuk kompatibilitas ke belakang) ke DPI-Unaware.Tidak ada referensi langsung ke majelis WPF dan tidak ada panggilan API terkait DPI.
Aplikasi menyertakan komponen pihak ketiga (dan, mungkin, dependensi eksternal).
Karena DPI-Awareness telah menjadi aspek yang relevan dari presentasi UI, mengingat keragaman resolusi layar yang tersedia (dan pengaturan penskalaan DPI terkait), sebagian besar produsen komponen telah beradaptasi dengan DPI-Tinggi dan produk mereka adalah DPI-Aware (skala ketika DPI berubah terdeteksi) dan menggunakan rakitan DPI-Aware (sering merujuk rakitan WPF, DPI-Aware menurut definisi).
Ketika salah satu dari komponen DPI-Aware ini direferensikan dalam sebuah proyek (secara langsung atau tidak langsung), aplikasi DPI-Unaware akan menjadi DPI-Aware, ketika DPI-Awareness belum dinonaktifkan secara eksplisit.
Metode yang lebih langsung (dan direkomendasikan) untuk mendeklarasikan DPI-Awareness perakitan, adalah mendeklarasikannya secara eksplisit dalam manifes aplikasi.
Lihat jawaban Hans Passant untuk pengaturan manifes aplikasi sebelum Visual Studio 2017:
Cara mengkonfigurasi aplikasi untuk dijalankan di mesin dengan pengaturan DPI tinggi
Di Visual Studio 2015-Upd.1 dan Visual Studio 2017 app.manifest, pengaturan ini sudah ada, hanya perlu tidak diberi komentar. Mengatur bagian: <dpiAware>false</dpiAware>.
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
//(...)
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">false</dpiAware>
</windowsSettings>
</application>
//(...)
</assembly>
Lihat artikel MSDN ini untuk informasi lebih lanjut:
Pengembangan aplikasi desktop DPI tinggi di Windows
Mengatur kesadaran DPI default untuk suatu proses
Metode lain adalah untuk mengatur konteks proses DPI-Awareness menggunakan fungsi Windows API berikut:
Windows 7
SetProcessDPIAware
[DllImport("user32.dll", SetLastError=true)]
static extern bool SetProcessDPIAware();
Windows 8.1
SetProcessDpiAwareness
[DllImport("shcore.dll")]
static extern int SetProcessDpiAwareness(ProcessDPIAwareness value);
enum ProcessDPIAwareness
{
DPI_Unaware = 0,
System_DPI_Aware = 1,
Per_Monitor_DPI_Aware = 2
}
Windows 10, versi 1703
SetProcessDpiAwarenessContext ()
(Saat memilih Per-Monitor DPI-Awareness, gunakan Context_PerMonitorAwareV2)
Lihat juga: Penskalaan DPI Mode Campuran dan API yang mendukung DPI - MSDN
Windows 10, versi 1809 (Oktober 2018) yang
baru DPI_AWARENESS_CONTEXTtelah ditambahkan:DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED
DPI tidak menyadari peningkatan kualitas konten berbasis GDI. Mode ini berperilaku mirip dengan DPI_AWARENESS_CONTEXT_UNAWARE, tetapi juga memungkinkan sistem untuk secara otomatis meningkatkan kualitas rendering teks dan primitif berbasis GDI lainnya ketika jendela ditampilkan pada monitor DPI tinggi.
Gunakan GetWindowDpiAwarenessContext()fungsi untuk mengambil DPI_AWARENESS_CONTEXTpegangan Window dan GetThreadDpiAwarenessContext()untuk DPI_AWARENESS_CONTEXTpegangan utas saat ini. Kemudian GetAwarenessFromDpiAwarenessContext()untuk mendapatkan kembali DPI_AWARENESSnilai dari DPI_AWARENESS_CONTEXTstruktur.
[DllImport("user32.dll", SetLastError=true)]
static extern IntPtr GetWindowDpiAwarenessContext(IntPtr hWnd);
[DllImport("user32.dll", SetLastError=true)]
static extern IntPtr GetThreadDpiAwarenessContext();
[DllImport("user32.dll", SetLastError=true)]
static extern int GetAwarenessFromDpiAwarenessContext(IntPtr DPI_AWARENESS_CONTEXT);
[DllImport("user32.dll", SetLastError=true)]
static extern int SetProcessDpiAwarenessContext(ContextDPIAwareness value);
// Virtual enumeration: DPI_AWARENESS_CONTEXT is *contextual*.
// This value is returned by GetWindowDpiAwarenessContext() or GetThreadDpiAwarenessContext()
// and finalized by GetAwarenessFromDpiAwarenessContext(). See the Docs.
enum ContextDPIAwareness
{
Context_Unaware = ((DPI_AWARENESS_CONTEXT)(-1)),
Context_SystemAware = ((DPI_AWARENESS_CONTEXT)(-2)),
Context_PerMonitorAware = ((DPI_AWARENESS_CONTEXT)(-3)),
Context_PerMonitorAwareV2 = ((DPI_AWARENESS_CONTEXT)(-4)),
Context_UnawareGdiScaled = ((DPI_AWARENESS_CONTEXT)(-5))
}
Karena DPI-Awareness berbasis thread, pengaturan ini dapat diterapkan ke thread tertentu. Ini dapat berguna saat mendesain ulang antarmuka pengguna untuk mengimplementasikan DPI-Awareness, agar Sistem dapat menskalakan komponen yang kurang penting sambil berfokus pada fungsi yang lebih penting.
SetThreadDpiAwarenessContext
(Parameter yang sama seperti SetProcessDpiAwarenessContext())
Assemblyinfo.cs
Jika komponen pihak ketiga / eksternal, yang mereferensikan rakitan WPF, mendefinisikan ulang status DPI-Awareness suatu aplikasi, perilaku otomatis ini dapat dinonaktifkan, dengan memasukkan parameter di Proyek Assemblyinfo.cs:
[assembly: System.Windows.Media.DisableDpiAwareness]