Autor Beitrag
Peter18
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 489
Erhaltene Danke: 2


Delphi4
BeitragVerfasst: Mo 19.01.15 18:01 
Ein freundliches Hallo an alle,

anscheinend habe ich bei den Pointern in Pascal irgend etwas noch nicht richtig verstanden. Experimente mit der CAPI liefern Ergebnisse, doch was ich sehe scheint nicht richtig zu sein. Die Funktion "CAPI_GET_MESSAGE" gibt einen Pointer auf die eigentliche Nachricht zurück. Im Kopfteil stehen Informationen wie Länge und Art der Nachricht. Vielleicht habe ich die Schnittstelle auch nicht richtig deklariert, so das der Pointer in die "Wüste" zeigt.

Die Deklaration der Schnittstelle:
ausblenden Delphi-Quelltext
1:
2:
  Function  CAPI_GET_MESSAGE    (     D_AppID                : Longint;
                                  Var lpCapiBuffer           : Pointer    ): DWord; stdcallexternal 'Capi2032.dll';


Die Prozedur "CheckCall" wird von einem Timer gestartet:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
procedure TForm1.CheckCall( Sender: TObject );
var
  Buffer : Pointer;
  S      : String;

begin
  Res := CAPI_GET_MESSAGE( AppID, Buffer );

  S := '';
  if Res = $01104 then S := '   Warteschlange leer';
  if Res = $01102 then S := '   Command Error'     ;
  if Res <> $01104 then NoMsg := false;
  if not NoMsg then
  begin
    Form1.Memo1.Lines.Add( 'CAPI_GET_MESSAGE' );
    Form1.Memo1.Lines.Add( 'Res:  ' + IntToHex( Res,             8 ) + S );
    Form1.Memo1.Lines.Add( 'Buff: ' + IntToHex( Integer(Buffer), 8 )     );
    Form1.Memo1.Lines.Add( '' );
    if Res = $01104 then NoMsg := true;
  end;
  if Res = 0 then pLookAtMesssage( Buffer );
end;


Die Prozedur "pLookAtMesssage" soll die Nachricht auswerten:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure pLookAtMesssage( Buffer : Pointer );
var
  PBuffer : P_MessageHeader;

begin
  PBuffer := Buffer;
  Form1.Memo1.Lines.Add( 'Buffer:'  );
  Form1.Memo1.Lines.Add( 'Ptr:        ' + IntToHex( Integer(Buffer),     8 ) );
  Form1.Memo1.Lines.Add( 'Len:        ' + IntToHex( PBuffer.Length,      8 ) );
  Form1.Memo1.Lines.Add( 'AppId:      ' + IntToHex( PBuffer.AppId,       8 ) );
  Form1.Memo1.Lines.Add( 'Command:    ' + IntToHex( PBuffer.Command,     8 ) );
  Form1.Memo1.Lines.Add( 'Subcommand: ' + IntToHex( PBuffer.Subcommand,  8 ) );
  Form1.Memo1.Lines.Add( 'MsgNr:      ' + IntToHex( PBuffer.MsgNr,       8 ) );
  Form1.Memo1.Lines.Add( ''  );  
end;


Der Kopf ist folgendermaßen deklariert:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
Type MessageHeader = Record
  Length     : DWord;
  AppId      : DWord;
  Command    : Byte ;
  Subcommand : Byte ;
  MsgNr      : DWord;
end;

Type P_MessageHeader = ^MessageHeader;


"Subcommand" sollte einen Wert von $80 bis $83 haben,aber die Ergebnisse sehen so aus:
Zitat:
LISTEN_REQ
Res: 00000000
Msg: 00000000

CAPI_GET_MESSAGE
Res: 00000000
Buff: 0014E278

Buffer:
Ptr: 0014E278
Len: 0006000E
AppId: 00128105
Command: 00000001
Subcommand: 00000000
MsgNr: 00060000

CAPI_GET_MESSAGE
Res: 00001104 Warteschlange leer
Buff: 00000000

CAPI_GET_MESSAGE
Res: 00000000
Buff: 0014E278

Buffer:
Ptr: 0014E278
Len: 0006002F
AppId: 002B8202
Command: 00000001
Subcommand: 00000004
MsgNr: 80080001

CAPI_GET_MESSAGE
Res: 00001104 Warteschlange leer
Buff: 00000000
uall@ogc
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: Mo 19.01.15 22:49 
Der Record wird wohl falsch sein, nimm mal einen "packed record".
Wenn lpCapiBuffer zurückgegeben wird, kannst du statt "var" besser "out" nehmen.

Ansonsten schau mal hier:
www.rueckerweb.net/isdncapi.html

kann auch sein dass TotalLength nur ein WORD (und kein DWORD ist), dann verschiebt sich alles und in deinem Beispiel ist die länge dann 0006, die AppId 002f002b und der Command 82.
Solltest da nochmal in den Spezifikationen nachschauen.

