Практика
Написание DLL для использования в rundll32

:: Меню ::
:: На главную ::
:: FAQ ::
:: Заметки ::
:: Практика ::
:: Win API ::
:: Проекты ::
:: Скачать ::
:: Секреты ::
:: Ссылки ::

:: Сервис ::
:: Написать ::

:: MVP ::

:: RSS ::

Яндекс.Метрика


rundll32.exe – утилита командной строки, которая позволяет запускать некоторые функции, заложенные в DLL-файлах. Она входит в состав всех операционных систем Windows 32bit и 64bit.

Работать с ней из самой командной строки не самый удобный способ, гораздо удобнее создать *.bat файл или ярлык (в последнем случае можно выполнять команду и по нажатию горячих клавиш). У обладателей клавиатур с настраиваемыми клавишами есть еще одна, самая удобная, на мой взгляд, возможность - "посадить" команду на одну из таких клавиш.

В качестве примера работы с rundll32 рассмотрим следующую команду, которая прячет курсор мыши:

rundll32 user32,SetCursorPos

В интернете можно найти много других готовых команд, которые может выполнить с помощью rundll32. А в этой статье мы научимся создавать свои.

Для начала разберемся поподробнее, что же такое rundll32. Программа rundll32 загружает и выполняет 32-разрядные библиотеки. Эта программа позволяет вызывать функции только из тех библиотек, при разработке которых была реализована подобная возможность. Если при запуске программы rundll32 указать в качестве аргумента библиотеку DLL неподдерживаемого типа, программа завершит работу, при этом сообщение об ошибке не отображается.

При создании пользовательской библиотеки в нее необходимо поместить функцию <EntryPoint> со следующим прототипом

// ANSI
procedure EntryPoint( hWnd: Integer; hInstance: Integer; lpCmdLine: PAnsiChar; nCmdShow: Longint ); export; stdcall;
// UNICODE
procedure EntryPoint( hWnd: Integer; hInstance: Integer; lpCmdLine: PWideChar; nCmdShow: Longint ); export; stdcall;

Функции, являющейся точкой входа для программы rundll32, передаются следующие параметры:
  • hWnd — идентификатор окна, который должен указываться в качестве родительского окна при создании окон в функциях загружаемой библиотеки;
  • hInstance — заголовок экземпляра выбранной библиотеки;
  • lpCmdLine — командная строка, передаваемая библиотеке. Данная строка представляет собой последовательность символов, завершающуюся символом с кодом 0 (ноль);
  • nCmdShow — режим отображения окон выбранной библиотеки.
Посмотрим на пример команды, запускающей средство Rundll:

rundll32 SetupX.dll,InstallHinfSection 132 c:\Windows\Inf\Shell.inf

При выполнении данной команды средство rundll32 вызовет функцию InstallHinfSection(), находящуюся в библиотеке Setupx.dll, и передаст ей следующие параметры:
  • hWnd = (заголовок родительского окна);
  • hInstance = параметр HINSTANCE библиотеки SetupX.dll;
  • lpCmdLine = "132 c:\Windows\Inf\Shell.inf";
  • nCmdShow = (данные, передаваемые функции CreateProcess).
Следует обратить внимание на то, что функция <EntryPoint> (в приведенном выше примере — функция InstallHinfSection()) должна самостоятельно выполнять анализ командной строки и обрабатывать отдельные ее аргументы.

Использование rundll32.exe в операционных системах линейки Windows 9x несколько отличается от использования в операционных системах линейки Windows NT. Это сделано для корректной обработки командных строк, содержащих символы в формате UNICODE.

Операционные системы линейки Windows NT пытаются получить значение GetProcAddress для функции <EntryPoint>W. Если данная точка входа найдена, операционная система считает, что используется прототип с поддержкой UNICODE (различие ANSI и UNICODE прототипа заключается в параметре lpszCmdLine). Если операционная система не находит функцию <EntryPoint>W, то Windows NT пытается определить значение GetProcAddress для функций <EntryPoint>A и <EntryPoint>. Если одна из этих функций существует, она будет рассматриваться как точка входа ANSI и будет обрабатываться таким же образом, как в операционных системах линейки Windows 9x. Поэтому, если пользовательская разрабатываемая нами DLL должна работать в Windows 9x со строками ANSI и в Windows NT со строками UNICODE, необходимо экспортировать функции EntryPointW и EntryPoint. В операционных системах линейки Windows NT будет вызываться функция EntryPointW, которой будет передаваться строка в формате UNICODE, а в Windows 9x будет вызываться функция EntryPoint, которой будет передаваться строка в формате ANSI.

