2015-11-22 24 views
6

Esistono alcune routine API che accettano un puntatore a una variabile come parametro convertito in parametri var, ma possono essere specificate come puntatori nil in base ai file della guida di Windows.Come passare nil a un parametro var?

A titolo di esempio, la funzione ChangeDisplaySettings è dichiarata come:

function ChangeDisplaySettings(var lpDevMode: TDeviceMode; dwFlags: DWORD): Longint; stdcall; 

Ma il file della guida di Windows afferma chiaramente che "Passando NULL per il parametro LPDEVMODE è il modo più semplice per tornare alla modalità predefinita dopo una dinamica cambio di modalità. " La traduzione corretta avrebbe dovuto essere:

function ChangeDisplaySettings(lpDevMode: PDeviceMode; dwFlags: DWORD): Longint; stdcall; 

sto postando questa domanda e risposta per aiutare i neofiti aggirare questi problemi, senza dover ri-dichiarare le funzioni. Ricordo ancora che all'inizio era un problema per me.

+0

Riprovare la funzione è la soluzione corretta –

+0

@David, Grazie; Rispetto la tua opinione come un veterano chiaramente esperto a Delfi.Personalmente, però, non mi piace avere ri-dichiarazioni di routine standard ovunque e preferisco usare quelle standard ovunque io sia. Aggiornerò comunque la mia risposta per indicare che la pratica standard sembra preferire una nuova dichiarazione. –

+1

Ci dovrebbero essere due sovraccarichi. Uno con una var, e uno con un puntatore a struct. Quindi il chiamante può scegliere. –

risposta

11

Una soluzione consiste nel ri-dichiarare tali funzioni utilizzando i puntatori al posto dei parametri var, ma esiste una soluzione più semplice. Basta lanciare un Dereferenced nil-puntatore al tipo corretto, ad esempio, per l'esempio ChangeDisplaySettings, utilizzare il seguente per ripristinare la modalità di visualizzazione per le impostazioni del Registro di default:

ChangeDisplaySettings(TDeviceMode(nil^), 0); 

o

ChangeDisplaySettings(PDeviceMode(nil)^, 0); 

In questo modo stai passando un parametro var che si trova proprio all'indirizzo di memoria zero - il compilatore è felice e puoi passare un puntatore nullo alla routine API!

Modifica: Dal commento di David Hefferman, sembra che la pratica standard sia piuttosto ri-dichiarare tali routine. Personalmente, preferisco usare le dichiarazioni standard se posso farlo nelle mie unità personali, ma per scopi professionali suggerisco agli sviluppatori di seguire le pratiche standard.

+3

Personalmente trovo la seconda opzione molto più naturale. –

+0

@Andreas, preferisco anche usare il secondo, ma elencato il primo per i casi in cui un tipo di puntatore non è stato dichiarato. La preferenza di David Hefferman per la re-dichiarazione, d'altra parte, è l'unica soluzione che non causerà errori quando una nuova versione di Delphi corregge la dichiarazione standard! –

+0

@Jannie - un altro modo in cui questa soluzione si interromperebbe sarebbe se il compilatore dovesse mai essere modificato per identificare correttamente questo tipo di trucco typecast come interrompere il contratto con i parametri e rifiutare il codice come non valido. La dichiarazione fornita in questo caso è corretta solo come effetto collaterale. Non importa la "pratica standard", la soluzione corretta è usare una dichiarazione corretta, non escogitare un effetto collaterale per contrastare un altro effetto collaterale. – Deltics

6

A parte le altre risposte e i commenti, che sono utili, ho ancora un altro giro su questo. In questa istanza, chiunque abbia tradotto questa API dall'intestazione, in realtà non ha esaminato attentamente la documentazione dell'API. Se lo avessero fatto, sarebbe stato chiaro che passare "nulla" è una cosa valida da fare.

In questo caso, la linea di condotta corretta sarebbe quella di dichiarare un insieme di sovraccarichi che entrambi fanno riferimento alla stessa importazione. Una sarebbe la bella versione del parametro "var" e l'altra sarebbe la versione "pointer-to-structure". Ciò farebbe in modo che si possa passare direttamente una variabile TDeviceMode (non è necessario prendere l'indirizzo della var) e passare comunque nulla se necessario. Il compilatore avrebbe "abbinato" il "nil" al puntatore alla struttura che sarebbe poi stato referenziato. Poiché entrambe le API si risolvono nella stessa API e il modo effettivo in cui vengono trasmessi i parametri non è diverso, tutto funziona come previsto.

Poiché non esiste una versione sovraccaricata di tale API quando si dovrebbe essere in grado di passare un "nil" che è un bug di traduzione API. Sentiti libero di indicarlo in un rapporto allo http://quality.embarcadero.com.

Per la cronaca, ho fatto molte traduzioni API nel prodotto nel corso degli anni ... è certamente ipotizzabile che lo I sia stato lo stupido sviluppatore che non ha fatto la ricerca corretta su questo:).

+0

Personalmente, penso che la "bella versione del parametro var" sia obsoleta. OK, è stato pratico sin dai tempi di Borland Pascal, ma in realtà preferirei una traduzione più vicina all'originale, cioè sempre usando i puntatori. Questo ha il vantaggio che i documenti per l'API (che sono quasi sempre basati su C) non causano alcuna confusione quando, in Delphi, viene usata una "var" (principio di minimo stupore). I documenti mostrano un puntatore, la traduzione ha un puntatore. Nessuna confusione, non c'è bisogno di leggere i documenti così da vicino. I sovraccarichi sarebbero necessari solo per le traduzioni già esistenti. –

+1

Inoltre, non è possibile dichiarare un sovraccarico per una funzione che non è ancora contrassegnata come sovraccarico, quindi è necessario modificare l'unità di importazione originale, che non è sempre possibile, oppure * nascondere * l'originale e, nel propria unità, si dichiarano entrambe le funzioni sovraccaricate, di cui una è una ri-dichiarazione esatta. –

+0

Ecco perché ho suggerito di segnalarlo nel portale di qualità. Non ho mai suggerito che potesse essere fatto senza che l'originale fosse stato contrassegnato come sovraccarico. –

Problemi correlati