Api set loader-åtgärd

Viktig

Informationen i det här avsnittet gäller för alla versioner av Windows 10 och senare. Vi refererar till dessa versioner här som "Windows", och kallar eventuella undantag där det behövs.

API-uppsättningar förlitar sig på os-stöd i biblioteksinläsaren för att introducera en omdirigering av modulnamnområde i biblioteksbindningsprocessen. Ett API-uppsättningskontraktnamn namnger inte en fil. Inläsaren utför en körningsomdirigering från det kontraktsnamnet till värdbinärfilen som innehåller implementeringen.

När inläsaren påträffar ett beroende av en API-uppsättning vid körningen läser den konfigurationsdata i avbildningen för att identifiera värdbinärfilen för den API-uppsättningen. Dessa konfigurationsdata kallas API-uppsättningsschemat. Schemat monteras som en egenskap för operativsystemet och mappningen mellan API-uppsättningar och binärfiler kan variera beroende på vilka binärfiler som ingår på en viss enhet. Schemat är det som gör att en importerad funktion i en enda binär fil dirigeras korrekt på olika enheter, även när modulen som är värd för implementeringen har bytt namn, delats upp eller omstrukturerats.

Hur importen når en implementering

En binär fil kan nå en API-uppsättningsimplementering på två sätt, vilket bestäms av namnet i importtabellen:

  • Direkt-API-uppsättningsimport. Binärfilen importerar ett API-uppsättningskontraktnamn. Inläsaren löser det namnet via API-uppsättningens schema till värdbinärfilen på den aktuella enheten.
  • Import av äldre modul. Binärfilen importerar ett äldre Windows modulnamn, till exempel samplefeature.dll. På en utgåva som levererar modulen binder lastaren direkt till den. På en utgåva som har ersatt den omdirigerar en omvänd vidarebefordrare med samma namn importen till en API-uppsättning som inläsaren sedan löser via schemat.

Vilket av dessa namn som hamnar i importtabellen bestäms vanligtvis av biblioteket som du länkar till i stället för av källan du skriver. Se Windows paraplybibliotek.

Föredra api-uppsättningens kontraktnamn för kod som riktar sig mot aktuella versioner av Windows. Inläsaren löser det direkt till värden, utan någon vidarebefordrare däremellan. Importera det äldre modulnamnet när du behöver en enda binär fil som också körs på Windows versioner som släpptes innan API-uppsättningen fanns. Omvänd vidarebefordran håller binärt arbete på utgåvor där den äldre modulen har ersatts.

Import av direkt-API-uppsättning

Upplösning är en trestegssekvens:

  1. Binärfilen importerar ett API-uppsättningskontraktnamn eller skickar ett till LoadLibrary.
  2. Inläsaren letar upp kontraktet i API-uppsättningens schema på den aktuella enheten och hittar värdbinärfilen som schemat mappar det till.
  3. Inläsaren läser in värdbinärfilen och binder den importerade funktionen till värdens export.

Eftersom mappningen finns i schemat i stället för i filsystemet kan samma import matcha olika binärfiler på olika enheter:

Enhet api-win-core-samplefeature mappar till
En enhet som innehåller funktionen samplefeature.dll
En enhet som levererar en omstrukturerad implementering samplefeaturecore.dll
En enhet som inte innehåller funktionen Ingen mappning

Namnen samplefeature som används här är illustrativa namn för en fiktiv Windows komponent.

Den förbrukande binärfilen är omedveten om vilken värd den var bunden till. Det är mekanismens punkt: kontraktet är stabilt, medan modulen som implementerar den kan ändras från en enhet till en annan.

En import av ett kontraktnamn matchas i en enda åtgärd, utan någon mellanliggande vidarebefordrarmodul. Det är det mest effektiva formuläret och den normala sökvägen för kod som skrivits mot API-uppsättningar.

API-uppsättningsnamn och suffixet .dll

Eftersom mappningarna sparas i schemat i stället för på disk refererar inte ett API-uppsättningsnamn som slutar med .dll till en fil med det namnet. Den .dll delen är bara en namngivningskonvention som överförs från det sätt som modulnamn stavas i en importtabell. API-uppsättningens namn är mer som ett alias eller ett virtuellt namn för en fysisk DLL-fil.

När en inläsningsåtgärd tar emot ett namn som börjar med api- eller ext-dirigerar inläsaren den till API-uppsättningens körningsmiljö, ett tillägg av inläsaren som löser kontrakt via schemat. API-uppsättningens runtime parsar namnet efter API-uppsättningens namngivningsregler i stället för som ett filnamn, så .dll suffixet är inte en del av kontraktnamnet som löses. Inkludera suffixet när du arbetar från ett namn som det visas i en importtabell. annars kan du lämna det av.

Inläsaren löser båda formerna av kontraktnamn, ett versionskontraktsnamn och ett kontraktalias, via samma schema. De konventioner som styr dessa namn finns i API-angivna kontraktsnamn.

Namnstabilitet är inte detsamma som tillgänglighet

Ett API-uppsättningsnamn är stabilt mellan Windows enheter, i den meningen att samma namn alltid identifierar samma kontrakt oavsett var det identifieras. Det är en egenskap för namnområdet, inte en garanti för någon viss enhet.

