경우에 따라 지정된 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;
}
IsApiSetImplemented
는 apiquery2.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 집합 또는 그룹에 대한 올바른 이름이 이미 포함되어 있습니다.
쿼리의 결과는 함수 세분화가 아닌 계약 또는 그룹 세분화입니다. 동일한 명명된 그룹에서 지원되는 두 도우미는 각 도우미의 이름이 다른 API에 대해 이름이 지정되더라도 항상 동일한 결과를 반환합니다.
선택적 코드 경로에 연결할 수 있도록 유지
선택적 API가 정적 가져오기로 연결된 경우 가용성 검사는 프로세스 시작을 보호할 수 없습니다. 로더는 코드가 실행되기 전에 정적 가져오기를 확인하므로 실행이 검사에 도달하기 전에 누락된 모듈이 프로세스에 실패합니다.
다음 방법 중 하나를 사용합니다.
-
지연 로드를 위해 선택적 API를 전달하는 모듈을 구성합니다. 지연 로드는 링커 설정입니다. delayimp.lib를 지정
/DELAYLOAD:<module>하고 연결합니다. 이진 파일의 가져오기 테이블에 표시되는 모듈 이름을 지정합니다. 이 이름은 API 집합 계약 이름이 아닌 클래식 DLL 이름일 수 있습니다. 앞의 예제에서는 해당 이름이 WTSAPI32.dll. - 또는 가용성 쿼리가 성공한 후 LoadLibrary 및 GetProcAddress 를 사용하여 대상을 동적으로 확인합니다.
가용성 쿼리 대신 LoadLibrary 또는 GetProcAddress 를 사용하지 마세요. 모듈에 답변하고 질문을 내보내고, 명명된 그룹 상태를 평가하지 않으며, 앞에서 설명한 대로 역방향 전달 스텁으로 해결할 수 있습니다. 쿼리 후에 이를 사용하여 별도의 모듈을 처리하고 검사를 내보냅니다.
이전 버전의 Windows 동작
OneCore.lib에서 제공하는 구현은 런타임에 사용 가능한 쿼리 메커니즘을 선택하므로 IsApiSetImplemented를 호출하는 애플리케이션은 기본 쿼리 지원을 이전의 시스템에서 계속 실행할 수 있습니다.
사용할 수 있는 쿼리 메커니즘이 없는 시스템에서 다음을 수행합니다.
- 포함하는 그룹 정규화된 이름은 FALSE를 반환합니다
~. 명명된 그룹을 평가할 수 없는 시스템은 그룹을 사용할 수 있다고 보고할 수 없습니다. - 그룹 정규화되지 않은 이름은 TRUE를 반환할 수 있습니다. 이렇게 하면 Windows 초기 버전에서 자체 전달자 DLL을 제공한 애플리케이션과의 호환성이 유지됩니다. 여기서 계약은 실제로 해당 전달자에 의해 충족되었습니다.
API 집합 가용성 쿼리를 보안 또는 권한 부여 검사로 사용하지 마세요.