|
EnumPrinters |
|
Description The EnumPrinters function enumerates available printers, print servers, domains, or print providers. C++ Syntax
PowerBASIC Syntax
Unicode version:
Parameters Flags [in] Specifies the types of print objects that the function should enumerate. This value can be one or more of the following values.
If Level is 4, you can only use the PRINTER_ENUM_CONNECTIONS and PRINTER_ENUM_LOCAL constants. Name [in] If Level is 1, Flags contains PRINTER_ENUM_NAME, and Name is non-NULL, then Name is a pointer to a null-terminated string that specifies the name of the object to enumerate. This string can be the name of a server, a domain, or a print provider. If Level is 1, Flags contains PRINTER_ENUM_NAME, and Name is NULL, then the function enumerates the available print providers. If Level is 1, Flags contains PRINTER_ENUM_REMOTE, and Name is NULL, then the function enumerates the printers in the user's domain. If Level is 2 or 5, Name is a pointer to a null-terminated string that specifies the name of a server whose printers are to be enumerated. If this string is NULL, then the function enumerates the printers installed on the local machine. If Level is 4, Name should be NULL. The function always queries on the local machine. When Name is NULL, setting Flags to PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS enumerates printers that are installed on the local machine. These printers include those that are physically attached to the local machine as well as remote printers to which it has a network connection. When Name is not NULL, setting Flags to PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME enumerates the local printers that are installed on the server Name. Level [in] Specifies the type of data structures pointed to by pPrinterEnum. Valid values are 1, 2, 4, and 5, which correspond to the PRINTER_INFO_1, PRINTER_INFO_2, PRINTER_INFO_4, and PRINTER_INFO_5 data structures. Windows 95/98/Me: The value can be 1, 2, or 5. Windows NT/2000/XP: This value can be 1, 2, 4, or 5. pPrinterEnum [out] Pointer to a buffer that receives an array of PRINTER_INFO_1, PRINTER_INFO_2, PRINTER_INFO_4, or PRINTER_INFO_5 structures. Each structure contains data that describes an available print object. If Level is 1, the array contains PRINTER_INFO_1 structures. If Level is 2, the array contains PRINTER_INFO_2 structures. If Level is 4, the array contains PRINTER_INFO_4 structures. If Level is 5, the array contains PRINTER_INFO_5 structures. The buffer must be large enough to receive the array of data structures and any strings or other data to which the structure members point. If the buffer is too small, the pcbNeeded parameter returns the required buffer size. Windows 95/98/Me: The buffer cannot receive PRINTER_INFO_4 structures. It can receive any of the other types. cbBuf [in] Specifies the size, in bytes, of the buffer pointed to by pPrinterEnum. pcbNeeded [out] Pointer to a value that receives the number of bytes copied if the function succeeds or the number of bytes required if cbBuf is too small. pcReturned [out] Pointer to a value that receives the number of PRINTER_INFO_1, PRINTER_INFO_2, PRINTER_INFO_4, or PRINTER_INFO_5 structures that the function returns in the array to which pPrinterEnum points. Return Value If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. Remarks Do not call this method in DllMain. If EnumPrinters returns a PRINTER_INFO_1 structure in which PRINTER_ENUM_CONTAINER is specified, this indicates that there is a hierarchy of printer objects. An application can enumerate the hierarchy by calling EnumPrinters again, setting Name to the value of the PRINTER_INFO_1 structure's pName member. The EnumPrinters function does not retrieve security information. If PRINTER_INFO_2 structures are returned in the array pointed to by pPrinterEnum, their pSecurityDescriptor members will be set to NULL. To get information about the default printer, call the GetProfileString function with the section name string set to "windows" and the key name string set to "device". The returned string contains the name of the default printer, the name of the printer DRV file, and the port to which the printer is attached. Windows 2000/XP: To get information about the default printer, call GetDefaultPrinter. Windows NT/2000/XP: The PRINTER_INFO_4 structure provides an easy and extremely fast way to retrieve the names of the printers installed on a local machine, as well as the remote connections that a user has established. When EnumPrinters is called with a PRINTER_INFO_4 data structure, that function queries the registry for the specified information, then returns immediately. This differs from the behavior of EnumPrinters when called with other levels of PRINTER_INFO_* data structures. In particular, when EnumPrinters is called with a level 2 (PRINTER_INFO_2) data structure, it performs an OpenPrinter call on each remote connection. If a remote connection is down, or the remote server no longer exists, or the remote printer no longer exists, the function must wait for RPC to time out and consequently fail the OpenPrinter call. This can take a while. Passing a PRINTER_INFO_4 structure lets an application retrieve a bare minimum of required information; if more detailed information is desired, a subsequent EnumPrinter level 2 call can be made. Windows 95/98/Me: To quickly enumerate local and network printers, use the PRINTER_INFO_5 structure. This causes EnumPrinters to query the registry rather than make remote calls, and is similar to using the PRINTER_INFO_4 structure as described in the preceding paragraph. Windows Vista: The printer data returned by EnumPrinters is retrieved from a local cache when the value of Level is 4. Examples The following table shows the EnumPrinters output for various Flags values when the Level parameter is set to 1. In the Name parameter column of the table, you should substitute an appropriate name for Print Provider, Domain, and Machine. For example, for Print Provider, you could use the name of the network print provider, or the name of the local print provider. To retrieve print provider names, call EnumPrinters with Name set to NULL.
|
가.프린터 열거
Win32 API의 놀라운 기능 중의 하나는 장치에 독립적이라는 것인데 인쇄할 때도 마찬가지로 프린터가 바뀐다고 해서 출력 코드가 바뀔 필요는 없다. 그래서 직접 프린터를 제어하는 것보다 훨씬 더 작은 코드로 복잡한 출력을 해 낼 수가 있는 것이다. 그러나 장치 독립적이라는 것은 굉장히 멋진 개념이지만 실제로는 진실이 아닐 수도 있다. 흑백 프린터에 컬러를 출력할 수 없고 플로터에 비트맵을 출력할 수 없는 것처럼 프린터의 특성에 따라 코드가 달라져야 하는 어쩔 수 없는 경우가 존재한다.
프린터는 굉장히 복잡한 물건이고 그러다 보니 선택할 수 있는 옵션이나 설정 상태가 다양하다. 프로그램이 프린터에 독립적으로 인쇄물을 찍어 내고 또 프린터의 특성을 충분히 활용하는 호환성을 확보하기 위해서는 프린터의 여러 가지 설정 정보를 알아 내고 작업에 맞게 옵션을 강제로 변경할 수 있어야 한다. 여기서는 프린터로부터 정보를 구하는 방법과 변경하는 방법에 대해 알아 볼 예정이되 프린터 정보에 관련된 API 들은 운영체제 버전에 따라 상이한 경우가 많고 플래그의 종류도 많아 본문에서는 꼭 필요한 부분만 보이도록 한다. 모든 옵션과 플래그를 다 설명하자면 오히려 핵심이 되는 내용이 가려지게 되므로 나머지 생략된 부분은 항상 레퍼런스를 참조하기 바란다.
먼저 시스템에 어떤 종류의 프린터가 설치되어 있는지를 조사해 보도록 하자. 윈도우즈는 한꺼번에 여러 대의 프린터를 설치할 수 있으며 인쇄를 하기 전에 사용자가 출력할 프린터를 선택할 수 있도록 해 준다. 그래서 인쇄를 하는 프로그램은 시스템에 설치된 프린터의 목록을 구할 수 있어야 하는데 다음 함수는 시스템에 설치된 프린터와 프린터 서버, 도메인 등의 목록을 조사해 준다.
BOOL EnumPrinters(DWORD Flags, LPTSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned);
지금까지 EnumFonts, EnumWindows 등의 여러 가지 열거 함수들을 사용해 왔는데 이 함수는 다른 열거 함수들과는 달리 콜백 함수를 사용하지 않고 배열에 모든 정보를 한번에 리턴해 준다. 그래서 사용하는 방법이 조금 특이하다. 이 함수의 인수는 두 개의 그룹으로 나눌 수 있는데 앞쪽 세 인수는 조사할 정보의 종류를 지정하고 뒤쪽 네 개의 인수는 조사된 결과를 돌려 받기 위한 버퍼를 제공한다. 첫번째 인수 Flags는 조사할 오브젝트의 종류를 지정하며 이 인수에 따라 Name, Level 인수에 지정할 수 있는 값들이 달라진다. Flags 인수에는 다음 플레그들의 조합을 지정할 수 있다.
|
플레그 |
설명 |
|
PRINTER_ENUM_LOCAL |
로컬 프린터의 이름을 조사한다. 단 95는 네트웍 프린터도 로컬과 동일하게 취급하므로 같이 조사된다. |
|
PRINTER_ENUM_NAME |
Name이 지정하는 프린터를 열거하는데 서버, 도메인, 인쇄 프로바이더 등을 열거할 수 있다. Name이 NULL이면 인쇄 프로바이더가 조사된다. |
|
PRINTER_ENUM_SHARED |
공유된 프린터만 조사된다. 이 옵션은 단독으로 사용될 수 없으며 다른 플래그와 같이 사용한다. |
|
PRINTER_ENUM_DEFAULT |
95 : 기본 프린터를 조사한다. NT에서는 이 플래그를 사용할 수 없다. |
|
PRINTER_ENUM_CONNECTIONS |
NT : 이전에 연결한 적이 있는 프린터의 목록을 조사한다. |
|
PRINTER_ENUM_NETWORK |
NT : 같은 도메인에 속한 프린터의 목록을 구한다. Level은 반드시 1이어야 한다. |
|
PRINTER_ENUM_REMOTE |
NT : 같은 도메인에 속한 네트웍 프린터와 서버의 목록을 구한다. Level은 반드시 1이어야 한다. |
Name인수는 Flags와 Level에 따라 사용할 수 있는가 없는가가 결정되는데 조사 대상에 대한 추가 정보라고 생각하면 된다. 예를 들어 특정 도메인내의 프린터 목록을 조사한다면 Name에 도메인명을 전달한다. Level은 조사할 정보의 레벨을 지정하는데 이 값에 따라 조사되는 정보의 상세함이 달라지며 요구되는 버퍼도 달라진다. 95/98에서는 1,2,5중 하나의 값을 지정해 줄 수 있고 NT에서는 4를 추가로 지정할 수 있다.
네 번째 이후의 인수는 조사한 정보를 리턴받기 위한 버퍼이다. 네번째 인수 pPrinterEnum에 열거된 정보들이 복사되는데 한꺼번에 여러 오브젝트의 정보가 리턴되므로 이 인수는 배열형이어야 한다. Level인수에 따라 이 배열은 PRINTER_INFO_*형이 된다. 여기서 *는 Level과 동일한 정수값인데 예를 들어 Level이 2라면 이 배열은 PRINTER_INFO_2 구조체 배열이어야 한다.
이 배열은 반드시 조사된 모든 정보를 다 담을 수 있을만큼 충분한 크기를 가져야 한다. cbBuf는 이 배열의 크기를 지정하는데 만약 크기가 충분하지 않을 경우 이 함수는 정보를 조사하는 대신 pcbNeeded에 필요한 버퍼의 크기를 리턴해 준다. 그래서 보통 처음 호출할 때는 cbBuf에 0을 전달하여 고의로 에러를 유발시키며 이때 리턴되는 pcbNeeded로 필요한 메모리 양을 알아낸다. 마지막 인수 pcReturned는 리턴된 구조체 배열의 배열 크기를 리턴해 주는데 이 값은 곧 조사된 오브젝트의 개수가 된다.
다음 예제는 이 함수로 조사할 수 있는 여러 가지 인쇄 오브젝트의 목록을 조사하여 열거한 것이다.
void EnumPrintObject()
{
PRINTER_INFO_1 *pi1;
PRINTER_INFO_2 *pi2;
PRINTER_INFO_4 *pi4;
DWORD cbNeed, cbReturn;
UINT i;
HDC hdc;
int y=1;
char Mes[256];
hdc=GetDC(hWndMain);
// 로컬 프린터 열거 - 레지스트리에서 얻음
lstrcpy(Mes,"로컬 프린터 열거 - 레지스트리");
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &cbNeed, &cbReturn);
pi4=(PRINTER_INFO_4 *)malloc(cbNeed);
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 4, (PBYTE)pi4, cbNeed, &cbNeed, &cbReturn);
for (i=0;i<cbReturn;i++) {
wsprintf(Mes,"프린터 이름: %s, 종류:%s ", pi4[i].pPrinterName, pi4[i].Attributes==PRINTER_ATTRIBUTE_LOCAL ? "로컬":"네트웍");
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
}
free(pi4);y++;
// 로컬 프린터 열거 - 실제 프린터에서 구함
lstrcpy(Mes,"로컬 프린터 열거 - 실제 프린터에서 구함");
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 2, NULL, 0, &cbNeed, &cbReturn);
pi2=(PRINTER_INFO_2 *)malloc(cbNeed);
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 2, (PBYTE)pi2, cbNeed, &cbNeed, &cbReturn);
for (i=0;i<cbReturn;i++) {
wsprintf(Mes,"프린터 이름: %s, 포트:%s, 용지:%d ", pi2[i].pPrinterName, pi2[i].pPortName, pi2[i].pDevMode->dmPaperSize);
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
}
free(pi2);y++;
// 인쇄 프로바이더 열거
lstrcpy(Mes,"인쇄 프로바이더 열거");
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
EnumPrinters(PRINTER_ENUM_NAME, NULL, 1, NULL, 0, &cbNeed, &cbReturn);
pi1=(PRINTER_INFO_1 *)malloc(cbNeed);
EnumPrinters(PRINTER_ENUM_NAME, NULL, 1, (PBYTE)pi1, cbNeed, &cbNeed, &cbReturn);
for (i=0;i<cbReturn;i++) {
wsprintf(Mes,"인쇄 프로바이더: %s, 설명:%s ", pi1[i].pName, pi1[i].pComment);
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
}
free(pi1);y++;
// 도메인내의 프린터 열거
lstrcpy(Mes,"도메인내의 프린터 열거");
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
EnumPrinters(PRINTER_ENUM_REMOTE, NULL, 1, NULL, 0, &cbNeed, &cbReturn);
pi1=(PRINTER_INFO_1 *)malloc(cbNeed);
EnumPrinters(PRINTER_ENUM_REMOTE, NULL, 1, (PBYTE)pi1, cbNeed, &cbNeed, &cbReturn);
for (i=0;i<cbReturn;i++) {
wsprintf(Mes,"프린터 이름: %s ", pi1[i].pName);
TextOut(hdc,10,y++*20,Mes,lstrlen(Mes));
}
free(pi1);y++;
ReleaseDC(hWndMain,hdc);
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
switch(iMessage) {
case WM_LBUTTONDOWN:
EnumPrintObject();
return 0;
case WM_PAINT:
hdc=BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
로컬 프린터를 열거하는 루틴만 보도록 하자. EnumPrinters 함수를 두번 호출하는데 Flags인수에 ENUM_PRINTER_LOCAL 플래그를 주었고 이 경우 Name 인수는 필요치 않으며 조사되는 정보의 레벨은 4번으로 지정하였다. 이때 pPrinterEnum에 NULL, cbBuf에 0을 전달하여 버퍼를 제공하지 않았는데 이렇게 되면 EnumPrinters 함수는 필요한 버퍼양을 cbNeed에 리턴해 준다. 리턴된 크기만큼 메모리를 할당하고 다시 EnumPrinters 함수를 호출하여 로컬 프린터의 목록을 조사하였다.
버퍼 크기를 0으로 전달함으로써 고의로 에러를 유발시켜 필요한 버퍼 크기를 알아낸 후 조사된 크기만큼 메모리를 할당한다. 물론 이때 필요한 캐스팅은 해 주어야 하며 할당된 버퍼를 다시 전달함으로써 원하는 정보를 획득하였다. 이런 식으로 사용 메모리가 가변적인 함수들은 두 번 호출하는 것이 정형화되어 있는데 앞으로 이런 함수들을 몇개 더 보게 될 것이며 보안 관련 함수들도 이런 유형이 많으므로 동작 원리와 사용 방법을 잘 익혀 두도록 하자.
나머지 열거 루틴도 Flags와 Level만 달라질 뿐 거의 동일하다. 실행 결과는 시스템에 설치된 프린터 목록에 따라 달라지는데 필자의 컴퓨터에서는 다음과 같이 출력되었다. 네트웍에 물려 있기 때문에 설치된 프린터가 많다.
조사된 정보는 for 루프를 돌며 cbReturn, 즉 조사된 오브젝트 개수만큼 출력되는데 이때 조사된 정보의 어떤 멤버를 참조할 수 있는가는 Level인수에 따라 달라진다. 얼마나 정보를 상세하게 조사할 것인가에 따라 리턴되는 구조체가 달라지며 따라서 정보의 양도 달라지는데 이렇게 레벨을 지정할 수 있도록 해 놓은 이유는 필요에 따라 원하는 정보만을 조사할 수 있도록 하기 위해서이다.
프린터 이름만 알면 되는데 굳이 프린터 설정이나 보안 속성, 현재 상태에 대한 정보까지 같이 조사할 필요는 없기 때문이다. 또한 어떤 정보는 실제로 네트웍에 접속해야만 알 수 있는 것도 있는데 이런 경우는 굉장히 시간이 많이 걸릴 수도 있다. 그래서 원하는 정보의 종류를 레벨로 선택함으로써 불필요한 시간을 낭비하지 않도록 해 놓은 것이다. 다음 도표는 Level 인수에 따라 조사되는 정보를 요약한 것이다.
|
레벨 |
설명 |
|
1 |
가장 기본적인 정보를 조사해 준다. 프린터의 이름, 설명 정도가 있다. |
|
2 |
가장 상세한 정보가 조사된다. 서버명, 공유명, 포트, 드라이버, 용지 방향, 프린터의 속성, 출력 속도 등 수십가지의 정보가 한꺼번에 조사된다. |
|
3 |
프린터의 보안 정보를 조사한다. SECURITY_DESCRIPTOR 구조체가 포함되어 있다. |
|
4 |
프린터 이름과 종류, 서버명 등의 일반적인 프린터 정보를 조사한다. |
|
5 |
상세한 프린테 정보를 조사한다. 이름, 포트, 속성, 타임아웃값 등이 포함된다. |
|
6 |
용지 걸림이나 토너 부족 등 프린터의 현재 상태를 조사한다. 6번 이후의 레벨은 95/98에서는 사용할 수 없으면 NT/2000이상에서만 사용할 수 있다. |
|
7 |
디렉토리 서비스와 관련된 프린터 정보를 조사한다. |
|
8 |
프린터의 전역 설정에 관한 초기화 정보 |
|
9 |
프린터의 사용자별 설정에 관한 초기화 정보 |
각각의 PRINTER_INFO_* 구조체가 어떻게 선언되어 있고 각 멤버의 의미가 무엇인가는 본문에서 정리하지 않기로 한다. 멤버가 단순히 많기만 한 정도가 아니라 운영체제 버전과 상황에 따라 의미가 달라지는 것들도 있기 때문에 필요할 때 레퍼런스를 찾아보기 바란다. 여기서는 대표적으로 가장 자주 사용되는 PRINTER_INFO_2 구조체를 구경만 해 보도록 하자.
typedef struct _PRINTER_INFO_2 {
LPTSTR pServerName;
LPTSTR pPrinterName;
LPTSTR pShareName;
LPTSTR pPortName;
LPTSTR pDriverName;
LPTSTR pComment;
LPTSTR pLocation;
LPDEVMODE pDevMode;
LPTSTR pSepFile;
LPTSTR pPrintProcessor;
LPTSTR pDatatype;
LPTSTR pParameters;
PSECURITY_DESCRIPTOR pSecurityDescriptor;
DWORD Attributes;
DWORD Priority;
DWORD DefaultPriority;
DWORD StartTime;
DWORD UntilTime;
DWORD Status;
DWORD cJobs;
DWORD AveragePPM;
} PRINTER_INFO_2, *PPRINTER_INFO_2;
멤버가 참 많은데 이름만 봐도 대충 그 의미를 짐작할 수 있도록 되어 있다. EnumPrinters 함수는 그다지 자주 사용되는 함수가 아니며 일반적인 사용예는 앞에서 보인 예제 정도만 이해하면 실무에 적용하기 충분하다.