Ett visst kontrakt kan vara frånvarande från en enhet, eller presenteras men inte mappas till en värd. Inget om namnet talar om vilket. Information om huruvida implementeringen faktiskt finns där finns i Identifiera TILLGÄNGLIGHET för API-uppsättningar.

Vilken lösning som krävs

För ett anrop via en API-uppsättning för att nå en implementering måste allt följande innehålla:

  • Kontraktet finns i schemat på den aktuella enheten.
  • Schemat mappar kontraktet till en värdbinär och den värden kan läsas in.
  • Värden exporterar den specifika funktion som binärfilen anropar.

När en av dessa inte finns där felytorna beror på hur du importerade API-uppsättningen:

Importformat Beteende när kontraktet inte kan lösas
Statisk import Det går inte att starta processen. Inläsaren löser statiska importer innan någon av koden körs.
Fördröjd import Processen startar normalt. Lösningen skjuts upp till det första anropet till API:et, där koden kan hantera felet.

En saknad export rapporteras separat från ett saknat kontrakt. En binär fil som importerar en funktion som värden inte exporterar misslyckas med ett fel med en startpunkt som saknas.

Vad en lyckad belastning inte säger dig

Lösning binder ett kontrakt till en värd. Den utvärderar inte tillståndet för en enskild funktion i kontraktet.

Ett kontrakt kan organisera sina individuellt tillgängliga funktioner i namngivna grupper. En grupp kan vara otillgänglig på en enhet även om kontraktet som bär den löser sig normalt, eftersom lastaren binder vid kontraktkornighet och inte konsulterar grupptillstånd när den binder. Det är avsiktligt: att vägra binda en värd är dödligt för en statisk import, så inläsaren tar den tillåtande sökvägen och lämnar den finare frågan till anroparen.

Konsekvensen för din kod är att en lyckad inläsning eller ett lyckat LoadLibrary-anrop inte är ett bevis på att en viss funktion är tillgänglig. Ställ den frågan explicit med en tillgänglighetsfråga. Se Identifiera tillgänglighet för API-uppsättningar.

Valfria API-uppsättningar och fördröjningsinläsning

Om ditt program anropar en API-uppsättning som kanske inte finns räcker det inte med en tillgänglighetskontroll på egen hand: med en statisk import startar inte processen, så körningen når aldrig kontrollen.

För att den valfria kodsökvägen ska kunna nås konfigurerar du antingen modulen som har det valfria API:et för fördröjningsinläsning eller löser målet dynamiskt med LoadLibrary och GetProcAddress när en tillgänglighetsfråga har slutförts. Mer information om båda metoderna finns i Identifiera tillgänglighet för API-uppsättningar.

Omvänd vidarebefordran

Namn på API-uppsättningar ger ett stabilt namnområde för moduler mellan enheter, men det är inte alltid praktiskt att konvertera varje binär fil till det här systemet. Ett program kan ha använts gemensamt i många år och det kanske inte går att kompilera om binärfilerna. Vissa program måste också fortsätta köras på system som skapades innan specifika API-uppsättningar introducerades.

För att hantera detta innehåller utgåvor som inte bär de ursprungliga modulerna en uppsättning omvända vidarebefordrare: kompatibilitetsbinärfiler som bär modulnamnen som ursprungligen introducerades på Windows datorer och som omdirigerar deras export till API-uppsättningar.

En fullständig skrivbordsutgåva skickar de ursprungliga modulerna, så en import av ett äldre modulnamn binder till modulen som den alltid har gjort. På en utgåva som har ersatt modulen täcker den omvända vidarebefordraren med samma namn gapet.

Inläsningsåtgärden fungerar så här:

  1. Inläsaren är beroende av ett äldre Windows datormodulnamn som inte finns på enheten.
  2. Inläsaren letar upp en omvänd vidarebefordrare som bär modulnamnet och läser in den.
  3. Den omvända vidarebefordraren omdirigerar den importerade funktionen till en API-uppsättning.
  4. Inläsaren löser det API som angetts via schemat, enligt beskrivningen tidigare i det här avsnittet.

Konceptuellt ser mappningen ut så här:

Importerad DLL: samplefeature.dll

  • På en utgåva som har den ursprungliga modulen: samplefeature.dll
  • På en utgåva som har ersatt den: samplefeature.dll omvänd vidarebefordrare ->api-win-core-samplefeature -samplefeaturecore.dll>

Gränsen för den här sökvägen är exporttäckning. En omvänd vidarebefordrare har bara exporter som har API-uppsättningsekvivalenter, så den exporterar inte nödvändigtvis alla funktioner som den ursprungliga modulen gjorde. En binär fil som importerar en funktion som den omvända vidarebefordraren inte har kan inte läsas in med ett fel som saknas i startpunkten.

Omvänd vidarebefordran är också ett skäl till att inte behandla en lyckad lösning som ett bevis på att en implementering finns. Ett GetProcAddress-anrop mot ett äldre modulnamn kan returnera en giltig funktionspekare som matchar en stub som returnerar ett fel. Fråga tillgänglighet explicit i stället. Se Identifiera tillgänglighet för API-uppsättningar.

Not

Omvänd vidarebefordran omfattar endast en delmängd av Win32 API-ytan. Det tillåter inte att program som riktar sig mot skrivbordsversioner av Windows körs på alla Windows enheter. Om binärfilen riktar in sig på aktuella versioner av Windows är api-uppsättningens kontraktnamn det mer direkta valet.

Se även