Çoklu Monitör Kullanımında Ekranı Farklı Monitörde Açma

Çalışmakta olduğum bir projede kullanıcılar ekrandan tıklama yaparak yeni bir pencere açmakta ve açılan penceredeki bilgiler doğrultusunda ana pencerede işlem yapmaktadırlar. Ancak çift monitör ile çalışan kullanıcılar yeni pencerenin ana pencere ile aynı monitörde açılması nedeniyle her defasında yeni pencereyi sürükleyerek diğer monitöre aktarmakta ve bu da zaman kaybına yol açmaktadır. Bu nedenle açılacak olan yeni pencerenin ana pencerenin bulunduğu monitörden farklı diğer moniterde açılması için düzenleme yapma kararı aldık.

Bu noktada ilk akla gelen çözüm sunucu tarafında yeni bir process ile pencere açmak ve SetWindowPos metodu ile açılan pencereyi ilgili ekrana taşımaktır. Temel çalışma mantığı aşağıdaki gibi olacaktır.

ProcessStartInfo startInfo = new ProcessStartInfo("IExplore.exe");
startInfo.Arguments = url;
Process newProcess = System.Diagnostics.Process.Start(startInfo);
IntPtr newProcessPtr = newProcess.MainWindowHandle;
SetWindowPos(newProcessPtr, IntPtr.Zero, x, y, cx, cy, SetWindowPosFlags.ShowWindow);

SetWindowPos metodu pencerenin pozisyonunu x ve y parametreleri ile almaktadır. cx ve cy ise pencere boyutlarını belirlemektedir. Bu noktada ana pencerenin hangi ekranda olduğunun bilinmesi ve yeni açılacak pencerenin de buna göre hangi pozisyonda açılacağının hesaplanması gerekmektedir.

Ekran bilgilerini almak için System.Windows.Forms kütüphanesi içinde Screen sınıfı bulunmaktadır. Bu sınıfın AllScreens adlı özelliği bilgisayara bağlı tüm ekranları yine Screen sınıfından bir dizi ile vermektedir. Screen sınıfının sınır koordinatlarından haraketle ekranları aşağıdaki gibi sıraya dizmek mümkündür.

List<KeyValuePair<int, int>> screenList = new List<KeyValuePair<int, int>>();
for (int i = 0; i < Screen.AllScreens.Length; i++)
      screenList.Add(new KeyValuePair<int, int>(i, Screen.AllScreens[i].Bounds.X));
screenList.Sort((firstPair, nextPair) =>
{
      return firstPair.Value.CompareTo(nextPair.Value);
});

Javascript tarafında ana pencerenin bulunduğu pozisyon bilgisini window.screenLeft ile alıp sunucu tarafına aktarabiliriz. Bu değer üzerinden sıralanmış ekranlar içinde açılacak olan yeni pencerenin ekranını belirlemek mümkün olacaktır.

int targetScreenId = 0;
foreach (KeyValuePair<int, int> screen in screenList)
{
      if (screen.Value > screenLeft)
          targetScreenId = screen.Key;
}

Açılacak yeni pencerenin konumu belirlendikten sonra değerleri SetWindowPos metoduna aktarılabilir. Burada pencere boyutları için ekran boyutlarını geçerek pencerenin tam ekran olarak açılmasınını sağlıyoruz.

SetWindowPos(newProcessPtr, IntPtr.Zero,
Screen.AllScreens[targetScreenId].WorkingArea.Left, 
Screen.AllScreens[targetScreenId].WorkingArea.Top, 
Screen.AllScreens[targetScreenId].Bounds.Size.Width, 
Screen.AllScreens[targetScreenId].Bounds.Size.Height, 
SetWindowPosFlags.ShowWindow);

1
2

Bu hali ile kodumuzu çalıştırdığımızda process üzerinden MainWindowHandle ulaştığımız anda “Process has exited, so the requested information is not available.” şeklinde istisna fırlatıldığını görürüz. Bu durum aşağıdaki sayfada açıklanmış ve çözümü verilmiştir.

http://stackoverflow.com/questions/1825105/process-startiexplore-exe-immediately-fires-the-exited-event-after-launch

