Introduction
Some time ago, we developed a set of printer software for our customers and made some research on calling printers in C.
---------------------------------------------
Problem
. NET Framework 1.1 provides us with a printersettings class to provide information about how documents are printed, including the printers that print documents. The static attribute installedprinters allows us to obtain the names of all the printers installed on the computer.
Unfortunately, this property only provides the name of the installed printer. There is no way to obtain information about the printer (such as the printer type. The problem arises because the customer cannot provide the printer SDK, so the printer screening (for commercial purposes, the customer requires the software to be output only when using their printer) it can only be identified by the printer driver.
----------------------------------------------
Solution 1 Use WMI to obtain printer information
WMI, Windows Management Instrumentation. Is a scalable system management structure. It uses a unified, standard-based, and scalable object-oriented interface. WMI provides you with standard methods for interacting with system management information and basic WMI APIs. WMI is mainly used by system management application developers and administrators to access and manage operating system information.
The system. Management class in. NET Framework provides support for WMI. Among them, managementobjectsearcher is used to retrieve the set of managementobject or managementclass Objects Based on the specified query or enumeration.
/** // <Summary>
/// Code 1: WMI search example
/// <Summary>
/// <Param name = "strdrivername"> driver name </param>
/// <Returns> return to the list of printers found </returns>
/// <Remarks> strdrivername supports "%" and "_" wildcard queries, similar to queries in SQL statements <remarks>
Public stringcollection getprintswithdrivername (string strdrivername)
{
Stringcollection scprinters = new stringcollection ();
String strcheck = "";
If (strdrivername! = "" & Strdrivername! = "*")
Strcheck = "where drivername like/'" + strdrivername + "/'";
String searchquery = "Select name from win32_printer" + strcheck;
Managementobjectsearcher searchprinters =
New managementobjectsearcher (searchquery );
Managementobjectcollection printercollection = searchprinters. Get ();
Foreach (managementobject printer in printercollection)
{
String printname = printer. properties ["name"]. value. tostring ();
Scprinters. Add (printname );
}
Searchprinters. Dispose ();
Printercollection. Dispose ();
Return scprinters;
}
The problem seems to have been basically solved, and the running program indeed obtains the correct printer list. However, after a period of time, the user found that sometimes the printer could not be correctly obtained. It seems that the stability of dotnet wmi calling is indeed a problem ......
WMI itself is quite powerful, and vbs can basically cover the most basic Windows operations. For more information, see the msdn documentation.
Http://msdn.microsoft.com/library/default.asp? Url =/library/en-US/wmisdk/WMI/wmi_start_page.asp
-------------------------------------------
Solution 2: Use WIN32API to get a printer
Turn around and go back to WIN32API again, helpless ...... No wonder C ++ is still so delicious .....
. NET provides us with dllimport to operate Unmanaged DLL (found that C # is so strong ~~~~ ).
The enumprinters function in winspool. DRV is mainly used. The Code is as follows:
[Dllimport ("winspool. DRV", setlasterror = true, charset = charset. Auto)]
[Return: financialas (unmanagedtype. bool)]
Private Static extern bool enumprinters ([financialas (unmanagedtype. U4)] printer_enum flags,
[Financialas (unmanagedtype. lpstr)] string sname,
Uint ilevel,
Intptr pprinterdesc,
Uint isize,
[Financialas (unmanagedtype. U4)] ref uint Ineeded,
[Financialas (unmanagedtype. U4)] ref uint ireturned
);
Note: The marshal attribute provides data delivery for hosted and unmanaged code.
The Win32 API of enumprinters is defined as follows:
Bool enumprinters (
DWORD flags, // printer Object Types
Lptstr name, // name of printer object
DWORD level, // information level
Lpbyte pprinterenum, // printer information Buffer
DWORD cbbuf, // size of printer information Buffer
Lpdword encoded needed, // bytes encoded ed or required
Lpdword pcreturned // Number of printers Enumerated
);
The problem is that enumprinters gets printer_info through level, while printer_info_2 is the driver, and printer_info_2 is not in C.
After checking the data for half a day, the Internet is basically the definition of printer_info_1, while printer_info_2 is different from printer_info_1. It also includes the devmode structure and the unmanaged structure ~~~~
Finally, we found that instead of defining a structure in C # To correspond to an unmanaged structure, we should directly use classes instead. Therefore, two classes are defined.
Printer_info_2 and devmode (Note: Since printer_info_2 only uses the devmode structure to receive information about the printer driver, only this class is defined, and no specific implementation is made for other classes ).
In printer_info_2, all data of the DWORD type corresponds to the int32 type, and all data of the lptstr, lpdevmode, and psecurity_descriptor correspond to the intptr pointer type.
To retrieve unmanaged data, we used the following function to obtain printer information.
.
Printer_info_2 Pi = new printer_info_2 ();
// Transfers data from the unmanaged memory to the managed memory
For (INT I = 0; I <numprinters; I ++)
{
Marshal. ptrtostructure (prinfo, Pi); // prinfo is the printer obtained from enumprinters above
String driver = marshal. ptrtostringauto (PI. pdrivername );
If (printerdriver = "" | driver. tolower (). indexof (printerdriver )! =-1)
{
// Handle the problem
}
Prinfo = new intptr (prinfo. toint32 () + marshal. sizeof (typeof (printer_info_2); // get the start of the next printer information segment
}
.
The problem is now basically solved. However, the call to unmanaged functions in C # And Data encapsulation between them are still difficult. You need to sort them out when you are free.
Source: http://spaces.msn.com/sharkoo/Blog/cns! D8e832ce4545af! 158. Entry
Supplement: In 2.0, the fixed keyword can be used to define a fixed-size array cache, rather than defining a number size as in 1.x. However, this method can only be used for structure (struct), but not for class definition.