Теперь перейдем к практике, напишем библиотеку, реализующую как ANSI, так и UNICODE вариант функции, и как следствие работающей как под Windows 9x, так и под Windows NT. Суть этой библиотеки заключается в том, что она изменяет текст на контроле активного окна, имеющего фокус.

library send32;

uses
  Windows, Messages, SysUtils, Classes;

{$R *.res}

function GetFocusedWindow: HWND;
var
  CurrThID, ThID: DWORD;
begin
   Result := GetForegroundWindow;
   if Result <> 0 then
   begin
      CurrThID := GetCurrentThreadId;
      ThID := GetWindowThreadProcessId( Result, nil );
      Result := 0;
      if CurrThID = ThId then
         Result := GetFocus
      else
      begin
         if AttachThreadInput( CurrThID, ThID, True ) then
         begin
            Result := GetFocus;
            AttachThreadInput( CurrThID, ThID, False );
         end;
      end;
   end;
end;

procedure InsertText( hWnd: Integer; hInstance: Integer; lpCmdLine: PAnsiChar; nCmdShow: Longint ); export; stdcall;
var
  Wnd: THandle;
begin
   if ParamCount = 2 then
   begin
      Wnd := GetFocusedWindow;
      if Wnd <> 0 then
         SendMessageA( Wnd, WM_SETTEXT, 0, LParam( PAnsiChar( ParamStr( 2 ) ) ) );
   end;
end;

procedure InsertTextW( hWnd: Integer; hInstance: Integer; lpCmdLine: PWideChar; nCmdShow: Longint ); export; stdcall;
var
  Wnd: THandle;
begin
   if ParamCount = 2 then
   begin
      Wnd := GetFocusedWindow;
      if Wnd <> 0 then
         SendMessageW( Wnd, WM_SETTEXT, 0, LParam( PWideChar( ParamStr( 2 ) ) ) );
   end;
end;

exports
  InsertText,
  InsertTextW;

begin
end.

Библиотека экспортирует 2 функции - InsertText, которая будет вызываться в операционных системах семейства Windows 9x, и InsertTextW, которая будет вызываться в операционных системах семейства Windows NT. Чтобы воспользоваться данной командой запрограммируем кнопку на клавиатуре, не забыв скопировать скомпилированную библиотеку в "%WINDIR%\System32\".


А вот как выглядит результат ее работы:


Все же в настоящее время операционки семейства Windows 9x встречаются довольно редко, и добавлять их поддержку в библиотеку следует в том случае, если вы планируете распространять свою библиотеку. Для себя же (или для внутреннего использования в рамках небольшого предприятия), как мне кажется, можно обойтись поддержкой только систем линейки Windows NT. В качестве примера приведу исходный код библиотеки, с помощью которой я открываю/закрываю лоток своего DVD привода (до кнопки на самом приводе мне лень тянуться :)).

library cd32;

uses
  SysUtils, Classes, Types, MMSystem, ShellAPI, Math;

{$R *.res}

function Open_CD( Drive: Char; Open: Boolean ): boolean;
var
  Res: MciError;
  OpenParm: TMCI_Open_Parms;
  Flags: DWord;
  S: string;
  DeviceID: Word;
begin
   Result := False;
   S := Drive + ':';
   Flags := mci_Open_Type or mci_Open_Element;
   with OpenParm do
   begin
      dwCallback := 0;
      lpstrDeviceType := 'CDAudio';
      lpstrElementName := PChar( S );
   end;
   Res := mciSendCommand( 0, mci_Open, Flags, Longint( @OpenParm ) );
   if Res <> 0 then
      Exit;
   DeviceID := OpenParm.wDeviceID;
   try
      Res := mciSendCommand( DeviceID, MCI_SET, IfThen( Open, MCI_SET_DOOR_OPEN, MCI_SET_DOOR_CLOSED ), 0 );
      if Res = 0 then
         Exit;
      Result := True;
   finally
      mciSendCommand( DeviceID, mci_Close, Flags, Longint( @OpenParm ) );
   end;
end;

procedure OpenCDW( hWnd: Integer; hInstance: Integer; lpCmdLine: PChar; nCmdShow: Longint ); export; stdcall;
begin
   if ParamCount = 3 then
      Open_CD( ParamStr( 2 )[1], StrToBool( ParamStr( 3 ) ) );
end;

exports
  OpenCDW;

begin
end.

Данная библиотека экспортирует 1 функцию для открытия/закрытия лотка привода, которая принимает 2 параметра - букву этого самого привода, и команду (true - открыть, false - закрыть). Снова не забываем скопировать DLL в "%WINDIR%\System32\" и запрограммировать пару кнопок на клавиатуре. Вот как это выглядит у меня:


На этом все, удачного вам программирования!

.: Пример к данной статье :.


При использовании материала - ссылка на сайт обязательна