Visual Basic 2008でプリンタ設定 への返答

投稿で使用できる特殊コードの説明。(別タブで開きます。)
本名は入力しないようにしましょう。
投稿した後で削除するときに使うパスワードです。返答があった後は削除できません。
返答する人が目安にします。相手が小学生か社会人かで返答の仕方も変わります。
最初の投稿が質問の場合、質問者が解決時にチェックしてください。(以降も追加書き込み・返信は可能です。)
※「過去ログ」について書くときはその過去ログのURLも書いてください。

以下の返答は逆順(新しい順)に並んでいます。

投稿者 モル  (社会人) 投稿日時 2010/7/8 18:10:17
魔界の仮面弁士さん、ご意見+ご指摘ありがとうございます。

まだまだ勉強不足ゆえ、使い方を誤ったまま使用していた所が多々あるのですね。。。
それぞれを確認しつつ修正していきたいと思います。

.Net Frameworkが使えると、私のやりたい事は全て出来るのでしょうか?
せっかくの.Netへの移行なのでAPIより.Netを使いたいのは山々なのですが。。。
ご助言などいただけると幸いです。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2010/7/8 17:59:19
> Alias "OpenPrinterA"
VB6 当時とは異なり、VB.NET の場合、ANSI 系 API を固定的に呼び出すメリットはほぼありません。
通常は Auto 指定にするか、Unicode 版の API を利用するようにしましょう。
Declare Auto Function OpenPrinter Lib "winspool.drv" (ByVal ~~

以下、Auto 指定の宣言がされた場合という前提で回答します。


> Declare Function OpenPrinter
第 3 引数は、「PRINTER_DEFAULTS への参照」が求められています。
今回は、第 3 引数を ByVal にしているのですから、PRINTER_DEFAULTS を Structure で
宣言するのは誤りです。PRINTER_DEFAULTS を Class に変更してください。

なお、戻り値は As Boolean で構いませんが、この場合の推奨表現は
「As <MarshalAs(UnmanagedType.Bool)> Boolean」となります。
http://msdn.microsoft.com/ja-jp/library/ms182206.aspx


> Declare Function GetPrinter
第 3 引数は、「PRINTER_INFO_2 への参照」です。こちらは、OpenPrinter のような
間違いは無いようですね(ByRef + Structure または ByVal + Class であれば OK)。


> Alias "RtlMoveMemory"
Marshal.StructureToPtr メソッド / Marshal.PtrToStructure メソッドと RtlMoveMemory を
併用されているのは何故でしょうか。Marshal クラスだけで充分な気がします。


> Public Declare Sub CopyMemory1 Lib "KERNEL32" Alias "RtlMoveMemory"
> Public Declare Sub CopyMemory2 Lib "KERNEL32" Alias "RtlMoveMemory"
VB.NET ではオーバーロードが可能なため、それぞれに別名をつける必要はありません。


> Public Structure PRINTER_DEFAULTS
メンバーの型宣言が間違っています。32bit OS で動かす場合は、As Long にはなりません。
<MarshalAs(UnmanagedType.LPTStr)>String, IntPtr, Integer にするか、あるいは
IntPtr, IntPtr, Integer などにしておきましょう。
http://dobon.net/cgi-bin/vbbbs/cbbs.cgi?mode=all&namber=26997&space=0&type=0&no=0


> Public Structure PRINTER_INFO_2
PRINTER_INFO_2 は文字列の受け渡しを伴うため、クラス(もしくは構造体)に
以下の属性を付与して、CharSet を明示しましょう。PRINTER_DEFAULTS も同様です。
 <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>


> Public Const CCHFORMNAME = 32
> Public Const CCHDEVICENAME = 32
Const にも As を宣言しましょう。


> Public Structure DEVMODE
こちらも StructLayout をつけておいた方が良いでしょう。今回は共用体(union)を
使わないので、LayoutKind.Sequential で良さそうですね。


>    <VBFixedString(CCHDEVICENAME)> Public dmDeviceName As String
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCHDEVICENAME)> を付与しましょう。
これは、dmFormname メンバーについても言える事です。

