【转载记录】Accessing Device Drivers from C#

来源:http://www.drdobbs.com/cpp/accessing-device-drivers-from-c/184416423/

Device Drivers are written largely in C or C++. No explicit support for Device Driver communication is included in the current .NET framework. David implements support with C# and discusses how to access Win32 APIs using the Platform Invocation Services and how to make that reusable from within the .NET framework with Overloading.

In the new Microsoft vision user space applications are written with managed code in C#, VB, Managed C++, J#, or other languages using the .NET framework. This article is for those writing user space applications for Microsoft Windows in C# that need to communicate with or control device drivers.

Device Drivers still must be written largely in C or C++. No explicit support for Device Driver communication is included in the current .NET framework. The focus of this article is how to implement support with C#. We will discuss how to access Win32 APIs using the Platform Invocation Services and how to make that reusable from within the .NET framework with Overloading.

The examples for this article will be in C#. Similar code could be done in VB.NET as well as with any other .NET language. C# appears to be the preeminent .NET language. Device Driver writers familiar with C++ should find the C# examples comprehensible even with a cursory familiarity with C#. Once written in C#, the code can be freely used with Managed VB, and so on.

Note that driver writers aren’t precluded from using other languages that generate appropriate binary code. Video Drivers typically have a lot of assembler code. One could write device drivers in assembler, Delphi, or in fact anything that can generate native system code. It’s just not common.

Namespaces

Namespaces are a logical naming scheme for grouping related types into logical categories of related functionality. This allows a heirarchical structure of classes and methods. Namespaces make it easier to browse and reference code and help resolve ambiguities between symbols of the same name. Example:

namespace VibrenNameSpace {
    class SomeClass     {
         // some code
    }
    namespace OS {
        class WinCE      {
            public void MyMethod() {
                // some code
            }
        }
    }
}

This allows us in C# to use the shorthand:

using VibrenNameSpace.OS ;
WinCE myVar = new WinCE();
myVar.MyMethod();

Further explanations of Namespaces can be found in the Visual Studio .NET help.

PInvoke

Device Driver access is platform specific and on platforms supporting the Win32 API, has a well documented interface. The Platform Invocation Services (PInvoke) allows new “managed” code in C# to interoperate seamlessly with older “unmanaged” code (written in any language) that is exported via a dll.

Note: Though there will be .NET implementations on other (non-Windows) platforms, they are not yet available. This article describes the Windows implementation only. The code presented was compiled and tested with Visual Studio .NET Beta 2 and the final release. It was tested against drivers built with the XP DDK for Windows 2000 and Windows XP (only).

For Win32, API functions are contained in system dll’s, including Kernel32.dll, User32.dll, Gdi32.dll, etc. For basic Device Driver communication we will only need Kernel32.dll.

The method of access to the functions in any system dll’s is the same. The access functions are defined in System.Runtime.InteropServices. A simple sample of using the MessageBox function is in Example 1 and will serve as an example of how to use PInvoke. It is similar to the Microsoft .NET Documentation Sample.

Example 1: The MessageBox function in C++ and in C#
// in C,C++ - Win32:

int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);

// To make accessible and use in C#:

using System.Runtime.InteropServices;

namespace System.Runtime.InteropServices {
   using System;
   using System.Runtime.InteropServices;

   public class Win32Method   {
     [DllImport("User32.dll", CharSet=CharSet.Auto)]
     public static extern int MessageBox(int hWnd,
        String text, String caption, uint type);
   }
}
public class Win32MessageBox {
   public static void Main(){
       Win32Method.MessageBox(0,"Win32 MessageBox",
        "C-Sharp Example", 0);
   }
}

Considering there is a MessageBox function already in System.Windows.Forms in the .NET framework it’s not a very useful sample, but it illustrates a couple of things:

First, this example extends System.Runtime.InteropServices. It illustrates the use of Namespaces in C#. The compiler expands “CharSet.Auto” to:

System.Runtime.InteropServices.CharSet.Auto

We could alternately have declared a new namespace. The sample also indicates that the C# String class is sufficient to handle LPCTSTR. Technically we could define an explicit unmanaged string and pass that into the Win32 API function, but that should seldom be necessary. To do so, we would define in our class:

[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=256 )]
public String lpFileName = null;

