Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi IOCTL von C nach Delphi portieren (https://www.delphipraxis.net/91832-ioctl-von-c-nach-delphi-portieren.html)

NetSonic 10. Mai 2007 15:18


IOCTL von C nach Delphi portieren
 
Ich habe folgenden C Quelltext vorliegen, den ich nach Delphi portieren möchte um die digitalen IO-Ports eines Industrie-Mainboards anzusprechen.

Code:
#define WDT_DEVICE         "\\\\.\\WDT_DEVICE"
#define WDT_DEVICE_NAME         L"\\Device\\WDT_DEVICE"
#define WDT_DOS_DEVICE_NAME      L"\\DosDevices\\WDT_DEVICE"

// Device type
#define WDT_TYPE         35001

// The IOCTL function codes from 0x800 to 0xFFF are for customer use.
#define IOCTL_SYS_WDT_SET_TIMEOUT   CTL_CODE(WDT_TYPE, 0x800, METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define IOCTL_SYS_WDT_START      CTL_CODE(WDT_TYPE, 0x801, METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define IOCTL_SYS_WDT_STOP      CTL_CODE(WDT_TYPE, 0x802, METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define IOCTL_SYS_WDT_RESTART      CTL_CODE(WDT_TYPE, 0x803, METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define IOCTL_SYS_DIO_READ      CTL_CODE(WDT_TYPE, 0x804, METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define IOCTL_SYS_DIO_WRITE      CTL_CODE(WDT_TYPE, 0x805, METHOD_BUFFERED, FILE_WRITE_ACCESS)
Wie muss die Umwandlung der IOCTL_SYS Funktionscodes aussehen? Diese müssen ja als CONST in Delphi definiert werden,
aber wir muss das aussehen? Vielleicht so...? Und wie rufe ich die dann auf?

Delphi-Quellcode:
CONST
  WDT_DEVICE = '\\.\WDT_DEVICE';
  WDT_TYPE = $35001;
  IOCTL_SYS_DIO_WRITE = $805;
Danke schonmal vorab, für eure Hilfe!

Robert Marquardt 10. Mai 2007 15:33

Re: IOCTL von C nach Delphi portieren
 
WDT_TYPE = 35001;
Das ist eine Dezimalzahl.

CTL_CODE ist ein Makro das ein DWORD per Bitschieben zusammensetzt. METHOD_BUFFERED und FILE_WRITE_ACCESS sind einfache Zahlenkonstanten.
Es kommen also einfache Zahlen heraus. Ich bin jetzt gerade zu muede das alles nachzuschlagen und auszurechnen.

shmia 10. Mai 2007 15:34

Re: IOCTL von C nach Delphi portieren
 
Code:
#define CTL_CODE(DeviceType, Function, Method, Access) (
  ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)
)
Das entspricht folgendem Delphi-Code
Delphi-Quellcode:
(DeviceType shl 16) or (Access shl 14) or (Function shl 2) or Method
Da es in Delphi keine Makros gibt, kannst du den Ausdruck mit dem Taschenrechner ausrechnen und als Konstante
hinschreiben oder du schreibst dir die Funktion CTL_CODE(DeviceType, Function, Method, Access:integer):integer und
verwendest statt Konstanten eben Variablen.

himitsu 10. Mai 2007 15:36

Re: IOCTL von C nach Delphi portieren
 
WDT_TYPE ist falsch - diese Zahl ist kein hexadezimaler Wert :warn:

CTL_CODE ist ja ein Makro, welches die Angaben zusammenrechnet, dieses kannst du auch selber machen und dann das ergebnis angeben (halt wo wie es bei dir schon aussieht).
> Die Rechenformel und Konstanten findest du ja im PSDK.


Zitat:

Zitat von NetSonic
Und wie rufe ich die dann auf?

Wie meinst du das?
Es sind ja alles nur einfache Konstanten.

NetSonic 10. Mai 2007 15:43

Re: IOCTL von C nach Delphi portieren
 
Das heißt, es könnte dann so aussehen...

Delphi-Quellcode:
const
     WDT_TYPE = 35001;

     WDT_DEVICE   = '\\.\WDT_DEVICE';
     WDT_DEVICE_NAME = '\Device\WDT_DEVICE';
     WDT_DOS_DEVICE_NAME = '\DosDevices\WDT_DEVICE';

     IOCTL_SYS_DIO_WRITE = (WDT_TYPE shl 16) or (2 shl 14) or ($805 shl 2) or 0;
     IOCTL_SYS_WDT_STOP = (WDT_TYPE shl 16) or (2 shl 14) or ($802 shl 2) or 0;

