Для начала нужно добавить к проекту следующие библиотечные файлы: dinput8.lib, dxguid.lib. Заголовочный файл для DirectInput следующий: dinput.h.

Инициализация DirectInput включает следующие шаги:
Получение интерфейса IDirectInput8.
Создание устройства.
Установка формата данных.
Установка уровня взаимодействия с операционной системой.
Получение доступа к устройству ввода.

После инициализации программа сможет принимать ввод от устройства.

1. Создание объекта DirectInput. Функция DirectInput8Create.

Прежде всего необходимо получить указатель на интерфейс IDirectInput8. Этот интерфейс является основным в DirectInput и c помощью него создаются устройства (клавиатура, мышь). Для создания объекта DirectInput в функцию DirectInput8Create нужно передать указатель на интерфейс IDirectInput8 (4-ый аргумент):

HRESULT DirectInput8Create(
HINSTANCE hinst,    // экземпляр приложения
DWORD dwVersion,    // версия DirectInput
REFIID riidltf,     // какая-то COM'овская фигня
LPVOID * ppvOut,    // указатель  на IDirectInput8
LPUNKNOWN pUnkOuter // тоже относится  к COM
);

Как мы видим, функция DirectInput8Create сложнее аналогичной функции из Direct3D (Direct3DCreate9).

hinst
Экземпляр приложения.

dwVersion
Версия DirectInput. Всегда передавайте DIRECTINPUT_VERSION.

riidltf
Уникальный идентификатор устройства. Данный параметр относится к COM. Всегда передавайте IID_IDirectInput8.

ppvOut
указатель на IDirectInput8. Функция DirectInput8Create вызывается именно для того, чтобы создать объект DirectInput.

pUnkOuter
Ещё одно поле связанное с COM. Заполняется при агрегации. Передавайте NULL.

2. Создание устройства

После создания объекта DirectInput, через него можно создать устройство (например, клавиатуру) - получить указатель на интерфейс IDirectInputDevice8. Получить IDIrectInputDevice8 можно через метод IDirectInput8::CreateDevice:

HRESULT CreateDevice(
REFGUID rguid, // глобальный идентификатор (COM)
LPDIRECTINPUTDEVICE * lplpDirectInputDevice, // указатель  на устройство
LPUNKNOWN pUnkOuter // COM
);

rguid
Данный параметр относится к COM. Здесь необходимо запомнить два значения: GUID_SysMouse - мышь, GUID_SysKeyboard - клавиатура.

lplpDirectInputDevice
Адрес переменной, которая получит указатель на интерфейс IDirectInputDevice8.

pUnkOUter
Такой же параметр как и в функции DirectInput8Create. Передавайте NULL.

Теперь у нас есть переменная, которая представляет устройство. Тип устройства зависит от того, какое значение вы передали первому аргументу метода CreateDevice. В ближайшее время мы будем пользоваться клавиатурой. Соответсвенно первый параметр должен был быть таким: GUID_SysKeyboard.

3. Установка формата данных

Теперь нужно задать формат данных, который будет получать устройство. Для этого есть метод IDirectInputDevice8::SetDataFormat:

HRESULT SetDataFormat(LPCDIDATAFORMAT lpdf);

Единственным аргументом данного метода является адрес структуры, которая описывает формат данных. DirectInput предоставляет уже заполненные структуры LPCDIDATAFORMAT: для клавиатуры - c_dfDIKeyboard, для мышки - c_dfDIMouse.

4. Установка уровня взаимодействия с ОС.

Уровень взаимодействия с операционной системой задаётся через метод IDirectInputDevice8::SetCooperativeLevel:

HRESULT SetCooperativeLevel(
HWND hwnd,
DWORD dwFlags
);

hWnd
Первый аргумент - описатель окна, у которого нужно забирать ввод.

dwFlags
Второй аргумент - набор флагов, определящий каким образом будет происходить взаимодействие с операционной системой. Нам нужно получать данные с устройства только когда окно активно. Этому условию отвечает значение: DISCL_FOREGROUND | DISCL_NONEXCLUSIVE.

5. Получение доступа к устройству ввода.

И последнее что нужно сделать прежде чем устройство будет способно получать ввод от пользователя во время программы - "приобрести" (acquire) устройство IDirectInputDevice8::Acquire. Этот метод позволяет получить доступ к устройству ввода.

С данным методом связан вот ещё какой момент. Когда пользователь переключается на другую программу, то доступ к устройству ввода теряется. Поэтому, когда пользователь возвращается в программу, необходимо снова вызвать данный метод.

При завершении программы, до уничтожения устройства методом Release, нужно вызвать метод IDirectInputDevice8::Unacquire, чтобы отменить доступ к устройству.

А теперь посмотрим как всё это работает (создаём устройство ввода с клавиатуры):

IDirectInput8* di;           // объект DirectInput
IDirectInputDevice8* didev;  // устройство DirectInput

DirectInput8Create(hInstance,           // экземпляр приложения
               DIRECTINPUT_VERSION, // версия DirectInput
               IID_IDirectInput8,   // глобальный идентификатор (COM)
               (void**)&di, // адрес указателя объекта DirectInput
               NULL);

di->CreateDevice(GUID_SysKeyboard,  // устройство ввода  с клавиатуры
             &didev,            // адрес указателя  на устройство
             NULL);

didev->SetDataFormat(&c_dfDIKeyboard);
didev->SetCooperativeLevel(hWnd,DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
didev->Acquire();