PrintDlgでプリンタの名前を正しく取得するには?

解決


コージ  2008-07-11 11:36:55  No: 140062  IP: 192.*.*.*

当方全くの初心者で成り行きでプログラムを任されているものです。
  手探りでプログラムをしてしましたが、どうしても躓いてしまったのでご助力頂ければと思います。

  VB6.0にてPrintDlgを使った印刷処理を行おうと思い、サンプルプログラムなどを参考にして組み立ててみた所、PrintDlgでプリンタを指定してvDevNamesに設定をコピーするとプリンタの名称が一部化けてしまい、その後の処理に上手く乗せることが出来ません。

  選択したプリンタ名は「自動 Canon BJ S6300」なのですが、変数に入ってきた名前を見ると「試ゥ動 Canon BJ S6300」となってしまいます。(最終的に処理されると「動 Canon BJ S6300」となる)

  どの辺りが悪いのかも見当が付きません。
  どうかよろしくお願い致します。

  デフォルト設定を拾ってきた時は、こんな感じで値が入ってきます。
  "winspool・自動 Canon BJ S6300・Ne00:・ 

  ダイアログボックスで拾ってきた値は、こんな感じです。
  "winspool・試ゥ動 Canon BJ S6300・e\\W2KSERVER\BJS-6300・

  プリンタはLANで接続された他のPCに接続されています。

  足りない情報等ありましたら、ご指摘ください。

編集 削除
魔界の仮面弁士  2008-07-11 13:32:59  No: 140063  IP: 192.*.*.*

> どの辺りが悪いのかも見当が付きません。

現在のコードを見せてもらわないと、コードが間違っているのか
それとも環境側に問題があるのか見当がつきません。

編集 削除
コージ  2008-07-11 13:50:11  No: 140064  IP: 192.*.*.*

>>魔界の仮面弁士様
  ご指摘ありがとうございます。
  そのフォームを丸ごとコピーしてみました。
  ほぼ、某所のサンプルの丸写しで、お恥ずかしいです。

  当方、店頭接客業務の人間でプログラムは10年前にC言語検定3級を取った程度でパソコン用語は人並みに分かりますが、プログラムの用語なると、まだまだ分からない物が多いです。

状況としては
  上司より、「自社製のプログラムの調子が悪いから直せ」>ソースを書いた人間は10年前に退社、連絡不能>「人手が無いので自力で調べて直せ」>「何でこんな物の修正に1週間も掛かってるんだ」←今ここ

  以上、どうか、よろしくお願い致します。  


Option Explicit
    Public OSKIND
    Public UsePrinterForm As Form
    Public vDevNames As DEVNAMES

    Public Const GMEM_MOVEABLE = &H2
    Public Const GMEM_ZEROINIT = &H40
    Public Const GHND = GMEM_MOVEABLE Or GMEM_ZEROINIT

    Public Const CCHDEVICENAME = 32
    Public Const CCHFORMNAME = 32

    Public Const PD_ALLPAGES = &H0
    Public Const PD_COLLATE = &H10
    Public Const PD_DISABLEPRINTTOFILE = &H80000
    Public Const PD_ENABLEPRINTHOOK = &H1000
    Public Const PD_ENABLEPRINTTEMPLATE = &H4000
    Public Const PD_ENABLEPRINTTEMPLATEHANDLE = &H10000
    Public Const PD_ENABLESETUPHOOK = &H2000
    Public Const PD_ENABLESETUPTEMPLATE = &H8000
    Public Const PD_ENABLESETUPTEMPLATEHANDLE = &H20000
   Public Const PD_HIDEPRINTTOFILE = &H100000
   Public Const PD_NONETWORKBUTTON = &H200000
   Public Const PD_NOPAGENUMS = &H8
   Public Const PD_NOSELECTION = &H4
   Public Const PD_NOWARNING = &H80
   Public Const PD_PAGENUMS = &H2
   Public Const PD_PRINTSETUP = &H40
   Public Const PD_PRINTTOFILE = &H20
   Public Const PD_RETURNDC = &H100
   Public Const PD_RETURNDEFAULT = &H400
   Public Const PD_RETURNIC = &H200
   Public Const PD_SELECTION = &H1
   Public Const PD_SHOWHELP = &H800
   Public Const PD_USEDEVMODECOPIES = &H40000
   Public Const PD_USEDEVMODECOPIESANDCOLLATE = &H40000

Public Type PrintDlg
    lStructSize As Long
    hwndOwner As Long
    hDevMode As Long
    hDevNames As Long
    hdc As Long
    Flags As Long
    nFromPage As Integer
    nToPage As Integer
    nMinPage As Integer
    nMaxPage As Integer
    nCopies As Integer
    hInstance As Long
    lCustData As Long
    lpfnPrintHook As Long
    lpfnSetupHook As Long
    lpPrintTemplateName As String
    lpSetupTemplateName As String
    hPrintTemplate As Long
    hSetupTemplate As Long