var
  Form1: TForm1;
  DriveHandle: THandle;
  n: DWord;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
    DriveHandle := CreateFile('\\.\WDT_DEVICE', GENERIC_READ OR GENERIC_WRITE, FILE_SHARE_READ OR
                                   FILE_SHARE_WRITE, NIL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL OR
                                   FILE_FLAG_NO_BUFFERING, 0);
 if (DriveHandle > 0) then
 begin
      DeviceIoControl(DriveHandle, IOCTL_SYS_DIO_WRITE, NIL, sizeof('0xf'), NIL, 0, n, NIL);
      CloseHandle(DriveHandle);
 end
 else begin
     MessageDlg('Fehler, kein (gültiges) Handle!', mtWarning, [mbOK], 0);
 end;

end;
oder hab ich es jetzt voll nicht richtig verstanden?!?!

Robert Marquardt 10. Mai 2007 15:53

Re: IOCTL von C nach Delphi portieren
 
Die Konstante WDT_DEVICE auch verwenden. Dazu ist sie da.
Der DeviceIoControl Aufruf ist falsch.

NetSonic 10. Mai 2007 15:58

Re: IOCTL von C nach Delphi portieren
 
Ok, die Konstante hab ich jetzt eingepflegt und der Kompiler mosert und sagt mir:

"Konstantenausdruck verletzt untere grenzen"

Steh ehrlich gesagt ein bissl auf'm Schlauch! Hat das mit dem fehlerhaften DeviceIOControl-Aufruf zu tun, den Du meinst?

Robert Marquardt 11. Mai 2007 07:16

Re: IOCTL von C nach Delphi portieren
 
Das Problem ist das WDT_TYPE Integer und nicht Cardinal ist. Indem man es in eine Hex-Konstante umwandelt wird der Typ der Konstante implizit Cardinal statt Integer. Das sollte die Fehlermeldung erlegen.
Delphi-Quellcode:
const
     WDT_TYPE = $88B9;

     WDT_DEVICE = '\\.\WDT_DEVICE';
     WDT_DEVICE_NAME = '\Device\WDT_DEVICE';
     WDT_DOS_DEVICE_NAME = '\DosDevices\WDT_DEVICE';

     IOCTL_SYS_DIO_WRITE = (WDT_TYPE shl 16) or (2 shl 14) or ($805 shl 2) or 0;
     IOCTL_SYS_WDT_STOP = (WDT_TYPE shl 16) or (2 shl 14) or ($802 shl 2) or 0;

var
  Form1: TForm1;
  DriveHandle: THandle;
  n: DWORD;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
    DriveHandle := CreateFile(WDT_DEVICE, GENERIC_READ orGENERIC_WRITE, FILE_SHARE_READ or
                                   FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or
                                   FILE_FLAG_NO_BUFFERING, 0);
    if DriveHandle <> INVALID_HANLDE_VALUE then
    begin
      DeviceIoControl(DriveHandle, IOCTL_SYS_DIO_WRITE, nil, sizeof('0xf'), nil, 0, n, nil);
      CloseHandle(DriveHandle);
    end
    else
      MessageDlg('Fehler, kein (gültiges) Handle!', mtWarning, [mbOK], 0);
end;
Der DeviceIoControl-Aufruf macht ueberhaupt keinen Sinn. Es wird ein Puffer mit Laenge fuer die Eingabedaten und ein Puffer mit Laenge fuer die Ausgabedaten uebergeben. Je nach Aufruf kannn einer oder beide der Puffer nil sein. Das haengt davon ab welchen Befehl (hier IOCTL_SYS_DIO_WRITE) man angibt. Wo keine Daten rein- oder rausgehen da braucht man auch keinen Puffer. Die Angabe dafuer ist aber nil fuer den Pufferzeiger und 0 fuer die Pufferlaenge.
"nil, sizeof('0xf')" ist aber eine unsinnige Angabe. Es soll etwas geschrieben werden (der Namensbestandteil _WRITE signalisiert das ueberdeutlich), aber es wird kein Puffer angegeben. "sizeof('0xf')" ist ein ebenso unsinniger Ausdruck. Soll das die Laenge des Strings '0xf' sein oder die Groesse eines Pointers? Gib doch mal den originalen C-Aufruf an.

