D open your own device on Windows / Linux

I am very new to D and I have a C ++ background.

I would like to try sending SCSI CDs using D lang. Is there a class in D that allows me to open my own device in Windows like the Windows CreateFile function ? I'm looking for a D file similar to sg_io_hdr_t .

Any ideas I can play with? If D can't do this, then I know I can write C ++ code and link to D.


source to share

3 answers

You can just call the Windows function CreateFile

directly (although you might need to use CreateFileA

or CreateFileW

instead, because D doesn't always define convenience names like C ++). There are three ways:

1) Some operating system headers are included in boot D. import core.sys.windows.windows;

will work for CreateFileA

(or W, which is a wide version of char (unicode)). WriteFile

is also present along with many other basic functions.

For Unix headers import core.sys.posix.unistd;

and friends can help. If you are using #include <foo.h>

in C, try import core.sys.posix.foo;

in D.

2) The Windows headers that come with D are really minimal. You can try downloading the more complete ones https://github.com/AndrejMitrovic/DWinProgramming/tree/master/WindowsAPI

For other libraries try a quick search here and maybe you will find the bindings http://code.dlang.org/ too

3) If they fail, you can always copy / paste the definitions of functions and structures from C to D. For example, if you need to ReadFileEx

, you find that it is not in core.sys.windows.windows

. You can call it simply by adding this to your file:

            BOOL ReadFileEx(HANDLE, LPVOID, DWORD, OVERLAPPED*, void*);


Definition you can get from MSDN, with some additional macros and such removed. Instead WINAPI

you write extern(Windows)

, then the rest of the function will be the same. (Be sure to keep the core.sys.windows.windows file, although you have definitions for things like DWORD.)

Then you can just call the function normally. You can also do extern(C)

for Unix functions from other libraries. Be sure to use c_long

and c_ulong

when you see long

and unsigned long

in C, though, since they are not necessarily the same as long

in D. These types are in import core.stdc.config;


Likewise, structs can be copied / pasted as needed into D and provided, again, just cast the member types to the D's equivalent, making sure they are in the same order. Council. If the struct is always only used by a pointer, you can just loop void*

around instead of the actual definition. You lose a bit of type safety, but it often works.



To add to Adam's answer:

As of DMD 2.066, you can use the function windowsHandleOpen

to link any Windows HANDLE

(obtained with CreateFile

) to a std.stdio.File

. This should allow you to use D std.stdio

with arbitrary Windows descriptor objects (devices, pipes, etc.).



With the help of Adam, I came up with this code ( rewrite this ):

import core.sys.posix.sys.ioctl  : ioctl;
import core.stdc.errno           : errno;
import std.stdio                 : write, writef, writeln, writefln, File;
import std.xml                   : isChar;

const int SG_IO = 0x2285;
const int SG_DXFER_FROM_DEV = -3;
const uint SG_INFO_OK_MASK = 1;
const uint SG_INFO_OK = 0;
const uint INQ_LEN = 6;

struct _sg_io_hdr
   int interface_id; /* [i] 'S' for SCSI generic (required) */
   int dxfer_direction; /* [i] data transfer direction  */
   ubyte cmd_len; /* [i] SCSI command length ( <= 16 bytes) */
   ubyte mx_sb_len; /* [i] max length to write to sbp */
   ushort iovec_count; /* [i] 0 implies no scatter gather */
   uint dxfer_len; /* [i] byte count of data transfer */
   void * dxferp; /* [i], [*io] points to data transfer memory
                  or scatter gather list */
   ubyte * cmdp; /* [i], [*i] points to command to perform */
   ubyte * sbp; /* [i], [*o] points to sense_buffer memory */
   uint timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */
   uint flags; /* [i] 0 -> default, see SG_FLAG... */
   int pack_id; /* [i->o] unused internally (normally) */
   void * usr_ptr; /* [i->o] unused internally */
   ubyte status; /* [o] scsi status */
   ubyte masked_status;/* [o] shifted, masked scsi status */
   ubyte msg_status; /* [o] messaging level data (optional) */
   ubyte sb_len_wr; /* [o] byte count actually written to sbp */
   ushort host_status; /* [o] errors from host adapter */
   ushort driver_status;/* [o] errors from software driver */
   int resid; /* [o] dxfer_len - actual_transferred */
   uint duration; /* [o] time taken by cmd (unit: millisec) */
   uint info; /* [o] auxiliary information */
} /* 64 bytes long (on i386) */

void writeBuffer(char[] buff, int length)
   for (int k = 0; k < buff.length; ++k)
      if ((k > 0) && (0 == (k % 8)))
      // breaks output of non-chars in char range, but who cares in this code
      if (isChar(buff[k]))
         write(" ", cast(char)(buff[k]), " ");
         writef("%02x ", buff[k]);

int main()
   _sg_io_hdr io_hdr;

   char[INQ_LEN] cdb = 0;
   char[32] senseBuffer = 0;
   char[96] inqBuffer = 0;

   cdb[0] = 0x12;
   cdb[4] = cdb.length;

   io_hdr.interface_id      = 'S';
   io_hdr.cmd_len           = cdb.length;
   io_hdr.cmdp              = cast(ubyte*)(cdb);
   io_hdr.mx_sb_len         = senseBuffer.length;
   io_hdr.sbp               = cast(ubyte*)(senseBuffer);
   io_hdr.dxfer_len         = inqBuffer.length;
   io_hdr.dxferp            = cast(ubyte*)(inqBuffer);
   io_hdr.dxfer_direction   = SG_DXFER_FROM_DEV;
   io_hdr.timeout           = 5000;

   auto file = File("/dev/sdb", "rb");
   int res = ioctl(file.fileno(), SG_IO, &io_hdr);

   if (res < 0)
      writeln("ERROR: inquiry SG_IO ioctl error ", errno);
   if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK)
      writeln("ERROR: io_hdr.info is not OK");

   writefln("INQUIRY duration=%u millisecs, resid=%d\n",
      io_hdr.duration, io_hdr.resid);

   writeln("INQUIRY inquiry buffer: ");
   writeBuffer(inqBuffer, inqBuffer.length);
   return 0;




All Articles