_________________
wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
Peter18 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 489
Erhaltene Danke: 2


Delphi4
BeitragVerfasst: Di 20.01.15 16:09 
Hallo Daniel,

danke für Deine Antwort! "packed record" scheint nicht notwendig zu sein, den beides funktioniert gleichermaßen, aber der Tipp mit "Word" statt "DWord" war gut. Da in der "CAPI-Beschreibung" nur Prozessorregister angegeben waren, habe ich anscheinend einen Fehler eingebaut. Nun sehe ich die richtigen Daten mit:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
Type MessageHeader = packed Record
  Length     : Word;
  AppId      : Word;
  Command    : Byte;
  Subcommand : Byte;
end;

Der Link ist auch gut, den hab ich leider bei meiner Suche nicht gefunden. Wie die Rufnummer extrahiert wird scheint mir etwas umständlich, aber ich habe die Software noch nicht so weit studiert.

Meine Idee war einen Pointer mit dem Typen eines offenen Arrays auf die Nummer zu setzen und dann Byte für Byte zu lesen. Wenn er einem Pascal-String entspricht kann man ihn vielleicht direkt herauskopieren. Aber da ist wieder der "Pascal-Pointer" (C ist einfacher). Ich habe einen Typ mit offenen Array erstellt:
ausblenden Delphi-Quelltext
1:
Type Nr_Array = Array of Byte;					

Dann den Zeiger der darauf zeigt:
ausblenden Delphi-Quelltext
1:
Type P_Nr = ^Nr_Array;					

In der Procedure ist die Variable folgendermaßen deklariert:
ausblenden Delphi-Quelltext
1:
PNr : P_Nr;					

Wenn ich dann folgender maßen zugreife
ausblenden Delphi-Quelltext
1:
2:
    PNr := @PConnectInd.CalledPartyNumber;
    S   := IntToHex( PNr^[0], 2 );

erhalte ich einen Zugriffsfehler.

Ich hoffe auf einen Tipp, wo der Fehler liegt.

Grüße von der nebligen Nordsee (Ein Dichter liegt über dem Land)

Peter
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Di 20.01.15 16:17 
Für dynamische Arrays musst Du zunächst Speicher reservieren, das geht mit SetLength.
Peter18 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 489
Erhaltene Danke: 2


Delphi4
BeitragVerfasst: Di 20.01.15 16:56 
Hallo WasWeißDennIch,

auch Dir Dank für die Antwort! Aber auch da gibt es eine Zugriffsverletzung. Ich habe auch probiert den Pointer hoch zu zählen, Banane, Fehler. In der Hilfe steht zwar Addition auf Pointer ist möglich aber es gibt kein Beispiel, an dem ich mich orientieren könnte.

Grüße von der nebligen Nordsee

Peter
Peter18 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 489
Erhaltene Danke: 2


Delphi4
BeitragVerfasst: Mi 21.01.15 17:15 
Ein freundliches Hallo an alle,

schade keine weiteren Antworten, dann eine neue Pointerfrage und diese schließe ich ab. Der Teil funktioniert ja jetzt.

Grüße von der Nordsee

Peter
uall@ogc
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: Mi 21.01.15 21:26 
Standardmäßig ist "packed record" und "record" nicht das gleiche, da ohne "packed" die Werte 4-Byte aligned werden.

Zum Testen:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
type
  TTest = record // 8 Byte
    B: Byte;
    c: DWord;
  end;

  TTest2 = packed record // 5 Byte
    B: Byte;
    c: DWord;
  end;

begin
  Caption := Format('%d %d', [SizeOf(TTest), SizeOf(TTest2)]);
  // Ausgabe: 8 - 4
end;


Es kann aber sein, dass du Code eingebunden hast, der das Alignment austellt, näheres findest du unter:

docs.embarcadero.com...alignfields_xml.html

Wobei "packed" = {$A1} entspricht und ohne Angabe ein {$A4} verwendet wird.

Wenn du also Structs von c++ uebersetzt bzw. von der WinAPI, nimm besser ein "packed record".

_________________
wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
SMO
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 120
Erhaltene Danke: 18


D2005 Personal
BeitragVerfasst: Mo 16.02.15 18:50 
user profile iconPeter18 hat folgendes geschrieben Zum zitierten Posting springen:

Die Prozedur "CheckCall" wird von einem Timer gestartet:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
procedure TForm1.CheckCall( Sender: TObject );
// ...
    Form1.Memo1.Lines.Add( 'CAPI_GET_MESSAGE' );
// ...
  if Res = 0 then pLookAtMesssage( Buffer );
end;


