Показаны сообщения с ярлыком Windows. Показать все сообщения
Показаны сообщения с ярлыком Windows. Показать все сообщения

воскресенье, 2 февраля 2025 г.

Пример приложения печатающий текст в стиле терминала из фильма Matrix "Матрица"

Спустя 12 лет довел до ума пример приложения, который печатает текст в стиле терминала из фильма Матрица с DelphiX на OpenGL. Оригинальный пример, на основе которого сделан мой пример, взят с сайта Delphi-Graphics. Мой код базируется на Delphi, OpenGL, WinAPI. Код моего примера оставляю без комментарий, т. к. написание проводилось без детального разбора работы алгоритма оригинального приложения и осуществлялся банальный подбор функций OpenGL и их параметров. На рисунке 1 представлена работа моего примера в Windows 10. 

Рисунок 1. Пример приложения вывода текста в стиле терминала из фильма "Матрица"

program matrix;

uses
  Windows,
  Messages,
  OpenGL;

var
  h_Rc: HGLRC;
  h_Dc: HDC;
  h_Wnd: HWND;
  base: GLuint;

  keys: array [0..255] of BOOL;

  text: array[0..4] of String = ('Wake up, Neo.',
                                 'The Matrix has you.',
                                 'Follow the White Rabbit.',
                                 'Disconnecting...',
                                 '');

  Active: bool = true;
  FullScreen:bool = true;
  f,z: boolean;
  a,b: byte;
  sx: integer;
  cur: byte;
  all: string;

procedure ReSizeGLScene(Width: GLsizei; Height: GLsizei);
begin
  if (Height=0) then
     Height:=1;
  glViewport(0, 0, Width, Height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, Width, Height, 0, 1, -1);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;
end;

procedure BuildFont;
var
  font: HFONT;
  oldfont: HFONT;
begin

  base := glGenLists(96);

  font := CreateFont( -30,
                        0,
                        0,
                        0,
                        FW_BOLD,
                        0,
                        0,
                        0,
                        ANSI_CHARSET,
                        OUT_TT_PRECIS,
                        CLIP_DEFAULT_PRECIS,
                        ANTIALIASED_QUALITY,
                        FF_DONTCARE or DEFAULT_PITCH,
                        'Courier New');

  oldfont := SelectObject(h_DC, font);                       
  wglUseFontBitmaps(h_DC, 32, 96, base);
  SelectObject(h_DC, oldfont);
  DeleteObject(font);
end;

procedure KillFont;
begin
  glDeleteLists(base, 96);
end;

procedure glPrint(const Fmt: String);
begin

if Fmt = '' then
  Exit;

glPushAttrib(GL_LIST_BIT);
glListBase(base - 32);
glCallLists(Length(Fmt), GL_UNSIGNED_BYTE, Pointer(Fmt));
glPopAttrib();

end;

function IntToStr(Num: Integer) : String;
begin
  Str(Num, Result);  
end;

function InitGL:bool;
begin

  glClearColor(0.0, 0.0, 0.0, 0.0);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, 640, 480, 0, 0, 1);
  glDisable(GL_DEPTH_TEST);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  BuildFont();

  a:=0;
  b:=0;
  sx:=0;
  cur:=0;

  Result:=true;
end;

procedure addchar;
begin
  if length(all)=length(text[cur]) then
  begin
    inc(cur);
    sx:=0;
    z:=false;
    all:='';
    Exit;
  end;
  if cur<=5 then
  begin
    all := all + text[cur][length(all)+1];
  end
  else
  begin
    exit;
  end;
end;

function DrawGLScene():bool;
var
  i: integer;
  kk2:integer;
  kk: integer;
begin
  glClear(GL_COLOR_BUFFER_BIT);

  glEnable(GL_ALPHA_TEST);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glLoadIdentity();

  if not z then
    inc(sx);
  if sx=200 then
    z:=true;

  if z then
  begin
    inc(B);
    if b=20 then addchar;
    if b>20 then b:=0;
  end;

  if not f then
    inc(a,5)
  else dec(a,5);

  if (a>=255)or(a=0) then f:=not f;

  glColor3f(0.196078, 0.8, 0.196078);
  for i := 0 to cur - 1 do
  begin
    glRasterPos2f(0.0, 20.0 + i*30);
    glPrint(text[i]);
  end;

  if i=5 then
  begin
    i:=0;
    cur:=0;
    a:=0;
    b:=0;
    sx:=0;
    z:=false;
    f:=False;
    all:='';
    glClearColor(0, 0, 0, 0);
    glClear(GL_COLOR_BUFFER_BIT);
  end;

  kk := 18*length(all);
  kk2 := cur*30;

  glRasterPos2f(0.0, 20.0 + cur*30);
  glPrint(all);

  glColor4f(0.196078, 0.8, 0.196078, a / 255); // Colour LimeGreen
  glRectf(0.0+kk, 0.0+kk2, 23.0+kk, 23.0+kk2);

  glDisable(GL_BLEND);
  glDisable(GL_ALPHA_TEST);

  Result := true;
end;


function WndProc(hWnd: HWND;
                 message: UINT;
                 wParam: WPARAM;
                 lParam: LPARAM):
                                  LRESULT; stdcall;
begin
  if message=WM_SYSCOMMAND then
  begin
      case wParam of
        SC_SCREENSAVE,SC_MONITORPOWER:
          begin
            result:=0;
            exit;
          end;
      end;
  end;
  
  case message of
    WM_ACTIVATE:
      begin
        if (Hiword(wParam)=0) then
          active:=true
        else
          active:=false;
        Result:=0;
      end;
    WM_CLOSE:
      Begin
        PostQuitMessage(0);
        result:=0
      end;
    WM_KEYDOWN:
      begin
        keys[wParam] := TRUE;
        result:=0;
      end;
    WM_KEYUP:
      begin
    	  keys[wParam] := FALSE;
        result:=0;
      end;
    WM_SIZe:
      begin
    	  ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));
        result:=0;
      end
    else

      begin
      	Result := DefWindowProc(hWnd, message, wParam, lParam);
      end;
    end;
end;


procedure KillGLWindow;
begin
  if FullScreen then
    begin
      ChangeDisplaySettings(devmode(nil^),0);
      showcursor(true);
    end;
  if h_rc<> 0 then
    begin
      if (not wglMakeCurrent(h_Dc,0)) then
        MessageBox(0,'Release of DC and RC failed.',' Shutdown Error',MB_OK or MB_ICONERROR);
      if (not wglDeleteContext(h_Rc)) then
        begin
          MessageBox(0,'Release of Rendering Context failed.',' Shutdown Error',MB_OK or MB_ICONERROR);
          h_Rc:=0;
        end;
    end;
  if (h_Dc=1) and (releaseDC(h_Wnd,h_Dc)<>0) then
    begin
      MessageBox(0,'Release of Device Context failed.',' Shutdown Error',MB_OK or MB_ICONERROR);
      h_Dc:=0;
    end;
  if (h_Wnd<>0) and (not destroywindow(h_Wnd))then
    begin
      MessageBox(0,'Could not release hWnd.',' Shutdown Error',MB_OK or MB_ICONERROR);
      h_Wnd:=0;
    end;
  if (not UnregisterClass('OpenGL',hInstance)) then
    begin
      MessageBox(0,'Could Not Unregister Class.','SHUTDOWN ERROR',MB_OK or MB_ICONINFORMATION);
    end;
end;


function CreateGlWindow(title:Pchar; width,height,bits:integer;FullScreenflag:bool):boolean stdcall;
var
  Pixelformat: GLuint;
  wc:TWndclass;
  dwExStyle:dword;
  dwStyle:dword;
  pfd: pixelformatdescriptor;
  dmScreenSettings: Devmode;
  h_Instance:hinst;
  WindowRect: TRect;
