プロが教えるわが家の防犯対策術!

開発言語:VB6

お世話になります。

DEVMODE構造体とDEVNAME構造体に、プリンタや用紙サイズ、印刷の向きなどを指定し、Win32APIのPrintDialogで印刷ダイアログを表示しています。
この処理を何回か呼び出すと、プログラムが異常終了します。
異常終了する場所は、PrintDialogを呼び出した直後だと思われます。

いろいろ調べたのですが、原因がわかりません。
長くなりますが、ソースコードの一部を添付させて頂きます。
ご覧頂き、おかしい点などございましたらご指摘いただけるとありがたいです。
よろしくお願いします。

以下ソースコード
-----------------------------------------------------------------------------------
PrintDlg.lStructSize = Len(PrintDlg)
PrintDlg.hwndOwner = phwnd
PrintDlg.flags = plngPrintFlags

strDeviceName = Printer.DeviceName
strDriverName = Printer.DriverName
sngPaperBin = Printer.PaperBin
strPortNo = Printer.Port
If mstrDriverName <> "" And _
mstrDeviceName <> "" And _
mstrPortNo <> "" Or _
msngDefaultSource <> 0 Then
For Each objPrinter In Printers
If objPrinter.DeviceName = mstrDeviceName Then
strDeviceName = mstrDeviceName
strDriverName = mstrDriverName
sngPaperBin = msngDefaultSource
strPortNo = mstrPortNo
Exit For
End If
Next
End If

udtPrinterDefaults.DesiredAccess = PRINTER_ACCESS_USE
If OpenPrinter(Trim(strDeviceName), hPrinter, udtPrinterDefaults) <> 0 Then
bufSize = DocumentProperties(NULLPTR, hPrinter, Trim(strDeviceName), NULLPTR, NULLPTR, 0)
If bufSize < 1 Then
MsgBox "プリンタ情報の取得に失敗しました。", vbCritical
GoTo Exit_Proc
Else
ReDim aDevMode(bufSize - 1)
Call DocumentProperties(NULLPTR, hPrinter, Trim(strDeviceName), aDevMode(0), NULLPTR, DM_OUT_BUFFER)
Call CopyMemory(DevMode, aDevMode(0), Len(DevMode))
End If
Call ClosePrinter(hPrinter)
Else
MsgBox Err.LastDllError
MsgBox "プリンタの取得に失敗しました。", vbCritical
GoTo Exit_Proc
End If

DevMode.dmPaperSize = mintPaperSize
DevMode.dmOrientation = mintPaperOrnt
DevMode.dmDefaultSource = sngPaperBin

PrintDlg.hDevMode = GlobalAlloc(GMEM_MOVEABLE Or GMEM_ZEROINIT, Len(DevMode))
lpDevMode = GlobalLock(PrintDlg.hDevMode)
If lpDevMode > 0 Then
CopyMemory ByVal lpDevMode, DevMode, Len(DevMode)
bReturn = GlobalUnlock(lpDevMode)
End If

With DevName
.wDriverOffset = 8
.wDeviceOffset = .wDriverOffset + 1 + Len(strDriverName)
.wOutputOffset = .wDeviceOffset + 1 + Len(strDeviceName)
.wDefault = 0
.extra = strDriverName & Chr(0) & strDeviceName & Chr(0) & strPortNo & Chr(0)
End With
PrintDlg.hDevNames = GlobalAlloc(GMEM_MOVEABLE Or _
GMEM_ZEROINIT, Len(DevName))
lpDevName = GlobalLock(PrintDlg.hDevNames)
If lpDevName > 0 Then
CopyMemory ByVal lpDevName, DevName, Len(DevName)
bReturn = GlobalUnlock(lpDevName)
End If

If PrintDialog(PrintDlg) Then

mhDC = PrintDlg.hdc

lpDevName = GlobalLock(PrintDlg.hDevNames)
CopyMemory DevName, ByVal lpDevName, Len(DevName)
bReturn = GlobalUnlock(lpDevName)
GlobalFree PrintDlg.hDevNames

lpDevMode = GlobalLock(PrintDlg.hDevMode)
CopyMemory DevMode, ByVal lpDevMode, Len(DevMode)
bReturn = GlobalUnlock(PrintDlg.hDevMode)
GlobalFree PrintDlg.hDevMode

mstrDriverName = Mid(DevName.extra, DevName.wDriverOffset - 8 + 1)
mstrDriverName = Left(mstrDriverName, InStr(mstrDriverName, Chr(0)) - 1)
mstrDeviceName = Mid(DevName.extra, DevName.wDeviceOffset - 8 + 1)
mstrDeviceName = Left(mstrDeviceName, InStr(mstrDeviceName, Chr(0)) - 1)
mstrPortNo = Mid(DevName.extra, DevName.wOutputOffset - 8 + 1)
mstrPortNo = Left(mstrPortNo, InStr(mstrPortNo, Chr(0)) - 1)
msngDefaultSource = DevMode.dmDefaultSource
mintPaperOrnt = DevMode.dmOrientation
mintPaperSize = DevMode.dmPaperSize

gfShowPrinter = True

End If

A 回答 (3件)