Da "CheckCall" eine Methode von Form1 ist, brauchst du Form1 nicht nochmal explizit anzugeben. Es wäre auch besser, in "pLookAtMesssage" nicht explizit auf Form1.Memo1 zuzugreifen. Denn das sind globale Abhängigkeiten, die man vermeiden möchte; außerdem müsstest du sämtliche Referenzen anpassen, falls du mal "Form1" oder "Memo1" umbenennst (und keine moderne Delphi-Version mit Refactoring hast, die die Bezeichner automatisch umbenennen kann). Etwas besser wäre also:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
// Den Konventionen nach beginnen Typen mit T und Pointer mit P,
// aber diesen Konventionen muss man nicht unbedingt folgen.
type 
  PMessageHeader = ^TMessageHeader;
  TMessageHeader = packed record
    Length     : Word;
    AppId      : Word;
    Command    : Byte;
    Subcommand : Byte;
  end;          

procedure TForm1.CheckCall( Sender: TObject );
// ...
    Memo1.Lines.Add( 'CAPI_GET_MESSAGE' );
// ...
  if Res = 0 then pLookAtMesssage( Buffer; Memo1.Lines );
end;


procedure pLookAtMesssage( Buffer : PMessageHeader; InfoOutput: TStrings );
begin
  InfoOutput.Add( 'Buffer:'  );
  // Pointer auf Integer zu casten sollte man sich abgewöhnen, denn das geht
  // auf 64-Bit-Plattformen in die Hose, weil Pointer dort 64 Bit groß sind,
  // Integer aber immer noch 32. Benutze stattdessen UIntPtr oder NativeUInt.
  InfoOutput.Add( 'Ptr:        ' + IntToHex( UIntPtr(Buffer),    8 ) );
  InfoOutput.Add( 'Len:        ' + IntToHex( Buffer.Length,      8 ) );
  InfoOutput.Add( 'AppId:      ' + IntToHex( Buffer.AppId,       8 ) );
  InfoOutput.Add( 'Command:    ' + IntToHex( Buffer.Command,     8 ) );
  InfoOutput.Add( 'Subcommand: ' + IntToHex( Buffer.Subcommand,  8 ) );
  InfoOutput.Add( 'MsgNr:      ' + IntToHex( Buffer.MsgNr,       8 ) );
  // Alternative zu IntToHex: Format mit %x, achtstellig = %.8x
  // InfoOutput.Add( Format('MsgNr:      %.8x', [Buffer.MsgNr]));
  InfoOutput.Add( ''  );  
end;



Den Sinn und Zweck von "packed" hat uall@ogc bereits erklärt.
Es fehlt noch der wichtige Unterschied zwischen Statischen und dynamischen Arrays.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
type
  Nr_Array = Array of Byte;
  P_Nr = ^Nr_Array;

var
  PNr : P_Nr;
begin
  PNr := @PConnectInd.CalledPartyNumber;
  S   := IntToHex( PNr^[0], 2 );


Natürlich klappt das so nicht! Dynamische Arrays, wie auch Strings, sind bereits implizite Pointer. Sie zeigen auf einen dynamisch allozierten Speicherbereich, der vor der Adresse, auf die gezeigt wird, noch Zusatzinfos wie Länge und Referenzcount enthält. An der Adresse von "PConnectInd.CalledPartyNumber" stehen diese Daten nicht, weil es sich eben nicht um einen Delphi-DynArray-Typ handelt. Man sollte also keine Pointer auf Strings und Dynamische Arrays casten, wenn man nicht genau weiß, was man macht.
Was du suchst, ist ein offenes statisches Array, oder einen Byte-Pointer.


ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
// Methode 1: Standardtyp aus SysUtils:
//  PByteArray = ^TByteArray;
//  TByteArray = array[0..32767] of Byte;
var
  PNr : PByteArray;
begin
  PNr := PByteArray(@PConnectInd.CalledPartyNumber);
  S   := IntToHex( PNr[0], 2 );


// Methode 2: Eigener, "offener" Array-Typ.
// Bereichsüberprüfung sollte natürlich ausgeschaltet sein, sonst gibt es Fehlermeldungen
// wenn versucht wird, auf Elemente nach [0] zuzugreifen
type
  PNrArray = ^TNrArray;
  TNrArray = array [0..0of Byte;

var
  PNr : PNrArray;
begin
  PNr := PNrArray(@PConnectInd.CalledPartyNumber);
  S   := IntToHex( PNr[0], 2 );


// Methode 3: Die "eleganteste", der Standardtyp PByte (= ^Byte) hat standardmäßig
// Pointerarithmetik und Arrayzugriff aktiviert -> {$POINTERMATH ON}
// Jedenfalls in modernen Versionen von Delphi, ab 2009 glaube ich.
var
  PNr : PByte;
begin
  PNr := PByte(@PConnectInd.CalledPartyNumber);
  S   := IntToHex( PNr[0], 2 );