In general ’String’ should be sufficient.

Device Driver Access

To access a Device Driver for C#, we’ll require at least the following namespaces:

using System;
using System.Runtime.InteropServices;

We’re also going to use two other classes for optimization.

SuppressUnmanagedCodeSecurityAttribute allows managed code to call into unmanaged code without a stack walk, and ComVisibleAttributes tells the system the methods either are visible or not visible to COM. Security optimizations are described athttp://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguidnf/html/cpconsecurityoptimizations.asp.

We will use them explicity to show what namespace they’re in for example purposes only:

System.Runtime.InteropServices.ComVisible(false)
System.Security.SuppressUnmanagedCodeSecurityAttribute()

We have to decide where (in what namespace) to create our methods. We could create a new namespace or chose to extend one. I chose System.IO initially because I was communicating with another piece of code, so that seemed like a reasonable place to put it. System.Runtime.InteropServices would be another good place because we need that namespace anyway. This is completely implementation specific and isn’t constrained by the .NET framework at all.

namespace System.IO {
    using System.Runtime.InteropServices;
    using System;
    using System.IO;

    public class Win32Methods {

    }
}

It’s important to compare what we’re trying to implement with how we’d do it in C or C++. Starting with the most basic communication between a user space application and a Device Driver, assume we have a driver that wants to send us information, perhaps from a file. For a Driver that has a name exported to DosDevices, we’d have something in the (C or C++ code) driver like Example 2.

Example 2: Exporting a driver’s name to DosDevices
RtlInitUnicodeString(&usDriverName, L"\\DosDevices\\MyDriver");
    ...
status = IoCreateDevice(DriverObject,
        sizeof(MYDEVICE_EXTENSION),
        &usDriverName, FILE_DEVICE_UNKNOWN,
        FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);

Example 3 contains simplified C code that one might use to get a few bytes of data back from a Device Driver, using a device IO control code.

Example 3: Getting data back from a Device Driver
#include <winioctl.h>
#include <iostream.h>
#include "ioctls.h"                  // this has our IOCTL
int main(int argc, char* argv[]) {
    HANDLE hDevice ;
    char   sOutPut[512];
    DWORD  dwDummy;
    hdevice = CreateFile("\\\\.\\MyDriver", GENERIC_READ | GENERIC_WRITE,
        0, NULL, OPEN_EXISTING, 0, NULL);
    if (hdevice == INVALID_HANDLE_VALUE) {
        cout << "Can‘t open Driver" << endl;
        return -1;
    }
    if (DeviceIoControl(hDevice, IOCTL_READ_FILE, NULL, 0,
          sOutPut, sizeof(sOutPut), &dwDummy, NULL)) {
        sOutPut[dwUnused] = 0;
        cout << sOutPut << endl;
    } else {
        cout << "Error " << GetLastError()
            << " in call to DeviceIoControl" << endl;
    }
    CloseHandle(hdevice);
return 0;
}

The minimum Win32 APIs we’ll need to implement in C# are:

CreateFile()
CloseHandle()
DeviceIoControl()

A full implementation would also contain ReadFile() and WriteFile(). For reusability we’ll need to have access to all the various constants, like GENERIC_READ, et al, and have to be able to understand an IOCTL. We will need to use DWORDS, HANDLES, NULL, and possibly other types used by the listed APIs.

In some ways, this is the most labor-intensive part of using PInvoke for Win32 APIs. Because C and C++ are not strongly typed languages, the APIs often use NULL in place of fairly complex structures to support different operations with the same API. In the case of DeviceIoControl, we could do Input, Output, or Both with a single call. If we don’t do all of them then we’ll have a lot of NULL’s where structures would have been filled in with data. C# will require overloading to support this behavior. In this way, the methods we create will be reusable. C# has a ‘Null’ keyword to reduce but not eliminate the overloading required.

Example 4 shows code from the appropriate Win32 header file. We need to understand the translation of some data types. LPSECURITY_ATTRIBUTES will provide an example of translating C/C++ structures to C#. In our example we’re not actually going to use it, but we’ll translate it anyway for future use (reusability) and for illustrative purposes.

