#define PERL_NO_GET_CONTEXT
/* Windows Vista required */
#define _WIN32_WINNT 0x600
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "Winspool.h"
#include "Stringapiset.h"
#ifndef WC_ERR_INVALID_CHARS
#define WC_ERR_INVALID_CHARS 0x80
#endif
static SV*
newSVdual(pTHX_ IV iv, const char *str) {
SV *sv = newSVpv(str, 0);
SvUPGRADE(sv, SVt_PVIV);
SvIOK_on(sv);
SvIV_set(sv, iv);
return sv;
}
/* Those definitions are missing from MinGW winspool.h */
#ifndef STRING_NONE
#define STRING_NONE 0x00000001L
#endif
#ifndef STRING_MUIDLL
#define STRING_MUIDLL 0x00000002L
#endif
#ifndef STRING_LANGPAIR
#define STRING_LANGPAIR 0x00000004L
#endif
/* #define DEBUG 1 */
#include "const-c.inc"
union printer_info_all {
PRINTER_INFO_1W pi1;
PRINTER_INFO_2W pi2;
// PRINTER_INFO_3W pi3;
PRINTER_INFO_4W pi4;
PRINTER_INFO_5W pi5;
// PRINTER_INFO_6W pi6;
PRINTER_INFO_7W pi7;
PRINTER_INFO_8W pi8;
PRINTER_INFO_9W pi9;
};
#define DEFAULT_BUFFER_SIZE ((sizeof(union printer_info_all) * 20))
static SV *
wchar_to_sv(pTHX_ const wchar_t *str, size_t wlen) {
if (str) {
size_t len;
if (!wlen) wlen = wcslen(str);
if (wlen) {
len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, str, wlen,
NULL, 0, NULL, NULL);
if (len) {
SV *sv = newSV(len + 2);
char *pv = SvPVX(sv);
if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, str, wlen,
pv, len + 1, NULL, NULL) == len) {
SvPOK_on(sv);
pv[len] = '\0';
SvCUR_set(sv, len);
SvUTF8_on(sv);
return sv;
}
}
Perl_warn(aTHX_ "Unable to convert wide char string to UTF8: %d, str: %p, wlen: %d", GetLastError(), str, wlen);
}
}
return &PL_sv_undef;
}
static wchar_t *
sv_to_wchar(pTHX_ SV *sv) {
if (SvOK(sv)) {
STRLEN len;
char *pv = SvPVutf8(sv, len);
if (len) {
wchar_t *buffer = NULL;
STRLEN wlen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
pv, len, NULL, 0);
if (!wlen) {
Perl_croak(aTHX_ "Unable to convert UTF8 string to wchar_t*");
return NULL;
}
Newx(buffer, wlen + 1, wchar_t);
SAVEFREEPV(buffer);
if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
pv, len, buffer, wlen) != wlen) {
Perl_croak(aTHX_ "Unable to convert UTF8 string to wchar_t*");
return NULL;
}
buffer[wlen] = L'\0';
return buffer;
}
return L"";
}
return NULL;
}
static SV *
pi1_to_sv(pTHX_ PPRINTER_INFO_1W pi1) {
HV *hv = newHV();
SV *sv = sv_2mortal(newRV_noinc((SV*)hv));
hv_stores(hv, "Flags", newSViv(pi1->Flags));
hv_stores(hv, "Description", wchar_to_sv(aTHX_ pi1->pDescription, 0));
hv_stores(hv, "Name", wchar_to_sv(aTHX_ pi1->pName, 0));
hv_stores(hv, "Comment", wchar_to_sv(aTHX_ pi1->pComment, 0));
return sv;
}
static SV *
pointl_to_sv(pTHX_ PPOINTL p) {
HV *hv = newHV();
SV *sv = sv_2mortal(newRV_noinc((SV*)hv));
hv_stores(hv, "x", newSViv(p->x));
hv_stores(hv, "y", newSViv(p->y));
return sv;
}
static SV *
devmod_to_sv(pTHX_ LPDEVMODEW dm) {
HV *hv = newHV();
SV *sv = sv_2mortal(newRV_noinc((SV*)hv));
DWORD fields = dm->dmFields;
hv_stores(hv, "DeviceName", wchar_to_sv(aTHX_ dm->dmDeviceName, 0));
hv_stores(hv, "SpecVersion", newSViv(dm->dmSpecVersion));
hv_stores(hv, "DriverVersion", newSViv(dm->dmDriverVersion));
hv_stores(hv, "Size", newSViv(dm->dmSize));
hv_stores(hv, "DriverExtra", newSViv(dm->dmDriverExtra));
hv_stores(hv, "Fields", newSViv(fields));
if (fields & DM_ORIENTATION)
hv_stores(hv, "Orientation", newSViv(dm->dmOrientation));
if (fields & DM_PAPERSIZE)
hv_stores(hv, "PaperSize", dmpaper_to_sv(aTHX_ dm->dmPaperSize));
if (fields & DM_PAPERLENGTH)
hv_stores(hv, "PaperLength", newSViv(dm->dmPaperLength));
if (fields & DM_PAPERWIDTH)
hv_stores(hv, "PaperWidth", newSViv(dm->dmPaperWidth));
if (fields & DM_SCALE)
hv_stores(hv, "Scale", newSViv(dm->dmScale));
if (fields & DM_COPIES)
hv_stores(hv, "Copies", newSViv(dm->dmCopies));
if (fields & DM_DEFAULTSOURCE)
hv_stores(hv, "DefaultSource", dmbin_to_sv(aTHX_ dm->dmDefaultSource));
if (fields & DM_PRINTQUALITY)
hv_stores(hv, "PrintQuality", dmres_to_sv(aTHX_ dm->dmPrintQuality));
if (fields & DM_POSITION)
hv_stores(hv, "Position", SvREFCNT_inc(pointl_to_sv(aTHX_ &dm->dmPosition)));
if (fields & DM_DISPLAYORIENTATION)
hv_stores(hv, "DisplayOrientation", dmdo_to_sv(aTHX_ dm->dmDisplayOrientation));
if (fields & DM_DISPLAYFIXEDOUTPUT)
hv_stores(hv, "DisplayFixedOutput", dmdfo_to_sv(aTHX_ dm->dmDisplayFixedOutput));
if (fields & DM_COLOR)
hv_stores(hv, "Color", dmcolor_to_sv(aTHX_ dm->dmColor));
if (fields & DM_DUPLEX)
hv_stores(hv, "Duplex", dmdup_to_sv(aTHX_ dm->dmDuplex));
if (fields & DM_YRESOLUTION)
hv_stores(hv, "YResolution", newSViv(dm->dmYResolution));
if (fields & DM_TTOPTION)
hv_stores(hv, "TTOption", dmtt_to_sv(aTHX_ dm->dmTTOption));
if (fields & DM_COLLATE)
hv_stores(hv, "Collate", dmcollate_to_sv(aTHX_ dm->dmCollate));
if (fields & DM_FORMNAME)
hv_stores(hv, "FormName", wchar_to_sv(aTHX_ dm->dmFormName, 0));
if (fields & DM_LOGPIXELS)
hv_stores(hv, "LogPixels", newSViv(dm->dmLogPixels));
if (fields & DM_BITSPERPEL)
hv_stores(hv, "BitsPerPel", newSViv(dm->dmBitsPerPel));
if (fields & DM_PELSWIDTH)
hv_stores(hv, "PelsWidth", newSViv(dm->dmPelsWidth));
if (fields & DM_PELSHEIGHT)
hv_stores(hv, "PelsHeight", newSViv(dm->dmPelsHeight));
if (fields & DM_DISPLAYFLAGS)
hv_stores(hv, "DisplayFlags", newSViv(dm->dmDisplayFlags));
if (fields & DM_NUP)
hv_stores(hv, "Nup", dmnup_to_sv(aTHX_ dm->dmNup));
if (fields & DM_DISPLAYFREQUENCY)
hv_stores(hv, "DisplayFrequency", newSViv(dm->dmDisplayFrequency));
#if (WINVER >= 0x0400)
if (fields & DM_ICMMETHOD)
hv_stores(hv, "ICMethod", dmicmethod_to_sv(aTHX_ dm->dmICMMethod));
if (fields & DM_ICMINTENT)
hv_stores(hv, "ICMIntent", dmicm_to_sv(aTHX_ dm->dmICMIntent));
if (fields & DM_MEDIATYPE)
hv_stores(hv, "MediaType", dmmedia_to_sv(aTHX_ dm->dmMediaType));
if (fields & DM_DITHERTYPE)
hv_stores(hv, "DitherType", dmdither_to_sv(aTHX_ dm->dmDitherType));
hv_stores(hv, "Reserved1", newSViv(dm->dmReserved1));
hv_stores(hv, "Reserved2", newSViv(dm->dmReserved2));
#if (WINVER >= 0x0500) || (_WIN32_WINNT >= 0x0400)
if (fields & DM_PANNINGWIDTH)
hv_stores(hv, "PanningWidth", newSViv(dm->dmPanningWidth));
if (fields & DM_PANNINGHEIGHT)
hv_stores(hv, "PanningHeight", newSViv(dm->dmPanningHeight));
#endif
#endif
return sv;
}
static SV *
pi2_to_sv(pTHX_ PPRINTER_INFO_2W pi2) {
HV *hv = newHV();
SV *sv = sv_2mortal(newRV_noinc((SV*)hv));
hv_stores(hv, "ServerName", wchar_to_sv(aTHX_ pi2->pServerName, 0));
hv_stores(hv, "PrinterName", wchar_to_sv(aTHX_ pi2->pPrinterName, 0));
hv_stores(hv, "ShareName", wchar_to_sv(aTHX_ pi2->pShareName, 0));
hv_stores(hv, "PortName", wchar_to_sv(aTHX_ pi2->pPortName, 0));
hv_stores(hv, "DriverName", wchar_to_sv(aTHX_ pi2->pDriverName, 0));
hv_stores(hv, "Comment", wchar_to_sv(aTHX_ pi2->pComment, 0));
hv_stores(hv, "Location", wchar_to_sv(aTHX_ pi2->pLocation, 0));
hv_stores(hv, "DevMode", SvREFCNT_inc(devmod_to_sv(aTHX_ pi2->pDevMode)));
hv_stores(hv, "SetFile", wchar_to_sv(aTHX_ pi2->pSepFile, 0));
hv_stores(hv, "PrintProcessor", wchar_to_sv(aTHX_ pi2->pPrintProcessor, 0));
hv_stores(hv, "Datatype", wchar_to_sv(aTHX_ pi2->pDatatype, 0));
hv_stores(hv, "Parameters", wchar_to_sv(aTHX_ pi2->pParameters, 0));
// PSECURITY_DESCRIPTOR pSecurityDescriptor;
hv_stores(hv, "Attributes", newSViv(pi2->Attributes));
hv_stores(hv, "Priority", newSViv(pi2->Priority));
hv_stores(hv, "DefaultPriority", newSViv(pi2->DefaultPriority));
hv_stores(hv, "StartTime", newSViv(pi2->StartTime));
hv_stores(hv, "UntilTime", newSViv(pi2->UntilTime));
hv_stores(hv, "Status", status_to_sv(aTHX_ pi2->Status));
hv_stores(hv, "cJobs", newSViv(pi2->cJobs));
hv_stores(hv, "AveragePPM", newSViv(pi2->AveragePPM));
return sv;
}
/* TODO... */
// SV *pi3_to_sv(pTHX_ PPRINTER_INFO_3W pi3) { return &PL_sv_undef; }
static SV *
pi4_to_sv(pTHX_ PPRINTER_INFO_4W pi4) {
HV *hv = newHV();
SV *sv = sv_2mortal(newRV_noinc((SV*)hv));
hv_stores(hv, "PrinterName", wchar_to_sv(aTHX_ pi4->pPrinterName, 0));
hv_stores(hv, "ServerName", wchar_to_sv(aTHX_ pi4->pServerName, 0));
hv_stores(hv, "Attributes", newSViv(pi4->Attributes));
return sv;
}
static SV *
pi5_to_sv(pTHX_ PPRINTER_INFO_5W pi5) {
HV *hv = newHV();
SV *sv = sv_2mortal(newRV_noinc((SV*)hv));
hv_stores(hv, "PrinterName", wchar_to_sv(aTHX_ pi5->pPrinterName, 0));
hv_stores(hv, "PortName", wchar_to_sv(aTHX_ pi5->pPortName, 0));
hv_stores(hv, "Attributes", newSViv(pi5->Attributes));
hv_stores(hv, "DeviceNotSelectedTimeout", newSViv(pi5->DeviceNotSelectedTimeout));
hv_stores(hv, "TransmissionRetryTimeout", newSViv(pi5->TransmissionRetryTimeout));
return sv;
}
// SV *pi6_to_sv(pTHX_ PPRINTER_INFO_6W pi6) { return &PL_sv_undef; }
static SV *pi7_to_sv(pTHX_ PPRINTER_INFO_7W pi7) { return &PL_sv_undef; }
static SV *pi8_to_sv(pTHX_ PPRINTER_INFO_8W pi8) { return &PL_sv_undef; }
static SV *pi9_to_sv(pTHX_ PPRINTER_INFO_9W pi9) { return &PL_sv_undef; }
static SV *
sizel_to_sv(pTHX_ PSIZEL sl) {
HV *hv = newHV();
SV *sv = sv_2mortal(newRV_noinc((SV*)hv));
hv_stores(hv, "cx", newSViv(sl->cx));
hv_stores(hv, "cy", newSViv(sl->cy));
return sv;
}
static SV *
rectl_to_sv(pTHX_ PRECTL r) {
HV *hv = newHV();
SV *sv = sv_2mortal(newRV_noinc((SV*)hv));
hv_stores(hv, "left", newSViv(r->left));
hv_stores(hv, "top", newSViv(r->top));
hv_stores(hv, "right", newSViv(r->right));
hv_stores(hv, "bottom", newSViv(r->bottom));
return sv;
}
static SV *
fi1_to_sv(pTHX_ PFORM_INFO_1W fi1) {
HV *hv = newHV();
SV *sv = sv_2mortal(newRV_noinc((SV*)hv));
hv_stores(hv, "Flags", formflag_to_sv(aTHX_ fi1->Flags));
hv_stores(hv, "Name", wchar_to_sv(aTHX_ fi1->pName, 0));
hv_stores(hv, "Size", SvREFCNT_inc(sizel_to_sv(aTHX_ &fi1->Size)));
hv_stores(hv, "ImageableArea", SvREFCNT_inc(rectl_to_sv(aTHX_ &fi1->ImageableArea)));
return sv;
}
static SV *
fi2_to_sv(pTHX_ PFORM_INFO_2W fi2) {
HV *hv = newHV();
SV *sv = sv_2mortal(newRV_noinc((SV*)hv));
DWORD st = fi2->StringType;
hv_stores(hv, "Flags", formflag_to_sv(aTHX_ fi2->Flags));
hv_stores(hv, "Name", wchar_to_sv(aTHX_ fi2->pName, 0));
hv_stores(hv, "Size", SvREFCNT_inc(sizel_to_sv(aTHX_ &fi2->Size)));
hv_stores(hv, "ImageableArea", SvREFCNT_inc(rectl_to_sv(aTHX_ &fi2->ImageableArea)));
hv_stores(hv, "Keyword", newSVpv(fi2->pKeyword, 0));
hv_stores(hv, "StringType", stringtype_to_sv(aTHX_ st));
if (st & STRING_MUIDLL) {
hv_stores(hv, "MuiDll", wchar_to_sv(aTHX_ fi2->pMuiDll, 0));
hv_stores(hv, "ResourceId", newSViv(fi2->dwResourceId));
}
if (st & STRING_LANGPAIR) {
hv_stores(hv, "DisplayName", wchar_to_sv(aTHX_ fi2->pDisplayName, 0));
hv_stores(hv, "LangId", newSViv(fi2->wLangId));
}
return sv;
}
MODULE = Win32::EnumPrinters PACKAGE = Win32::EnumPrinters
BOOT:
boot_constants(aTHX);
void
EnumPrinters(SV *flags = &PL_sv_undef, SV *name = &PL_sv_undef, IV level = 2)
PREINIT:
IV flags_iv;
wchar_t *name_wchar;
DWORD buffer_size = DEFAULT_BUFFER_SIZE;
PPCODE:
flags_iv = (SvOK(flags) ? sv_to_enum(aTHX_ flags) : PRINTER_ENUM_LOCAL);
name_wchar = (SvOK(name) ? sv_to_wchar(aTHX_ name) : NULL);
while (1) {
DWORD required = 0;
DWORD items = 0;
LPBYTE buffer = NULL;
Newx(buffer, buffer_size, BYTE);
SAVEFREEPV(buffer);
if (EnumPrintersW(flags_iv, name_wchar, level,
buffer, buffer_size,
&required, &items)) {
DWORD i;
for (i = 0; i < items; i++) {
SV *sv;
switch (level) {
case 1:
sv = pi1_to_sv(aTHX_ (PPRINTER_INFO_1W)buffer + i);
break;
case 2:
sv = pi2_to_sv(aTHX_ (PPRINTER_INFO_2W)buffer + i);
break;
/* case 3:
sv = pi3_to_sv(aTHX_ (PPRINTER_INFO_3W)buffer + i);
break; */
case 4:
sv = pi4_to_sv(aTHX_ (PPRINTER_INFO_4W)buffer + i);
break;
case 5:
sv = pi5_to_sv(aTHX_ (PPRINTER_INFO_5W)buffer + i);
break;
default:
Perl_warn(aTHX_ "level %d not supported", level);
sv = &PL_sv_undef;
break;
}
XPUSHs(sv);
}
XSRETURN(items);
}
else {
if (required > buffer_size) {
buffer_size = required;
continue;
}
XSRETURN(0);
}
}
SV *
GetDefaultPrinter()
PREINIT:
DWORD len = 0;
CODE:
RETVAL = &PL_sv_undef;
GetDefaultPrinterW(NULL, &len);
if (len) {
wchar_t *buffer;
Newx(buffer, len + 2, wchar_t);
if (GetDefaultPrinterW(buffer, &len))
RETVAL = wchar_to_sv(aTHX_ buffer, 0);
}
OUTPUT:
RETVAL
void
EnumForms(SV *printer, int level = 2)
PREINIT:
wchar_t *printer_wchar;
HANDLE handle = 0;
DWORD returned = 0;
PPCODE:
printer_wchar = sv_to_wchar(aTHX_ printer);
if (OpenPrinterW(printer_wchar, &handle, NULL)) {
DWORD buffer_size = DEFAULT_BUFFER_SIZE;
while(1) {
DWORD required = 0;
LPBYTE buffer = NULL;
Newx(buffer, buffer_size, BYTE);
SAVEFREEPV(buffer);
if (EnumFormsW(handle, level, buffer, buffer_size, &required, &returned)) {
int i;
for (i = i; i < returned; i++) {
SV *sv;
switch(level) {
case 1:
sv = fi1_to_sv(aTHX_ (PFORM_INFO_1W)buffer + i);
break;
case 2:
sv = fi2_to_sv(aTHX_ (PFORM_INFO_2W)buffer + i);
break;
default:
Perl_warn(aTHX_ "level %d not supported", level);
sv = &PL_sv_undef;
break;
}
XPUSHs(sv);
}
}
else {
if (required > buffer_size) {
buffer_size = required;
continue;
}
}
break;
}
ClosePrinter(handle);
}
XSRETURN(returned);