Domanda

Sto usando Delphi 2009 non che abbia un grande impatto su quello che sto facendo. Penso che incontrerei lo stesso se fossi ancora nel 2007 .

Ho una chiamata scsi che trasmette i dati a un puntatore (modo sbagliato di guardarli ma ho difficoltà a spiegarlo).

Inizialmente ho usato Sposta per popolare un array statico di byte con i dati che sono tornati, ma vorrei passare a un array dinamico di cui è nota la lunghezza al momento della chiamata. Ho provato diverse cose con vari risultati, alcuni ottengono i dati ma hanno violazioni dell'accesso pazzo altri non hanno errori ma ottengono dati non validi.

L'aggiunta di setlength all'array e quindi l'utilizzo di sposta , causa innanzitutto un array vuoto di lunghezza impostata e quindi non è possibile accedere ai dati tramite < strong> OutputData [0] come ho fatto quando era statico, nel debugger dopo lo spostamento tutto mostra come valore inaccessibile o altro.

Di seguito c'è qualcosa che ho provato dopo aver letto un articolo in cui l'oposit ha preso un array dinamico e ha fornito un puntatore a quell'indirizzo. Ha menzionato errori come dati orfani.

var
  Output: Pointer;
  OutputData: Array of byte;
  I: Integer;
begin
GetMem(Output, OutputLength.Value);
if SendPSPQuery(Char(DriveLetter[1]), cbxQuery.Items.IndexOf(cbxQuery.Text), Output, OutputLength.Value) = 0 then
  begin
    OutputData := @Output;
    for I := 0 to OutputLength.Value - 1 do
    begin
      edtString.Text := edtString.Text + Char(OutputData[I]);
    end;

Esistono vari altri elementi che utilizzano i dati di uscita per essere messi in stringa, esadecimale e cose simili.

Comunque, come posso prendere un puntatore mettere quei dati in un array dinamico e poi afferrarli nel modo in cui indirizzeresti ad un array.

Grazie.

È stato utile?

Soluzione

Per utilizzare un array dinamico con la procedura Move , devi passare il primo elemento dell'array. Ad esempio:

var
  Source: Pointer;
  SourceSize: Integer;
  Destination: array of Byte;

SetLength(Destination, SourceSize);
Move(Source^, Destination[0], SourceSize);

Si noti inoltre che il secondo parametro dereferenzia il puntatore. Questo perché Move prende il valore che stai copiando, non un puntatore al valore. Stai copiando le cose a cui punta il tuo puntatore, quindi è quello che devi passare a Sposta .

Per inciso, quella stessa sintassi funziona se anche Destination è un array statico. E hai ragione, questo non è specifico per Delphi 2009. È vero che risale a Delphi 4, che è quando sono state introdotte le matrici dinamiche. E Move ha avuto per sempre la stessa strana sintassi parametro non tipizzato .


Non allocare la tua memoria con GetMem e quindi digita il cast per fare in modo che il compilatore pensi che quello che hai sia un array dinamico. Non è . Le matrici dinamiche hanno conteggi di riferimento e campi di lunghezza che non avranno un normale buffer di byte e poiché non hai il controllo di tutto il codice generato dal compilatore per accedere alla matrice dinamica presunta, c'è il pericolo che il tuo programma proverà ad accedere i dati inesistenti della struttura dei dati.

Puoi fare in modo che la funzione PSP memorizzi i suoi dati direttamente in un array dinamico. Ecco un po 'di codice per farlo:

var
  Output: array of Byte;

SetLength(Output, OutputLength.Value);
if SendPSPQuery(Char(DriveLetter[1]),
                cbxQuery.Items.IndexOf(cbxQuery.Text),
                @Output[0],
                OutputLength.Value) = 0
then

Non è necessario liberare successivamente la memoria; il compilatore inserisce il codice per deallocare l'array dinamico quando Output esce dall'ambito e non ci sono altri riferimenti all'array. Questo codice accetta un array dinamico e lo passa come se fosse un normale buffer. Funziona ed è sicuro perché un array dinamico è, in effetti, un sottotipo di un vecchio buffer semplice. La funzione accetterà un puntatore al primo elemento dell'array e tratterà il puntatore come un puntatore a un mucchio di byte perché è esattamente quello che è. La funzione non ha bisogno di sapere che ci sono altre cose adiacenti a quei byte che il programma usa per la contabilità di array dinamici.


