Reprinted P/invoke call, from msdn

Source: Internet
Author: User
Call Win32 DLL through P/invoke in C #

Release date: 1/13/2005 | Updated on: 1/13/2005

Jason clark

DownloadCode: Net0307.exe (133kb)

Me
I noticed a trend in my recent programming. It was this trend that led to the topic of this month. Recently, based on Microsoft. NET Framework
ApplicationProgramCompleted a lot of Win32 InterOP. I am not saying that my application is full of custom InterOP code, but sometimes I will
Some minor but complex and inadequate content are encountered in the framework class library. by calling this Windows API, you can quickly reduce the trouble.

Therefore
In my opinion, the. NET Framework 1.0 or 1.1 class libraries have any functional limitations that Windows does not have. After all, 32-bit
Windows (whatever version) is a mature operating system that has served customers for more than 10 years. In contrast,. NET Framework is a new thing.

As more and more developers move production applications to managed code, it is natural for developers to study the underlying operating system more frequently to figure out some key functions-at least for the moment.

Value
Fortunately, the InterOP function of the Common Language Runtime Library (CLR) is called a platform call.
(P/invoke. In this column, I will focus on how to use P/invoke to call Windows API functions. When it refers to the CLR
When using the com InterOP function, P/invoke is used as a noun. When using this function, P/invoke is used as a verb. I am not going to directly introduce com
InterOP, because it is more accessible than P/invoke, but more complex, this is a bit self-contradictory, which makes the com InterOP
The discussion as a topic is not very concise.

Enter P/invoke

Start with a simple P/invoke example. Let's take a look at how to call the Win32 messagebeep function. Its unmanaged declaration is shown in the following code:

 
Bool messagebeep (
Uint utype // beep type
);

To call messagebeep, you need to add the following code to a class or structure definition in C:

 
[Dllimport ("user32.dll")]
Static extern Boolean messagebeep (uint32 beeptype );

Surprisingly, you only need this code to make the hosted Code call the unmanaged messagebeep
API. It is not a method call, but an external method definition. (In addition, it is close to a C #
The allowed direct port, so it is helpful to introduce some concepts based on it .) The possible calls from managed code are as follows:

 
Messagebeep (0 );

Note that the messagebeep method is declared as static. This is required by the P/invoke method, because
Windows APIs do not have consistent instance concepts. Next, note that this method is marked as extern. This indicates that the compiler uses
Therefore, you do not need to provide a method body.

If the method body is missing, have you noticed messagebeep?
Does the Declaration contain a method body? And mostAlgorithmDifferent hosting methods consist of intermediate language (IL) commands. The P/invoke method is only metadata, real-time (JIT)
The compiler connects the hosted code to a non-hosted DLL function at runtime. An important information required to execute the connection to the unmanaged world is to export the DLL of the unmanaged method.
. This information is provided by the dllimport custom attribute before the messagebeep method declaration. In this example, we can see that messagebeep
Unmanaged APIs are exported by user32.dll in windows.

So far, there have been two topics not introduced about calling messagebeep. Please review that the called code is very similar to the following code snippet:

 
[Dllimport ("user32.dll")]
Static extern Boolean messagebeep (uint32 beeptype );

The last two topics are data packet aling)
Topics related to actual method calls from managed code to unmanaged functions. To call the unmanaged messagebeep function, you can find the extern in the scope.
Messagebeep
Declare any managed code execution. This call is similar to any other call to a static method. It shares the same with any other managed method call in that it brings the need for data sending and processing.

C #
One of the rules is that its calling syntax can only access CLR data types, such as system. uint32 and system. boolean. C # apparently not recognized
The C-based data types (such as uint and bool) used in Windows APIs are only the C-language Type Definitions. So when
When you compile the Windows API function messagebeep as follows:

 
Bool messagebeep (uint utype)

External methods must be defined using the CLR type, as shown in the previous code snippet. The CLR type that needs to be different from the basic API function type but is compatible with it is one of the hard-to-use aspects of P/invoke. Therefore, I will use the complete section below this column to introduce data sending and processing.

Style

In C #, it is very easy to call the Windows api p/invoke. But if the class library refuses to make your application beep, you should try to call windows to make it do the work, right?

Yes
. But it is related to the method selected and has a great relationship! Generally, if a Class Library provides some way to implement your intent, it is best to use APIs instead of directly calling unmanaged code, because CLR
There is a big difference between the type and Win32 style. I can sum up my suggestions on this issue into one sentence. When you run p/invoke
Do not make the application logic directly belong to any external method or component. If you follow this small rule, you will often save a lot of trouble in the long run.

The code in Figure 1 shows the minimum additional code for the messagebeep external method I have discussed. Figure 1
There are no significant changes, but only some general improvements to the external method without packaging, which can make the work easier. From the top, you will notice
The complete type, which is dedicated to messagebeep. If I need to use the Windows API function playsound
To add support for playback waveforms, You can reuse the sound
Type. However, I am not angry with making public the type of a single public static method. After all, this is only the application code. It should also be noted that sound
Is sealed and defines an empty private constructor. These are just some details, so that users do not mistakenly derive classes from sound or create its instances.

Figure 1
The next feature of the Code in is that the actual external method where P/invoke appears is the private method of sound. This method is only composed of public messagebeep
The method is indirectly exposed, and the latter accepts parameters of the beeptypes type. This indirect extra layer is a key detail and provides the following benefits. First, we should introduce a future
The beep hosting method can repeatedly use the public messagebeep method to use managed APIs without changing the rest of the code in the application.

This wrapper
The second advantage of the method is that when you call P/invoke, you give up the right to avoid access conflicts and other low-level damages, which is usually caused by CLR
Provided. The Caching method can protect the rest of your application from access conflicts and similar problems (even if it does not do anything but just pass parameters ). This buffer method will be
Any potential errors introduced by the call are localized.

The third and last benefit of hiding a private external method behind the public packaging is that it provides the opportunity to add some minimal CLR styles to the method. For example, in Figure 1
. I also defined a bucket named beeptypes.
Its members correspond to the defined values used with the Windows API. Because C #
Definition is not supported. Therefore, managed enumeration types can be used to avoid the spread of magic numbers to the entire application code.

The last benefit of the packaging method for simple windows APIs
Functions (such as messagebeep) are insignificant. However, when you start calling more complex unmanaged functions, you will find that
Style conversion brings more and more benefits to CLR-friendly methods. The more you plan to reuse InterOP in the entire application
The more you need to carefully consider the packaging design. At the same time, I think it is not impossible to use CLR-friendly parameters in non-object-oriented static packaging methods.

DLL import attributes

Now
It is time to go deeper. Dllimportattribute
Type plays an important role. The main function of dllimportattribute is to instruct CLR which DLL to export the function you want to call. Related DLL
Is passed to dllimportattribute as a constructor parameter.

If you cannot determine which DLL you want to use
For Windows API functions, the Platform SDK documentation provides you with the best help resources. In Windows API
The function topic text is near the end. The SDK documentation specifies the. Lib file that must be linked by the C application to use the function. In almost all cases, the. Lib
The file has the same name as the system DLL file defining the function. For example, if the function requires the c Application to link to kernel32.lib, the function is defined in
In kernel32.dll. You can find the topic of the Platform SDK documentation for messagebeep in messagebeep. At the end of the topic, you will notice that the library file is user32.lib; this indicates that messagebeep is exported from user32.dll.

Optional dllimportattribute

In addition to the host DLL, dllimportattribute also contains optional attributes, four of which are particularly interesting: entrypoint, charset, setlasterror, and callingconvention.

Entrypoint
If you do not want the external hosting method to have the same name as the DLL export, you can set this attribute to indicate the exported DLL
The name of the function entry point. This is especially useful when you define two external methods that call the same unmanaged function. In addition, in Windows, you can bind their serial numbers to the exported
DLL function. If you need to do this, the entrypoint values such as "#1" or "#129" indicate the serial number value of the non-hosted function in the DLL instead of the function name.

CharsetFor character sets, not all versions of Windows are also created. Windows 9X
The series products lack important Unicode support, while the Windows NT and Windows CE series are used at the beginning.
Unicode. CLR running on these operating systems uses Unicode for internal representation of string and char data. But do not worry-when calling
Windows 9XWhen using an API function, CLR automatically converts it From Unicode to ANSI.

If
If the DLL function does not process text in any way, you can ignore the charset attribute of dllimportattribute. However, when Char or
When string data is part of the equation, set the charset attribute to charset. Auto. In this way, the CLR
Use the appropriate character set. If the charset attribute is not explicitly set, the default value is charset. ANSI. This default value is flawed because
InterOP on Windows 2000, Windows XP, and Windows NT
It negatively affects the performance of text parameter sending.

Select charset. ANSI or charset. Unicode explicitly
Charset value instead of charset. Auto: You explicitly specify an export function, which is specific to the two Win32 OS
. This is an example of the readdirectorychangesw API function, which only exists in Windows NT
And only supports Unicode. In this case, you should explicitly use charset. Unicode.

Sometimes, Windows
The relationship between character sets of APIS is not obvious. One way to check whether the function's c
Language header file. (If you are not sure which header file to view, you can view the header files of each API function listed in the Platform SDK documentation .) If you find that
If an API function is indeed defined as a macro mapped to a function name ending with a or W, the character set is related to the function you are trying to call. An example of Windows API functions is in
Getmessage API declared in winuser. H, you may be surprised to find that it has two versions: A and W.

SetlasterrorError handling is very important, but it is often forgotten during programming. When you call P/invoke, you may also face other challenges-handling the differences between Windows API error handling and exceptions in managed code. I can give you some suggestions.

For example
If you are using P/invoke to call a Windows API function, you can use getlasterror for this function.
To find the extended error information, set the setlasterror attribute
True. This applies to most external methods.

This causes the CLR to cache
Function setting error. Then, in the packaging method, you can call the system. runtime. interopservices. Marshal
Type to obtain the cached error value. My suggestion is to check that these expectations come from the API
Function error values, and a perceptible exception is thrown for these values. For all other failures (including unexpected failures ),
System. componentmodel
The value returned by Marshal. getlastwin32error is passed to it. If you look back at the code in Section 1, you will see that this method is used in the public packaging of the extern messagebeep method.

Callingconvention
The last or least important dllimportattribute I will introduce here is callingconvention. With this attribute, you can
CLR indicates which function call conventions should be used for parameters in the stack. Callingconvention. winapi
The default value is the best option, which is feasible in most cases. However, if this call does not work, you can check the Declaration header file in the Platform SDK to see what you call
Whether the API function is an abnormal API that does not meet the call criteria.

Generally, local functions (such as Windows API functions or C-runtime)
The call Convention describes how to push parameters into the thread stack or clear them from the thread stack. Most windows APIs
The function first pushes the last parameter of the function into the stack, and then the called function clears the stack. On the contrary, many c-runtime DLL
Functions are defined to push method parameters into the stack in the order they appear in the method signature, and stack cleanup is handed over to the caller.

Fortunately, P/invoke
You only need to make the peripheral devices understand the call conventions. Generally, the default value callingconvention. winapi is the best choice. Then
During the runtime DLL function and a few functions, you may need to change the Convention to callingconvention. cdecl.

Data delivery Processing

Quantity
Data delivery processing is a challenging aspect of P/invoke. When data is transmitted between hosted and unmanaged code, CLR
Many rules are followed, and few developers often encounter them until they can be remembered. Unless you are a class library developer, you usually do not have to know the details. To most effectively
When using P/invoke on CLR, even application developers who only occasionally need InterOP should still understand some basic knowledge about data sending.

In the rest of this month's column, I will discuss the data sending and processing of simple numbers and string data. I will start from the most basic digital data sending and processing, and then introduce simple pointer sending and string sending processing.

Number mails and logical scalar

Windows OS is mostly written in C. Therefore, the data type used by Windows APIS is either C type or C type remarked by type definition or macro definition. Let's take a look at data sending without pointers. For the sake of simplicity, we will focus on numbers and boolean values.

When passing parameters to Windows API functions by Using values, you need to know the answer to the following questions:

    • Is the data basically integer or floating-point?

    • If the data is an integer, is it signed or unsigned?

    • If the data is an integer, what is the number of digits?

    • If the data is float, is it single-precision or double-precision?

Sometimes the answer is obvious, but sometimes it is not. Windows APIs redefine the basic C data type in various ways. Figure 2 lists some common data types of C and Win32 and their specifications, as well as a Common Language Runtime Library type with matching specifications.

Connect
Normally, your code works as long as you select a CLR type whose specification matches the Win32 type of this parameter. However, there are some special cases. For example
The bool type defined in API is a signed 32-bit integer. However, bool is used to indicate that the Boolean value is true or
False. Although you do not need to use the bool parameter as the system. int32 value, if you use system. Boolean
Type to obtain a more suitable ing. The ing of character types is similar to bool, because a specific CLR type (system. Char) indicates the meaning of characters.

After learning this information, it may be helpful to gradually introduce the example. The beep topic is still used as an example. Let's try kernel32.dll low-level beep, which will beep through the computer's speaker. The Platform SDK documentation for this method can be found in beep. The local API is recorded as follows:

 
Bool BEEP (
DWORD dwfreq, // frequency
DWORD dwduration // duration in milliseconds
);

In terms of parameter sending, your job is to understand what CLR data types are compatible with the DWORD and bool data types used by beep API functions. Review 1 2
DWORD is a 32-bit unsigned integer, just like system. uint32 of the CLR type. This means you can use
The uint32 value is used as two parameters for sending data to beep. Bool return value is a very interesting situation, because the chart tells us that in Win32, bool is
A 32-bit signed integer. Therefore, you can use the system. int32 value as the return value from beep. However, CLR also defines
The system. boolean type is used as the meaning of the Boolean value, so it should be used instead. By default, CLR mails the system. boolean value
A 32-bit signed integer. The external method definition shown here is the result P/invoke Method for beep:

 
[Dllimport ("kernel32.dll", setlasterror = true)]
Static extern Boolean BEEP (
Uint32 frequency, uint32 duration );

Pointer Parameters

Many windows APIs
The function uses pointers as one or more of their parameters. Pointers increase the complexity of data encapsulation because they add an indirect layer. If no pointer exists, you can pass data in the thread stack. With a finger
You can transmit data through reference by pushing the memory address of the data into the thread stack. Then, the function indirectly accesses data through the memory address. The managed code indicates that there are many methods for attaching the indirect layer.
Type.

In C #, if the method parameter is defined as ref or out, data is passed through reference rather than through value. Even if you do not use InterOP
The same is true, but it is called from one managed method to another. For example, if system. int32 is passed through ref
Parameter, the address of the data is transmitted in the thread stack, rather than the integer itself. The following is an example of how to receive an integer by referencing it:

Void flipint32 (ref int32 num ){
Num =-num;
}

Here, the flipint32 method obtains the address of an int32 value, accesses data, reverse it, And then assigns the reverse value to the original variable. In the following code, the flipint32 method calls the variable of the program.XFrom 10 to-10:

 
Int32 x = 10;
Flipint32 (ref X );

You can reuse this capability in managed code to pass pointers to unmanaged code. For example, the fileencryptionstatus API function returns the file encryption status in the form of a 32-bit unsigned mask. This API is recorded as follows:

 
Bool fileencryptionstatus (
Lptstr lpfilename, // file name
Lpdword lpstatus // encryption status
);

Note that this function returns a Boolean value instead of its return value.
Indicates whether the call is successful. When the request succeeds, the actual status value is returned through the second parameter. It works by calling a program to direct the function to a DWORD.
Variable pointer, And the API function fills in the memory location pointed to by the status value. The following code snippet shows a call to the unmanaged fileencryptionstatus
Possible external method definitions of functions:

 
[Dllimport ("advapi32.dll", charset = charset. Auto)]
Static extern Boolean fileencryptionstatus (string filename,
Out uint32 status );

This definition uses the out keyword to indicate the by-ref parameter for the uint32 status value. Here I can also select ref
Keyword. In fact, the same machine code is generated during running. The out keyword is only a norm of the by-ref parameter #
The compiler indicates that the transmitted data is only transmitted outside the called function. On the contrary, if the ref keyword is used, the compiler assumes that data can be transmitted inside and outside the called function.

Toy
Another good aspect of the out and ref parameters in the Code is that the address is used as the by-ref
The variables passed by parameters can be a local variable in the thread stack, a class or structure element, or an element reference in an array with appropriate data types. This flexibility of the calling program makes
The by-ref parameter is a good starting point for sending buffer pointers and single value pointers. Only when I find ref or out
When the parameters do not meet my needs, I will consider sending pointers to more complex CLR types (such as classes or array objects ).

If you are not familiar with C syntax or call
For Windows API functions, it is sometimes difficult to know whether a pointer is required for a method parameter. A common indicator is to check whether the parameter type starts with the letter P or lp. For example:
Lpdword or pint. In these two examples, LP and P indicate that the parameters are a pointer, and they point to the Data Types of DWORD or
Int. However, in some cases, you can use the asterisk (*) in the C syntax to define an API function as a pointer. The following code snippet shows an example:

 
Void takesapointer (DWORD * pnum );

We can see that the only parameter of the above function is the pointer to the DWORD variable.

When P/invoke is used
When sending pointers, ref and out are only used to host the value types in the code. When the CLR type of a parameter uses struct
When defining a keyword, you can consider this parameter as a value type. Out and ref
It is used to encapsulate pointers to these data types, because usually value type variables are objects or data, and there is no reference to the value type in the managed code. On the contrary, when sending a reference type object, it is not required
Ref and out keywords, because the variable is already an object reference.

If you are not familiar with the differences between the reference type and value type, refer toMsdn
Magazine, you can find more information in the topic of the. NET column. Most CLR types are reference types. However, except for system. String and
System. Object. All primitive types (such as system. int32 and system. Boolean) are value types.

Opaque pointer: a special case

Sometimes in a Windows API, the pointer passed or returned by a method is not transparent, which means that the pointer value is a pointer technically, but it is not directly used by the Code. Instead, the Code returns the pointer to Windows for subsequent reuse.

A very common example is the handle concept. In Windows, the internal data structure (from the file to the button on the screen) is represented as a handle in the application code. The handle is actually an opaque pointer or a value with a pointer width. The application uses it to represent the internal OS structure.

In rare cases, API functions also define opaque pointers as pvoid or lpvoid types. In the definition of Windows API, these types mean that the pointer has no type.

When
When an opaque pointer is returned to your application (or your application expects an opaque pointer), you should encapsulate the parameter or return value as a special type in CLR-
System. intptr. When you use the intptr type, the out or ref parameter is usually not used, because the intptr
Indicates holding a pointer directly. However, If You encapsulate a pointer as a pointer, it is appropriate to use the by-ref parameter for intptr.

In the CLR
Type system, the system. intptr type has a special attribute. Unlike other base types in the system, intptr
There is no fixed size. On the contrary, its running size depends on the normal pointer size of the underlying operating system. This means that in 32-bit windows, intptr
The variable width is 32-bit, while in 64-bit windows, the Code Compiled by the real-time compiler regards the intptr value as 64
Bit value. This automatic size adjustment feature is useful when an opaque pointer is enclosed between hosted and unmanaged code.

Remember that any API function that returns or accepts a handle actually operates on an opaque pointer. Your code should mail the handle in windows to the system. intptr value.

You
You can forcibly convert the intptr value to an integer of 32 or 64 bits in the managed code, or convert the latter to the former. However, when Windows APIs are used
When using a function, pointers should be non-transparent, so they cannot be used in addition to being stored and passed to external methods. The two Special Cases of this "Storage-only and transfer-only" rule are when you need to pass
The NULL pointer value and the intptr value to be compared with the null value. To do this, you cannot forcibly convert zero to system. intptr. Instead
Intptr uses the int32.zero static public field to obtain the null value used for comparison or assignment.

Mail text

In
Text data is often processed during programming. Text creates some trouble for InterOP for two reasons. First, the underlying operating system may use Unicode
String, or ANSI. In rare cases, for example, the two parameters of the multibytetowidechar API function are inconsistent in the character set.

The
The two reasons are that when P/invoke is required, you also need to know that C and CLR have different ways to process text. In C
In fact, the string is only an array of character values, usually using null as the Terminator. Most Windows API functions process strings according to the following conditions:
ANSI, which is used as an array of character values. For Unicode, it is used as an array of wide character values.

Fortunately, CLR is designed to be quite flexible, and the issue can be easily solved when text is sent, without worrying about what Windows API functions expect from your application. Here are some important considerations to remember:

    • Does your application transmit text data to API functions or does API functions return string data to your applications? Or are they both?

    • What type of hosting should your external method use?

    • What format is the expected result of an API function?

Me
First, answer the last question. Most Windows API functions contain lptstr or lpctstr
Value. (From the function perspective) They are modifiable and unchangeable buffers, including
Array of ending characters. "C" indicates a constant, which means that the parameter information is not passed outside the function. "T" in lptstr indicates that the parameter can be UNICODE or
ANSI depends on the character set you selected and the character set of the underlying operating system. Because most string parameters in Windows APIs are one of these two types
Select charset. Auto in dllimportattribute, and CLR will work in the default mode.

However, some APIs
The function or custom DLL function uses different methods to represent strings. If you want to use such a function, you can use marshalasattribute
Modifies the string parameters of the external method, and indicates a string format different from the default lptstr. For more information about marshalasattribute, see
The topic of the marshalasattribute class platform SDK document.

Now
Let's take a look at the direction in which string information is transmitted between your code and unmanaged functions. There are two ways to know the transfer direction of information when processing strings. The first and most reliable method is
The purpose of the parameters. For example, if you are calling a parameter whose name is similar to createmutex with a string, you can imagine that the string information is from the application to the API
Function passed. At the same time, if you call GetUserName, the function name indicates that the string information is transmitted from this function to your application.

In addition to this comparison
In addition, the second method for finding the information transfer direction is to find the letter "C" in the API parameter type ". For example, GetUserName API
The first parameter of the function is defined as the lptstr type, which represents a long pointer to a unicode or ANSI string buffer. But createmutex
The parameter name is converted to ltctstr. Note that the type definition here is the same, but the addition of the letter "C" indicates that the buffer zone is a constant and API functions cannot be written.

Once you confirm whether a text parameter is only used as input or output, you can determine which CLR type is used as the parameter type. Here are some rules. If the string parameter is only used as the input, the system. string type is used. In managed code, strings are unchanged and suitable for buffers that are not changed by local API functions.

For example
If the string parameter can be used as input and/or output, the system. stringbuilder type is used. Stringbuilder
A type is a very useful class library type. It can help you build strings effectively, or pass the buffer to the local function, where the local function fills you with string data. Once the function is returned
You can obtain a String object by calling the tostring of the stringbuilder object.

The getmediapathname API function is used to show when to use string and when to use stringbuilder, because it has only three parameters: an input string, an output string, and a parameter that specifies the length of Characters in the output buffer.

Figure 3 shows the annotation-added unmanaged getaskpathname function documentation, which simultaneously specifies both input and output string parameters. It introduces the managed external method definition, as shown in 3. Note that the first parameter is enclosed as system. String because it is only used as the input parameter. The second parameter represents an output buffer, which uses system. stringbuilder.

Summary

Ben
The P/invoke function described in the monthly column is sufficient to call Many API functions in windows. However, if you use
InterOP, you will eventually find that you have provided a very complex data structure, and may even need to directly access the memory through pointers in the managed code. In fact, InterOP in the local code
It can be a real Pandora box that hides details and low-level bits in it. CLR, C #, and managed C ++ provide many useful functions. Maybe I will introduce the advanced
P/invoke topic.

At the same time, as long as you feel that the. NET Framework class library cannot play your voice or perform other functions for you, you can know how to seek help from the original and excellent Windows API.

Send your questions and comments to Jason to your dot-net@microsoft.com.

Jason clark trained and consulted Microsoft and wintellect (http://www.wintellect.com), former Windows NT and Windows 2000 Server team developer. He has co-authored programming server - side applications for Microsoft Windows 2000 (Microsoft Press, 2000 ). You can contact Jason through JClark@Wintellect.com.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.