End Type


Public Type DEVMODE
    dmDeviceName As String * CCHDEVICENAME
    dmSpecVersion As Integer
    dmDriverVersion As Integer
    dmSize As Integer
    dmDriverExtra As Integer
    dmFields As Long
    dmOrientation As Integer
    dmPaperSize As Integer
    dmPaperLength As Integer
    dmPaperWidth As Integer
    dmScale As Integer
    dmCopies As Integer
    dmDefaultSource As Integer
    dmPrintQuality As Integer
    dmColor As Integer
    dmDuplex As Integer
    dmYResolution As Integer
    dmTTOption As Integer
    dmCollate As Integer
    dmFormName As String * CCHFORMNAME
    dmLogPixels As Integer
    dmBitsPerPel As Long
    dmPelsWidth As Long
    dmPelsHeight As Long
    dmDisplayFlags As Long
    dmDisplayFrequency As Long
End Type

    Public Type DEVNAMES
        wDriverOffset As Integer
        wDeviceOffset As Integer
        wOutputOffset As Integer
        wDefault As Integer
        extData As String * 256
    End Type


Public Declare Function GlobalAlloc Lib _
    "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long

Public Declare Function GlobalLock Lib _
    "kernel32" (ByVal hMem As Long) As Long

Public Declare Sub CopyMemory Lib _
    "kernel32" Alias "RtlMoveMemory" (Destination As Any, SOURCE As Any, ByVal Length As Long)

Public Declare Function GlobalUnlock Lib _
    "kernel32" (ByVal hMem As Long) As Long
    
Public Declare Function PrintDlg Lib _
    "comdlg32.dll" Alias "PrintDlgA" (pPrintdlg As PrintDlg) As Long
    
Public Declare Function GlobalFree Lib _
    "kernel32" (ByVal hMem As Long) As Long



    
    


Public Function ShowPrinter(frmOwner As Form, Optional PrintFlags As Variant) As Boolean

Dim vDevMode As DEVMODE
Dim hDevMode As Long
Dim lpDevMode As Long
Dim lpDevNames As Long
Dim hDevNames As Long
Dim vPrintDlg As PrintDlg
Dim NewDeviceName
Dim strSetting
Dim objprm As Printer

vDevMode.dmSize = Len(vDevMode)
vDevMode.dmSpecVersion = 0
vDevMode.dmDriverVersion = 0
vDevMode.dmDriverExtra = 0
vDevMode.dmFields = 0

On Error Resume Next

vDevMode.dmDeviceName = Printer.DeviceName
   
If Printer.PaperSize <> vbPRPSUser Then
    vDevMode.dmFields = vDevMode.dmFields Or vDevMode.dmPaperSize
    vDevMode.dmPaperSize = Printer.PaperSize
    vDevMode.dmPaperWidth = 0
    vDevMode.dmPaperLength = 0
Else
    vDevMode.dmFields = vDevMode.dmFields Or vDevMode.dmPaperSize Or _
        vDevMode.dmPaperLength Or vDevMode.dmPaperWidth
    vDevMode.dmPaperSize = vbPRPSUser
    vDevMode.dmPaperWidth = Printer.ScaleX(Printer.Width, Printer.ScaleMode, vbHimetric)
    vDevMode.dmPaperLength = Printer.ScaleY(Printer.Height, Printer.ScaleMode, vbHimetric)
End If

vDevMode.dmDefaultSource = Printer.PaperBin

If Err.Number = 0 Then
    vDevMode.dmFields = vDevMode.dmFields Or vDevMode.dmDefaultSource
Else
    Err.Clear
End If

vDevMode.dmPrintQuality = Printer.PrintQuality
   
If Err.Number = 0 Then
    vDevMode.dmFields = vDevMode.dmFields Or vDevMode.dmPrintQuality
Else
    Err.Clear
End If

vDevMode.dmColor = Printer.ColorMode

If Err.Number = 0 Then
    vDevMode.dmFields = vDevMode.dmFields Or vDevMode.dmColor
Else
    Err.Clear
End If

vDevMode.dmDuplex = Printer.Duplex
   
If Err.Number = 0 Then
    vDevMode.dmFields = vDevMode.dmFields Or vDevMode.dmDuplex
Else
    Err.Clear
End If

vDevMode.dmOrientation = Printer.Orientation

If Err.Number = 0 Then
    vDevMode.dmFields = vDevMode.dmFields Or vDevMode.dmOrientation
Else
    Err.Clear
End If

vDevMode.dmScale = Printer.Zoom
   
If Err.Number = 0 Then
    vDevMode.dmFields = vDevMode.dmFields Or vDevMode.dmScale