Example 4: The CreateFile() function
HANDLE CreateFile(
  LPCTSTR lpFileName,                         // File Name
  DWORD dwDesiredAccess,                      // Access Mode
  DWORD dwShareMode,                          // Share Mode
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // Security Descriptor
  DWORD dwCreationDisposition,                // How to Create
  DWORD dwFlagsAndAttributes,                 // File Attributes
  HANDLE hTemplateFile                        // Handle to Template File
);

There is some documentation on converting structures and unions. There is a class called System.Runtime.InteropServices. StructLayoutAttribute that can be used as follows:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto )]
public struct SECURITY_ATTRIBUTES {
    public int       nLength ;
    public IntPtr  lpSecurityDescriptor;
    public bool    bInheritHandle;
}

We can then access the data in C# as usual:

Win32Methods.SECURITY_ATTRIBUTES foo = new Win32Methods.SECURITY_ATTRIBUTES();
sa.nLength = ...

In this instance, Win32Methods is the class name we’ve chosen to implement this in.

This is documented in the .NET documentation under “Structures” and the “StructLayout Class,” but as shown, the usage doesn’t quite follow the name listed as the method. This is certainly a point to check after the final version of Visual Studio .NET ships. If you need to use other Win32 APIs, many of them have structures defined in the header files that will need converting in a similar fashion.

The purpose for the overloading in Example 5 should be clear. If I want to actually use lpSecurityAttributes, we have to have that type explicitly in the method definition. In most cases, we would pass a NULL to this method. The easiest way to do that is via an IntPtr to 0, so by overloading this function with another copy that has lpSecurityAttributes typed to IntPtr we can use this either way.

Example 5: Overloading

