プリンタ― ドライバ―のアップデートを行った際に DEVMODE 情報が更新されない

  1. 1. 前提となるシナリオ
  2. 2. 発生する問題
  3. 3. 回避方法
  4. 4. 参考情報

この記事は、プリンタードライバーのアップデートを行った際に発生する問題について説明します。



前提となるシナリオ

プリンタードライバーのアップデート時にプライベート部分の DEVMODE サイズや構造体をプリンタードライバーが更新している場合になります。


発生する問題

プリンタードライバーのアップデート時、更新されるドライバーでは、プライベート部分の DEVMODE サイズや構造体を更新している場合があります。このようなシナリオでは、プリンタードライバーが依頼した新しいバージョンの DEVMODE の形式に合わせて、各種 DEVMODE の値を OS が レジストリへ保存・更新します。
しかし、ドライバーの更新タイミングにおいて、OS がレジストリへの書き込み処理を行わない問題があるため、システムの再起動等を行うと、印刷設定が保存されなかったり、プリンタードライバーが予期せぬ挙動を示したりする場合があります。


回避方法

以下の OS のバージョンでは修正されております。

・Windows 11
・Windows 10 21H2 (2022 年 6 月 28 日 — KB5014666 (OS ビルド 19042.1806、19043.1806、19044.1806) プレビュー) 以降の更新プログラムが適用されている環境


それ以外の OS バージョンでは、明示的に SetPrinter を呼び出して DEVMODE が格納されているレジストリの更新を行います。なお、この際、スプーラーサービスが保持している DEVMODEと同じ値で SetPrinter を実行した場合、レジストリの更新が行われないため、次のように確実にてレジストリが更新されるよう、一時的に DEVMODE の値を変更して SetPrinter を 実行します。


回避方法としては、プリンタ―ドライバ―のアップデート後に、GetPrinter を1回、SetPrinter を2回呼び出します。
1.まず、GetPrinter を呼び出して、現在の DEVMODE を取得します。
2.続いて、1で取得した現在の DEVMODE から、値を変更(例えば、dmOrientation の値など)した DEVMODE を SetPrinter でセットします。この時、レジストリが更新されます。
3.最後に 1 で取得した現在の DEVMODE をそのまま、SetPrinter でセットし、値を元に戻し、かつレジストリも再度更新します。


Modify printer settings by using the SetPrinter function のサンプル コードをベースとした回避方法の例となります。
上記 「1.まず、GetPrinter を呼び出して、現在の DEVMODE を取得します。」については、dmOrientation の値を変更するための DEVMODE と 「3.最後に 1 で取得した現在の DEVMODE をそのまま、SetPrinter でセットし、値を元に戻し、かつレジストリも再度更新します。」で 元に戻す DEVMODE を取得するために 2 回 GetPrinter を呼び出しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// MySetPrinter
// Demonstrates how to use the SetPrinter API. This particular function changes the orientation
// for the printer specified in pPrinterName to the orientation specified in dmOrientation.
// Valid values for dmOrientation are:
// DMORIENT_PORTRAIT (1) or DMORIENT_LANDSCAPE (2)
BOOL MySetPrinter(LPTSTR pPrinterName, short dmOrientation)
{
HANDLE hPrinter = NULL;
DWORD dwNeeded = 0;
PRINTER_INFO_2 *pi2 = NULL;
PRINTER_INFO_2 *pi2_org = NULL;
DEVMODE *pDevMode = NULL;
PRINTER_DEFAULTS pd;
BOOL bFlag;
LONG lFlag;

// Open printer handle (on Windows NT, you need full-access because you
// will eventually use SetPrinter)...
ZeroMemory(&pd, sizeof(pd));
pd.DesiredAccess = PRINTER_ALL_ACCESS;
bFlag = OpenPrinter(pPrinterName, &hPrinter, &pd);
if (!bFlag || (hPrinter == NULL))
return FALSE;

// The first GetPrinter tells you how big the buffer should be in
// order to hold all of PRINTER_INFO_2. Note that this should fail with
// ERROR_INSUFFICIENT_BUFFER. If GetPrinter fails for any other reason
// or dwNeeded isn't set for some reason, then there is a problem...
SetLastError(0);
bFlag = GetPrinter(hPrinter, 2, 0, 0, &dwNeeded);
if ((!bFlag) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER) || (dwNeeded == 0))
{
ClosePrinter(hPrinter);
return FALSE;
}