begin
  WindowRect.Left := 0;
  WindowRect.Top := 0;
  WindowRect.Right := width;
  WindowRect.Bottom := height;
  h_instance:=GetModuleHandle(nil);
  FullScreen:=FullScreenflag;
  with wc do
    begin
      style:=CS_HREDRAW or CS_VREDRAW or CS_OWNDC;
      lpfnWndProc:=@WndProc;
      cbClsExtra:=0;
      cbWndExtra:=0;
      hInstance:=h_Instance;
      hIcon:=LoadIcon(0,IDI_WINLOGO);
      hCursor:=LoadCursor(0,IDC_ARROW);
      hbrBackground:=0;
      lpszMenuName:=nil;
      lpszClassName:='OpenGl';
    end;
  if  RegisterClass(wc)=0 then
    begin
      MessageBox(0,'Failed To Register The Window Class.','Error',MB_OK or MB_ICONERROR);
      Result:=false;
      exit;
    end;
  if FullScreen then
    begin
      ZeroMemory( @dmScreenSettings, sizeof(dmScreenSettings) );
      with dmScreensettings do
        begin
          dmSize := sizeof(dmScreenSettings);
          dmPelsWidth  := width;
	        dmPelsHeight := height;
          dmBitsPerPel := bits;
          dmFields     := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
        end;

      if (ChangeDisplaySettings(dmScreenSettings, CDS_FULLSCREEN))<>DISP_CHANGE_SUCCESSFUL THEN
        Begin

          if MessageBox(0,'This FullScreen Mode Is Not Supported. Use Windowed Mode Instead?'
                                             ,'Matrix',MB_YESNO or MB_ICONEXCLAMATION)= IDYES then
                FullScreen:=false
          else
            begin

              MessageBox(0,'Program Will Now Close.','Error',MB_OK or MB_ICONERROR);
              Result:=false;
              exit;
            end;
          end;
    end;
  if FullScreen then
    begin
      dwExStyle:=WS_EX_APPWINDOW;
      dwStyle:=WS_POPUP or WS_CLIPSIBLINGS or WS_CLIPCHILDREN;
      Showcursor(false);
    end
  else
    begin
      dwExStyle:=WS_EX_APPWINDOW or WS_EX_WINDOWEDGE;
      dwStyle:=WS_OVERLAPPEDWINDOW or WS_CLIPSIBLINGS or WS_CLIPCHILDREN;
    end;
  AdjustWindowRectEx(WindowRect,dwStyle,false,dwExStyle);

  H_wnd:=CreateWindowEx(dwExStyle,
                               'OpenGl',
                               Title,
                               dwStyle,
                               0,0,
                               WindowRect.Right-WindowRect.Left,
                               WindowRect.Bottom-WindowRect.Top,
                               0,
                               0,
                               hinstance,
                               nil);
  if h_Wnd=0 then
    begin
      KillGlWindow();
      MessageBox(0,'Window creation error.','Error',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;
      exit;
    end;
  with pfd do
    begin
      nSize:= SizeOf( PIXELFORMATDESCRIPTOR );
      nVersion:= 1;
      dwFlags:= PFD_DRAW_TO_WINDOW
        or PFD_SUPPORT_OPENGL
        or PFD_DOUBLEBUFFER;
      iPixelType:= PFD_TYPE_RGBA;
      cColorBits:= bits;
      cRedBits:= 0;
      cRedShift:= 0;
      cGreenBits:= 0;
      cBlueBits:= 0;
      cBlueShift:= 0;
      cAlphaBits:= 0;
      cAlphaShift:= 0;
      cAccumBits:= 0;
      cAccumRedBits:= 0;
      cAccumGreenBits:= 0;
      cAccumBlueBits:= 0;
      cAccumAlphaBits:= 0;
      cDepthBits:= 16;
      cStencilBits:= 0;
      cAuxBuffers:= 0;
      iLayerType:= PFD_MAIN_PLANE;
      bReserved:= 0;
      dwLayerMask:= 0;
      dwVisibleMask:= 0;
      dwDamageMask:= 0;
    end;
  h_Dc := GetDC(h_Wnd);
  if h_Dc=0 then
    begin
      KillGLWindow();
      MessageBox(0,'Cant''t create a GL device context.','Error',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;
      exit;
    end;
  PixelFormat := ChoosePixelFormat(h_Dc, @pfd);
  if (PixelFormat=0) then
    begin
      KillGLWindow();
      MessageBox(0,'Cant''t Find A Suitable PixelFormat.','Error',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;
      exit;
    end;
  if (not SetPixelFormat(h_Dc,PixelFormat,@pfd)) then
    begin
      KillGLWindow();
      MessageBox(0,'Cant''t set PixelFormat.','Error',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;
      exit;
    end;
  h_Rc := wglCreateContext(h_Dc);
  if (h_Rc=0) then
    begin
      KillGLWindow();
      MessageBox(0,'Cant''t create a GL rendering context.','Error',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;
      exit;
    end;
  if (not wglMakeCurrent(h_Dc, h_Rc)) then
    begin
      KillGLWindow();
      MessageBox(0,'Cant''t activate the GL rendering context.','Error',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;
      exit;
    end;
  ShowWindow(h_Wnd,SW_SHOW);
  SetForegroundWindow(h_Wnd);
  SetFOcus(h_Wnd);
  ReSizeGLScene(width,height);
  if (not InitGl()) then
    begin
      KillGLWindow();
      MessageBox(0,'initialization failed.','Error',MB_OK or MB_ICONEXCLAMATION);
      Result:=false;
      exit;
    end;
  Result:=true;
end;


function WinMain(hInstance: HINST;
		 hPrevInstance: HINST;
		 lpCmdLine: PChar;
		 nCmdShow: integer):
                        integer; stdcall;
var
  msg: TMsg;
  done: Bool;

begin
  done:=false;

  if MessageBox(0,'Would You Like To Run In FullScreen Mode?','Start FullScreen', MB_YESNO or MB_ICONQUESTION)=IDNO then
    FullScreen:=false
  else
    FullScreen:=true;

  if not CreateGLWindow('Matrix',640,480,16,FullScreen) then
  begin
    Result := 0;
    exit;
  end;

  while not done do
  begin
    if (PeekMessage(msg, 0, 0, 0, PM_REMOVE)) then
    begin
      if msg.message=WM_QUIT then
        done:=true
      else
      begin
        TranslateMessage(msg);
        DispatchMessage(msg);
      end;
    end
    else
    begin

      if (Active and (not DrawGLScene()) or keys[VK_ESCAPE]) then
      begin
        done:=true;
      end;

      if (keys[VK_F1]) then
      begin
        Keys[VK_F1] := false;
        KillGLWindow();
        FullScreen := not FullScreen;

        if not CreateGLWindow('Matrix',640,480,16,fullscreen) then Result := 0;
      end;

      SwapBuffers(h_Dc);
    end;
  end;

  KillFont();
  killGLwindow();

  result:=msg.wParam;
end;

begin
  WinMain( hInstance, hPrevInst, CmdLine, CmdShow );
end.

пятница, 25 октября 2024 г.

Запуск UltraVNC сервера на удаленном компьютере в локальной сети через PsExec

Есть такой программный комплекс LANDesk Remote Control, который используется для удаленного подключения к компьютерам в локальной сети. Данная программа состоит: 

- из программы LANDesk Remote Control Agent - это агент, который устанавливается на все компьютеры к которым нужно подключаться.  
- из программа LANDesk Remote Control Viewer – это удаленный просмоторщик, с помощью неё мы подключаемся к нужному компьютеру, где установлен агент.
- из  LANDesk Remote Control Console — это консоль управления, что-то вроде каталога базы данных, куда агенты посылают информацию о компьютерах.

Данный программный комплекс можно использовать по назначению, но есть несколько проблем, которые я бы хотел перечислить:

1) Агент бывает иногда некорректно устанавливается на компьютер
2) Каталог базы данных бывает иногда глючит и не может корректно общаться с агентом
3) Агент иногда теряет связь с каталогом базы данных

Данные проблемы с горем пополам удается решить, но на решение данных проблем уходит большое количество времени от 30 минут до 4 часов в зависимости от характеристик компьютера.

По этой причине мне пришлось искать другие решения для удаленного управления компьютерами в локальной сети. Пора научиться пользоваться программой под названием UltraVNC.

Поискав в Google информацию по настройке сервера UltraVNC я наткнулся на сайт, где рассказывается как можно автоматизировать установку и настройку сервера UltraVNC. Вот это ссылка. На основе этой статьи я написал свои batch файлы для автоматизации развертывания сервера UltraVNC и по необходимости его удаление.           

Первым делом качаем портативную версию UltraVNC с официального сайта она поставляется в заархивированном виде (ZIP файл) https://uvnc.com/component/jdownloads/summary/470-ultravnc-1-4-6-zip.html . На момент написания статьи последняя версия представленная на официальном сайте 1.4.3.6 версия. На всякий случаю оставлю ссылку на главную страницу скачивания UltraVNC https://uvnc.com/downloads/ultravnc.html . Распакуйте архив и вы увидите в нем UltraVNC для платформы x86 и x64. Тут принцип простой если вы подключаетесь к компьютеру с 32-х битной системой Windows, то используйте сервер UltraVNC (winvnc.exe) из папки x86, если вы подключаетесь к компьютеру с 64-х битной системой Windows, то используйте сервер UltraVNC (winvnc.exe) из папки x64. Теперь запустите у себя на компьютере файл winvnc.exe из папки x86 или х64 в зависимости от разрядности вышей Windows. В результате этого у вас откроется окно показанное на рисунке 1.

Рисунок 1. Настройка сервера UltraVNC

В этом окне необходимо выполнить два действия:

1) Установить разные пароли для режима управления и режима просмотра удаленного компьютера (Смотрите рисунок 1 цифра 1)
2) Установить опцию отключения значка в трее на панели задач рядом с часами (Смотрите рисунок 1 цифра 2)
Нажмите кнопку Apply и Ok для сохранения всех настроек сервера UltraVNC в файл UltraVNC.ini.

Так же скачайте утилиту PsExec с официального сайта Microsoft по этой ссылке. Или по прямой ссылке. Достаем из архива файл PsExec.exe сохраняем его в папку cmd2 на диске F или в другую любую папку на ваш выбор. За одно в папку cmd2 копируем файл winvnc.exe и файл UltraVNC.ini.  

Переходим к написанию batch файлов.  В папке cmd2 создайте файл uvnc.bat и файл uvnc_del.bat.

Важно! У вас должны быть права администратора на удаленном компьютере. Файлы uvnc.bat и uvnc_del.bat необходимо запускать в командной строке Windows. Синтаксис запуска скриптов следующий: 

 uvnc.bat pc01
 uvnc_del.bat pc01

pc01 - это имя удаленного компьютера, куда вы запускаете сервер UltraVNC.    

Содержимое файла uvnc.bat c комментариями:

@echo off
cls

net use O: /delete /y

if exist \\%1\C$\Temp\ (
   net use O: \\%1\C$\Temp
) else (
  net use O: \\%1\C$
  mkdir O:\Temp
  net use O: /delete /y
  net use O: \\%1\C$\Temp
)
mkdir O:\uvnc
copy "%~d0%~p0\winvnc.exe" "O:\uvnc" /y
copy "%~d0%~p0\UltraVNC.ini" "O:\uvnc" /y

psexec \\%1 net stop uvnc_service
psexec \\%1 "taskkill.exe" /f /im winvnc*

psexec \\%1 C:\Temp\uvnc\winvnc.exe -install

pause 

Комментарий по коду выше:
net use O: /delete /y (Удаляем сетевой диск O: если он существует)

if exist \\%1\C$\Temp\ (Этой строкой проверим существует ли на удаленном компьютере папка Temp в корне диска C, где %1 — автоматически подставится имя удаленного компьютера)
Если папка Temp существует, то строкой  net use O: \\%1\C$\Temp создаем сетевой диск O, который указывает на папку C:\Temp на удаленном компьютере.  
Если папка Temp не существует, то строкой  net use O: \\%1\C$ создаем сетевой диск O, который указывает в корень диска C на удаленном компьютере, строкой mkdir O:\Temp на сетевом диске O: создаем папку Temp, строкой net use O: /delete /y удаляем сетевой диск O:, строкой  net use O: \\%1\C$\Temp создаем сетевой диск O, который указывает на папку C:\Temp на удаленном компьютере.
Строкой  mkdir O:\uvnc создадим папку uvnc на сетевом диске O.

Строками
copy "%~d0%~p0\winvnc.exe" "O:\uvnc" /y
copy "%~d0%~p0\UltraVNC.ini" "O:\uvnc" /y      
скопируем из папки F:\cmd2 файл winvnc.exe и файл UltraVNC.ini в папку uvnc на сетевом диске O. (%~d0%~p0 — это строка означает путь откуда выполняется скрипт uvnc.bat, в частности это F:\cmd2)

Строками
psexec \\%1 net stop uvnc_service
psexec \\%1 "taskkill.exe" /f /im winvnc*

psexec \\%1 C:\Temp\uvnc\winvnc.exe -install

мы на удаленном компьютере останавливаем службу uvnc_service сервера UltraVNC (psexec \\%1 net stop uvnc_service), потом выгружаем на удаленном компьютере процессы из памяти с именем winvnc если такие имеют место быть (psexec \\%1 "taskkill.exe" /f /im winvnc*), потом устанавливаем на удаленном компьютере сервер UltraVNC в виде службы с именем uvnc_service (psexec \\%1 C:\Temp\uvnc\winvnc.exe -install).

Содержимое файла uvnc_del.bat c комментариями: 

@echo off
cls

psexec \\%1 net stop uvnc_service
psexec \\%1 "taskkill.exe" /f /im winvnc*

psexec \\%1 C:\Temp\uvnc\winvnc.exe -uninstall

del /q O:\uvnc\*
rd O:\uvnc

net use O: /delete /y
pause  

Строкой psexec \\%1 net stop uvnc_service мы на удаленном компьютере останавливаем службу uvnc_service сервера UltraVNC.
Строкой psexec \\%1 "taskkill.exe" /f /im winvnc* выгружаем на удаленном компьютере процессы из памяти с именем winvnc если такие есть.
Строкой psexec \\%1 C:\Temp\uvnc\winvnc.exe -uninstall удаляем на удаленном компьютере службу с именем uvnc_service

Строкой del /q O:\uvnc\* удаляем на удаленном компьютере все файлы в папке uvnc на сетевом диске O.
Строкой rd O:\uvnc удаляем на удаленном компьютере папку uvnc на сетевом диске O.
Строкой net use O: /delete /y удаляем сетевой диск O: если он существует.

Всё. Как только сервер UltraVNC заработает на удаленном компьютере, то вам останется запустить vncviewer.exe программу удаленный просмоторщик, ввести имя удаленного компьютера к которому мы хотим подключится, вводим пароль который мы установили при настройке сервера UltraVNC и нажимаем кнопку Connect (Смотрите рисунок 2).

Рисунок 2. Окно программы просмоторщика UltraVNC

В данном примере мы запустили сервер UltraVNC в режиме службы, я пытался запустить также сервер в режиме приложения, но данный режим по какой-то причине не работает, скорее всего не хватает прав, смотрите рисунок 3 как это выглядит при попытке подключения к удаленному компьютеру.
 
Рисунок 3. Просмоторщик UltraVNC запущенный в режиме приложения

понедельник, 7 октября 2024 г.

Проблемы подключения принтеров HP LaserJet P2014 и HP LaserJet P2015 в Windows 10 Pro 64 bit или 32 bit по USB

Сразу скажу, что принтера HP LaserJet P2014, HP LaserJet P2015 с Windows 10 Pro 64 bit или 32 bit дружат плохо (в частности пробовал заставить работать данный принтер на Windows 10 Pro 64 bit 20H2). У меня возникала проблема подключения принтера по USB кабелю. Даже при наличии в Windows предустановленных драйверов на принтер, он не желает устанавливаться ни автоматическом, ни в ручном режимах.

Симптомы проблемы следующие:

- в диспетчере устройств в разделе "Контроллеры USB устройств" вы видите устройство "Поддержка USB принтера", которое висит с восклицательным знаком. Если зайти в свойства этого устройства, то во вкладке общие в секции состояния устройства вы видите код ошибки 10 (Запуск это устройства прервано или остановлено).

Что пробовал делать:

- Переключение принтера в другие свободные USB порты на системном блоке проблему не решает

- Перезагрузка компьютера проблему не решает

- Выключение и включение принтера проблему не решает

Путем долгих часов поиска решения проблемы выяснилось, что для это принтера необходимо установить в Windows 10 Pro 64 bit программу (драйвер порта от HP) который называется Dot4x64.msi (Windows 10 Pro 64 bit) или Dot4x86.msi (Windows 10 Pro 32 bit).

На сайте HP на странице скачивания драйверов к принтеру HP LaserJet 1320 для Windows 10 можно скачать файл (Dot4x86.msi) (Windows 10 Pro 32 bit) и (Dot4x64.msi) (Windows 10 Pro 64 bit). Внимание! Обязательно проверяйте скаченные файлы антивирусом.

Данные принтеры (HP LaserJet P2014, HP LaserJet P2015) в Windows 10 Pro 64 bit после отключения системного блока или перезагрузке самопроизвольно создают порты DOT4USB и создают новый логический принтер с новым именем (добавляется в конце имени Копия 1,2,3..N). Так же наблюдается периодическое отвал этих моделей принтеров от USB разъёма компьютера, что требуется переключение их в другой свободный USB порт, после этого данные модели принтеров будут работать до следующего отвала от USB. Данная проблема наблюдается в Windows 10 Pro 64 bit, замечу что в Windows 7 Pro 32 bit такой проблемы нет, в Winndows 7 данные модели принтеров работают отлично.

воскресенье, 12 сентября 2021 г.

Печать выделенного фрагмента из PDF документа в Adobe Acrobat Reader DC

Если вам необходимо напечатать выделенный фрагмент PDF документа, то вам нужно открыть PDF документ в программе Adobe Acrobat Reader DC, выбрать пункт меню «Редактирование» (смотрите рисунок 1), выбрать подпункт «Сделать снимок», потом выделить на нужной странице фрагмент в PDF документе.

Рисунок 1. Создание снимка выделенного PDF фрагмента

Теперь можно заходить в окно печати, для этого выберите пункт меню «Файл», потом подпункт “Печать…” или нажмите сочетание клавиш Ctrl+P, и вы увидите подготовленный выделенный PDF фрагмент для печати. В разделе «Настройка размера и обработка страниц» установите опцию «Подогнать» для того чтобы выделенный PDF фрагмент занимал всю печатающуюся область.  

Конфликт имен в Excel 2016

Я думаю вы уже сталкивались с такой проблемой при работе в Excel, когда вы копируете лист Excel, то при создании копии листа вы получаете окно «Конфликт имен» (Смотрите рисунок 1). Картинка взята с сайта.

Рисунок 1. Окно "Конфликт имен"

Данная проблема связана с тем, что в окне «Диспетчер имен» (открывается при нажатии сочетания клавиш Ctrl+F3), который можно открыть на ленте во вкладке «Формулы», раздел «Определенные имена», созданы большое число именованных диапазонов, они и конфликтуют при копирование листа. Эти именованные диапазоны создаются при генерации отчета в каких-нибудь бухгалтерских приложениях.  Чтобы лист скопировать без этого окна «Конфликт имен», то вам необходимо в окне «Диспетчер имен» удалить все именованные диапазоны, которые там есть. Но, даже если вы удалите все эти именованные диапазоны, то этого будет не достаточно, так как существуют скрытые именованные диапазоны, которые не видны в окне «Диспетчер имен» и поэтому вам необходимо воспользоваться специальным VBA-макросом, который удалит все скрытые именованные диапазоны. Для того чтобы создать и запустить макрос, вам необходима для начала включить вкладку «Разработчик» на ленте. Для этого зайдите в меню параметры (смотрите рисунок 2), в окне параметров Excel в разделе «Настроить ленту» вам нужно включить вкладку «Разработчик» (смотрите рисунок 3).

Рисунок 2. Открытие окна "Параметры Excel"

Рисунок 3. Окно "Параметры Excel"

 Далее во вкладке разработчик нажмите кнопку «Visual Basic» (смотрите рисунок 4).

Рисунок 4. Вкладка "Разработчик" на ленте

В приложение “Microsoft Visual Basic for Applications” на панели проектов дважды нажмите левой кнопкой мыши по разделу «Эта книга» в проекте VBAProject (Книга1) (смотрите рисунок 5 цифра 1), то откроется окно ввода VBA-кода (смотрите рисунок 5 цифра 2). Скопируйте и вставьте в это окно следующий код, который я взял с этой страницы.


Рисунок 5. Окно приложения “Microsoft Visual Basic for Applications”

Sub do_loop_names()
    Dim vJunkName As Name
    Debug.Print ThisWorkbook.Names.Count
    
    For Each vJunkName In ThisWorkbook.Names
    vJunkName.Delete
    Next vJunkName
End Sub

Запустите данный код на исполнение (смотрите рисунок 5 цифра 3). После того как код отработает, все скрытые именованные диапазоны будут удалены и вы сможете копировать листы Excel без всяких проблем, и не будите получать окно «Конфликт имен».

На этой странице https://kompsekret.ru/q/why-does-excel-now-give-me-already-existing-name-range-warning-on-copy-sheet-24247/ вы можете взять другой пример VBA-кода для очистки именованного диапазона.

понедельник, 30 августа 2021 г.

Ошибка PCL при печати PDF документа

Так. Сам текст ошибки, которая печатается принтером я не видел. Как только эта ошибка повторится, обязательно её опишу. Сейчас опишу решение проблемы:

У разных принтеров и МФУ одна и та же проблема  проявляется по разному:

- У HP LJ 1320 он вместо печати нужного документа печатает станицу с текстом об ошибке
- У Xerox WC 3345 или Xerox VL B405 они выводят на дисплее МФУ, что неверный формат документа

Чтобы эти проблемы не появлялись, галочка "Выбрать источник бумаги по размеру страницы PDF" в окне "Печать"в программе Acrobat Reader не должна стоять (Смотрите рисунок 1).


Рисунок 1. Окно "Печать" в программе Acrobat Reader

Все эти проблемы связаны с тем, что на принтер и МФУ посылают на печать страницы размеры, которых превышают размеры стандартного формата А4. Я так понял эти принтеры и МФУ не умеют самостоятельно масштабировать изображение на бумагу под размеры формата А4. Если в процессе будут появляться принтеры и МФУ с такими же проблемами, то список буду пополнять.

суббота, 27 марта 2021 г.

Отличные бесплатные программы для записи CD и DVD дисков, для сканирования в PDF

Small CD Writer - портативная бесплатная программа для записи CD и DVD дисков (смотрите рисунок 1). Скачать можно по следующей ссылке: http://avt-lab.ru/scdwriter.htm

Рисунок 1. Внешний вид программы Small CD Writer

WinScan2PDF - портативная бесплатная программа для сканирования и сохранения отсканированных документов в многостраничный PDF документ (смотрите рисунок 2). Скачать можно по следующей ссылке: http://www.softwareok.com/?seite=Microsoft/WinScan2PDF

Рисунок 2. Внешний вид программы WinScan2PDF

NAPS2 - бесплатная программа для сканирования и сохранения отсканированных документов в многостраничный PDF документ (смотрите рисунок 3). Скачать можно по следующей ссылке: https://www.naps2.com/

Рисунок 3. Внешний вид программы NAPS2

Сканирование через USB сканер по сети с других компьютеров при помощи программы NAPS2

Есть такая полезная программа для сканирования по USB или по сети, в зависимости от того как подключен у вас сканер, называется она NAPS2. Преимущества программы:

- Программа относится к открытому исходному коду. Исходники находятся на Github (https://github.com/cyanfish/naps2). Бинарные файлы для Windows качаем от сюда (https://www.naps2.com/).

- Программа поддерживает, как и Twain устройства, так и Wia.

- Умеет сканировать как с автоподатчика так и со стекла.

- Умеет сохранять сканированные страницы в многостраничный PDF документ.

Так же на GitHub есть программа, называется она NAPS2 Remote Scan Server, данная программа позволяет сканировать через USB сканер по сети с других компьютеров. Для своей работы программа использует консольное приложение NAPS2 и профиль сканирования, который у вас настроен по умолчанию. Качаем эту программу с Github (https://github.com/corred/NAPS2-Remote-Scan).

Как эту программу настроить? Если вы скачали программу Naps2 remote scan, то распакуйте ее из архива, скопируйте из папки Win32/Debug два файла NAPS2RemoteScan.exe и index.html в папку C:\Scan.  Предоставьте к папке C:\Scan общий доступ по сети для всех пользователей или для конкретного пользователя с правами на запись и чтение. Т.е. общая папка у нас будет расположена по следующему пути \\pc01\Scan . Далее на компьютере pc01 запускаем программу NAPS2RemoteScan.exe, прописываем путь до консольной программы NAPS2.Console.exe (Смотрите рисунок 1). Сканер по USB подключен к pc01. Уточню, что pc01 имеет IP адрес 192.168.1.2, а pc02 имеет IP адрес 192.168.1.3

Рисунок 1. Внешний вид программы NAPS2 Remote Scan

Как проверить работу USB сканера через сеть? Идем на компьютер pc02, открываем браузер в адресной строке вводим http://pc01 и у вас должна открыться страница со ссылкой на сканирование (смотрите рисунок 2). 

Рисунок 2. Удаленный запуск сканирования на другой компьютер по USB

Нажимаете ссылку Scan и после этого запустится сканирование на USB сканер подключенного к компьютеру pc01. Сканирование будет осуществляться программой NAPS2, профилем, установленным по умолчанию. Если у вас в профиле сканирование настроено брать листы сканирования из автоподатчика, то листы должны лежать в автоподатчике, иначе сканирование не заработает. Если вам нужно сканировать со стекла, то настройте профиль сканирования на работу со стекла и установите его по умолчанию (смотрите рисунок 3). 

Рисунок 3. Создание профиля сканирования в программе NAPS2

Все отсканированные страницы будут сохранятся в формате jpg в общей папке Scan и в подпапках с именами соответствующие IP адресам компьютеров с которых производилось сканирование. Например, для компьютера pc02 подпапка будет называться 192.168.1.3. Сетевой путь будет такой \\pc01\Scan\192.168.1.3 .

Можно ли настроить представленный механизм сканирования на сохранение всех отсканированных страниц в многостраничный PDF файл? Наверное, можно, но я не пробовал настраивать. Если у кого получиться, то отпишитесь в комментариях. 

Далее вам нужно найти программу в Интернет, которая умеет объединять картинки из jpg в один pdf файл.

вторник, 21 июля 2020 г.

[VBA][LibreOffice] Макрос вывода списка файлов определенной папки по маске

Напишем пример VBA программы в LibreOffice 6.4.4.2. Я уже писал о том, как написать простой макрос в этой статье. Перейдите по этой ссылке и посмотрите с чего начать. Наш макрос будет читать имена файлов в выбранной директории и выводить их в ячейки таблицы по определенной маске. Маска будет задаваться в коде макроса. Директория будет выбираться при помощи стандартного диалога Windows. Код сопровождается необходимыми комментариями, что бы понять работу макроса.

Рисунок 1. Результат работы макроса

Код макроса:
Sub Main
 Dim oFP As Object
 Dim DirName As String
 'Вызываем диалог выбора директории и сохраним путь папки в переменной DirName
 DirName = ""
 oFP = CreateUnoService("com.sun.star.ui.dialogs.FolderPicker")
 oFP.DisplayDirectory = ConvertToURL("C:\")
 oFP.Description = "Select a directory"
 oFP.Title = "Select the backup directory"
 
 If oFP.execute = _
  com.sun.star.ui.dialogs.ExecutableDialogResults.OK Then
  DirName = oFP.Directory
 End If
 
    Dim TableHeaderRangeData(0, 2)
    Dim TableHeaderRangeObject as Object
    Dim MainDocument as Object 
    MainDocument = ThisComponent
    'Очистим весь контент
    MainDocument.CurrentSelection.ClearContents(1 OR 2 OR 4 OR 8 OR 16 OR 32 OR 64 OR 128 OR 256 OR 512)
    'Сформируем заголовки таблицы (Сканируемая папка, Фильтр, Список файлов)
    TableHeaderRangeData(0, 0) = "Scan Folder"
    TableHeaderRangeData(0, 1) = "Filter"
    TableHeaderRangeData(0, 2) = "List Files"
 
    TableHeaderRangeObject = MainDocument.Sheets(0).getCellRangebyName("A1:C1")
    TableHeaderRangeObject.charWeight = com.sun.star.awt.FontWeight.BOLD
    TableHeaderRangeObject.setDataArray(TableHeaderRangeData)
 'Сформируем маску
 ReDim TableHeaderRangeData(0, 1)
 Dim strMaskSearch as String
 strMaskSearch = "*.pdf*"
 'Выведем путь папки и фильтр в ячейки таблицы
 TableHeaderRangeData(0, 0) = DirName
    TableHeaderRangeData(0, 1) = strMaskSearch 
 TableHeaderRangeObject = MainDocument.Sheets(0).getCellRangebyName("A2:B2") 
 TableHeaderRangeObject.charWeight = com.sun.star.awt.FontWeight.NORMAL
 TableHeaderRangeObject.setDataArray(TableHeaderRangeData)
 'Выведем список файлов в ячейки таблицы
 Dim FileName As String
 Dim iCounter As Integer
 FileName = Dir(DirName & "/" & strMaskSearch, 0)
 iCounter = 0
 Do While (FileName <> "")
     iCounter = iCounter + 1
 
  MainDocument.Sheets(0).getCellByPosition(2,iCounter).String = ""
     MainDocument.Sheets(0).getCellByPosition(2,iCounter).String = FileName
     FileName = Dir()
 Loop
End Sub

вторник, 23 июня 2020 г.

Windows и Profilelist

На случай если профиль пользователя по какой-то причине повредился (не нажимается кнопка «Пуск» или еще какие-то проблемы), то можно попробовать пересоздать профиль пользователя. Посмотреть список SID профилей можно в реестре Windows по пути

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList

В ветке Profilelist ищем SID профиля, который мы хотим пересоздать. SID текущего пользователя можно узнать командной:

Whoiam /user

Переименовываем необходимый SID профиля. Далее остается зайти под профилем, который мы пересоздаем и скопировать данные из старого профиля, в новый, только что созданный.


понедельник, 15 июня 2020 г.

Попытка установки .NET Framework 3.5 на Windows 10 64 bit выдает ошибку 0x800F0950

Столкнулся с такой проблемой, что программа AutoCad 2011 для своей установки требовала установку .NET Framework 3.5. При попытке установить .NET Framework 3.5 выдается ошибка 0x800F0950 и установка не происходит. Проблему не решила и попытка скачивания и установки .NET Framework 3.5 с сайта Microsoft. Так же не удалось установить .NET Framework 3.5 через включение и отключение компонентов Windows 10 (Смотрите рисунок 1).

Рисунок 1. Попытка установки .NET Framework 3.5 через "Включение и отключение компонентов Windows"

Проблему получилось решить воспользовавшись командным инструментом под названием Deployment Image Servicing and Management (DISM). Идея взята с сайта Microsoft. Скачайте образ полной версии Windows 10. Из это образа мы будем устанавливать .NET Framework 3.5.
Установите программу Daemon Tools Lite или Virtual CloneDrive и смонтируйте образ Windows 10. Убедитесь, что в смонтированном образе есть каталог sources\sxs. В каталоге должны находится пакеты .NET Framework 3.5 (Смотрите рисунок 2). 

Рисунок 2. Обзор смонтированного образа Windows 10


Далее необходимо запустить командную строку и выполнить команду на установку (Смотрите рисунок 3).

DISM /Online /Enable-Feature /FeatureName:NetFx3 /All /LimitAccess /Source:d:\sources\sxs


Рисунок 3. Выполнение команды на установку .NET Framework 3.5

среда, 18 декабря 2019 г.

Смартфон определяется как MTP - устройство с ошибкой под Windows 8.1

Недавно столкнулся с проблемой, что при подключении смартфона Samsung J3 к ноутбуку с Windows 8.1 определялся как "MTP-устройство" с восклицательным знаком. При этом в проводнике Windows данный смартфон не виден как носитель информации, чтобы на него можно было зайти. Это означает, что Windows попыталась использовать не подходящий MTP драйвер. В частности, в свойствах этого драйвера можно увидеть код ошибки 10 (Запуск этого устройства не возможен) или код ошибки 1 (Неправильная секция установки службы в этом INF-файле).

Решением данной проблемы оказалось простым. Необходимо в «Диспетчере устройств» на MTP - устройстве щелкнуть правой кнопкой мыши и выбрать пункт меню «Обновить драйвер». Далее нажать кнопку «Выполнить поиск драйверов на этом компьютере», потом нажать кнопку «Выбрать драйвер из списка уже установленных драйверов». Далее у вас может запросится тип устройств, нужно выбрать "Портативные устройства", потом на следующем шаге выбрать драйвер который называется "USB - устройство MTP". После этого смартфон должен определится как надо, по названию. Например Samsung J3. В проводке Windows он будет доступен как носитель информации.

Идея позаимствована с этих сайтов:
1. GeekTeam - https://geekteam.pro/chto-delat-esli-telefon-ne-opredelyaetsya-kak-mtp-ustroystvo-na-windows-10-8-1-8-7/
2. DriverFresh - https://driverfresh.com/ru/info/mtp-device.html

вторник, 20 августа 2019 г.

Диспетчер печати Windows 10 останавливается

Столкнулся с такой проблемой, что «Диспетчер печати» под управлением Windows 10 64 bit самопроизвольно останавливался. Как следствие все принтера и виртуальные принтера становились недоступны.  Решение данной проблемы удалось найти в интернете на этом ресурсе http://setupwindows.ru/setup/sluzhba-dispetcher-pechati. В статье описывается несколько методов решения проблемы. Способ, который помог мне решить проблему, это произвести очистку папки «Диспетчера печати». Эта папка расположена C:\Windows\System32\spool\PRINTERS. Каким-то образом пользователь умудрился отправить на печать файл объемом 125 Мб и имеющий формат Shockwave Flash Object (SWF). По этой причине «Диспетчер печати» прекращал свою работу и останавливался.

четверг, 8 августа 2019 г.

Автоматический вход на рабочий стол под доменной учетной записью в Windows 7, 10

Иногда бывает необходимость настроить на Windows 7, 10 автоматический вход на рабочий стол под доменной учетной записью. Для этого откройте реестр Windows, перейдите в раздел реестра HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\Currentversion\Winlogon и изменить значения следующих строковых параметров:

"AutoAdminLogon"="1"
"DefaultDomainName"="DOMAIN"
"DefaultUserName"="User"
"DefaultPassword"="Password"

Строковые параметры DefaultUserName и DefaultPassword по умолчанию отсутствуют, их необходимо создать вручную. Потом необходимо перезагрузить компьютер.

суббота, 27 июля 2019 г.

Прячем VBS-скрипты в ресурсном файле Windows в приложениях на Delphi 7

Внимание! Данный пример был разработан в среде программирования Delphi 7 и под управлением Windows XP.

Я уже писал про совместное использование Delphi 7 и VBScripts, для чего это связка нужна и как ее использовать (http://notidealrunner.blogspot.com/2019/05/vbscripts-delphi.html). Возможно, вы тоже нашли в этом пользу использования. Единственное что мне не понравилось это то, что наши VBS-скрипты всегда необходимо таскать вместе с исполняемой частью программы. А что будет, если при выполнение программы, необходимых скриптов не окажется? Программа откажется работать или будет работать не так как вы задумывали. Или, если вы не хотите демонстрировать программный код VBS-скриптов, распространяемые вместе с исполняемой программой, то вам понадобится спрятать, каким-то образом, это код. Долго я пытался я найти решение как лучше спрятать VBS-скрипты. Единственным решением которое я нашел, это засунуть VBS-скрипт в ресурсный файл Windows и скомпоновать его с исполняемой программой. Единственный минус данного способа, это увеличение объема исполняемой программы, но и это можно обойти, я так предполагаю, скомпоновав данный ресурсный файл с динамической линкуемой dll – библиотекой, но в этом посте мы не будем рассматривать это способ.

Давайте приступим к созданию нового проекта. Создайте в среде Delphi 7 проект с именем p6.dpr. Основной модуль программы сохраните под именем p6_unit1.pas. Файл скрипта назовите sum.vbs и сохраните его в корне папки проекта p6. Код скрипта будет такой-же как и в проекте по этому посту http://notidealrunner.blogspot.com/2019/05/vbscripts-delphi.html.

On Error Resume Next
Function Sum(a,b)
 Sum = a + b
End function

Далее нам необходимо в каталоге проекта p6 создать файл ресурсов компиляции с именем sum.rc. Содержимое файла компиляции будет следующее:

VBSCRIPTS RCDATA sum.vbs

То есть мы в качестве ресурсов нашего приложения указываем наш файл VBS-скрипта.

Файл ресурсов компиляции sum.rc необходимо скомпилировать при помощи компилятора ресурсов brcc32.exe, этот компилятор ресурсов расположен в каталоге, где расположены бинарные файлы Delphi, у меня это каталог c:\Program Files\BORLAND\Delphi7\Bin\. Убедитесь, что данный каталог добавлен в переменную окружения Path. Запустите командную строку Windows, перейдите в каталог проекта и выполните команду компиляции ресурсов.

brcc32.exe sum.rc

В качестве результата в каталоге проекта вы получите файл ресурсов sum.RES, который необходимо в файле модуля p6_unit1.pas подключить. Смотрите код ниже:

var
  Form1: TForm1;
  ScriptString: String;

implementation

{$R *.dfm}
{$R sum.res}

На форму с именем Form1 необходимо перетащить компонент ScriptControl. Я уже писал, как установить данный компонент и задействовать его в проекте (Смотрите статью http://notidealrunner.blogspot.com/2019/05/vbscripts-delphi.html).

Перейдем к описанию кода проекта Delphi.


1. В обработчике создания формы мы обращаемся к потоку ресурсов нашего приложения типа TResourceStream с целью получить доступ к нашему пользовательскому ресурсу данных в формате RCDATA с именем VBSCRIPTS. Потом создаем пустой строковый поток типа TStringStream, в который мы будем копировать данные из потока ресурсов типа TResourceStream. Далее сохраняем данные из строкового потока в глобальную строковую переменную типа String с именем ScriptString. В конце работы обработчика мы освобождаем используемые потоки типа TResourceStream и типа TStringStream.

procedure TForm1.FormCreate(Sender: TObject);
var
  RS: TResourceStream;
  SS: TStringStream;
begin
  RS := TResourceStream.Create(hInstance, 'VBSCRIPTS', RT_RCDATA);
  SS := TStringStream.Create('');
  try
    SS.CopyFrom(RS, RS.Size);
    ScriptString := SS.DataString;
  finally
    FreeAndNil(SS);
    FreeAndNil(RS);
  end;
end;

2. Далее в обработчике нажатия кнопки мы загружаем скрипт из глобальной строковой переменной типа String с именем ScriptString и запускаем функцию Sum на исполнение.

procedure TForm1.Button1Click(Sender: TObject);
var
  varr, res:Variant;
  pPar:PSafeArray;
  SA : TSafeArrayBound;
  a: Integer;
  b: Integer;
  ValErrorCode: Integer;
begin
  Val(Edit1.Text, a, ValErrorCode);
  Val(Edit2.Text, b, ValErrorCode);
  try
    ScriptControl1.Language := 'VBScript';
    ScriptControl1.AddCode(ScriptString);
    SA.cElements := 2;
    pPar := SafeArrayCreate(varVariant, 1, SA);
    varr := VarArrayCreate([0, 1], varVariant);
    varr[0]:=a;
    varr[1]:=b;
    pPar:=PSafeArray(TVarData(varr).VArray);
    res:=ScriptControl1.Run('Sum',pPar);
    Label5.Caption := VarToStr(res);
  except
    on E: Exception do
      MessageDlg(PWideChar(E.Message), mtError, [mbOK], 0);
  end;
end;

3. Вот и все. Теперь исполняемый файл программы можно будет перемещать без VBS-скриптов.

Код модуля p6_unit1.pas

unit p6_unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, OleCtrls, MSScriptControl_TLB;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Button1: TButton;
    ScriptControl1: TScriptControl;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  ScriptString: String;

implementation

{$R *.dfm}
{$R sum.res}
uses ActiveX;

procedure TForm1.FormCreate(Sender: TObject);
var
  RS: TResourceStream;
  SS: TStringStream;
begin
  RS := TResourceStream.Create(hInstance, 'VBSCRIPTS', RT_RCDATA);
  SS := TStringStream.Create('');
  try
    SS.CopyFrom(RS, RS.Size);
    ScriptString := SS.DataString;
  finally
    FreeAndNil(SS);
    FreeAndNil(RS);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  varr, res:Variant;
  pPar:PSafeArray;
  SA : TSafeArrayBound;
  a: Integer;
  b: Integer;
  ValErrorCode: Integer;
begin
  Val(Edit1.Text, a, ValErrorCode);
  Val(Edit2.Text, b, ValErrorCode);
  try
    ScriptControl1.Language := 'VBScript';
    ScriptControl1.AddCode(ScriptString);
    SA.cElements := 2;
    pPar := SafeArrayCreate(varVariant, 1, SA);
    varr := VarArrayCreate([0, 1], varVariant);
    varr[0]:=a;
    varr[1]:=b;
    pPar:=PSafeArray(TVarData(varr).VArray);
    res:=ScriptControl1.Run('Sum',pPar);
    Label5.Caption := VarToStr(res);
  except
    on E: Exception do
      MessageDlg(PWideChar(E.Message), mtError, [mbOK], 0);
  end;
end;

end.

Код проекта p6.dpr

program p6;

uses
  Forms,
  p6_unit1 in 'p6_unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Код VBS-скрипта sum.vbs

On Error Resume Next

Function Sum(a,b)
 Sum = a + b
End function

вторник, 23 июля 2019 г.

Ошибка BSOD 0x0000006B: PROCESS1_INITIALIZATION_FAILED

Методов решения ошибки BSOD 0x0000006B в Интернет я нашел множество. Здесь я отмечу метод, который мне помог решить проблему. Данная проблема возникла на Windows 7 Corporate 32 bit. На сайте Microsoft (https://support.microsoft.com/ru-ru/help/981833/stop-error-code-0x0000006b-process1-initialization-failed-error-messag) пишут, что причина возникновения данной проблемы связана с повреждением файла Bootcat.cache или с изменением его размера. Как написано в статье по ссылке выше, решить проблему удалось путем загрузки c LiveDVD и удалением файла Bootcat.cache расположенный в папке %SystemRoot%\system32\codeintegrity и дальнейшей перезагрузки компьютера.  

среда, 3 июля 2019 г.

Настройка общего доступа и доступа по админским шарам с Windows XP на Windows 10

Недавно решил попробовать перекинуть файлы из (comp1) Windows XP на (comp2) Windows 10, ввел на comp1 в адресной строке проводника ip адрес comp2 (192.168.1.3) и получил сообщение об ошибке «Указанное сетевое имя более недоступно» (смотрите рисунок 1). 


Рисунок 1. Сообщение об ошибке "Указанное сетевое имя более недоступно" в Windows XP

При этом сам ip адрес 192.168.1.3 пингуется. На форуме oszone.net нашел решение, что необходимо на Windows 10 включить протокол SMB 1.0. Для этого необходимо запустить «Панель управления», отобразить «Все элементы панели управления», запустить «Программы и компоненты» (смотрите цифру 1 на рисунке 2). Потом нажать на кнопку «Включение или отключение компонентов Windows» (смотрите цифру 2 на рисунке 2). 

Рисунок 2. Открытие окна "Компоненты Windows" в Windows 10

В окне «Компоненты Windows» найти и выбрать опции «Поддержка общего доступа к файлам SMB 1.0/CIFS», «Клиент SMB 1.0/CIFS», «Сервер SMB 1.0/CIFS» (смотрите рисунок 3). 

Рисунок 3. Включение поддержки SMB 1.0 в Windows 10

После этого необходимо перезагрузить компьютеры comp1 и comp2. Пробуем еще раз на компьютере comp1 ввести в адресной строке проводника ip адрес comp2 (192.168.1.3). Видим, что данное сообщение не появилось и открылось окно с общими папками, общими принтерами на компьютере comp2 с Windows 10, или пустое окно как у меня, если общих папок и общих принтеров нет (смотрите рисунок 4). 

Рисунок 4. Доступ к сетевым ресурсам Windows 10 из Windows XP

Мы рассмотрели настройку, касающуюся общего доступа на Windows 10. 

Теперь рассмотрим настройку доступа по админским шарам. Пробуем ввести на comp1 в адресной строке проводника ip адрес comp2 (\\192.168.1.3) вместе с админской шарой C$ (\\192.168.1.3\C$) и получим окно ввода логина и пароля (например: логин user, пароль user) пользователя (смотрите рисунок 5). 

Рисунок 5. Запрос логина и пароля при заходе на Windows 10 по админской шаре

Данный пользователь должен быть заведен на компьютере comp2 и иметь на данном компьютере права локального администратора, т.е. учетная запись должна находиться в группе «Администраторы». Пробуем ввести логин user и пароль user и получим сообщение о том что доступ запрещен. Почему так вышло, спросите вы? Ответ я нашел на этих сайтах ссылка 1, ссылка 2. Вкратце расскажу своими словами. Это потому-что Microsoft придумали новую развлекаловку под названием Remote UAC. Для учетных записей входящих в группу локальных администраторов доступ в Windows 10 закрыт и его необходимо открывать через реестр (То есть через реестр мы должны отключить этот Remote UAC). Правда для встроенной учетной записи локального администратора (Администратор) в Windows 10 доступ к админским шарам есть. Данная учетная запись по умолчанию отключена, поэтому ее нужно включить и задать пароль. Чтобы открыть доступ для учетных записей входящих в группу локальных администраторов (например, логин user) необходимо в Windows 10 запустить редактор реестра (редактор реестра запускается с правами администратора). Перейти в раздел реестра HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System (смотрите цифры 1,2 рисунка 6).

Рисунок 6. Отключаем Remote UAC через создания ключа реестра

В этом разделе создать параметр типа DWORD (32-бита) (смотрите цифру 3 рисунка 6) назвать его LocalAccountTokenFilterPolicy и присвоить ему значение 1 (смотрите цифру 4 рисунка 6). После этого необходимо перезагрузить компьютеры comp1 и comp2. Пробуем еще раз на компьютере comp1 ввести в адресной строке проводника ip адрес comp2 (\\192.168.1.3) вместе с админской шарой C$ (\\192.168.1.3\C$). В возникшем окне ввода логина и пароля вбиваем, например логин user, пароль user, видим, что доступ работает и нам открылся в проводнике список папок диска C на Windows 10 (смотрите рисунок 7). 

Рисунок 7. Открытая админская шара диска C$ Windows 10 из Windows XP

пятница, 21 июня 2019 г.

Получение списка адресов электронной почты из Active Directory в Delphi 7

Под этим способом подразумевается получение списка адресов электронной почты из Active Directory с использованием скрипта VBScript и передача этого списка в Delphi программу для дальнейшей обработки.

Теперь приступим к реализации этого способа.

Нам необходимо разработать VBScript для получения списка адресов электронной почты из Active Directory. Назовём этот скрипт main.vbs.

Ниже показан код скрипта main.vbs:
function GetADMailString()

On Error Resume Next

Dim adoCommand, adoConnection, strBase, strFilter, strAttributes
Dim objRootDSE, strDNSDomain, strQuery, adoRecordset, strMail
Dim strResult

' Setup ADO objects.
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"
Set adoCommand.ActiveConnection = adoConnection

' Search entire Active Directory domain.
Set objRootDSE = GetObject("LDAP://RootDSE")

If Err.Number <> 0 Then
 GetADMailString = ""
Else
 
 strDNSDomain = objRootDSE.Get("defaultNamingContext")
 strBase = "<LDAP://" & strDNSDomain & ">"

 ' Filter on user objects.
 strFilter = "(&(objectCategory=person)(objectClass=user) (mail=*))"

 ' Comma delimited list of attribute values to retrieve.
 strAttributes = "mail"

 ' Construct the LDAP syntax query.
 strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
 adoCommand.CommandText = strQuery
 adoCommand.Properties("Page Size") = 100
 adoCommand.Properties("Timeout") = 30
 adoCommand.Properties("Cache Results") = False

 ' Run the query.
 Set adoRecordset = adoCommand.Execute 
 
 ' Enumerate the resulting recordset.
 Do Until adoRecordset.EOF
  ' Retrieve values and display.
  strMail = adoRecordset.Fields("mail").Value
  
  If strMail <> "" Then
   strResult = strResult & strMail & vbCrLf
  End if
  ' Move to the next record in the recordset.
  adoRecordset.MoveNext
 Loop
 ' Clean up.
 adoRecordset.Close
 adoConnection.Close
 
 GetADMailString = strResult
end if

end function

Теперь пройдемся вкратце по коду скрипта main.vbs и функции GetADMailString

1. Объявляем необходимые переменные для работы скрипта, смотрите часть кода ниже:

On Error Resume Next

Dim adoCommand, adoConnection, strBase, strFilter, strAttributes
Dim objRootDSE, strDNSDomain, strQuery, adoRecordset, strMail
Dim strResult


2. Осуществляем подключение к Active Directory

' Setup ADO objects.
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"
Set adoCommand.ActiveConnection = adoConnection


3. Получаем домен по умолчанию

' Search entire Active Directory domain.
Set objRootDSE = GetObject("LDAP://RootDSE")

If Err.Number <> 0 Then
    GetADMailString = ""
Else
   
    strDNSDomain = objRootDSE.Get("defaultNamingContext")
    strBase = "<LDAP://" & strDNSDomain & ">"


4. Формируем запрос и выполняем его для получения строки со всеми почтовыми адресами из Active Directory

' Filter on user objects.
    strFilter = "(&(objectCategory=person)(objectClass=user) (mail=*))"

    ' Comma delimited list of attribute values to retrieve.
    strAttributes = "mail"

    ' Construct the LDAP syntax query.
    strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
    adoCommand.CommandText = strQuery
    adoCommand.Properties("Page Size") = 100
    adoCommand.Properties("Timeout") = 30
    adoCommand.Properties("Cache Results") = False

    ' Run the query.
    Set adoRecordset = adoCommand.Execute   


5. Формируем строку из всех почтовых адресов

' Enumerate the resulting recordset.
    Do Until adoRecordset.EOF
        ' Retrieve values and display.
        strMail = adoRecordset.Fields("mail").Value
       
        If strMail <> "" Then
            strResult = strResult & strMail & vbCrLf
        End if
        ' Move to the next record in the recordset.
        adoRecordset.MoveNext
    Loop


6. Закрываем соединение с Active Directory и возвращаем результат выполнения скрипта main.vbs

    ' Clean up.
    adoRecordset.Close
    adoConnection.Close
   
    GetADMailString = strResult
end if

end function


Так со скриптом main.vbs мы разобрались. Теперь нам необходимо передать результат выполнения скрипта в нашу программу и сохранить все почтовые адреса в текстовый файл.

Создайте проект на в среде Delphi 7 и сохраните его под именем p22.dpr. Основной модуль программы сохраните под именем p22_unit1.pas. Файл скрипта main.vbs должен лежать в той же директории, что и файл проекта, и исполняемый файл программы.

На форму с именем Form1 необходимо перетащить компонент ScriptControl. Я уже писал, как установить данный компонент и задействовать его в проекте (Смотрите статью http://notidealrunner.blogspot.com/2019/05/vbscripts-delphi.html).  Вкратце опишу основные моменты работы программы.

1. В обработчике показа формы мы проверяем существует ли скрипт main.vbs, открываем его на чтение, считываем его построчно.

procedure TForm1.FormShow(Sender: TObject);
var
    ScriptFile: TextFile;
    Script, LineScript: String;
    SA : TSafeArrayBound;
    pPar:PSafeArray;
    res:Variant;
begin
    AssignFile(ScriptFile,'main.vbs');
    Reset(ScriptFile);
    if IOResult <> 0 then
    begin
      MessageBox(0,'File access error.','Error',0);
      exit;
    end;

    while not EOF(ScriptFile) do
    begin
      readln(ScriptFile, LineScript);
      Script := Script + LineScript+#13+#10;
    end;
    CloseFile(ScriptFile);


2. Далее выполняем скрипт mian.vbs и функцию GetADMailString. Получаем результат выполнения скрипта и сохраняем в строковую переменную strTemp.   

    try
      ScriptControl1.Language := 'VBScript';
      ScriptControl1.AddCode(Script);
      SA.cElements := 0;
      pPar := SafeArrayCreate(varVariant, 1, SA);
      res:=ScriptControl1.Run('GetADMailString',pPar);
      strTemp := VarToStr(res);
    except
      on E: Exception do
        MessageDlg(PWideChar(E.Message), mtError, [mbOK], 0);
    end;


3. Далее передаем строковую переменную strTemp в процедуру сохранения в текстовый файл.

StrToFile('Mail.txt', strTemp); 

Ниже код процедуры сохранения строки в файл (Код процедуры взят с сайта https://stackoverflow.com/questions/7752273/how-to-save-a-string-to-a-txt-file-in-delphi).

procedure StrToFile(const FileName, SourceString : string);
var
  Stream : TFileStream;
begin
  Stream:= TFileStream.Create(FileName, fmCreate);
  try
    Stream.WriteBuffer(Pointer(SourceString)^, Length(SourceString));
  finally
    Stream.Free;
  end;
end;


Вот и все. Список всех электронных почтовых адресов будет сохранен в файл Mail.txt в директории проекта. Ниже показан код проекта полностью.

unit p22_unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, OleCtrls, MSScriptControl_TLB, ActiveX;

type
  TForm1 = class(TForm)
    ScriptControl1: TScriptControl;
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure StrToFile(const FileName, SourceString : string);
var
  Stream : TFileStream;
begin
  Stream:= TFileStream.Create(FileName, fmCreate);
  try
    Stream.WriteBuffer(Pointer(SourceString)^, Length(SourceString));
  finally
    Stream.Free;
  end;
end;

var
  strTemp: String;

procedure TForm1.FormShow(Sender: TObject);
var
    ScriptFile: TextFile;
    Script, LineScript: String;
    SA : TSafeArrayBound;
    pPar:PSafeArray;
    res:Variant;
begin
    AssignFile(ScriptFile,'main.vbs');
    Reset(ScriptFile);
    if IOResult <> 0 then
    begin
      MessageBox(0,'File access error.','Error',0);
      exit;
    end;

    while not EOF(ScriptFile) do
    begin
      readln(ScriptFile, LineScript);
      Script := Script + LineScript+#13+#10;
    end;
    CloseFile(ScriptFile);

    try
      ScriptControl1.Language := 'VBScript';
      ScriptControl1.AddCode(Script);
      SA.cElements := 0;
      pPar := SafeArrayCreate(varVariant, 1, SA);
      res:=ScriptControl1.Run('GetADMailString',pPar);
      strTemp := VarToStr(res);
    except
      on E: Exception do
        MessageDlg(PWideChar(E.Message), mtError, [mbOK], 0);
    end;

    StrToFile('Mail.txt', strTemp);
end;

end.