Службы и GDI
От: al-tom  
Дата: 29.11.13 11:39
Оценка:
Всех приветствую.
Есть Windows служба:

  SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
  if(!hSCManager) {
    return false;
  }
  
  GetModuleFileName(NULL, servicePath, MAX_PATH);
  DWORD wver = LOWORD(GetVersion());
  wver = (LOBYTE(wver) << 8) + HIBYTE(wver);
  DWORD stype = SERVICE_WIN32_OWN_PROCESS;
  if (wver < 0x0600) {
    stype |= SERVICE_INTERACTIVE_PROCESS;
  }
  SC_HANDLE hService = CreateService(
     hSCManager,
     serviceName,
     serviceName,
     SERVICE_ALL_ACCESS,
     stype,
     SERVICE_AUTO_START,
     SERVICE_ERROR_NORMAL,
     servicePath,
     NULL, NULL, NULL, NULL, NULL
  );

  CloseServiceHandle(hService);
  CloseServiceHandle(hSCManager);


Служба запускает другой процесс:

  STARTUPINFO si;
  PROCESS_INFORMATION pi;
  memset(&si, 0, sizeof(si));
  si.cb = sizeof(si);
  memset(&pi, 0, sizeof(pi));
  
  HANDLE proc = GetCurrentProcess();
  HANDLE cur_token, new_token;
  DWORD session = WTSGetActiveConsoleSessionId();
  
  if (!OpenProcessToken(proc, TOKEN_DUPLICATE, &cur_token)) {
    return false;
  }
  if (!DuplicateTokenEx(cur_token, TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &new_token)) {
    return false;
  }
  if (!SetTokenInformation(new_token, (TOKEN_INFORMATION_CLASS) TokenSessionId, &session, sizeof(session))) {
    return false;
  }

  CreateProcessAsUser(new_token, NULL, "program.exe scr", NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);

  CloseHandle(token);
  CloseHandle(userToken);


Этот процесс занимается снятием скриншотов:

  HDC winDC = CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
  HDC bmpDC = CreateCompatibleDC(winDC);
 
  int width = GetDeviceCaps(winDC, HORZRES);
  int height = GetDeviceCaps(winDC, VERTRES);
 
  BITMAPINFOHEADER bmpInfoHeader;
  BITMAPFILEHEADER bmpFileHeader;
 
  bmpFileHeader.bfType = 0x4d42;
  bmpFileHeader.bfSize = 0;
  bmpFileHeader.bfReserved1 = 0;
  bmpFileHeader.bfReserved2 = 0;
  bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
 
  bmpInfoHeader.biSize = sizeof(bmpInfoHeader);
  bmpInfoHeader.biWidth = width;
  bmpInfoHeader.biHeight = height;
  bmpInfoHeader.biPlanes = 1;
  bmpInfoHeader.biBitCount = 24;
  bmpInfoHeader.biCompression = BI_RGB;
  bmpInfoHeader.biSizeImage = width * height * 3;
  bmpInfoHeader.biXPelsPerMeter = 0;
  bmpInfoHeader.biYPelsPerMeter = 0;
  bmpInfoHeader.biClrUsed = 0;
  bmpInfoHeader.biClrImportant = 0;
  
  BITMAPINFO info;
  info.bmiHeader = bmpInfoHeader;
 
  void* memory;
  HBITMAP bitmap;
  bitmap = CreateDIBSection(winDC, &info, DIB_RGB_COLORS, &memory, NULL, 0);
  SelectObject(bmpDC, bitmap);
  if (!BitBlt(bmpDC, 0, 0, width, height, winDC, 0, 0, SRCCOPY | CAPTUREBLT)) {
    return false;
  }

  FILE *f = fopen("scrsht.bmp", "wb");
  fwrite(&bmpFileHeader, 1, sizeof(BITMAPFILEHEADER), f);
  fwrite(&bmpInfoHeader, 1, sizeof(BITMAPINFOHEADER), f);
  fwrite(memory, 1, bmpInfoHeader.biSizeImage, f);
  fclose(f);


Проблема заключается в том, что при работе под Windows XP приходится делать службу интерактивной
(иначе на скриншоте получаем черный прямоугольник), а интерактивные службы могут быть запрещены на машине клиента.
Но, коль скоро скриншоты делает не служба, а запущенный процесс, напрашивается вывод о том, что процесс
наследует какие-то права службы, которые мешают ему получить доступ к консоли.
При этом, даже в случае интерактивной службы, при отображении экрана приветствия получается черный прямоугольник.
По некоторым причинам служба и дочерний процесс представлены одним exe файлом, но непосредственно функционал
(запуск дочернего процесса и снятие скриншотов) расположен в dll (общей). Было сомнение по поводу того, что
проблема может быть связана с загрузкой этой dll из сервиса и последующим использованием ее же в дочернем
процессе, но разнесение функционала по разным dll не помогло.
Возможно, я не стал бы биться над решением этой задачи, списал бы это на ограничения винды, если бы не TightVNC,
которая производит аналогичные манипуляции, при этом не регистрируя сервис как интерактивный.
Собственно, отсюда 2 вопроса:
1. Какие права нужно добавить/отобрать у процесса, чтобы его работа не зависела от настроек службы (если проблема в правах)?
2. Что может мешать получению скриншота с экрана приветствия?
служба gdi
Re: Службы и GDI
От: anonymous185  
Дата: 29.11.13 12:22
Оценка:
у вас
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
memset(&pi, 0, sizeof(pi));

а надо
STARTUPINFO si = {sizeof(si)};
si.lpDesktop = L"WinSta0\\Default";
PROCESS_INFORMATION pi;//не надо обнулять
Re: Службы и GDI
От: ononim  
Дата: 29.11.13 12:24
Оценка: 1 (1)
'интерактивность' сервиса помимо интерактивной сессии сажает его процесс еще и в оконную станцию Winsta0 (и еще десктоп Default — но это и для неинтерактивных сервисов тоже обычно так). То есть вся многоюзерская виндовая матрешка выглядит так [Система [Сессии [Window stations [Desktops]]] ]
Так вот, при запуске процесса процесс 'наследует' родительские имена станции и десктопа, потому дочерний процесс оказываясь в нужной сессии — оказывается в оконной станции с тем же именем что имел его предок. Так что _перед_ тем как создавать UI или вызывать SetWindowsHookEx — он должен сделать OpenWindowStation('Winsta0') и потом SetProcessWindowStation с полученным хэндлом.
Второй, более простой и правильный способ: указать имя оконной станции и десктопа через слэш ("Winsta0\\Default") в STARTUPINFO, передаваемом в ваш CreateProcessAsUser
Как много веселых ребят, и все делают велосипед...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.