#2 です。

なかなか解決できずに困っていらっしゃるようですね・・・・・・。

前者の方法も後者と効果は変わらないようにおもいますが、以下のような感じですね。

(DevMode の設定値変更後のコメントが付いている 3 行が追加変更です)
-----
DevMode.dmPaperSize = mintPaperSize
DevMode.dmOrientation = mintPaperOrnt
DevMode.dmDefaultSource = sngPaperBin

CopyMemory aDevMode(0), DevMode, Len(DevMode) ' 追加、DevMode を aDevMode に書き戻す

PrintDlg.hDevMode = GlobalAlloc(GMEM_MOVEABLE Or GMEM_ZEROINIT, bufSize) ' バッファサイズを bufSize に
lpDevMode = GlobalLock(PrintDlg.hDevMode)
If lpDevMode > 0 Then
CopyMemory ByVal lpDevMode, aDevMode(0), bufSize 'コピー元を aDevMode に、サイズを bufSize に
bReturn = GlobalUnlock(lpDevMode)
-----
aDevMode のサイズが DevMode と同じであれば意味はありません。ダイアログ表示後に取り出すデータは必要なとこだけ取り出せればいいので、DevMode に取り出すことで問題ないと思います。

解決できるといいですね・・・・・・・。
    • good
    • 0

DEVMODE の扱いがちょっと気になりました。

一応調べてみたほうがいいかも・・・・・・レベルなのですが、書きます。

DEVMODE というのはデバイスによってサイズが違うことになっているため、単一の DEVMODE 構造体で全体を表現することはできないことになっています。

DevMode 変数の構造がわからないのですが、おそらくどのデバイスでも共通部分の構造体のみが定義されているのではないかと推測しますが、デバイスが返す DEVMODE は、そのデバイス固有の Private エリアがあり、そのためサイズが一定しないことになっています(ソースで使われている DevMode という変数のサイズと、サイズを取得して ReDim した aDevMode のサイズが違うことがあると考えます)。

このため、aDevMode から DevMode だけ引き出し、内容を設定して hDevMode に DevMode のサイズ分だけ設定しているため、プライベートエリアの情報が欠落(というか範囲外のメモリなので不定値になってしまっている)と考えられます。

対処法は2つ考えられます。ひとつは DevMode を設定した後、DevMode をもう一度 aDevMode に書き戻し、hDevMode には aDevMode 分のサイズのメモリを確保・設定します。

もう一つはDevMode 構造体の最後に、必要以上の空の配列でも用意していおいて(要は余裕で aDevMode が入るであろうサイズ)、aDevMode をそっくり DevMode にコピーしてから DevMode を設定し、hDevMode に DevMode のサイズを確保・設定します(aDevMode 分のサイズでもいいはずですが、大きい分には構わないはず)。

という感じでは・・・・・・・、とちょっと推測します。おそらく、範囲外になっているプライベートエリアの部分が、最初は 0 フィルされていたのが実行中にダーティデータ化して誤動作しているのでは、と推測しています。

ちょっと長くてごめんなさい。一度確認してみてはいかがでしょうか??

この回答への補足

引き続きご回答を頂けるとありがたいです。

>対処法は2つ考えられます。ひとつは DevMode を設定した後、DevMode をもう一度 aDevMode に書き戻し
>hDevMode には aDevMode 分のサイズのメモリを確保・設定します。

ひとつめの方法、具体的にどのようなコーディングになりますでしょうか?
お手数をおかけして申し訳ありませんが、ご教示いただけるとありがたいです。

よろしくお願いします。

補足日時:2011/10/06 11:23
    • good
    • 0
この回答へのお礼

回答を頂きありがとうございました。(お礼が遅くなり申し訳ありません)

> もう一つはDevMode 構造体の最後に、必要以上の空の配列でも用意していおいて(要は余裕で
> aDevMode が入るであろうサイズ)、aDevMode をそっくり DevMode にコピーしてから DevMode
> を設定し、hDevMode に DevMode のサイズを確保・設定します(aDevMode 分のサイズでもいいは
> ずですが、大きい分には構わないはず)。

こちらを試してみました。
具体的には、DevMode 構造体の最後に、以下の1行を追加しました。
dmFiller(2048) As Byte
が、うまくいきませんでした。


その後、いろいろ試しているのですが、未だ解決には至っておりません。
もし、また何かありましたらお願いします。

お礼日時:2011/09/28 16:37

>PrintDlg.hDevMode = GlobalAlloc(GMEM_MOVEABLE Or GMEM_ZEROINIT, Len(DevMode))


>lpDevMode = GlobalLock(PrintDlg.hDevMode)

これらのハンドルは解放されていますか。
思いつくのはこれぐらいですが。
    • good
    • 0
この回答へのお礼

回答を頂きありがとうございました。(お礼が遅くなり申し訳ありません)

最後に、以下の2行を追加してみましたが、ダメでした。
GlobalFree PrintDlg.hDevNames
GlobalFree PrintDlg.hDevMode

その後、いろいろ試しているのですが、未だ解決には至っておりません。
もし、また何かありましたらお願いします。

お礼日時:2011/09/28 16:30

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!