[DllImport("Kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]

public static extern int CreateFile(String lpFileName, 

                                  int dwDesiredAccess, 

                                  int dwShareMode,

                                  IntPtr lpSecurityAttributes, 

                                  int dwCreationDisposition,

                                  int dwFlagsAndAttributes, 

                                  int hTemplateFile);

[DllImport("Kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]

public static extern int CreateFile(String lpFileName,

                                  int dwDesiredAccess, 

                                  int dwShareMode,

                                  ref SECURITY_ATTRIBUTES lpSecurityAttributes, 

                                  int dwCreationDisposition,

                                  int dwFlagsAndAttributes, 

                                  int hTemplateFile);

This way we can implement any of the APIs that can take NULL in place of a structure or variable. In some cases where we translate the variable in C# to an Int or IntPtr, we might not have to do this. Further Overloading will allow us to use all the possible arrangements of data input to the Win32 API functions.

CloseHandle is simple. In C & C++, from the Win32 header is:

BOOL CloseHandle(HANDLE hObject);

This can be converted to C# as:

[DllImport(“Kernel32.dll”, ExactSpelling=true, CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool CloseHandle(int hHandle);

We are really using unmanaged code within the context of managed C#, so we still have to callCloseHandle after the CreateFile, just like with the Win32 API.

We could have made CloseHandle an int or a bool. That’s more or less dependent on how you plan to use it in the user application. In C++ a bool is an int, in C# it’s a distinct type.

Finally from the Win32 Headers, we have:

BOOL DeviceIoControl(
  HANDLE hDevice,
  DWORD dwIoControlCode,     // operation
  LPVOID lpInBuffer,        // input data
                           // buffer
  DWORD nInBufferSize,       // size of input
                           // data buffer
  LPVOID lpOutBuffer,        // output data
                           // buffer
  DWORD nOutBufferSize,      // size of output
                           // data buffer
  LPDWORD lpBytesReturned,   // byte count
  LPOVERLAPPED lpOverlapped  // overlapped
                           // information
);

LPOVERLAPPED is not straightforward. If we don’t need it, we’re almost done. First, let’s finish a working sample then get back to what to do about LPOVERLAPPED. In many cases, we really don’t need LPOVERLAPPED.

[DllImport(“Kernel32.dll”, CharSet=CharSet.Auto, SetLastError = true)]
public static extern bool DeviceIoControl(
  int hDevice,
  int dwIoControlCode,
  byte[] InBuffer,
  int nInBufferSize,
  byte[] OutBuffer,
  int nOutBufferSize,
  ref int pBytesReturned,
  int pOverlapped);

I’ve made a few simplifications, including getting the data back as an array of bytes. We could (and in the real world would) overload this with functions to return data in a series of required forms such as Strings, arrays of Strings, etc. To do that, we might have to do some marshalling of unprotected data, which we touched on briefly above.

The last things we need to actually talk to a Driver are some constants and to have the code understand what an IOCTL is. From the Win32 C/C++ header files the macro declaration for CTL_CODE is shown in Example 6.

Example 6: The macro declaration for CTL_CODE
#define CTL_CODE( DeviceType, Function, Method, Access ) (   ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) )

Some macros are hard to turn into nice functions. This one is easy! Along with CTL_CODE we’ll build DEVICE_TYPE_FROM_CTL_CODE for future use, though it won’t be used in this sample (See Example 7).

Example 7: CTL_CODE and DEVICE_TYPE_FROM_CTL_CODE
public static int CTL_CODE(int DeviceType, int Function, int Method,
    int Access) {
   return
     (((DeviceType) << 16)|((Access) << 14)|((Function) << 2)|(Method));
} 

public int DEVICE_TYPE_FROM_CTL_CODE(int ctrlCode)     {
    return (int)(( ctrlCode & 0xffff0000) >> 16) ;
}

We can combine the ideas and code snippets discussed to create Example 8. In the downloadable file, DeviceDriver.cs, I have a few more constants than I strictly need for this sample. They have been cut from Example 8 for brevity. Note that in this listing, I’ve extended the namespace System.IO rather than System.Runtime.InteropServices shown previously simply to show that any namespace could be created or extended. As shown, when declaring constants “int” for 0x80000000 (or larger) we used “unchecked”.

Example 8: Combining the code snippets

?


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

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

namespace System.IO {

    using System;

    using System.Runtime.InteropServices;

    using System.IO;

    [

    System.Runtime.InteropServices.ComVisible(false),

    System.Security.SuppressUnmanagedCodeSecurityAttribute()

    ]

    public class Win32Methods     {

        public const int

            INVALID_HANDLE_VALUE    = (-1),

            NULL                = 0,

            ...

            ERROR_SUCCESS        = 0,

            FILE_READ_DATA        = (0x0001),

            ...           

            FILE_SHARE_READ        = 0x00000001,

            ...

            OPEN_EXISTING        = 3,

            GENERIC_READ              = unchecked((int)0x80000000),

            ...           

            METHOD_BUFFERED         = 0,

            ...               

            METHOD_NEITHER          = 3,

            FILE_ANY_ACCESS         = 0,

            ...

            FILE_DEVICE_VIRTUAL_DISK = 0x00000024;

    [DllImport("Kernel32.dll", ExactSpelling=true, CharSet=CharSet.Auto,

      SetLastError=true)]public static extern bool CloseHandle(int

    hHandle);

  

    // CreateFile is is Overloaded for having SecurityAttributes or not

    [DllImport("Kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]

    public static extern int CreateFile(String lpFileName,

    int dwDesiredAccess, int dwShareMode,IntPtr lpSecurityAttributes,

    int dwCreationDisposition,int dwFlagsAndAttributes,

    int hTemplateFile);

    [DllImport("Kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]

    public static extern int CreateFile(String lpFileName, int

    dwDesiredAccess, int dwShareMode, SECURITY_ATTRIBUTES

    lpSecurityAttributes, int dwCreationDisposition,int

    dwFlagsAndAttributes,int hTemplateFile);

    // DeviceIoControl is Overloaded for byte or int data

    [DllImport("Kernel32.dll", CharSet=CharSet.Auto,

    SetLastError = true)] public static extern bool DeviceIoControl(

    int hDevice, int dwIoControlCode,  byte[] InBuffer, int

    nInBufferSize, byte[] OutBuffer,int nOutBufferSize,ref int

    pBytesReturned, int pOverlapped);

    [DllImport("Kernel32.dll", CharSet=CharSet.Auto,

    SetLastError = true)]public static extern bool DeviceIoControl(

    int hDevice, int dwIoControlCode,  int[] InBuffer, int nInBufferSize,

    int[] OutBuffer,int nOutBufferSize,ref int pBytesReturned, int

    pOverlapped);

    // These replace Macros in winioctl.h

    public static int CTL_CODE( int DeviceType, int Function,

    int Method, int Access ) {

      return (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2)

        | (Method) ) ;

    }    

    public int DEVICE_TYPE_FROM_CTL_CODE(int ctrlCode)     {

        return    (int)(( ctrlCode & 0xffff0000) >> 16) ;

    }

    [ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Auto )]

    public struct SECURITY_ATTRIBUTES     {

        public int        nLength ;             // DWORD

        public IntPtr       lpSecurityDescriptor;    // LPVOID

        public int          bInheritHandle;        // BOOL

    }                                               

    // End win32methods

}    // end

Now we need a simple test program to use this code. With Visual Studio .NET you can build a project and include two files based on Example 8 and Example 9. Alternately, you can include both files (combined into DeviceDriver.cs, available online) in one file and build them in the IDE or on the command line.

Example 9: CallDriver class

?


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

36

37

38

39

40

41

42

public class CallDriver {

   public static void Main() {

    // Here‘s how we‘d declare the sa, though we

    // won‘t be using it here

    Win32Methods.SECURITY_ATTRIBUTES sa

        = new Win32Methods.SECURITY_ATTRIBUTES();

    int hFileHandle = new int();

    hFileHandle = Win32Methods.INVALID_HANDLE_VALUE;

    hFileHandle = Win32Methods.CreateFile("\\\\.\\MyDriver",

        Win32Methods.GENERIC_READ | Win32Methods.GENERIC_WRITE,

        0, (IntPtr) 0, Win32Methods.OPEN_EXISTING,0,Win32Methods.NULL);

    if (hFileHandle == Win32Methods.INVALID_HANDLE_VALUE) {   

        MessageBox.Show("Cannot Open theDriver!", "LAME");

        // This is usually a place to throw an exception, perhaps by:

        // throw new FileNotFoundException(Res.GetString(Res.IOError));

        return ;

    }

    try     {           

        int IOCTL_READ_FILE = new int();

        // Note you get to define whatever code you want for the IOCTL

        IOCTL_READ_FILE = Win32Methods.CTL_CODE (

           Win32Methods.FILE_DEVICE_UNKNOWN, (int) 0x969,

           Win32Methods.METHOD_BUFFERED,Win32Methods.FILE_ANY_ACCESS);

        byte[] sOutput = new byte[512];

        byte[] Input = new byte[8] ;

        int bytesReturned = new int();

                

        if (Win32Methods.DeviceIoControl(hFileHandle,

           IOCTL_READ_FILE,Input,0,sOutput,512,ref bytesReturned,0 ))

            MessageBox.Show("Success", "IOCTL = ?" );

        else

            MessageBox.Show("Failure", "IOCTL = ?" );

            // show what‘s in sOutput

    // try - note normally we‘d have an except to handle errors

    finally { // cleanup

        Win32Methods.CloseHandle(hFileHandle);           

    }           

   }

}

If you’re a driver writer then you already have a driver you can try this on. If not, you can try any driver sample with virtually any IOCTL. Changing this to write data to a Driver should be clear, and I hope the overloading of types shows how to change the format of the data that will get sent to the driver.

One simple example you can try this on is the FILEIO driver sample from the Walter Oney bookProgramming the Windows Driver Model, from Microsoft Press. There are also many simple driver examples in the various Windows DDK’s (NT, W2K, XP, and so on).

SetupApi

To open a driver more typically one uses the SetupDiXXX functions. They are in Setupapi.dll. For Example:

?


1

2

3

SetupDiEnumDeviceInfo()

SetupDiGetClassDevs()

etc.

We can convert these APIs in the same manner as before, using Overloading as needed (see Example 10).

Example 10: Converting APIs

?


1

2

3

4

5

6

7

8

9

10

11

12

[DllImport("Setupapi.dll", CharSet=CharSet.Auto, SetLastError = true)]

public static extern bool SetupDiEnumDeviceInfo(

int DeviceInfoSet,int MemberIndex,ref SP_DEVINFO_DATA DeviceInfoData);

[DllImport("Setupapi.dll", CharSet=CharSet.Auto, SetLastError = true)]

public static extern int SetupDiGetClassDevs(

ref Guid ClassGuid, ref String Enumerator,int hwndParent,int Flags );

            

[DllImport("Setupapi.dll", CharSet=CharSet.Auto, SetLastError = true)]

public static extern int SetupDiGetClassDevs(

IntPtr ClassGuid, ref String Enumerator,int hwndParent,int Flags );

...

Some of the APIs need to return data unmanaged as shown in Example 11.

Example 11: Some of the APIs need to return data unmanaged

?


1

2

3

4

5

[DllImport("Setupapi.dll", CharSet=CharSet.Auto, SetLastError = true)]

public static extern bool SetupDiGetDeviceInstanceId(

int DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData,

[MarshalAs(UnmanagedType.LPWStr)] String DeviceInstanceId,

int DeviceInstanceIdSize, ref int RequiredSize);

GUIDS are supported by the .NET Framework:

?


1

2

Guid GUID_DEVINTERFACE_TOASTER = new

Guid(“781EF630-72B2-11d2-B852-00C04FAD5171”);

An example showing the use of many of these functions is TalkToToaster.cs (available online), which uses the Toaster sample driver from the Windows DDK and can be downloaded as part of this month’s source code.

Loose Ends

There are several loose ends that we have not covered here. We didn’t present an example of using SecurityAttributes, though this should be straightforward with the material presented here. We also didn’t present an example of using Overlapped IO.

There are a couple ways to do this. There is support for C# Overlapped structures in System.Threading. There are classes System.Threading.Overlapped and System.Threading.NativeOverlapped. There are methods Overlapped.Pack and Overlapped.Unpack to transfer data from a managed “Overlapped” class to an unmanaged “NativeOverlapped” structure. In managed code, these should be used if at all possible.

The RTM (final released version) documentation has the following information on the Overlapped Class:

“The Overlapped type supports the .NET Framework infrastructure and is not intended to be used directly from your code.”

Despite this comment, there is a good sample called HandleRef using Overlapped IO that uses PInvoke as well as the keyword “null” to avoid having to Overload the definitions of some classes if the need was solely to deal with a NULL passed in place of some data type.

When we get a buffer back from ReadFile or DeviceIoControl, we might have a block of data that we need to decode. We can use a Structure, or choose to parsing data directly. One way is to use System.Runtime.InteropServices.Marshal.ReadInt32 (or ReadInt64). We can extract what we want using, based on knowing where the data actually is. For example:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

public const int Offset0 = 0;

public const int Offset4 = 4;

byte[] pDataBuf = new byte[256];

// ... code to fill this from

// DeviceIoControl or

// etc. goes here...

int Value1 =

    Marshal.ReadInt32(

    (int)pDataBuf,(int)Offset0);

int Value2 =

    Marshal.ReadInt32(

    (IntPtr)((((long)pDataBuf)+

    (((long)Offset4)))));

//... etc;

Finally, it is common to use an int or IntPtr to store the results of a native handle. In most cases this is sufficient. There is a class, System.Runtime.Interopservices.HandleRef, which can be used to wrap the handle returned via PInvoke. This will keep the managed object from being garbage collected and ensure the handle is valid for further use with PInvoke.

In summary, PInvoke, the Platform Invocation Services, combined with Overloading allows for reusable access to all the Win32 system calls from inside a managed language like C#. In particular, it easily allows for all the ones we’d ever need to communicate with a Device Driver.

时间: 2024-10-02 00:54:24

【转载记录】Accessing Device Drivers from C#的相关文章

[中英对照]User-Space Device Drivers in Linux: A First Look

如对Linux用户态驱动程序开发有兴趣,请阅读本文,否则请飘过. User-Space Device Drivers in Linux: A First Look | 初识Linux用户态设备驱动程序 User-Space Device Drivers in Linux: A First Look Mats Liljegren Senior Software Architect Device drivers in Linux are traditionally run in kernel spa

Device Drivers

Types of Device Drivers Windows可能会有User-mode的驱动,但是我们只关注Kernel-Mode的驱动. WDM Drivers WDM是一种驱动模型,是比较常用的驱动模型. WDM可以分为以下几类: Bus Drivers 负责检测到连接到该总线的设备的与PnP/Power相关的事件,并且通知IO Manager, PnP Manager, Power Manager. Function Drivers 负责某一具体设备的driver,是狭义上的driver

linux device drivers - debugging之proc

在书籍"linux device drivers"的第四章,专门介绍驱动开发中的debugging技术. printk只是其中一种技术,这种技术要求printk打印消息,并且会写入到磁盘里的文件中,这会拖慢整个代码的执行速度. 还有其中的debugging技术,并且对代码执行速度的影响比prink小. 其中讲到了,我们可以让驱动程序在proc文件系统里面创建文件,并且将需要的debugging信息写入到这个文件里面. linux内核提供了相应的API,来和proc文件系统交互,例如创建

《Linux Device Drivers》 第十七章 网络驱动程序——note

简介 网络接口是第三类标准Linux设备,本章将描述网络接口是如何与内核其余的部分交互的 网络接口必须使用特定的内核数据结构注册自身,以备与外界进行数据线包交换时调用 对网络接口的常用文件操作是没有意义的,因此在它们身上无法体现Unix的"一切都是文件"的思想 网络驱动程序异步自外部世界的数据包 网络设备向内核请求把外部获得的数据包发送给内核 Linux内核中的网络子系统被设计成完全与协议无关 在网络世界中使用术语"octet"指一组8个的数据位,它是能为网络设备和

《Linux Device Drivers》第十二章 PCI驱动程序——note

简介 本章给出一个高层总线架构的综述 讨论重点是用于访问Peripheral Component Interconnect(PCI,外围设备互联)外设的内核函数 PCI总线是内核中得到最好支持的总线 本章主要介绍PCI驱动程序如果寻找其硬件和获得对它的访问 本章也会介绍ISA总线 PCI接口 PCI是一组完整的规范,定义了计算机的各个不同部分之间应该如何交互 PCI规范涵盖了与计算机接口相关的大部分问题 PCI架构被设计为ISA标准的替代品,有三个主要目标 获得在计算机和外设之间传输数据时更好的

《Linux Device Drivers》第十五章 内存映射和DMA——note

简单介绍 很多类型的驱动程序编程都须要了解一些虚拟内存子系统怎样工作的知识 当遇到更为复杂.性能要求更为苛刻的子系统时,本章所讨论的内容迟早都要用到 本章的内容分成三个部分 讲述mmap系统调用的实现过程 讲述怎样跨越边界直接訪问用户空间的内存页 讲述了直接内存訪问(DMA)I/O操作,它使得外设具有直接訪问系统内存的能力 Linux的内存管理 地址类型 Linux是一个虚拟内存系统,这意味着用户程序所使用的地址与硬件使用的物理地址是不等同的 有了虚拟内存,在系统中执行的程序能够分配比物理内存很

《Linux Device Drivers》第十四章 Linux 设备模型

简介 2.6内核的设备模型提供一个对系统结构的一般性抽象描述,用以支持多种不同的任务 电源管理和系统关机 与用户空间通信 热插拔设备 设备类型 对象生命周期 kobject.kset和子系统 kobject是组成设备模型的基本结构 对象的引用计数 sysfs表述 数据结构关联 热插拔事件处理 kobject基础知识 <linux/kobject.h> 嵌入的kobject 内核代码很少去创建一个单独的kobject对象,kobject用于控制对大型域相关对象的访问 kobject的初始化 首先

《Linux Device Drivers》第五章 并发和竞态——note

并发及其管理 竞态通常作为对资源的共享访问结果而产生 当两个执行线程需要访问相同的数据结构(或硬件资源)时,并发的可能性就永远存在 只要可能就应该避免资源的共享,但共享通常是必须的,硬件本质上就是共享的 访问管理的常见技术称为"锁定"或者"互斥" 信号量和互斥体 建立临界区:在任意给定的时刻,代码只能被一个线程执行 可以使用一种锁定机制,当进程在等待对临界区的访问时,此机制可让进程进入休眠状态 一个信号量本质上是一个整数值,它和一对函数联合使用,这一对函数通常称为P

《Linux Device Drivers》第十一章 核心数据类型——note

基本介绍 因为Linux多平台特性,不管是哪一个重要驱动力应该是便携 与内核代码相关的核心问题应该是访问的同时是数据项的已知长度.能力和利用不同的处理器 内核使用的数据类型主要分为三类 类似int这种标准C语言类型 类似u32这种有确定大小的类型 像pid_t这种用于特定内核对象的类型 本章将讨论在什么情况下使用这三种类型以及怎样使用 使用标准C语言类型 当我们须要"两个字节的填充符"或者"用四个字节字符串表示的某个东西"时.我们不能使用标准类型,由于在不同的体系架