Search code examples
c#dllimporthandleioctlkernel32

C# - Possible to use IOCTL


I'm trying to code for a Point Of Sale system which allows for a "Cash Drawer" attachment. Code is provided in the manual for opening the cash drawer (in C++ using IOCTL). Since I am coding in C# .NET, is it possible to perform something similar from within C# or will I have to write some unmanaged code?

Am I able to get a handle to "\\.\ADVANSYS" from within C#? Do I need to use DLLImport?

Would appreciate it if someone could point me in the right direction.

// IOCTL Codes
#define GPD_TYPE 56053
#define ADV_OPEN_CTL_CODE CTL_CODE(GPD_TYPE, 0x920, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define ADV_STATUS_CTL_CODE CTL_CODE(GPD_TYPE, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS)
void OpenDrawer(UCHAR uWhichDrawer) // uWhichDrawer = 1 => CD#1, uWhichDrawer = 2 => CD#2
{
    HANDLE hFile;
    BOOL bRet
    UCHAR uDrawer = uWhichDrawer;

    // Open the driver
    hFile = CreateFile(TEXT("\\\\.\\ADVSYS"),
    GENERIC_WRITE | GENERIC_READ,
    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

    if (m_hFile == INVALID_HANDLE_VALUE)
    {
        AfxMessageBox("Unable to open Cash Drawer Device Driver!");
        return;
    }

    // Turn on the Cash Drawer Output (Fire the required solenoid)
    bRet = DeviceIoControl(hFile, ADV_CD_OPEN_CTL_CODE,
    &uDrawer, sizeof(uDrawer),
    NULL, 0,
    &ulBytesReturned, NULL);

    if (bRet == FALSE || ulBytesReturned != 1)
    {
        AfxMessageBox("Failed to write to cash drawer driver");
        CloseHandle(hFile);
        return;
    }
    CloseHandle(hFile);
}

Solution

  • The C++ is riddled with mistakes, not sure if I got it right. The best thing to do is to declare DeviceIoControl() with altered argument types so that it is easy to call. You also have to P/Invoke CreateFile because FileStream cannot open devices. It ought to look similar to this:

    using System;
    using System.IO;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    
    class Program {
        static void Main(string[] args) {
            IntPtr hdl = CreateFile("\\\\.\\ADVSYS", FileAccess.ReadWrite,
                FileShare.None, IntPtr.Zero, FileMode.Open,
                FileOptions.None, IntPtr.Zero);
            if (hdl == (IntPtr)(-1)) throw new Win32Exception();
            try {
                byte drawer = 1;
                bool ok = DeviceIoControl(hdl, CTLCODE, ref drawer, 1, IntPtr.Zero,
                    0, IntPtr.Zero, IntPtr.Zero);
                if (!ok) throw new Win32Exception();
            }
            finally {
                CloseHandle(hdl);
            }
        }
        // P/Invoke:
        private const uint CTLCODE = 0xdaf52480;
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CreateFile(string filename, FileAccess access,
              FileShare sharing, IntPtr SecurityAttributes, FileMode mode,
              FileOptions options, IntPtr template
        );
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool DeviceIoControl(IntPtr device, uint ctlcode,
            ref byte inbuffer, int inbuffersize,
            IntPtr outbuffer, int outbufferSize,
            IntPtr bytesreturned, IntPtr overlapped
        );
        [DllImport("kernel32.dll")]
        private static extern void CloseHandle(IntPtr hdl);
    }