跳转到主要内容
从 v0 到 v4 的每个 GameInput API 版本都定义了自己的一组 COM 接口。GameInput 运行时在相同的底层对象上实现所有版本,因此单个对象(例如设备)同时具有 v0 接口、v4 接口以及介于两者之间的每个版本的接口。若要将对象从一个版本切换到另一个版本,请对其调用 QueryInterface。 这使你可以:
  • 向基于较早 API 版本(例如 v0)构建的应用程序添加 v4 功能,而无需一次性迁移整个应用程序。
  • 调用针对较早版本 GameInput 构建的 SDK、中间件或引擎插件,即使应用程序的其余部分使用 v4。

支持的 API 版本

GameInput 运行时支持低于或等于其自身主版本号的每个 API 版本。例如,安装 GameInput 4.0 运行时后,每个 GameInput 对象都会实现 v0、v1、v2、v3 和 v4 接口,你的应用程序可以使用它们的任意组合。 如果你请求的 API 版本比已安装的运行时更新,QueryInterface 和 GameInputInitialize 会失败并返回 E_NOINTERFACE。应用程序使用的最高 API 版本决定了它所需的最低运行时版本。 在 PC 上,已安装的运行时是 GameInput 可再发行组件或你随游戏部署的运行时。有关详细信息,请参阅 GameInput NuGet 包和 GameInput 旁加载。在 XBOX 上,运行时包含在 GDK 中,你也可以随游戏部署更新的运行时。有关详细信息,请参阅 XBOX 主机上的 GameInput。

设置项目

对于新的实现,请使用最新 Microsoft.GameInput NuGet 包中的头文件和 GameInput.lib。在该包中,<GameInput.h> 是最新的头文件,每个较早的头文件都位于带版本号的文件夹中,例如 <v0/GameInput.h> 或 <v2/GameInput.h>。该库导出 GameInputInitialize,它会为你请求的任何 API 版本创建 GameInput。 较早版本中的库(例如 GDK 中的库)也仍可正常工作。有关详细信息,请参阅保留较早的库。

包含 v0 头文件

从 v1 开始,每个 GameInput 头文件都在带版本号的命名空间(例如 GameInput::v4)中声明其类型。而 v0 头文件在全局命名空间中声明其类型。若要将 v0 头文件与最新头文件一起使用,请将以下样板代码放入你自己的头文件中,例如 GameInputV0.h:
此头文件的工作方式如下:
  • 它首先包含最新的 <GameInput.h>。这会在其自身的命名空间中声明最新的 API,并包含 v0 头文件所依赖的 Windows 和 COM 头文件,因此 v0 头文件不会在命名空间内再次包含它们。
  • 前向声明使 v0 头文件中的 typedef 在 GameInput::v0 中声明新接口,而不是引用最新头文件已声明的接口。
  • 在命名空间内包含 <v0/GameInput.h> 会将每个 v0 类型、枚举和常量移入 GameInput::v0,例如 GameInput::v0::IGameInput 和 GameInput::v0::GameInputKindGamepad。
在 v0 源文件中,请包含 GameInputV0.h 而不是 <GameInput.h>,并添加 using namespace GameInput::v0;,以便现有代码可以继续使用非限定名称。任何包含 GameInputV0.h 的源文件也可以通过完整名称使用最新的 API,例如 GameInput::v4::IGameInput。

示例:向 v0 应用程序添加 v4 功能

你无需重写 v0 代码即可开始使用 v4 功能。将 v0 代码保留在其自己的源文件中,并将新的 v4 代码放入单独的源文件中。在两者之间以 IUnknown* 形式传递 GameInput 对象,并让每一方针对其所需的版本调用 QueryInterface。

现有的 v0 代码

以下 v0 代码创建 GameInput 并读取游戏手柄。与原始 v0 代码相比,只有三处更改:
  • 它包含 GameInputV0.h 而不是 <GameInput.h>。
  • 它通过使用 v0 IID 调用 GameInputInitialize 来创建 GameInput,因为最新的库不导出 v0 GameInputCreate 函数。
  • 按下 A 按钮时,它会调用新的 v4 代码。

新的 v4 代码

新的源文件包含 v4 头文件,并对 v0 代码传入的设备调用 QueryInterface。然后,它使用 v4 中新增的 IGameInputDevice::SetLightingColor 和 IGameInputDevice::ApplyLightingColors 更改控制器中心灯的颜色。如果设备没有中心灯,则会忽略该调用。
如果 QueryInterface 失败,则表示已安装的运行时不支持 v4。你的 v0 代码会继续正常工作,应用程序可以跳过 v4 功能。 当所有代码都使用 v4 时,你可以删除 GameInputV0.h 和 QueryInterface 调用。

保留较早的库

如果你继续链接较早版本中的库(例如 GDK 中的库),则现有的 v0 代码完全无需更改。继续包含该版本中的 v0 头文件并调用 GameInputCreate 即可。该调用会转发给已安装的运行时,因此当运行时为 4.0 或更高版本时,它返回的对象也会实现 v4 接口,前面所示的新 v4 代码无需更改即可正常工作。 较早的库不导出 GameInputInitialize。请仅通过调用 QueryInterface 获取 v4 接口,并且不要调用 v4 GameInputCreate,因为它会调用 GameInputInitialize。

在版本之间切换的规则

不同 API 版本中的接口具有相同的名称(例如 IGameInput 和 IGameInputDevice),但它们是具有不同方法的不同类型。指向 v0 接口的指针并不是指向 v4 接口的指针,即使两者引用的是同一个对象。
强制转换与调用 QueryInterface 不同。始终使用 QueryInterface 获取其他 API 版本的接口。切勿使用 static_cast、reinterpret_cast 或 C 样式强制转换在版本之间进行转换。强制转换可以通过编译,但通过转换后的指针调用方法可能会调用错误的方法,从而导致未定义的行为。
还请牢记以下规则:
  • 不要在 v0 头文件之后包含其他 GameInput 头文件。 v0 头文件将其错误代码(例如 GAMEINPUT_E_DEVICE_DISCONNECTED)定义为宏,这些宏会破坏在其之后包含的任何 GameInput 头文件中的声明。GameInputV0.h 通过首先包含最新头文件来遵循此规则。
  • 返回的对象使用你所调用的版本。 例如,对 v4 读取调用 GetDevice 会返回 v4 设备,而通过 v4 IGameInput 注册的设备回调会收到 v4 设备。在将这些对象传递给使用其他版本的代码之前,请先调用 QueryInterface。
  • 每次调用 QueryInterface 都会添加一个引用。 与任何其他 COM 接口指针一样,不再需要返回的指针时请将其释放。
  • 检查结果。 当你请求较新的 API 版本时,失败意味着已安装的运行时不支持该版本。
  • GameInputV0.h 中的 v0 类型是单独的 C++ 类型。 GameInput::v0::IGameInput 与单独使用 <v0/GameInput.h> 时的全局 IGameInput 描述的是同一个接口,但 C++ 将它们视为不同的类型。如果现有代码或其他库使用全局 v0 类型声明函数,请从仅包含 <v0/GameInput.h> 的源文件中调用这些函数。

调用使用较早 API 版本的 SDK

某些 SDK、中间件和引擎插件是针对特定 GameInput API 版本构建的,并要求使用该版本的 GameInput 接口。你的应用程序可以使用较新的版本,同时仍然调用这些组件。调用 QueryInterface 获取组件所需的版本,然后传递该指针。 请将该调用放在包含与组件所用相同 GameInput 头文件的源文件中。在以下示例中,ThirdPartySdk.h 声明了 ThirdPartySdkInitialize(GameInput::v2::IGameInput*),应用程序的其余部分以 IUnknown* 形式传递其 GameInput 对象:
同样的方法也适用于反方向。如果组件向你的应用程序提供较早版本的 GameInput 对象,请对其调用 QueryInterface 以获取 v4 接口。 由于每个版本都由同一个对象实现,因此 SDK 和你的应用程序共享设备、读取和状态。SDK 的 API 版本也必须低于或等于已安装的运行时版本。

另请参阅

GameInput API 版本管理
GameInput NuGet 包
GameInput 旁加载
GameInputInitialize
GameInput API 参考
最后修改于 2026年11月6日