API セットの可用性を検出する

場合によっては、特定の API セット コントラクト名が、一部のWindows デバイス上の空のモジュール名に意図的にマップされることがあります。 この理由はさまざまですが、一般的な例として、リソースが制限されたデバイス用に構成されている場合、システム リソースに関してコストの高い機能がWindows OS から削除される可能性があります。 これにより、アプリケーションが API レベルでオプション機能を適切に処理することが困難になります。

Win32 API を使用できるかどうかをテストするための従来のアプローチは、LoadLibrary を使用するか、GetProcAddress 使用することです。 ただし、逆 転送のため、これらは API セットをテストするための信頼できる手段ではありません。 特定の API に逆転送が適用されている場合、 LoadLibrary または GetProcAddress は、内部実装が削除された場合でも、有効な関数ポインターに解決される可能性があります。 この場合、関数ポインターは、単にエラーを返すスタブ関数を指します。

このケースを検出するには、IsApiSetImplemented 関数を使用して、特定の API 実装の基になる可用性を照会できます。 このテストでは、実行中のデバイスの構成済み API セット スキーマに API セットが存在するかどうか、および実装モジュールにマップされているかどうかを報告します。

Important

可用性クエリが成功しても、特定の呼び出しが成功するとは限りません。 モジュールの読み込みエラー、不足しているエクスポート、および API 独自の文書化されたエラー結果を引き続き処理します。

次のコード例では 、IsApiSetImplemented を使用して、 WTSEnumerateSessionsW を含む API セットを呼び出す前に現在のデバイスで使用できるかどうかを判断する方法を示します。

#include <windows.h>
#include <apiquery2.h>
#include <stdio.h>
#include <wtsapi32.h>

#pragma comment(lib, "OneCore.lib")
#pragma comment(lib, "Wtsapi32.lib")

int __cdecl wmain(int /* argc */, PCWSTR /* argv */ [])
{
    PWTS_SESSION_INFOW pInfo = nullptr;
    DWORD count = 0;

    if (!IsApiSetImplemented("ext-ms-win-session-wtsapi32-l1-1-0"))
    {
        wprintf(L"ext-ms-win-session-wtsapi32-l1-1-0 is not available.\n");
        return 0;
    }

    if (WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pInfo, &count))
    {
        wprintf(L"SessionCount = %lu\n", count);

        for (DWORD i = 0; i < count; i++)
        {
            PWTS_SESSION_INFOW pCurInfo = &pInfo[i];
            wprintf(L"    %ls: ID = %lu, state = %d\n", pCurInfo->pWinStationName,
                pCurInfo->SessionId, static_cast<int>(pCurInfo->State));
        }

        WTSFreeMemory(pInfo);
    }
    else
    {
        wprintf(L"WTSEnumerateSessionsW failure: %lu\n", GetLastError());
    }

    return 0;
}

IsApiSetImplementedapiquery2.h で宣言され、通常のパブリック SDK リンク パスによって OneCore.lib から提供されます。 そのライブラリは、オプションのターゲット API 自体を呼び出すために必要なインポート ライブラリとは別のものです。

上の例では、 Wtsapi32.lib を静的インポート用にリンクしています。この例は短く保ちます。 API セットが存在しない場所で実行する必要がある運用アプリケーションは、「 オプションのコード パスに到達可能な状態を維持する」のガイダンスも適用する必要があります。

クエリ名を選択する

テストする API セットの名前を渡します。 API セット名は、通常、 .dll サフィックスなしで記述されており、このページの例ではその形式が使用されています。 サフィックスは API セット名の一部ではありません。

渡す名前を見つけるには、呼び出す API のリファレンス ページの 要件 の表を参照してください。 そのテーブルに API セット 行がある場合は、コントラクト名を指定します。 それ以外の場合は DLL 行を使用しますが、名前自体が api- または ext- で始まるコントラクト名がある場合にのみ、 Wtsapi32.dll などの物理モジュール名がコントラクトではなく、クエリを実行すると FALSE が返されます。

名前の形式は、API のアドレス指定方法によって異なります。

API の公開範囲 クエリ フォーム
名前付きグループ <contract>~<group> api-win-core-samplefeature~AdvancedOperations
既定のグループ コントラクトエイリアス (なし) ~Default api-win-core-samplefeature
バージョン管理されたコントラクト 完全なバージョン管理されたコントラクト名 ext-ms-win-core-samplefeature-l1-1-0

samplefeature名は、架空のWindows コンポーネントのわかりやすい名前です。 名前プレフィックス (api- または ext-) は、可用性の動作では役割を果たしません。

名前付きグループの場合、クエリが成功すると、グループが存在し、そのコントラクトが実装モジュールにマップされ、そのホストが現在の実行環境で使用でき、グループが無効にされず、グループに関連付けられているシステム機能が有効になります。 コントラクトエイリアスを使用するクエリは、コントラクトとホストのチェックを適用します。

API のパブリック ヘッダーが Is<APIName>Present ヘルパーを提供する場合は、そのヘルパーを使用します。 API を含む API セットまたはグループの正しい名前が既に含まれています。

クエリの結果は、関数の詳細ではなく、コントラクトまたはグループの詳細です。 同じ名前付きグループに基づく 2 つのヘルパーは、各ヘルパーが異なる API に対して名前付けされている場合でも、常に同じ結果を返します。

オプションのコード パスに到達可能な状態を維持する

オプションの API が静的インポートとしてリンクされている場合、可用性チェックではプロセスの起動を保護できません。 ローダーは、コードを実行する前に静的インポートを解決するため、不足しているモジュールは、実行がチェックに達する前にプロセスを失敗します。

次のいずれかの方法を使用します。

  • 遅延読み込み用の省略可能な API を含むモジュールを構成します。 遅延読み込みはリンカー設定です。 /DELAYLOAD:<module> とリンク delayimp.lib を指定します。 バイナリのインポート テーブルに表示されるモジュール名を指定します。これは、API セット コントラクト名ではなく、クラシック DLL 名である可能性があります。 前の例では、その名前は WTSAPI32.dllです。
  • または、可用性クエリが成功した後、 LoadLibraryGetProcAddress を使用してターゲットを動的に解決します。

可用性クエリの代わりに LoadLibrary または GetProcAddress を使用しないでください。 モジュールに回答して質問をエクスポートし、名前付きグループの状態を評価せず、前述のように逆転送スタブに解決できます。 クエリの後にこれらを使用して、個別のモジュールを処理し、チェックをエクスポートします。

以前のバージョンのWindowsでの動作

OneCore.lib によって提供される実装では、実行時に使用可能なクエリ メカニズムが選択されるため、IsApiSetImplemented を呼び出すアプリケーションは、基になるクエリサポートの前にあるシステムで引き続き実行できます。

クエリ メカニズムが使用できないシステムでは、次の手順を実行します。

  • ~を含むグループ修飾名は FALSE を返します。 名前付きグループを評価できないシステムは、グループが使用可能であることを報告できません。
  • グループ修飾されていない名前は TRUE を返すことができます。 これにより、初期バージョンの Windows で独自のフォワーダー DLL を提供したアプリケーションとの互換性が維持されます。この場合、コントラクトは実際にそのフォワーダーによって満たされていました。

API セットの可用性クエリをセキュリティチェックまたは承認チェックとして使用しないでください。

こちらも参照ください