Se hai i tuoi dati in un buffer e vuoi trattare quel buffer come se esso fosse l'array, invece di copiare i dati in una struttura di dati separata, allora hai due opzioni.

  1. Dichiara un puntatore ad array statico, quindi digita il puntatore del buffer su quel tipo. Questa è la tecnica classica, e puoi vederla usata nel codice ovunque, specialmente nel codice che precede Delphi 4. Ad esempio:

    type
      PByteArray = ^TByteArray;
      TByteArray = array[0..0] of Byte;
    var
      ByteArray: PByteArray;
    
    ByteArray := PByteArray(Output);
    for i := 0 to Pred(OutputLength.Value) do begin
      {$R-}
      edtString.Text := edtString.Text + Chr(ByteArray[i]);
      {$R+}
    end;
    

    Le direttive $ R assicurano che il controllo dell'intervallo sia disattivato per quel codice poiché il tipo di array è dichiarato avere una lunghezza di 1. L'array è dichiarato con quella dimensione in parte da servire come indizio che non dovresti davvero dichiarare una variabile di quel tipo. Usalo solo attraverso un puntatore. D'altra parte, se sai quale sarà la dimensione massima adeguata dei dati, puoi utilizzare quella dimensione per dichiarare invece il tipo di array, quindi puoi mantenere attivo il controllo dell'intervallo. (Se normalmente il controllo della portata viene disabilitato, stai solo chiedendo problemi.)

  2. Dichiara il buffer come PByte anziché Puntatore e quindi utilizza il nuovo Delphi (a partire da Delphi 2009) supporto per il trattamento di tipi di puntatori arbitrari come puntatori di array . Nelle versioni precedenti, solo PChar , PAnsiChar e PWideChar supportavano questa sintassi. Ad esempio:

    var
      Output: PByte;
    
    for i := 0 to Pred(OutputLength.Value) do begin
      edtString.Text := edtString.Text + Chr(Output[i]);
    end;
    

    La direttiva del compilatore $ POINTERMATH non è richiesta per abilitare questa funzione per PByte perché quel tipo è dichiarato mentre quella direttiva è in vigore. Se si desidera eseguire operazioni con puntatori simili a C con altri tipi di puntatori , posizionare {$ POINTERMATH ON} prima del codice che utilizza la nuova sintassi estesa.


Come nota finale, non è necessario costruire le stringhe un carattere alla volta. È dispendioso in due modi. Innanzitutto, stai costruendo molte stringhe, ognuna con solo due byte più grandi della precedente. In secondo luogo, poiché stai memorizzando il risultato della stringa in un controllo di modifica, stai forzando l'implementazione del sistema operativo di quel controllo per allocare anche un sacco di nuove stringhe. Inserisci i tuoi dati in una stringa, quindi aggiungili tutti in una volta al controllo di modifica:

var
  OutputString: AnsiString;

SetString(OutputString, PAnsiChar(Buffer), OutputLength.Value);
edtString.Text := edtString.Text + OutputString;

Altri suggerimenti

Nevermind ... lol dopo 2 ore e mezza di pasticciata con questo ho finalmente capito qualcosa ... È un po 'disordinato dalle cose che ho provato ma funziona anche.

    type
  PDynByteArray = ^TDynByteArray;
  TDynByteArray = array of byte;

procedure TfrmMain.btnQueryClick(Sender: TObject);
var
  Output: Pointer;
  OutputData: PDynByteArray;
  WorkingData: Array of byte;
  DriveLetter: ShortString;
  I: Integer;
  HexOutput: String;
begin
edtSTRING.Clear;
memHEX.Clear;
GetMem(Output, OutputLength.Value);
DriveLetter := edtDrive.Text;
if SendPSPQuery(Char(DriveLetter[1]), cbxQuery.Items.IndexOf(cbxQuery.Text), Output, OutputLength.Value) = 0 then
  begin
    //Move(Output^,OutputData,56);
    OutputData := PDynByteArray(@Output);
    for I := 0 to OutputLength.Value - 1 do
    begin
      edtString.Text := edtString.Text + Char(OutputData^[I]);
    end;
    for I := 0 to OutputLength.Value - 1 do
    begin
      HexOutput := HexOutput + InttoHex(OutputData^[I],2) + ' ';
    end;
    memHex.Lines.Append(HexOutput);
    FreeMem(Output);
    memHex.SelStart := 0;
  end
else edtSTRING.Text := 'SCSI Command Failed';
end;

Puoi usare PByte. Con la direttiva {$ POINTERMATH ON} puoi usare questo puntatore come array di byte.

{$POINTERMATH ON}
var
  Output: Pointer;
  ar: PByte;
begin
  GetMem(Output, 100);
  ar:=Output;
  ShowMessage(IntToStr(ar[0])+IntToStr(ar[1])+'...');
end;

Ad ogni modo, il motivo per cui il disegno di output richiede tempo è perché stai guardando l'assegnazione edtString.text. Questo dovrebbe essere assegnato solo una volta, non in un ciclo. Ogni volta che lo si riassegna, è necessario elaborare molti livelli di roba, dalla concatenazione di stringhe fino al disegno del sistema operativo sullo schermo. Puoi creare prima una stringa e poi assegnarla alla fine nel peggiore dei casi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top