NetSonic 11. Mai 2007 08:08

Re: IOCTL von C nach Delphi portieren
 
Ok, hier nachfolgend der originale c-aufruf

Code:
DWORD   nReturn;
WDTPARAM   cParam;

typedef struct tagWDTPARAM {
   unsigned char   timeout;
   unsigned char   data_b;
} WDTPARAM, *PWDTPARAM;

void CDigitalIODlg::Onwrite()
{   CHAR pbufio[256];
    UCHAR data_b;
   // TODO: Add your control notification handler code here
    m_edit1.GetWindowText(pbufio, sizeof(pbufio));   
   // MessageBox(pbufio,MB_OK);
    sscanf(pbufio, "%2x", &data_b); ;
   cParam.data_b = data_b;
   DeviceIoControl(((CDigitalIOApp*)AfxGetApp())->m_hDIO, IOCTL_SYS_DIO_WRITE,
   &cParam, sizeof(WDTPARAM), NULL, 0, &nReturn, NULL);
//   printf("Digital IO Write Successfully. \n");

//
}
Ich danke Dir schon mal für die Mühe, die Du Dir machst mir zu Helfen...

Robert Marquardt 11. Mai 2007 08:29

Re: IOCTL von C nach Delphi portieren
 
Wie du davon auf so einen Murks kommst ist mir unverstandlich.
sscanf parst einen String in dem ("%2x") ein Byte als zwei Hexziffern steht. Offensichtlich eine Eingabe des Users, denn der Text wird aus einem Editfeld geholt.
Delphi-Quellcode:
type
  PWDTPARAM = ^WDTPARAM;
  WDTPARAM = record
    timeout: Byte;
    data_b: Byte;
  end;

var
  cParam: WDTPARAM;
  nReturn: DWORD;

procedure TForm1.FormWrite(Sender: TObject)
begin
  cParam.data_b := IntToStr('$' + Edit1.Text);
  DeviceIoControl(hDIO, IOCTL_SYS_DIO_WRITE, @cParam, SizeOf(cParam), nil, 0, nReturn, NULL);
end;
Das habe ich hier mal reingehackt.

NetSonic 11. Mai 2007 08:50

Re: IOCTL von C nach Delphi portieren
 
Das ganze sieht jetzt so aus, allerdings mosert der kompiler...

Delphi-Quellcode:
type
  PWDTPARAM = ^WDTPARAM;
  WDTPARAM = record
    timeout: Byte;
    data_b: Byte;
  end;

const
     WDT_TYPE = $88B9; //35001;

     WDT_DEVICE   = '\\.\WDT_DEVICE';
     WDT_DEVICE_NAME = '\Device\WDT_DEVICE';
     WDT_DOS_DEVICE_NAME = '\DosDevices\WDT_DEVICE';

     IOCTL_SYS_DIO_WRITE = (WDT_TYPE shl 16) or (2 shl 14) or ($805 shl 2) or 0;
     IOCTL_SYS_WDT_STOP = (WDT_TYPE shl 16) or (2 shl 14) or ($802 shl 2) or 0;
var
  hDIO: THandle;
  cParam: WDTPARAM;
  nReturn: DWORD;

procedure TForm1.Button1Click(Sender: TObject);
begin
     hDIO := CreateFile(WDT_DEVICE, GENERIC_READ OR GENERIC_WRITE, FILE_SHARE_READ OR
                    FILE_SHARE_WRITE, NIL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL OR
                    FILE_FLAG_NO_BUFFERING, 0);
     cParam.data_b := IntToStr('$' + Edit1.Text);
     DeviceIoControl(hDIO, IOCTL_SYS_DIO_WRITE, @cParam, SizeOf(cParam), nil, 0, nReturn, NULL);
end;
Er sagt hier...
Zitat:

cParam.data_b := IntToStr('$' + Edit1.Text);
"Es gibt keine überladene Version von 'IntToStr', die man mit diesen Argumenten aufrufen kann", was man vielleicht so lösen könnte:

Delphi-Quellcode:
cParam.data_b := StrToInt('$' + Edit1.Text);
???? :gruebel:

und hier...
Zitat:

DeviceIoControl(hDIO, IOCTL_SYS_DIO_WRITE, @cParam, SizeOf(cParam), nil, 0, nReturn, NULL);
immer noch: "Konstantenausdruck verletzt untere Grenzen" sowie "Inkompatible Typen: 'Variant' und 'POverlapped'" :?:

Wie ich vorhin auf so einen Quatsch gekommen bin, ist mir selber ein Rätsel... Vielleicht liegt es daran, dass mir dieser Bereich noch mehr als neu ist. Nur leider komme ich nicht darum herum. Vorher habe ich die notwendigen Hardware-Zugriffe per USB und dem Zugriff auf eine DLL gelöst, nur müssen wir jetzt leider diese auf dem Mainboard integrierten IO-Ports nutzen und dafür gibt es leider keine DLL oder ähnliches. Es geht alles nur über die Windows-API und die DeviceIOControl-Aufrufe... Leider!

Robert Marquardt 11. Mai 2007 09:04

Re: IOCTL von C nach Delphi portieren
 
Falsche Funktion benutzt. Es soll StrToInt sein und nicht IntToStr. :oops:
Delphi-Quellcode:
cParam.data_b := Byte(StrToInt('$' + Edit1.Text));
Umzutypen sollte vielleicht auch noetig sein.
Delphi-Quellcode:
DeviceIoControl(hDIO, IOCTL_SYS_DIO_WRITE, @cParam, SizeOf(cParam), nil, 0, nReturn, nil);
Zuviel C in letzter Zeit.
Delphi-Quellcode:
IOCTL_SYS_DIO_WRITE = DWORD((WDT_TYPE shl 16) or (2 shl 14) or ($805 shl 2) or 0);
Das forciert den richtigen Typ.

NetSonic 11. Mai 2007 09:27

Re: IOCTL von C nach Delphi portieren
 
Strike! Jetzt klappt es. Hab den Hardware-Zugriff auch eben getestet und es funktioniert wie gewünscht! Danke für die tolle Unterstützung! :thumb:

Nachfolgend nochmal der komplette Quellcode, bzw. korrekte Abschnitt:

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TfrmMain = class(TForm)
    gboxWatchdog: TGroupBox;
    gboxIOOperations: TGroupBox;
    Button1: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

type
  PWDTPARAM = ^WDTPARAM;
  WDTPARAM = record
    timeout: Byte;
    data_b: Byte;
  end;

const
     WDT_TYPE = $88B9; //35001;

     WDT_DEVICE   = '\\.\WDT_DEVICE';
     WDT_DEVICE_NAME = '\Device\WDT_DEVICE';
     WDT_DOS_DEVICE_NAME = '\DosDevices\WDT_DEVICE';

     IOCTL_SYS_WDT_SET_TIMEOUT = DWORD((WDT_TYPE shl 16) or (2 shl 14) or ($800 shl 2) or 0);
     IOCTL_SYS_WDT_START = DWORD((WDT_TYPE shl 16) or (2 shl 14) or ($801 shl 2) or 0);
     IOCTL_SYS_WDT_STOP   = DWORD((WDT_TYPE shl 16) or (2 shl 14) or ($802 shl 2) or 0);
     IOCTL_SYS_WDT_RESTART = DWORD((WDT_TYPE shl 16) or (2 shl 14) or ($803 shl 2) or 0);
     IOCTL_SYS_DIO_READ   = DWORD((WDT_TYPE shl 16) or (2 shl 14) or ($804 shl 2) or 0);
     IOCTL_SYS_DIO_WRITE = DWORD((WDT_TYPE shl 16) or (2 shl 14) or ($805 shl 2) or 0);

var
  frmMain: TfrmMain;
  hDIO: THandle;
  cParam: WDTPARAM;
  nReturn: DWORD;

implementation

{$R *.DFM}

procedure TfrmMain.Button1Click(Sender: TObject);
begin
     hDIO := CreateFile(WDT_DEVICE, GENERIC_READ OR GENERIC_WRITE, FILE_SHARE_READ OR
                    FILE_SHARE_WRITE, NIL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL OR
                    FILE_FLAG_NO_BUFFERING, 0);

     if (hDIO > 0) then
     begin
          try
             cParam.data_b := Byte(StrToInt('$' + Edit1.Text));
          except
                cParam.data_b := Byte(StrToInt('$' + '0'));
          end;
          DeviceIoControl(hDIO, IOCTL_SYS_DIO_WRITE, @cParam, SizeOf(cParam), nil, 0, nReturn, nil);
          CloseHandle(hDIO);
     end
     else begin
          MessageDlg('Fehler, kein (gültiges) Handle!', mtWarning, [mbOK], 0);
     end;
