Operazione del caricatore del set di API

Importante

Le informazioni contenute in questo argomento si applicano a tutte le versioni di Windows 10 e versioni successive. Queste versioni verranno indicate qui come "Windows", richiamando eventuali eccezioni, se necessario.

I set di API si basano sul supporto del sistema operativo nel caricatore di librerie per introdurre un reindirizzamento dello spazio dei nomi del modulo nel processo di associazione della libreria. Un nome di contratto del set di API non assegna un nome a un file. Il caricatore esegue un reindirizzamento in fase di esecuzione dal nome del contratto al file binario host che contiene l'implementazione.

Quando il caricatore rileva una dipendenza da un set di API in fase di esecuzione, consulta i dati di configurazione nell'immagine per identificare il file binario host per il set di API. Questi dati di configurazione vengono chiamati schema del set di API . Lo schema viene assemblato come proprietà del sistema operativo e il mapping tra set di API e file binari può variare a seconda dei file binari inclusi in un determinato dispositivo. Lo schema è ciò che consente di instradare correttamente una funzione importata in un singolo file binario in dispositivi diversi, anche quando il modulo che ospita l'implementazione è stato rinominato, suddiviso o sottoposto a refactoring.

Come le importazioni raggiungono un'implementazione

Un file binario può raggiungere un'implementazione del set di API in due modi, deciso dal nome nella tabella di importazione:

  • Importazione diretta del set di API. Il file binario importa un nome di contratto del set di API. Il caricatore risolve il nome tramite lo schema del set di API sul file binario host nel dispositivo corrente.
  • Importazione del modulo legacy. Il file binario importa un nome di modulo di Windows legacy, ad esempio samplefeature.dll. In un'edizione fornita da tale modulo, il caricatore viene associato direttamente a tale modulo. In un'edizione che l'ha sostituita, un server d'inoltro inverso con lo stesso nome reindirizza l'importazione a un set di API, che il caricatore risolve quindi tramite lo schema.

Quale di questi nomi finisce nella tabella di importazione è in genere determinata dalla libreria a cui si esegue il collegamento anziché dall'origine scritta. Vedere Windows librerie generiche.

Preferisce il nome del contratto del set di API per il codice destinato alle versioni correnti di Windows. Il caricatore lo risolve direttamente nell'host, senza un server d'inoltro tra. Importare il nome del modulo legacy quando è necessario un singolo file binario che viene eseguito anche in Windows versioni rilasciate prima dell'esistenza del set di API. L'inoltro inverso mantiene il lavoro binario sulle edizioni in cui è stato sostituito il modulo legacy.

Importazione del set di API diretta

La risoluzione è una sequenza in tre passaggi:

  1. Il file binario importa un nome di contratto del set di API o ne passa uno a LoadLibrary.
  2. Il caricatore cerca il contratto nello schema del set di API nel dispositivo corrente e trova il file binario host a cui lo schema esegue il mapping.
  3. Il caricatore carica il file binario host e associa la funzione importata all'esportazione dell'host.

Poiché il mapping si trova nello schema anziché nel file system, la stessa importazione può risolversi in file binari diversi in dispositivi diversi:

Dispositivo api-win-core-samplefeature esegue il mapping a
Un dispositivo che include la funzionalità samplefeature.dll
Un dispositivo che fornisce un'implementazione sottoposta a refactoring samplefeaturecore.dll
Un dispositivo che non include la funzionalità Non mappato

I samplefeature nomi usati qui sono nomi illustrativi per un componente fittizio Windows.

Il file binario di utilizzo non è a conoscenza dell'host a cui è stato associato. Questo è il punto del meccanismo: il contratto è stabile, mentre il modulo che implementa è libero di passare da un dispositivo all'altro.

Un'importazione di un nome di contratto viene risolta in una singola operazione, senza che sia coinvolto alcun modulo di inoltro intermedio. Si tratta del formato più efficiente e del percorso normale per il codice scritto in set di API.

Nomi dei set di API e suffisso .dll

Poiché i mapping vengono mantenuti nello schema anziché su disco, un nome del set di API che termina con .dll non fa riferimento a un file di tale nome. La parte.dll è solo una convenzione di denominazione, riportata dal modo in cui i nomi dei moduli vengono digitati in una tabella di importazione. Il nome del set di API è più simile a un alias o a un nome virtuale per un file DLL fisico.

Quando un'operazione del caricatore riceve un nome che inizia con api- o ext-, il caricatore lo indirizza al runtime del set di API, un'estensione del caricatore che risolve i contratti tramite lo schema. Il runtime del set di API analizza il nome in base alle regole di denominazione del set di API anziché come nome file, quindi il suffisso.dll non fa parte del nome del contratto che viene risolto. Includere il suffisso quando si lavora da un nome come appare in una tabella di importazione; in caso contrario, è possibile lasciarlo spento.

Il caricatore risolve entrambe le forme di nome del contratto, un nome di contratto con versione e un alias del contratto, tramite lo stesso schema. Per le convenzioni che regolano questi nomi, vedere Nomi di contratto del set di API.

La stabilità dei nomi non corrisponde alla disponibilità

Un nome del set di API è stabile tra i dispositivi Windows, nel senso che lo stesso nome identifica sempre lo stesso contratto ovunque venga riconosciuto. Si tratta di una proprietà dello spazio dei nomi, non una garanzia su qualsiasi dispositivo specifico.