Most probably is that you have IE already running as a process, so when you try to launch it again as a new process it looks that there are IE running already, tells it that user initiated a new window (so the initial IE will create a “new” window rather than a new one) and exit. Possible solution: try starting the process with “-nomerge” command line option.

“-nomerge” komutu IE8 öncesi tarayıcılarda kullanılmakta olup IE8 ve sonrası için bu komut yerine “-noframemerging” komutu kullanılmaktadır. Detaylı bilgi için aşağıdaki bağlantıyı inceleyebilirsiniz.

https://msdn.microsoft.com/en-us/library/hh826025(v=vs.85).aspx

ProcessStartInfo startInfo = new ProcessStartInfo("IExplore.exe");
startInfo.Arguments = "-noframemerging " + url;
Process newProcess = System.Diagnostics.Process.Start(startInfo);

Bu hali ile kodumuzu çalıştırdığımızda istediğimizin gerçekleşmediğini görürüz. Debug ettiğimizde ise newProcessPtr değerinin 0 olduğu ortaya çıkar. Bu durum aşağıdaki sayfada açıklanmış ve çözümü verilmiştir.

https://social.msdn.microsoft.com/Forums/vstudio/en-US/021c76c6-1a66-4b96-8ce7-89c2a12f4bcb/procstart-procwaitforinputidle-setwindowpos-does-not-work?forum=csharpgeneral

The reason for SetWindowPos method execution failed is that the Process. MainWindowHandle is not acquired in time, it is still 0 when SetWindowPos was executed.As BinaryCoder suggested, all we need to do is add a loop to check the handle status, we all in the loop until the handle was obtained.

int count = 0;
while (newProcess.MainWindowHandle == IntPtr.Zero && count < 2000)
{
     Thread.Sleep(20);
     count += 20;
}

IntPtr newProcessPtr = newProcess.MainWindowHandle;

Yukarıdaki durumu araştırırken aşağıdaki gibi bir bilgi ile karşılaştım. Bu nedenle döngü içine newProcess.Refresh(); eklemenin doğru olacağını düşünüyorum.

The main window is the window opened by the process that currently has the focus (the TopLevel form). You must use the Refresh method to refresh the Process object to get the current main window handle if it has changed.

Sonuç olarak birden fazla monitör kullanan kullanıcılarda açılacak olan yeni pencerenin ana pencerenin bulunduğu monitörden farklı bir moniterde açılmasını sağlayan kodumuz aşağıdaki gibidir.

if (Screen.AllScreens.Length > 1)
{
        ProcessStartInfo startInfo = new ProcessStartInfo("IExplore.exe");
        startInfo.WindowStyle = ProcessWindowStyle.Maximized;
        startInfo.Arguments = "-noframemerging " + url;

        Process newProcess = System.Diagnostics.Process.Start(startInfo);

        if (newProcess.Id >= 0)
        {
               int count = 0;
               while (newProcess.MainWindowHandle == IntPtr.Zero && count < 2000)
               {
                      Thread.Sleep(20);
                      newProcess.Refresh();
                      count += 20;
               }
               IntPtr newProcessPtr = newProcess.MainWindowHandle;

               List<KeyValuePair<int, int>> screenList = new List<KeyValuePair<int, int>>();

               for (int i = 0; i < Screen.AllScreens.Length; i++)
                    screenList.Add(new KeyValuePair<int, int>(i, Screen.AllScreens[i].Bounds.X));

               screenList.Sort((firstPair, nextPair) =>
               {
                    return firstPair.Value.CompareTo(nextPair.Value);
               });

               int targetScreenId = 0;
               foreach (KeyValuePair<int, int> screen in screenList)
               {
                    if (screen.Value > screenLeft)
                           targetScreenId = screen.Key;
               }

               SetWindowPos(newProcessPtr, IntPtr.Zero, Screen.AllScreens[targetScreenId].WorkingArea.Left, Screen.AllScreens[targetScreenId].WorkingArea.Top,             Screen.AllScreens[targetScreenId].Bounds.Size.Width, Screen.AllScreens[targetScreenId].Bounds.Size.Height, Win32.SetWindowPosFlags.ShowWindow);

       }
}
Reklamlar

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Google fotoğrafı

Google hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s

%d blogcu bunu beğendi: