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 Provide us with
Printersettings Class to provide information about how the document is printed, including the printer that prints the document. Static attributes
Installedprinters We can 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, Full nameWindows 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 for system management applications.ProgramDevelopers and administrators can access and manage operating system information.
. NET FrameworkSystem. ManagementClass provides support for WMI, whereManagementobjectsearcherUsed to search by specified query or enumerationManagementobjectOrManagementclassObject set.
/**/ /// <Summary>
/// Code 1: WMI search example
/// <Summary>
/// <Param name = "strdrivername"> Driver name </Param>
/// <Returns> Back to the printer list </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)
{
StringPrintname=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 ~~~~ ).
Mainly used in winspool. DRVEnumprintersFunction,CodeAs 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 added needed, // Bytes already ed or required
Lpdword pcreturned // Number of printers Enumerated
);
The problem is coming again. enumprinters gets printer_info through level, while printer_info_2 is the driver of the printer. in C #, The printer_info_2 structure is not found, and the system is dizzy again .....
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 unmanaged memory to managed memory
For ( Int I = 0 ; I < Numprinters; I ++ )
{
Marshal. ptrtostructure (prinfo, Pi ); // Prinfo is the printer obtained from enumprinters.
String Driver = Marshal. ptrtostringauto (PI. pdrivername );
If (printerdriver = " " || driver. tolower (). indexof (printerdriver) ! = - 1 )
{
//Handle
}
Prinfo= NewIntptr (prinfo. toint32 ()+Marshal. sizeof (Typeof(Printer_info_2 )));//Start from obtaining 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.