프린터로 문서를 출력하기 위한 Windows API 호출 과정은 다음과 같다.
- 컴퓨터에 장착된 프린터를 찾아 그 중 하나를 선택한다. (EnumPrinters)
- 선택한 프린터를 열어 그 핸들을 얻는다. (OpenPrinter)
1. EnumPrinters 함수를 사용하여 프린터 정보 얻기
EnumPrinters
함수는 다음과 같이 선언되어 있다.
BOOL EnumPrinters(
_In_ DWORD Flags,
_In_ LPTSTR Name,
_In_ DWORD Level,
_Out_ LPBYTE pPrinterEnum,
_In_ DWORD cbBuf,
_Out_ LPDWORD pcbNeeded,
_Out_ LPDWORD pcReturned);
(참조: https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd162692(v=vs.85).aspx)
Level별 PRINTER_INFO 구조체
EnumPrinters 함수를 다루기에 앞서, Windows API에서 제공하는 프린터 정보 관련 구조체를 설명한다.
Windows API는 프린터 정보의 상세 정도에 따라 PRINTER_INFO_1
, PRINTER_INFO_2
, PRINTER_INFO_3
, PRINTER_INFO_4
, PRINTER_INFO_5
, PRINTER_INFO_6
등의 구조체를 제공한다. PRINTER_INFO_의 뒤에 붙는 숫자는 레벨(level)이라 부르며, Windows API에서 프린터에 대한 여러 정보 중 어떤 것을 선택적으로 얻을 지에 대한 옵션으로 쓰인다.
PRINTER_INFO_1
PRINTER_INFO_1
는 가장 간단한 정보(프린터 이름, 프린터에 대한 설명)를 나타낸다.typedef struct _PRINTER_INFO_1 {
DWORD Flags;
LPTSTR pDescription;
LPTSTR pName;
LPTSTR pComment;
} PRINTER_INFO_1, *PPRINTER_INFO_1;자세한 내용은 MSDN 레퍼런스(https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd162844(v=vs.85).aspx)를 참고한다.
PRINTER_INFO_2
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;자세한 내용은 MSDN 레퍼런스(https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd162845(v=vs.85).aspx)를 참고한다.
PRINTER_INFO_3
PRINTER_INFO_3
은 프린터의 보안 설정을 전달한다.typedef struct _PRINTER_INFO_3 {
PSECURITY_DESCRIPTOR pSecurityDescriptor;
} PRINTER_INFO_3, *PPRINTER_INFO_3;자세한 내용은 MSDN 레퍼런스(https://msdn.microsoft.com/en-us/library/windows/desktop/dd162846(v=vs.85).aspx)를 참고한다.
PRINTER_INFO_4
PRINTER_INFO_4
는 프린터의 위치 정보(원격에 있는지 로컬에 있는지에 대한 여부와 그 주소)를 나타낸다.typedef struct _PRINTER_INFO_4 {
LPTSTR pPrinterName;
LPTSTR pServerName;
DWORD Attributes;
} PRINTER_INFO_4, *PPRINTER_INFO_4;자세한 내용은 MSDN 레퍼런스(https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd162847(v=vs.85).aspx)를 참고한다.
PRINTER_INFO_5
PRINTER_INFO_5
는PRINTER_INFO_4
보다 더 상세한 정보를 전달한다.typedef struct _PRINTER_INFO_5 {
LPTSTR pPrinterName;
LPTSTR pPortName;
DWORD Attributes;
DWORD DeviceNotSelectedTimeout;
DWORD TransmissionRetryTimeout;
} PRINTER_INFO_5, *PPRINTER_INFO_5;자세한 내용은 MSDN 레퍼런스(https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd162848(v=vs.85).aspx)를 참고한다.
PRINTER_INFO_6
PRINTER_INFO_6
은 프린터의 상태(오프라인/온라인, 인쇄중인지 여부, 용지 걸림 여부 등)를 전달하기 위한 구조체이다.typedef struct _PRINTER_INFO_6 {
DWORD dwStatus;
} PRINTER_INFO_6, *PPRINTER_INFO_6;자세한 내용은 MSDN 레퍼런스(https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd162849(v=vs.85).aspx)를 참고한다.
PRINTER_INFO_7
PRINTER_INFO_7
은GetPrinter
와SetPrinter
에서 사용할 각종 상태 플래그를 전달하는 구조체이다.typedef struct _PRINTER_INFO_7 {
LPTSTR pszObjectGUID;
DWORD dwAction;
} PRINTER_INFO_7, *PPRINTER_INFO_7;자세한 내용은 MSDN 레퍼런스(https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd162850(v=vs.85).aspx)를 참고한다.
PRINTER_INFO_8
PRINTER_INFO_8
은 전역 범위에서 설정된 용지 방향 및 해상도 관련 정보를 전달하는 구조체이다.typedef struct _PRINTER_INFO_8 {
LPDEVMODE pDevMode;
} PRINTER_INFO_8, *PPRINTER_INFO_8;자세한 내용은 MSDN 레퍼런스(https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd162851(v=vs.85).aspx)를 참고한다.
PRINTER_INFO_9
PRINTER_INFO_9
는PRINTER_INFO_8
와 같은 내용이지만, 사용자 계정 별로 설정된 정보를 전달하는 구조체이다.typedef struct _PRINTER_INFO_9 {
LPDEVMODE pDevMode;
} PRINTER_INFO_9, *PPRINTER_INFO_9;자세한 내용은 MSDN 레퍼런스(https://msdn.microsoft.com/en-us/library/windows/desktop/dd162852(v=vs.85).aspx)를 참고한다.
컴퓨터에 장착된 프린터 열거하기
프린터를 열거하는 방법은 다음의 단계로 진행된다.
EnumPrinters
로 프린터 정보를 보관할 메모리의 크기 얻기- 위 단계에서 얻은 크기만큼 메모리 할당
- 할당한 메모리를
EnumPrinters
로 전달하여 프린터 정보를 복사하고 그 갯수를 얻기
첫 번째 과정은 프린터 정보가 열거된 배열을 얻기 위해 몇 바이트가 필요한지를 묻는 과정이다. 이를 위해 지정할 EnumPrinters
의 매개변수는 Flags
, Level
, pcbNeeded
, pcReturned
이며 나머지는 0
내지는 NULL
로 지정한다.
Flags는 비트 플래그로서 다음 상수들을 OR(|) 조합하여 사용한다.
- PRINTER_ENUM_LOCAL
- 로컬에 설치된 프린터들의 목록을 가져온다.
- PRINTER_ENUM_NAME
- Name 매개변수로 지정한 이름을 갖는 프린터를 모두 가져온다.
- PRINTER_ENUM_SHARED
- 공유된 프린터들의 목록을 가져온다.
- PRINTER_ENUM_CONNECTIONS
- 이전에 연결했던 적이 있는 프린터들의 목록을 가져온다.
- PRINTER_ENUM_NETWORK
- 네트워크 위치에 있는 프린터들의 목록을 가져온다. (레벨 1에 한함)
- PRINTER_ENUM_REMOTE
- 네트워크 위치에 있는 프린터 및 프린터 서버들의 목록을 가져온다. (레벨 1에 한함)
- PRINTER_ENUM_CATEGORY_3D
- 3D 프린터들의 목록을 가져온다,
- PRINTER_ENUM_CATEGORY_ALL
- 3D 프린터를 포함해서 모든 프린터들의 목록을 가져온다.
Level 1을 지정해서 컴퓨터에 연결된 프린터들의 기본 정보를 얻는 과정은 다음과 같다. 먼저, 프린터 정보를 얻기 위해 몇 바이트의 메모리가 필요한지를 묻는 과정이다. 두 개의 변수가 우선 선언되는데, cbPrinterInfo1
는 필요한 메모리의 크기를, nPrinterInfo1
는 컴퓨터에 연결된 프린터의 갯수를 나타낸다.
DWORD cbPrinterInfo1 = 0;
DWORD nPrinterInfo1 = 0;
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 1, NULL, 0, &cbPrinterInfo1, &nPrinterInfo1);
/* TODO: Something */
그러면 cbPrinterInfo1 변수를 통해 몇 바이트의 메모리가 필요한지 그 값이 반환된다. 이 때, nPrinterInfo1의 값은 의미가 없으므로 무시한다. cbPrinterInfo1를 통해 요구한 바이트만큼 메모리로부터 동적할당 받는다.
HLOCAL hLocal = NULL;
LPPRINTER_INFO_1 lpPrinterInfo1 = NULL;
DWORD cbPrinterInfo1 = 0;
DWORD nPrinterInfo1 = 0;
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 1, NULL, 0, &cbPrinterInfo1, &nPrinterInfo1);
hLocal = LocalAlloc(LHND, cbPrinterInfo1); assert(hLocal != NULL);
lpPrinterInfo1 = LocalLock(hLocal); assert(lpPrinterInfo1 != NULL);
/* TODO: Something */
LocalUnlock(hLocal);
LocalFree(hLocal);
할당된 메모리의 주소를 제공하여 다시 EnumPrinters
함수를 호출한다. 이 때는 Flags
, Level
, pcbNeeded
, pcReturned
와 함께 pPrinterEnum
, cbBuf
에도 매개변수를 지정한다.
HLOCAL hLocal = NULL;
LPPRINTER_INFO_1 lpPrinterInfo1 = NULL;
DWORD cbPrinterInfo1 = 0;
DWORD nPrinterInfo1 = 0;
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 1, NULL, 0, &cbPrinterInfo1, &nPrinterInfo1);
hLocal = LocalAlloc(LHND, cbPrinterInfo1); assert(hLocal != NULL);
lpPrinterInfo1 = LocalLock(hLocal); assert(lpPrinterInfo1 != NULL);
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 1, lpPrinterInfo1, cbPrinterInfo1, &cbPrinterInfo1, &nPrinterInfo1);
LocalUnlock(hLocal);
LocalFree(hLocal);
위와 같이 실행하면 동적할당한 메모리에 프린터에 대한 정보가 복사된다. 우선 그 목록을 출력해 본다.
HLOCAL hLocal = NULL;
LPPRINTER_INFO_1 lpPrinterInfo1 = NULL;
DWORD cbPrinterInfo1 = 0;
DWORD nPrinterInfo1 = 0;
DWORD dwIndex = 0;
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 1, NULL, 0, &cbPrinterInfo1, &nPrinterInfo1);
hLocal = LocalAlloc(LHND, cbPrinterInfo1); assert(hLocal != NULL);
lpPrinterInfo1 = LocalLock(hLocal); assert(lpPrinterInfo1 != NULL);
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 1, lpPrinterInfo1, cbPrinterInfo1, &cbPrinterInfo1, &nPrinterInfo1);
for (dwIndex = 0; dwIndex < nPrinterInfo1; dwIndex++)
{
printf("%d: %S\n", (int)i, lpPrinterInfo1[i].pName);
printf("- Description: %S\n", lpPrinterInfo1[i].pDescription);
printf("- Comment: %S\n", lpPrinterInfo1[i].pComment);
}
LocalUnlock(hLocal);
LocalFree(hLocal);
컴퓨터에 프린터가 설치되어 있다면 아래 결과와 같이 각 프린터에 맞는 정보가 출력될 것이다.
전체 소스코드는 다음과 같다.
#include <windows.h>
#include <assert.h>
#include <locale.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
HLOCAL hPrinterInfo1 = NULL;
LPPRINTER_INFO_1 lpPrinterInfo1 = NULL;
DWORD cbPrinterInfo1 = 0;
DWORD nPrinterInfo1 = 0;
// Wide Char를 출력하기 위한 로케일 설정
setlocale(LC_ALL, "");
// 프린터 정보를 열거하기 위해 메모리에 몇 바이트가 필요한지를 측정함.
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 1, NULL, 0, &cbPrinterInfo1, &nPrinterInfo1);
// 필요한 만큼의 메모리를 동적 할당
assert(hPrinterInfo1 = LocalAlloc(LHND, cbPrinterInfo1));
assert(lpPrinterInfo1 = LocalLock(hLocal));
// 프린터 정보를 메모리로 복사하기
assert(EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 1, (LPBYTE)lpPrinterInfo1, cbPrinterInfo1, &cbPrinterInfo1, &nPrinterInfo1));
// 프린터 개수만큼 반복하며 정보 출력
for (i = 0; i < nPrinterInfo1; i++)
{
printf("%d: %S\n", (int)i, lpPrinterInfo1[i].pName);
printf("- Description: %S\n", lpPrinterInfo1[i].pDescription);
printf("- Comment: %S\n", lpPrinterInfo1[i].pComment);
}
// 메모리 해제
lpPrinterInfo1 = NULL;
LocalUnlock(hLocal);
LocalFree(hLocal);
return 0;
}
OpenPrinter/ClosePrinter, StartDocPrinter/EndDocPrinter, StartPagePrinter/EndPagePrinter를 호출하기
OpenPrinter
/ClosePrinter
를 사용하여 위에서 열거한 프린터 중 하나를 골라 프린터를 열고 닫는다. 프린터를 연 후에는 StartDocPrinter
/EndDocPrinter
함수로 문서를 구분하고, 한 문서에 대해 StartPagePrinter
/EndPagePrinter
함수로 페이지를 구분한다. 호출 순서는 OpenPrinter
- { StartDocPrinter
- { StartPagePrinter
- EndPagePrinter
} - EndDocPrinter
} - ClosePrinter
의 순이며, 괄호는 반복 호출이 가능함을 의미한다.
Level별 DOC_INFO 구조체
PRINTER_INFO 구조체와 마찬가지로 문서 정보도 그 내용에 따라 몇 개의 레벨로 분화된다.
DOC_INFO_1
DOC_INFO_1
구조체는 일반적인 문서 정보를 프린터에게 전달한다.typedef struct _DOC_INFO_1 {
LPTSTR pDocName;
LPTSTR pOutputFile;
LPTSTR pDatatype;
} DOC_INFO_1;자세한 내용은 MSDN 레퍼런스(https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd162471(v=vs.85).aspx를 참조한다.)
DOC_INFO_2
DOC_INFO_2
구조체는 프린터와 데이터 교환을 할 것인지 여부를 전달한다.dwMode
가0
이면 일반적인 출력을 수행하고,DI_CHANNEL
이면WritePrinter
와ReadPrinter
함수를 통해 데이터를 교환한다.typedef struct _DOC_INFO_2 {
LPTSTR pDocName;
LPTSTR pOutputFile;
LPTSTR pDatatype;
DWORD dwMode;
DWORD JobId;
} DOC_INFO_2, *PDOC_INFO_2;자세한 내용은 MSDN 레퍼런스(https://msdn.microsoft.com/en-us/library/windows/desktop/dd162472(v=vs.85).aspx를 참조한다.)
DOC_INFO_3
DOC_INFO_3
구조체는 프린터와 데이터 교환을 할 것인지 여부를 전달한다.dwFlags
는DI_MEMORYMAP_WRITE
또는NULL
의 값을 가질 수 있다.typedef struct _DOC_INFO_3 {
LPTSTR pDocName;
LPTSTR pOutputFile;
LPTSTR pDatatype;
DWORD dwFlags;
} DOC_INFO_3, *PDOC_INFO_3;자세한 내용은 MSDN 레퍼런스(https://msdn.microsoft.com/en-us/library/windows/desktop/dd162473(v=vs.85).aspx를 참조한다.)
프린터를 열고 닫는 함수
프린터를 여는 함수는 OpenPrinter, 프린터를 닫는 함수는 ClosePrinter이다. 이 과정에서 프린터를 나타내는 핸들(HANDLE)이 사용된다.
BOOL OpenPrinter(
_In_ LPTSTR pPrinterName,
_Out_ LPHANDLE phPrinter,
_In_ LPPRINTER_DEFAULTS pDefault
);
BOOL ClosePrinter(
_In_ HANDLE hPrinter
);
pPrinterName
은 열려고 하는 프린터의 이름이다. 이 이름은 위의 EnumPrinter
함수를 통해 얻은 PRINTER_INFO_xxx
구조체의 pName
값을 그대로 쓴다.
phPrinter
및 hPrinter
는 프린터에 대한 핸들이다. OpenPrinter
는 phPrinter
를 통해 프린터 핸들을 반환한다.
pDefault
는 프린터 설정에 대한 초기 값이다. NULL을 지정해도 되고, MSDN 레퍼런스(https://msdn.microsoft.com/ko-kr/library/windows/desktop/dd162839(v=vs.85).aspx를 참고해서 구조체를 직접 구성해도 된다.
문서의 시작과 끝을 지정하는 함수
문서의 시작을 알리는 함수는 StartDocPrinter
이고 문서의 끝을 알리는 함수는 EndDocPrinter
이다.
DWORD StartDocPrinter(
_In_ HANDLE hPrinter,
_In_ DWORD Level,
_In_ LPBYTE pDocInfo
);
BOOL EndDocPrinter(
_In_ HANDLE hPrinter
);
hPrinter
는 OpenPrinter
를 통해 얻은 프린터 핸들이다.
Level
은 DOC_INFO_xxx
구조체의 레벨이다.
pDocInfo
는 문서 정보의 구조체이다.
페이지의 시작과 끝을 지정하는 함수
페이지의 시작과 끝을 지정하는 함수는 StartPagePrinter
와 EndPagePrinter
이다.
BOOL StartPagePrinter(
_In_ HANDLE hPrinter
);
BOOL EndPagePrinter(
_In_ HANDLE hPrinter
);
다음은 RAW 데이터를 프린터로 인쇄하는 예이다. 여기서 RAW 데이터란, 프린터가 인식 가능한 형태의 각종 인쇄 명령어 집합이다. 이 명령어 집합은 프린터 제작사마다 상이하며, HP의 Printer Control Language(PCL) 또는 Adobe의 PostScript가 일반적이다. 이와 관련된 내용은 아래 URL을 참조한다.
- Wikipedia: Page Description Language (https://en.wikipedia.org/wiki/Page_description_language)
- [MS-RPRN]: Glossary - MSDN - Microsoft (https://msdn.microsoft.com/en-us/library/cc244530.aspx#gt_a04e8872-d39e-4ae8-a870-b2e36b266302
LPBYTE lpDataRAW = NULL;
DWORD cbDataRAW = 0;
DWORD dwDataRAW = 0;
TCHAR szPrinter = NULL;
HANDLE hPrinter = NULL;
HANDLE hDocInfo1 = NULL;
LPDOC_INFO_1 lpDocInfo1 = NULL;
/* 생략: 프린터를 선택해서 그 이름을 szPrinter에 복사하는 과정 */
/* 생략: RAW 데이터를 lpDataRAW에 복사하는 과정 */
// 문서 정보를 위한 메모리를 동적 할당
hDocInfo1 = LocalAlloc(LHND, sizeof(DOC_INFO_1));
lpDocInfo1 = LocalLock(hDocInfo1);
// 문서 정보 세팅
lpDocInfo1->pDocName = TEXT("Untitled-Example1"); // 출력할 문서의 이름 (프린터 대기열에 표시될 이름)
lpDocInfo1->pOutputFile = TEXT(""); // 실제 출력으로 하지 않고, 파일로 인쇄할 경우 그 경로를 지정
lpDocInfo1->pDatatype = TEXT("RAW");
// 프린터 열기
if (OpenPrinter(szPrinter, &hPrinter, NULL))
{
assert(hPrinter != INVALID_HANDLE_VALUE);
// 프린터 대기열에 문서 추가
if (StartDocPrinter(hPrinter, 1, (LPBYTE)lpDocInfo1))
{
// 대기열 문서에 페이지 추가
if (StartPagePrinter(hPrinter))
{
// lpDataRAW: 프린터에 전달할 RAW 바이너리
// cbDataRAW: lpDataRAW의 크기 (바이트)
// dwDataRaw: WritePrinter가 프린터에 전달한 바이트 수
WritePrinter(hPrinter, lpDataRAW, cbDataRAW, dwDataRAW);
assert(cbDataRAW == dwDataRAW);
EndPagePrinter(hPrinter);
}
else
{
printf("Error: StartPagePrinter.\n");
}
EndDocPrinter(hPrinter);
}
else
{
printf("Error: StartDocPrinter.\n");
}
ClosePrinter(hPrinter);
}
else
{
printf("Error: OpenPrinter.\n");
}
'Application Programming Interface > Windows API' 카테고리의 다른 글
Win32 C++에서 Microsoft Excel 파일 다루는 방법 (0) | 2018.02.25 |
---|---|
Windows API로 콘솔(터미널) 입/출력하기 (0) | 2018.02.20 |
Windows NT 4.0 DDK 문서 - IMEAPPS.DOC [Part 2] (0) | 2014.10.02 |
Windows NT 4.0 DDK 문서 - IMEAPPS.DOC [Part 1] (2) | 2014.10.02 |
Windows DDK 문서 IMEIMES.DOC - 11. IME File Format and Data Structures (IME 파일 포맷 및 데이터 구조) #5 (완결) (0) | 2014.09.29 |