Un determinato contratto può essere assente da un dispositivo o presente ma non mappato a un host. Niente sul nome indica quale. Per scoprire se l'implementazione è effettivamente presente, vedere Rilevare la disponibilità dei set di API.

Quale risoluzione richiede

Per una chiamata tramite un set di API per raggiungere un'implementazione, tutti gli elementi seguenti devono contenere:

  • Il contratto è presente nello schema nel dispositivo corrente.
  • Lo schema esegue il mapping del contratto a un file binario host e tale host può essere caricato.
  • L'host esporta la funzione specifica che il file binario sta chiamando.

Quando una di queste non contiene, la posizione in cui le superfici di errore dipendono dal modo in cui è stato importato il set di API:

Stile di importazione Comportamento quando il contratto non può essere risolto
Importazione statica L'avvio del processo non riesce. Il caricatore risolve le importazioni statiche prima dell'esecuzione di uno qualsiasi del codice.
Importazione con caricamento ritardato Il processo viene avviato normalmente. La risoluzione viene posticipata alla prima chiamata all'API, in cui il codice può gestire l'errore.

Un'esportazione mancante viene segnalata separatamente da un contratto mancante; Un file binario che importa una funzione che l'host non esegue l'esportazione ha esito negativo con un errore di punto di ingresso mancante.

Quale caricamento ha esito positivo non indica

La risoluzione associa un contratto a un host. Non valuta lo stato di una singola funzionalità all'interno di tale contratto.

Un contratto può organizzare le funzionalità disponibili singolarmente in gruppi denominati. Un gruppo può non essere disponibile in un dispositivo anche se il contratto che lo porta normalmente viene risolto, perché il caricatore si associa alla granularità del contratto e non consulta lo stato del gruppo quando viene associato. Questo è intenzionale: rifiutare di associare un host è fatale a un'importazione statica, quindi il caricatore accetta il percorso permissivo e lascia la domanda più fine al chiamante.

La conseguenza per il codice è che un caricamento riuscito o una chiamata LoadLibrary riuscita non è evidenza che è disponibile una particolare funzionalità. Porre questa domanda in modo esplicito con una query di disponibilità. Vedere Rilevare la disponibilità dei set di API.

Set di API facoltativi e caricamento ritardato

Se l'applicazione chiama un set di API che potrebbe non essere presente, un controllo di disponibilità autonomo non è sufficiente: con un'importazione statica il processo non viene avviato, quindi l'esecuzione non raggiunge mai il controllo.

Per mantenere raggiungibile il percorso del codice facoltativo, configurare il modulo che contiene l'API facoltativa per il caricamento ritardato o risolvere la destinazione in modo dinamico con LoadLibrary e GetProcAddress dopo che una query di disponibilità ha esito positivo. Per informazioni dettagliate su entrambi gli approcci, vedere Rilevare la disponibilità dei set di API.

Inoltro inverso

Anche se i nomi dei set di API forniscono uno spazio dei nomi stabile per i moduli tra i dispositivi, non è sempre pratico convertire ogni file binario in questo sistema. Un'applicazione potrebbe essere stata in uso comune per molti anni e ricompilare i relativi file binari potrebbe non essere fattibile. Alcune applicazioni devono anche continuare a essere in esecuzione nei sistemi compilati prima dell'introduzione di set di API specifici.

A tale scopo, le edizioni che non contengono i moduli originali includono un set di server d'inoltro inverso: file binari di compatibilità che includono i nomi dei moduli originariamente introdotti nei PC Windows e che reindirizzano le esportazioni ai set di API.

Un'edizione desktop completa include i moduli originali, quindi un'importazione di un nome di modulo legacy viene associata al modulo come sempre. In un'edizione che ha sostituito tale modulo, il server d'inoltro inverso con lo stesso nome copre il divario.

L'operazione del caricatore si comporta come segue:

  1. Il caricatore viene presentato con una dipendenza da un nome di modulo pc legacy Windows non presente nel dispositivo.
  2. Il caricatore individua un server d'inoltro inverso che contiene il nome del modulo e lo carica.
  3. Il server d'inoltro inverso reindirizza la funzione importata a un set di API.
  4. Il caricatore risolve l'API impostata tramite lo schema, come descritto in precedenza in questo argomento.

Concettualmente, il mapping è simile al seguente:

DLL importata: samplefeature.dll

  • In un'edizione con il modulo originale: samplefeature.dll
  • In un'edizione che l'ha sostituita: samplefeature.dllserver d'inoltro inverso ->api-win-core-samplefeature>samplefeaturecore.dll

Il limite per questo percorso è la copertura dell'esportazione. Un server d'inoltro inverso contiene solo le esportazioni con equivalenti del set di API, quindi non esporta necessariamente tutte le funzioni eseguite dal modulo originale. Un file binario che importa una funzione che il server d'inoltro inverso non esegue il caricamento con un errore di punto di ingresso mancante.

L'inoltro inverso è anche un motivo per cui non considerare una risoluzione corretta come prova che è presente un'implementazione. Una chiamata GetProcAddress su un nome di modulo legacy può restituire un puntatore a funzione valido che viene risolto in uno stub che restituisce un errore. Eseguire query sulla disponibilità in modo esplicito. Vedere Rilevare la disponibilità dei set di API.

Nota

L'inoltro inverso copre solo un subset della superficie dell'API Win32. Non consente alle applicazioni destinate a versioni desktop di Windows di essere eseguite in tutti i dispositivi Windows. Se il file binario è destinato alle versioni correnti di Windows, il nome del contratto del set di API è la scelta più diretta.

Vedere anche