Else
    Err.Clear
End If

On Error GoTo 0
      
vDevNames.wDriverOffset = 8
vDevNames.wDefault = 0
vDevNames.wDeviceOffset = vDevNames.wDriverOffset + 1 + Len(Printer.DriverName)
vDevNames.wOutputOffset = vDevNames.wDeviceOffset + 1 + Len(Printer.DeviceName)
vDevNames.extData = Printer.DriverName & Chr(0) & Printer.DeviceName & Chr(0) & Printer.Port & Chr(0)

hDevMode = GlobalAlloc(GHND, Len(vDevMode))
lpDevMode = GlobalLock(hDevMode)
CopyMemory ByVal lpDevMode, vDevMode, Len(vDevMode)
GlobalUnlock hDevMode

hDevNames = GlobalAlloc(GHND, Len(vDevNames))
lpDevNames = GlobalLock(hDevNames)
CopyMemory ByVal lpDevNames, vDevNames, Len(vDevNames)
GlobalUnlock hDevNames
   
vPrintDlg.lStructSize = Len(vPrintDlg)
vPrintDlg.hwndOwner = frmOwner.hWnd
vPrintDlg.hDevMode = hDevMode
vPrintDlg.hDevNames = hDevNames
vPrintDlg.Flags = PD_PRINTSETUP

If PrintDlg(vPrintDlg) <> 0 Then

    lpDevMode = GlobalLock(vPrintDlg.hDevMode)
    CopyMemory vDevMode, ByVal lpDevMode, Len(vDevMode)
    GlobalUnlock lpDevMode

    lpDevNames = GlobalLock(vPrintDlg.hDevNames)
    CopyMemory vDevNames, ByVal lpDevNames, Len(vDevNames)
    GlobalUnlock lpDevNames

    GlobalFree vPrintDlg.hDevMode
    GlobalFree vPrintDlg.hDevNames


    NewDeviceName = Mid(vDevNames.extData, vDevNames.wDeviceOffset - 8 + 1)
    NewDeviceName = Left(NewDeviceName, InStr(NewDeviceName, Chr(0)) - 1)
         
    If Printer.DeviceName <> NewDeviceName Then
        For Each objprm In Printers
            If UCase(objprm.DeviceName) = UCase(NewDeviceName) Then
                Set Printer = objprm
            End If
        Next
    End If
           
    On Error Resume Next

    With Printer
        .Copies = vDevMode.dmCopies
        .Duplex = vDevMode.dmDuplex
        .Orientation = vDevMode.dmOrientation
    End With

    ShowPrinter = True

    On Error GoTo 0

    On Error Resume Next

    If vDevMode.dmPaperSize <> vbPRPSUser Then
        Printer.PaperSize = vDevMode.dmPaperSize
    Else
        Printer.PaperSize = vbPRPSUser
        Printer.Width = Printer.ScaleX(vDevMode.dmPaperWidth, vbHimetric, Printer.ScaleMode)
        Printer.Height = Printer.ScaleY(vDevMode.dmPaperLength, vbHimetric, Printer.ScaleMode)
   End If

    If Err.Number <> 0 Then
        MsgBox "指定された用紙サイズが不正です", vbOKOnly, "プリンタの設定"
        Err.Clear
    End If

    Printer.PaperBin = vDevMode.dmDefaultSource
    Printer.PrintQuality = vDevMode.dmPrintQuality
    Printer.ColorMode = vDevMode.dmColor
    Printer.Duplex = vDevMode.dmDuplex
    Printer.Orientation = vDevMode.dmOrientation
    Printer.Zoom = vDevMode.dmScale
    On Error GoTo 0
Else
    ShowPrinter = False
End If
         
         
'Display the results in the immediate (debug) window
With Printer
    If .Orientation = 1 Then
        strSetting = "Portrait. "
    Else
        strSetting = "Landscape. "
    End If
    
    Debug.Print "Copies = " & .Copies, "Orientation = " & strSetting
End With

End Function

編集 削除
熊谷隆史  2008-07-11 16:12:37  No: 140065  IP: 192.*.*.*

# 私も初心者なので何ですが。

> Public Declare Function PrintDlg Lib _
>     "comdlg32.dll" Alias "PrintDlgA" (pPrintdlg As PrintDlg) As Long

PrintDlg Function ()
http://msdn.microsoft.com/en-us/library/ms646940(VS.85).aspx

↑この辺とかを見ると、PrintDlgExに
  取って代わったとか書いてあるのだけれど。

PrintDlgEx 関数
http://msdn.microsoft.com/ja-jp/library/cc410959.aspx

PrintDlgExのユニコード版を使用すれば
いいのではないかと(多分)。

編集 削除
コージ  2008-07-11 18:02:00  No: 140066  IP: 192.*.*.*