// Allocate enough space for PRINTER_INFO_2...
pi2 = (PRINTER_INFO_2 *)GlobalAlloc(GPTR, dwNeeded);
if (pi2 == NULL)
{
ClosePrinter(hPrinter);
return FALSE;
}

//1.まず、GetPrinter を呼び出して、現在の DEVMODE を取得します。
//3.で DEVMODE の値を戻すために呼び出します。

// The second GetPrinter fills in all the current settings, so all you
// need to do is modify what you're interested in...
bFlag = GetPrinter(hPrinter, 2, (LPBYTE)pi2, dwNeeded, &dwNeeded);
if (!bFlag)
{
GlobalFree(pi2);
ClosePrinter(hPrinter);
return FALSE;
}

pi2_org = (PRINTER_INFO_2 *)GlobalAlloc(GPTR, dwNeeded);
if (pi2_org == NULL)
{
GlobalFree(pi2);
ClosePrinter(hPrinter);
return FALSE;
}

//1.GetPrinter を呼び出して、現在の DEVMODE を取得します。
//2.で dmOrientation の値を変更するために呼び出します。
bFlag = GetPrinter(hPrinter, 2, (LPBYTE)pi2_org, dwNeeded, &dwNeeded);
if (!bFlag)
{
GlobalFree(pi2);
GlobalFree(pi2_org);
ClosePrinter(hPrinter);
return FALSE;
}

// Driver is reporting that it doesn't support this change...
if (!(pi2->pDevMode->dmFields & DM_ORIENTATION))
{
GlobalFree(pi2);
GlobalFree(pi2_org);
ClosePrinter(hPrinter);
return FALSE;
}

// Specify exactly what we are attempting to change...
pi2->pDevMode->dmFields = DM_ORIENTATION;
pi2->pDevMode->dmOrientation = dmOrientation;

// Do not attempt to set security descriptor...
pi2->pSecurityDescriptor = NULL;

//2.続いて、1で取得した現在の DEVMODE から、値を変更(例えば、dmOrientation の値など)した
//DEVMODE を SetPrinter でセットします。この時、レジストリが更新されます。
// Update printer information...
bFlag = SetPrinter(hPrinter, 2, (LPBYTE)pi2, 0);
if (!bFlag)
// The driver doesn't support, or it is unable to make the change...
{
GlobalFree(pi2);
GlobalFree(pi2_org);
ClosePrinter(hPrinter);
return FALSE;
}

//3.最後に 1 で取得した現在の DEVMODE をそのまま、SetPrinter でセットし、値を元に戻し、かつレジストリも再度更新します。
bFlag = SetPrinter(hPrinter, 2, (LPBYTE)pi2_org, 0);
if (!bFlag)
{
GlobalFree(pi2);
GlobalFree(pi2_org);
ClosePrinter(hPrinter);
return FALSE;
}

// Tell other apps that there was a change...
SendMessageTimeout(HWND_BROADCAST, WM_DEVMODECHANGE, 0L,
(LPARAM)(LPCSTR)pPrinterName,
SMTO_NORMAL, 1000, NULL);

// Clean up...
if (pi2_org)
GlobalFree(pi2_org);
if (pi2)
GlobalFree(pi2);
if (hPrinter)
ClosePrinter(hPrinter);
if (pDevMode)
GlobalFree(pDevMode);
return TRUE;
}

参考情報

SetPrinter 関数
GetPrinter 関数
DEVMODEW 構造体


変更履歴
2022/09/20 created by mitsuchi

※ 本記事は 「jpwdkblog について」 の留意事項に準じます。
※ 併せて 「ホームページ」 および 「記事一覧」 もご参照いただければ幸いです。