VBFixedString 属性は、Declare で呼び出すアンマネージ API に対しては役に立ちません。
この属性が意味を持つのは、一部のマネージ API に対してだけです(FilePut など)。
投稿者 モル  (社会人) 投稿日時 2010/7/8 17:06:00
貴重なご意見ありがとうございます。

 (--;さん、返信ありがとうございます。
VB6の方では実行後、コンパネからプリンタドライバをプロパティで確認すると変わっているのを確認できるのですが、VB2008の方はエラーは確かに起きませんが、設定が変わっていないので上手くいきませんと書かせてもらいました。

動作はVB6のように実行したら用紙名がユーザー定義になり、用紙サイズが横4000mm縦25000mmと入って縦向き設定になることです。


daiveさん、返信ありがとうございます。
>DOBON.NET > プログラミング道 > .NET Tips
は参考にさせてもらってますが、取得だけで設定の方法が見つけれませんでしたので・・・

「VB2005逆引き大全500の極意」なら所持しています。
ですが、コチラの本にはやりたい内容の設定はありませんでした。
内部だけで簡潔させたいので、プリンタのプロパティ画面のようなのを表示して画面上で変更・・・といったやり方はしない方向で実装したいと思っております。

.Net Frameworkでやる方法があればもちろんやりたいのですが、色々サイトめぐりはしておりますがどうにも解決出来ませんでしたので投稿させていただきました。
投稿者 daive  (社会人) 投稿日時 2010/7/8 16:38:58
VB2005/2008/2010では、 .NETの機能を使ったプログラムを書くことになるので、
APIありきの考えは、捨ててください。
APIでないと、出来ない部分が無いとは、言いませんが、
.NETサポートが有る部分は、.NETを使うようにしてください。

参照サイト
MSDNの印刷関係

DOBON.NET > プログラミング道 > .NET Tips
   画像、印刷編メニュー

秀和:Visual Basic 2008逆引き大全555の極意 
秀和:Visual Basic 2008 逆引き大全 至高の技 データベース+印刷/帳票 編
位は、持っておいても良い本です。
投稿者 (--;  (高校生) 投稿日時 2010/7/8 16:32:39
SPAMかと思った。

> 上手くいきませんでした。

なにがどううまくいってないんですか?
ソース自体は問題ないようですけど。

どういう動作を期待して、どこでエラーになって、エラーはこういうもので、っていうのを出してください。
投稿者 モル  (社会人) 投稿日時 2010/7/8 16:00:09

'フォーム 
Imports System.Runtime.InteropServices

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles Button1.Click
        Dim PD As New System.Drawing.Printing.PrintDocument
        Dim PrinterName As String
        Dim PrnD As PRINTER_DEFAULTS
        Dim PrinterHandle As Long
        Dim Result As Long
        Dim Needed As Integer
        Dim pPrinterInfo As IntPtr = IntPtr.Zero 'Dim pi2_buffer() As Long 
        Dim PrinterInfo As PRINTER_INFO_2
        Dim pFullDevMode As Long
        Dim MyDevMode As DEVMODE = Nothing

        PrinterName = PD.PrinterSettings.PrinterName
        If PrinterName = "" Then Exit Sub

        PrnD.pDatatype = Nothing
        PrnD.pDevMode = 0&
        PrnD.DesiredAccess = PRINTER_ALL_ACCESS

        Call OpenPrinter(PrinterName, PrinterHandle, PrnD)
        Call GetPrinter(PrinterHandle, 2, IntPtr.Zero, 0, Needed)
        'メモリを割り当てる 
        pPrinterInfo = Marshal.AllocHGlobal(Needed)
        Call GetPrinter(PrinterHandle, 2, pPrinterInfo, Needed, Needed)
        PrinterInfo = CType(Marshal.PtrToStructure( _
                                            pPrinterInfo, GetType(PRINTER_INFO_2)), PRINTER_INFO_2)
        pFullDevMode = PrinterInfo.pDevMode
'??? この辺りから? 
        Call CopyMemory1(MyDevMode, pFullDevMode, Len(MyDevMode))
        With MyDevMode
            .dmOrientation = DMORIENT_PORTAIT '縦向き 
            .dmPaperSize = 256
            .dmPaperLength = 4000
            .dmPaperWidth = 2500
            .dmFields = DM_PAPERSIZE Or DM_PAPERWIDTH Or DM_PAPERLENGTH
        End With
        Call CopyMemory2(pFullDevMode, MyDevMode, Len(MyDevMode))

        Result = SetPrinter(PrinterHandle, 2, PrinterInfo, IntPtr.Zero)
'??? /この辺りまで? 
        Call ClosePrinter(PrinterHandle)
        Me.Cursor = Cursors.Default
    End Sub

    Private Sub Form1_Load(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles MyBase.Load

    End Sub
End Class
投稿者 モル  (社会人) 投稿日時 2010/7/8 15:59:37

'モジュール 
    Public Declare Function OpenPrinter Lib "winspool.drv" Alias "OpenPrinterA" _
            (ByVal PrinterName As StringByRef hPrinter As IntPtr, ByVal pDefault As PRINTER_DEFAULTS) As Boolean
    Public Declare Function GetPrinter Lib "winspool.drv" Alias "GetPrinterA" _
                (ByVal hPrinter As IntPtr, ByVal dwLevel As Integer, _
                        ByVal pPrinter As IntPtr, ByVal cbBuf As IntegerByRef pcbNeeded As IntegerAs Boolean
    Public Declare Sub CopyMemory1 Lib "KERNEL32" Alias "RtlMoveMemory" _
                                            (ByVal hpvDest As DEVMODE, ByVal hpvSource As IntPtr, ByVal cbCopy As Long)
    Public Declare Sub CopyMemory2 Lib "KERNEL32" Alias "RtlMoveMemory" _
                                            (ByVal hpvDest As IntPtr, ByVal hpvSource As DEVMODE, ByVal cbCopy As Long)
    Public Declare Function SetPrinter Lib "winspool.drv" Alias "SetPrinterA" (ByVal hPrinter As IntPtr, _
                    ByVal Level As IntegerByRef pPrinter As PRINTER_INFO_2, ByVal Command_Renamed As IntegerAs Boolean
    Public Declare Function ClosePrinter Lib "winspool.drv" (ByVal hPrinter As LongAs Boolean

    Public Structure PRINTER_DEFAULTS
        Public pDatatype As String
        Public pDevMode As Long
        Public DesiredAccess As Long
    End Structure

    Public Structure PRINTER_INFO_2
        Public pServerName As String
        Public pPrinterName As String
        Public pShareName As String
        Public pPortName As String
        Public pDriverName As String
        Public pComment As String
        Public pLocation As String
        Public pDevMode As IntPtr
        Public pSepFile As String
        Public pPrintProcessor As String
        Public pDatatype As String
        Public pParameters As String
        Public pSecurityDescriptor As IntPtr
        Public Attributes As System.UInt32
        Public Priority As System.UInt32
        Public DefaultPriority As System.UInt32
        Public StartTime As System.UInt32
        Public UntilTime As System.UInt32
        Public Status As System.UInt32
        Public cJobs As System.UInt32
        Public AveragePPM As System.UInt32
    End Structure

    Public Const CCHFORMNAME = 32
    Public Const CCHDEVICENAME = 32

    Public Structure DEVMODE
        <VBFixedString(CCHDEVICENAME)> Public dmDeviceName As String
        Public dmSpecVersion As Integer
        Public dmDriverVersion As Integer
        Public dmSize As Integer
        Public dmDriverExtra As Integer
        Public dmFields As Long
        Public dmOrientation As Integer
        Public dmPaperSize As Integer
        Public dmPaperLength As Integer
        Public dmPaperWidth As Integer
        Public dmScale As Integer
        Public dmCopies As Integer
        Public dmDefaultSource As Integer
        Public dmPrinterQuality As Integer
        Public dmColor As Integer
        Public dmDuplex As Integer
        Public dmYResolution As Integer
        Public dmTTOption As Integer
        Public dmCollate As Integer
        <VBFixedString(CCHFORMNAME)> Public dmFormname As String
        Public dmUnusedPadding As Integer
        Public dmBitsPerPel As Integer
        Public dmPelsWidth As Long
        Public dmPelsHeight As Long
        Public dmDisplayFlags As Long
        Public dmDisplayFrequency As Long
    End Structure

    Public Const DMORIENT_PORTAIT = 1
    Public Const STANDARD_RIGHTS_REQUIRED = &HF0000
    Public Const PRINTER_ACCESS_ADMINISTER = &H4
    Public Const PRINTER_ACCESS_USE = &H8
    Public Const PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or PRINTER_ACCESS_ADMINISTER Or PRINTER_ACCESS_USE)
    Public Const DM_PAPERSIZE = &H2             'dmPaperSize 
    Public Const DM_PAPERLENGTH = &H4           'dmPaperLength 
    Public Const DM_PAPERWIDTH = &H8            'dmPaperWidth 
投稿者 モル  (社会人) 投稿日時 2010/7/8 15:57:07

'フォーム 
Private Sub Command1_Click()
    Dim PrinterName   As String
    Dim pd            As PRINTER_DEFAULTS
    Dim PrinterHandle As Long
    Dim Result        As Long
    Dim Needed        As Long
    Dim pi2_buffer()  As Long
    Dim pFullDevMode  As Long
    Dim MyDevMode     As DEVMODE
    
    PrinterName = Printer.DeviceName
    If PrinterName = "" Then Exit Sub
    
    Me.MousePointer = 11
    pd.pDatatype = vbNullString
    pd.pDevMode = 0&
    pd.DesiredAccess = PRINTER_ALL_ACCESS
    
    Result = OpenPrinter(PrinterName, PrinterHandle, pd)
    Result = GetPrinter(PrinterHandle, 2, ByVal 0&, 0, Needed)
    
    ReDim pi2_buffer((Needed \ 4))
    Result = GetPrinter(PrinterHandle, 2, pi2_buffer(0), Needed, Needed)
    pFullDevMode = pi2_buffer(7)
    
    Call CopyMemory(MyDevMode, ByVal pFullDevMode, Len(MyDevMode))
    With MyDevMode
        .dmOrientation = DMORIENT_PORTAIT   '縦向き 
        .dmPaperSize = 256
        .dmPaperLength = 4000
        .dmPaperWidth = 2500
        .dmFields = DM_PAPERSIZE Or DM_PAPERWIDTH Or DM_PAPERLENGTH
    End With
    Call CopyMemory(ByVal pFullDevMode, MyDevMode, Len(MyDevMode))
    
    Result = SetPrinter(PrinterHandle, 2, pi2_buffer(0), 0&)
    Call ClosePrinter(PrinterHandle)
    Me.MousePointer = 0
End Sub
投稿者 モル  (社会人) 投稿日時 2010/7/8 15:56:30

'モジュール 
Public Type PRINTER_DEFAULTS
    pDatatype     As String
    pDevMode      As Long
    DesiredAccess As Long
End Type
'http://www.winapi-database.com/Struct/DEVMODE.html 
Public Const CCHFORMNAME = 32
Public Const CCHDEVICENAME = 32

Public Type DEVMODE
    dmDeviceName       As String * CCHDEVICENAME 'デバイス名の文字列 
    dmSpecVersion      As Integer                'DEVMODEのバージョン(Win3.1 = &H30A) 
    dmDriverVersion    As Integer                'ドライバのバージョン 
    dmSize             As Integer                'DEVMODE構造体のバイト数 
    dmDriverExtra      As Integer                'DEVMODE構造体に続くデータのバイト数 
    dmFields           As Long                   'DEVMODE構造体の有効なエントリ数を指定 
    dmOrientation      As Integer                '用紙の向きを指定する定数 
    dmPaperSize        As Integer                '用紙サイズ 
    dmPaperLength      As Integer                '用紙の長さ(10mm単位)(dmPaperSizeに優先) 
    dmPaperWidth       As Integer                '用紙の幅(10mm単位)(dmPaperSizeに優先) 
    dmScale            As Integer                '用紙の拡大倍率(x/100単位) 
    dmCopies           As Integer                'コピー数 
    dmDefaultSource    As Integer                'デフォルトのピン番号 
    dmPrinterQuality   As Integer                '印刷品質 
    dmColor            As Integer                'カラーモード 
    dmDuplex           As Integer                '両面印刷の設定 
    dmYResolution      As Integer                'Y方向の印刷品質(Dot/inch) 
    dmTTOption         As Integer                'TrueTypeフォントの印刷方法 
    dmCollate          As Integer                'ページそろえの設定 
    dmFormname         As String * CCHFORMNAME   'フォーム名(WindowsNT系のみ) 
    dmUnusedPadding    As Integer                '未使用 
    dmBitsPerPel       As Integer                'ピクセルあたりのビット数 
    dmPelsWidth        As Long                   '幅(単位:ピクセル) 
    dmPelsHeight       As Long                   '高さ(単位:ピクセル) 
    dmDisplayFlags     As Long                   'ディスプレイモードを指定する定数 
    dmDisplayFrequency As Long                   'ディスプレイの動作周波数 
End Type

Public Declare Function OpenPrinter Lib "winspool.drv" Alias "OpenPrinterA" _
                                (ByVal pPrinterName As String, phPrinter As Long, pDefault As PRINTER_DEFAULTS) As Long
Public Declare Function SetPrinter Lib "winspool.drv" Alias "SetPrinterA" _
                                (ByVal hPrinter As LongByVal Level As Long, pPrinter As Any, ByVal Command As LongAs Long
Public Declare Function GetPrinter Lib "winspool.drv" Alias "GetPrinterA" _
                (ByVal hPrinter As LongByVal Level As Long, pPrinter As Any, ByVal cbBuf As Long, pcbNeeded As LongAs Long
Public Declare Sub CopyMemory Lib "KERNEL32" Alias "RtlMoveMemory" (hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long)
Public Declare Function ClosePrinter Lib "winspool.drv" (ByVal hPrinter As LongAs Long

Public Const DMORIENT_PORTAIT = 1   '縦向き 
Public Const STANDARD_RIGHTS_REQUIRED = &HF0000
Public Const PRINTER_ACCESS_ADMINISTER = &H4
Public Const PRINTER_ACCESS_USE = &H8
Public Const PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or PRINTER_ACCESS_ADMINISTER Or PRINTER_ACCESS_USE)
Public Const DM_PAPERSIZE = &H2             'dmPaperSize 
Public Const DM_PAPERLENGTH = &H4           'dmPaperLength 
Public Const DM_PAPERWIDTH = &H8            'dmPaperWidth 
投稿者 モル  (社会人) 投稿日時 2010/7/8 15:55:58
こんにちは、モルです。いつも拝見させていただいています。

早速ですがVisual Basic 2008でプリンタ情報の設定の仕方でお聞きしたい事があります。
Visual Basic6では問題なく出来たのですが(API使用)、これをVisual Basic 2008に直したいのですが上手くいきませんでした。
しっかり理解しきれていない部分もあるので変換しきれないのだと思ってはいるのですが、何か方法がございましたら教えてくださると助かります。
APIを使わない方法など、ご意見などよろしくお願いします。

以下長いですがコードを貼らせてもらいます。