end;

end.
Falls nochmal Fragen auftreten sollten, kann mir bestimmt wieder geholfen werden! Danke nochmal!

Robert Marquardt 11. Mai 2007 09:48

Re: IOCTL von C nach Delphi portieren
 
So ist es sauberer und einfacher.
Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TfrmMain = class(TForm)
    gboxWatchdog: TGroupBox;
    gboxIOOperations: TGroupBox;
    Button1: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
  public
  end;

type
  PWDTPARAM = ^WDTPARAM;
  WDTPARAM = record
    timeout: Byte;
    data_b: Byte;
  end;

const
     WDT_TYPE = $88B9; //35001;

     WDT_DEVICE  = '\\.\WDT_DEVICE';
     WDT_DEVICE_NAME = '\Device\WDT_DEVICE';
     WDT_DOS_DEVICE_NAME = '\DosDevices\WDT_DEVICE';

     IOCTL_SYS_WDT_SET_TIMEOUT = DWORD((WDT_TYPE shl 16) or (2 shl 14) or ($800 shl 2) or 0);
     IOCTL_SYS_WDT_START = DWORD((WDT_TYPE shl 16) or (2 shl 14) or ($801 shl 2) or 0);
     IOCTL_SYS_WDT_STOP  = DWORD((WDT_TYPE shl 16) or (2 shl 14) or ($802 shl 2) or 0);
     IOCTL_SYS_WDT_RESTART = DWORD((WDT_TYPE shl 16) or (2 shl 14) or ($803 shl 2) or 0);
     IOCTL_SYS_DIO_READ  = DWORD((WDT_TYPE shl 16) or (2 shl 14) or ($804 shl 2) or 0);
     IOCTL_SYS_DIO_WRITE = DWORD((WDT_TYPE shl 16) or (2 shl 14) or ($805 shl 2) or 0);

var
  frmMain: TfrmMain;

implementation

{$R *.DFM}

procedure TfrmMain.Button1Click(Sender: TObject);
var
  hDIO: THandle;
  cParam: WDTPARAM;
  nReturn: DWORD;
begin
     hDIO := CreateFile(WDT_DEVICE, GENERIC_READ OR GENERIC_WRITE, FILE_SHARE_READ OR
                    FILE_SHARE_WRITE, NIL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL OR
                    FILE_FLAG_NO_BUFFERING, 0);

     if hDIO <> INVALID_HANDLE_VALUE then
     begin
          cParam.timeout := 0;
          cParam.data_b := Byte(StrToIntDef('$' + Edit1.Text, 0));
          DeviceIoControl(hDIO, IOCTL_SYS_DIO_WRITE, @cParam, SizeOf(cParam), nil, 0, nReturn, nil);
          CloseHandle(hDIO);
     end
     else begin
          MessageDlg('Fehler, kein (gültiges) Handle!', mtWarning, [mbOK], 0);
     end;
end;

end.
Lokale Variablen sind besser. Dazu muss dann aber das Element timeout explizit gesetzt werden. Bei globalen Variablen wird es implizit auf 0 gesetzt, aber bei lokalen Variablen ist der Inhalt Zufall.

NetSonic 11. Mai 2007 11:14

Re: IOCTL von C nach Delphi portieren
 
ok, du hast recht! so ist es einfach besser!
Hab mein "kleines" Programm zum Testen jetzt auch so weit zusammen, dass ich den integrierten Watchdog-Timer ansprechen kann. Übers Wochenende geht's dann an die Einbindung in das derzeitige Steuerungsprogramm.

Und da es schon Freitag-Mittag ist, wünsch ich Dir / allen anderen schon mal ein schönes Wochenende, mit halbwegs vernünftigem Wetter. Hier regnet es leider nur und wenn man schon so früh im Jahr mit so viel Sonne verwöhnt wird, nervt einen dieses Wetter echt ab!

MfG
NetSonic


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:26 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz