Autor Wątek: [.NET] Problem ze screenshotem  (Przeczytany 1930 razy)

Offline counterClockWise

  • Użytkownik

# Październik 26, 2009, 13:50:21
Wiem, że trochę mało związane z grami, ale czy jest możliwość w .NET Framework pobrać screenshot ekranu aktualnie zalogowanego użytkownika z poziomu aplikacji typu Windows Service?

W Windows Forms jest banalnie:

bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
Graphics graphics = Graphics.FromImage(bmp);
graphics.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
           
no ale ta metoda w Windows Service nie ma prawa działać i nie działa.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline ConayR

  • Użytkownik

# Październik 26, 2009, 14:00:12
W większości przypadków Service nie chodzi uwierzytelniony jako zalogowany użytkownik, więc to nie ma prawa działać tak, jak to sobie obmyśliłeś. Zależnie od licencji na komputerze może być zalogowanych nawet i kilkanaście osób. Skąd serwis ma wiedzieć której zrobić zrzut ekranu? Nie jest to niewykonalne, ale nie do końca przemyślałeś to, co chcesz zrobić. :) Więc co próbujesz napisać? (nie jak? - co?)

Offline Xion

  • Moderator
    • xion.log

# Październik 26, 2009, 14:02:16
Możesz spróbować zaimportować GDI i zrobić jak tam, czyli (ostrzeżenie: kod pisany z pamięci):
HDC screen = GetDC(0);
HDC hdcBmp = CreateCompatibleDC(screen);
HBITMAP hBmp = CreateCompatibleBitmap(screen);
SelectObject (hdcBmp, hBmp);
BitBlt (screen, /* dużo parametrów */, hdcBmp);

Offline counterClockWise

  • Użytkownik

# Październik 26, 2009, 14:02:28
W większości przypadków Service nie chodzi uwierzytelniony jako zalogowany użytkownik, więc to nie ma prawa działać tak, jak to sobie obmyśliłeś. Zależnie od licencji na komputerze może być zalogowanych nawet i kilkanaście osób. Skąd serwis ma wiedzieć której zrobić zrzut ekranu? Nie jest to niewykonalne, ale nie do końca przemyślałeś to, co chcesz zrobić. :) Więc co próbujesz napisać? (nie jak? - co?)

Coś w stylu VNC/Remote Desktop. Pierwsza przymiarka, aby zrobić to samemu, ale ostatecznie pewnie będzie trzeba skorzystać z jakiegoś gotowego mechanizmu.

Możesz spróbować zaimportować GDI i zrobić jak tam, czyli (ostrzeżenie: kod pisany z pamięci):
HDC screen = GetDC(0);
HDC hdcBmp = CreateCompatibleDC(screen);
HBITMAP hBmp = CreateCompatibleBitmap(screen);
SelectObject (hdcBmp, hBmp);
BitBlt (screen, /* dużo parametrów */, hdcBmp);

Też o tym pomyślałem, ale czy DC(0) nie zwracał kontekstu samego puplitu bez okienek na nim?
W C# jest Graphics.FromHDC(), więc spróbuję bezpośrednio :)

SOLVED - gdyby ktoś miał podobny problem:

const int SRCCOPY = 0xcc0020;
        [System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
        private static extern int BitBlt(
          IntPtr hdcDest,     
          int nXDest,         
          int nYDest,         
          int nWidth,         
          int nHeight,       
          IntPtr hdcSrc,     
          int nXSrc,         
          int nYSrc,         
          System.Int32 dwRop 
          );

        public static Bitmap GetScreenshot()
        {
            Graphics desktopGraphics = Graphics.FromHwnd(new IntPtr(0));
            int width = (int)desktopGraphics.VisibleClipBounds.Width;
            int height = (int)desktopGraphics.VisibleClipBounds.Height;
            Bitmap bmp = new Bitmap(width, height, desktopGraphics);
           
            Graphics bmpGraphics = Graphics.FromImage(bmp);
            BitBlt(bmpGraphics.GetHdc(), 0, 0, width, height, desktopGraphics.GetHdc(), 0, 0, SRCCOPY);
            bmpGraphics.ReleaseHdc();
            desktopGraphics.ReleaseHdc();
            return bmp;
        }
« Ostatnia zmiana: Październik 26, 2009, 14:50:22 wysłana przez counterClockWise »

Offline Xion

  • Moderator
    • xion.log

# Październik 26, 2009, 18:36:22
No właśnie GetDC(0) zwraca ci cały ekran :) Widzę zresztą, że zrobiłeś to mnie więcej tak, jak pisałem, ograniczając jedynie użycie WinAPI do minimum (o ile pamiętam w GDI+ nie ma niestety kopiowania Graphics -> Graphics, więc trzeba użyć BitBlt).

Offline ConayR

  • Użytkownik

# Październik 26, 2009, 20:08:48
Jak się zachowa ten kod, kiedy zalogowane będą dwie osoby? ;)

Offline counterClockWise

  • Użytkownik

# Październik 26, 2009, 22:50:01
Jak się zachowa ten kod, kiedy zalogowane będą dwie osoby? ;)

Może być zalogowanych wiele osób, ale sam Windows nie pozwala na interaktywne korzystanie kilku, jest tylko jeden InputDesktop.
Niestety tamto rozwiązanie nie było Solved, musiałem przełączyć się na odpowiednią maszynę "winsta0", pobierać odpowiednie uchwyty, dać prawa bieżącemu wątkowi na interakcję z pulpitem.
Powstał długi kod, który mam w pracy, jeżeli ktoś chce, to wkleję jutro :)