>>熊谷隆史さん
レスありがとうございます。
見てみます〜。


現在の状況:
どうも、途中の
vDevNames.extData = Printer.DriverName & Chr(0) & Printer.DeviceName & Chr(0) & Printer.Port & Chr(0)
で格納した現在のデフォルトプリンタ名等がPrintDlgの結果に混ざっている?みたいです。
試しに上記の箇所を
vDevNames.extData = Printer.DriverName & Chr(0) & "" & Chr(0) & Printer.Port & Chr(0)
と変更して実行し、vDevNames.extDataに
winspool・・Ne00:・
と入れておくと、PrintLogからの結果が
winspool・・自動 Canon BJ S6300・e\\W2KSERVER\BJS-6300・
となりました。(この状態なら処理が流せそう)

根本的な理由が分からず怖いのですが・・・。

編集 削除
K.J.K.  2008-07-12 11:26:26  No: 140067  IP: 192.*.*.*

とりあえず、使い分けるべきところで、正しく LenB を用いるとか。

ただ、既定の初期値を得るのならば PD_RETURNDEFAULT を与えて呼び出せば
済みますよね。DEVNAMESなどを自作しなくても済みますよね。

DEVNAMES の作り方・解釈の問題ですから、PrintDlgExを用いる/用いない
の問題ではありませんよね。

編集 削除
熊谷隆史  2008-07-12 17:18:44  No: 140068  IP: 192.*.*.*

> DEVNAMES の作り方・解釈の問題ですから、PrintDlgExを用いる/用いない
> の問題ではありませんよね。

K.J.K.さん、こんにちは。
ご指摘ありがとうございます。

こちらのサンプルで動作確認したところ、
http://pasofaq.jp/development/visualbasic/j042959.htm
> 'First get the DevName structure.
> lpDevName = GlobalLock(PrintDlg.hDevNames)
> CopyMemory DevName, ByVal lpDevName, 45
> bReturn = GlobalUnlock(lpDevName)

DEVNAMES構造体へコピーするバイト数が
Integer型のメンバが4つで8バイト、
45 - 8 = 残り37バイトがどういう計算なのかなとか
考えていました。

45バイトだとコピーした文字列の後半に
ゴミが乗ってこないぐらいで
文字化けはこちらでは確認できませんでした。
報告まで。

編集 削除
K.J.K.  2008-07-13 10:37:11  No: 140069  IP: 192.*.*.*

エラー処理を抜きにすれば、こんな感じでは。
# 差分になりそうなところのみ記述。
# 直うちなので、間違っているところを適宜修正してください。

Public Type DEVNAMES
    wDriverOffset As Integer
    wDeviceOffset As Integer
    wOutputOffset As Integer
    wDefault As Integer
End Type

Dim vDevNames As DEVNAMES
Dim abBuffer() As Byte
Dim iLength As Long

vDevNames.wDriverOffset = 8
abBuffer = StrConv(Printer.DriverName, vbFromUnicode)
iLength = UBound(abBuffer) + 1
vDevNames.wDeviceOffset = 8 + iLength + 1
abBuffer = StrConv(Printer.DeviceName, vbFromUnicode)
iLength = UBound(abBuffer) + 1
vDevNames.wOutputOffset = vDevNames.wDeviceOffset + iLength + 1

abBuffer = StrConv(Printer.DriverName & vbNullChar & _
    Printer.DeviceName & vbNullChar & Printer.Port & vbNullChar, _
    vbFromUnicode)
iLength = UBound(abBuffer) + 1

' 略

Call CopyMemory(ByVal lpDevNames, vDevNames, 8&)
Call CopyMemory(ByVal lpDevNames + 8&, abBuffer(0&), iLength)

ただ、こうまでしなくても、

vPrintDlg.lStructSize = Len(vPrintDlg)
vPrintDlg.Flags = PD_RETURNDEFAULT
vPrintDlg.hDevMode = 0&
vPrintDlg.hDevNames = 0&
Call PrintDlg(vPrintDlg)

とすれば、 vPrintDlg.hDevNames と vPrintDlg.hDevMode に有効な値が入ってくるので、
わざわざ DEVNAMES などを自作しないでもすみます。

編集 削除
コージ  2008-07-15 18:40:10  No: 140070  IP: 192.*.*.*

>>K.J.K.さま

反応が遅くなってしまい申し訳ありません。本業に戻っていたもので。
木曜日から、またプログラムを弄ることになりそうなので、そこでプログラムの改良を行ってみたいと思います。
また、結果をご報告したいと思います。

それでは。

編集 削除
コージ  2008-07-22 11:05:03  No: 140071  IP: 192.*.*.*

皆様、ご助力ありがとうございました。
何とか解決いたしました。

これから自分でも勉強して頑張ってみようと思います。
それでは。

編集 削除