PRINTERの「利用可能な用紙」などにつきまして への返答

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

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

投稿者 snowmansnow  (社会人) 投稿日時 2021/7/4 10:40:39
こんにちは、るきお様、魔界の仮面弁士様

VBAで解決できなくて、C#のフォームアプリケーション、クラスライブラリで取得してみました。

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing.Printing;

namespace WindowsFormsAppPrinterCLCS
{
 
    [Guid(GetPrinter.ClassId)]
    public class GetPrinter
    {
        //  COM用のGUID値
        public const string ClassId = "F0C14F0A-20B3-4716-AF7E-21458B1219D4";

        //SendMessage(データ転送)
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int SendMessage(int hWnd, int Msg, int wParam, StringBuilder lParam);
        //SendMessage(データ転送)
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int SendMessage(int hWnd, int Msg, int wParam, int lParam);

        const int CB_GETLBTEXT = 0x0148;
        const int CB_GETCOUNT = 0x0146;
        const int CB_GETLBTEXTLEN = 0x0149;


        public string GetSource(string pn)
        {
            string lineAll = "";

            PrintDocument printDoc = new PrintDocument();
            PaperSource pkSource = new PaperSource();

            printDoc.PrinterSettings.PrinterName = pn;
            for (int i = 0; i <= printDoc.PrinterSettings.PaperSources.Count - 1; i++)
            {
                pkSource = printDoc.PrinterSettings.PaperSources[i];
                lineAll += "," + i + ":" + pkSource.Kind + ":"+ pkSource.RawKind + ":" + pkSource.SourceName + Environment.NewLine;
            }
            return lineAll;
        }
        public string GetPaperHeightWidth(string printername,string customname)
        {
            string lineAll = "";
            PrinterSettings settings = new PrinterSettings();
            foreach (PaperSize size in settings.PaperSizes)
            {
                lineAll += "," + size.Kind + ":"  + size.RawKind + ":" + size.PaperName + ":" + size.Height + ":" + size.Width + Environment.NewLine;
            }
            return lineAll;
        }
        public string GetComboText(int cbHandle, int i)
        {
            //https://bytes.com/topic/c-sharp/answers/831658-getting-setting-edititem-combobox-using-sendmessage
            StringBuilder ssb = new StringBuilder(256, 256);
            SendMessage(cbHandle, CB_GETLBTEXT, i, ssb);
            return ssb.ToString();
        }
        public int GetComboCount(int cbHandle)
        {
           int CBcount = SendMessage(cbHandle, CB_GETCOUNT, 0, 0);
            return CBcount;
        }

    }
}

お騒がせしましたが、また宜しくお願いします。
(過去webとかも、もう少し調べてから御質問します)
もし、別途VBAで教えていただければ、とても有難いです。


投稿者 snowmansnow  (社会人) 投稿日時 2021/7/4 07:51:13
おはようございます るきお様、魔界の仮面弁士様

http://nonsoft.la.coocan.jp/Win32Api/Win32ApiEnumWin.html様の
function GetStatusBarTextの例を
コンボボックスに変えたつもりですが、
'共有メモリ初期化の   lngRet = WriteProcessMemory(~)の戻り値が0になっています。
 その後のsendmessage以前にダメなようでした。
それ以前は動いているようでした。

メモリ関係が良くわかりません・・・

何か良い対策は、ございますでしょうか?
よろしくお願いします。

投稿者 snowmansnow  (社会人) 投稿日時 2021/7/3 22:10:28
こんばんは、るきお様、魔界の仮面弁士様
勘違いしてまして
PAPERSOURCEのROWKINDと
PAPERSIZEのROWKINDは、対応しておらず、
トレイのサイズはCUSTOM設定のWIDTHやHEIGHTから分からないと思われました。

仕方が無いのでプリンタ設定のダイアログの用紙トレイから取得しようと思いました。
でも、COMBOBOXのhWndと個数までは取得できたのですが、
用紙テキストを取得しようとすると、エクセルが落ちます。

Private Function MyGetComboBoxItem(hWnd As LongAs String

' コンボボックス文字列取得 
'http://mt-soft.sakura.ne.jp/web_dl/vb-parts/get_combobox/ 
Dim strItemData             As String * 4096    '取得文字列格納用バッファ 
    Dim lngRc As Long
    Dim Cnt As Long
    Dim i As Long
    Dim Dt As String
     Dim p As Long
     Cnt = SendMessage(hWnd, CB_GETCOUNT, 0, CLng(0))    '個数取得 
    Dt = ""
    For i = 0 To Cnt - 1
        
       MsgBox "ここ1"
        lngRc = SendMessage(hWnd, CB_GETLBTEXT, i, strItemData) '文字列取得 
        MsgBox "ここ2"
        
        p = InStr(strItemData, vbNullChar) - 1          '文字列長 
        If p >= 0 Then Dt = Dt & Left(strItemData, p)
        Dt = Dt & vbCrLf
        
    Next i
    MyGetComboBoxItem = Dt
     
End Function
 


以前に魔界の仮面弁士様が別のサイトで、
http://hanatyan.sakura.ne.jp/vb60bbs/wforum.cgi?mode=allread&no=15777
メモリの確保が大事とおっしゃってましたが、まだよく分かりません。

COLLECTIONの例で
http://nonsoft.la.coocan.jp/Win32Api/Win32ApiEnumWin.htmlや
るきお様のページも見たのですが、フォームでない別アプリが良くわかりませんでした。

何か対策やヒントございましたらお願いしたいです。
宜しくお願いします。



投稿者 snowmansnow  (社会人) 投稿日時 2021/7/3 12:14:46
こんにちは、るきお様

月曜から体調不良で、昨日やっと確認できました。
WMIでプリンタの一覧とポートNOを取得してから、
GetBinListで、値を取得してみました。

複合機のA社の標準複合機ドライバのものは、エラーになり、BinListを取得できませんでしたが、
他の会社は、BinListを取得できました。
トレイ数と、BinList数は一致しておりました。
トレイ種類と、BinListを比較したかったのですが、
GetBinListでは、数字のみで紙サイズがわからず、一致を確認できませんでした。
VBNETでPaperSizeを取得した時も、APIは数字だけのものが多く、VBNETではサイズ表示をしていたので、
VBNETでBinListを取得できたら、サイズも確認できるのでは?と思っております。

複合機のA社の一部の標準複合機のプリンタドライバのものは、BinListを取得できてるようでした。
複合機のA社の標準複合機ドライバのものも、VBNETでは、エラーにならないのかな?と思っております。

APIのトレイ数字の一覧と、PaperSizeの一覧のようなものは、あるのでしょうか?
もしあったら見てみたいです。

投稿者 snowmansnow  (社会人) 投稿日時 2021/6/25 22:43:31
るきお様お返事ありがとうございます。
APIの例、ありがとうございます。
自分では動かなかったので、どんな値がでるか期待していました。
でも、VBNETのPAPERSIZEと同じ数でした(用紙の種類は表示がされたりされなかったりで、確実には確認できませんでいたが)
APIで気になっているのが
DC_BINSとDC_BINNAMESで、先に教えて頂いたwebにGETBINNAME()があり、
るきお様の修正してくれたコード
   Dim printer As String
    printer = Application.ActivePrinter
    strDeviceName = Split(printer, " on ")(0)
    strDevicePort = Split(printer, " on ")(1)
 

を使ったところ、トレイ?トレイ名?の出力を確認できました。
今日は複合機群の各出力を確認できず、プリンターダイアログのどれと対応してるのか確認できませんが、
月曜以降確認させて頂きたいと思います。

教えて頂いたC++のサイトは、用紙の例と、出力エラーのスプール残の確認用でしょうか?
スプールが残っていたら、紙が違うという事でしょうか?
複合機側もエラーになっていると思われ、共同作業場(遠隔)では、迷惑かもしれないと思います・・・

また、「実際に印刷可能な用紙の一覧や、セットされている紙はWindowsでは定義されていない情報だと思いますので、プリンタードライバーが独自に定義している場合のみ取得可能です。」
は、残念で、可能な場合の取得も敷居が高そうな話で悲しいです。
「selects the size of the paper to print on」の記載も、教えて頂いたwebのpdfで確認できず、
メーカー次第で悲しいですが、私の希望はかなわないのかもしれません。

月曜以降に少し期待しています。
その際は、また報告します。
大変ありがとうございます。









投稿者 るきお  (社会人) 投稿日時 2021/6/25 20:57:48
snowmansnowさんが、取得したいのは、紙が実際にセットされていて、印刷指示をすれば実際に印刷可能な用紙の一覧を取得したいということでしょうか?

Windowsではプリンタードライバーに最低限必要なインターフェースが定義されており、各プリンタードライバーは定義された情報を返します。
また、それとは別にプリンター独自の拡張機能や情報を保持している場合もあります。
実際に印刷可能な用紙の一覧や、セットされている紙はWindowsでは定義されていない情報だと思いますので、プリンタードライバーが独自に定義している場合のみ取得可能です。

標準化されている情報のリファレンスはたとえば、このあたりにあります。
https://docs.microsoft.com/ja-jp/windows/win32/api/wingdi/ns-wingdi-devmodea
https://docs.microsoft.com/ja-jp/windows/win32/printdocs/printer-info-1

DEVMODEA構造体のリファレンスにはこの構造体におけるPaperSizeの定数と意味も掲載されています。
プリンタードライバーが実際にこの情報として何を返すかは、このリファレンスでは「selects the size of the paper to print on」としか記載されておらず、詳細はメーカーに委ねられているかもしれません。

私はあまりこのあたりのことは詳しくありません。
ドライバー開発者用のリファレンスにはもっと情報が記載されているかもしれません。
投稿者 るきお  (社会人) 投稿日時 2021/6/25 20:44:47
とりあえず、Excelで動くようにしたプログラムを共有します。
久しぶりにExcel VBAをいじりました。

Application.Printer というものはExcelにはないのですね。そこをいじって動くようにしました。私の環境では動作しましたが、付け焼刃なので動かない環境もあるかもしれません。

' Declaration for the DeviceCapabilities function API call. 
Private Declare PtrSafe Function DeviceCapabilities Lib "winspool.drv" _
    Alias "DeviceCapabilitiesA" (ByVal lpsDeviceName As String, _
    ByVal lpPort As StringByVal iIndex As Long, lpOutput As Any, _
    ByVal lpDevMode As LongAs Long
    
     
' DeviceCapabilities function constants. 
Private Const DC_PAPERNAMES = 16
Private Const DC_PAPERS = 2
Private Const DC_BINNAMES = 12
Private Const DC_BINS = 6
Private Const DEFAULT_VALUES = 0

Sub GetPaperList()
    Dim lngPaperCount As Long
    Dim lngCounter As Long
    Dim hPrinter As Long
    Dim strDeviceName As String
    Dim strDevicePort As String
    Dim strPaperNamesList As String
    Dim strPaperName As String
    Dim intLength As Integer
    Dim strMsg As String
    Dim aintNumPaper() As Integer
     
    On Error GoTo GetPaperList_Err
     
    ' Get the name and port of the default printer. 
    
    Dim printer As String
    printer = Application.ActivePrinter
   
    strDeviceName = Split(printer, " on ")(0)
    strDevicePort = Split(printer, " on ")(1)
    'strDeviceName = Application.ActivePrinter.DeviceName 
    'strDevicePort = Application.ActivePrinter.Port 
     
    ' Get the count of paper names supported by the printer. 
    lngPaperCount = DeviceCapabilities(lpsDeviceName:=strDeviceName, _
        lpPort:=strDevicePort, _
        iIndex:=DC_PAPERNAMES, _
        lpOutput:=ByVal vbNullString, _
        lpDevMode:=DEFAULT_VALUES)
     
    ' Re-dimension the array to the count of paper names. 
    ReDim aintNumPaper(1 To lngPaperCount)
     
    ' Pad the variable to accept 64 bytes for each paper name. 
    strPaperNamesList = String(64 * lngPaperCount, 0)
 
    ' Get the string buffer of all paper names supported by the printer. 
    lngPaperCount = DeviceCapabilities(lpsDeviceName:=strDeviceName, _
        lpPort:=strDevicePort, _
        iIndex:=DC_PAPERNAMES, _
        lpOutput:=ByVal strPaperNamesList, _
        lpDevMode:=DEFAULT_VALUES)
     
    ' Get the array of all paper numbers supported by the printer. 
    lngPaperCount = DeviceCapabilities(lpsDeviceName:=strDeviceName, _
        lpPort:=strDevicePort, _
        iIndex:=DC_PAPERS, _
        lpOutput:=aintNumPaper(1), _
        lpDevMode:=DEFAULT_VALUES)
     
    ' List the available paper names. 
    strMsg = "Papers available for " & strDeviceName & vbCrLf
    For lngCounter = 1 To lngPaperCount
         
        ' Parse a paper name from the string buffer. 
        strPaperName = Mid(String:=strPaperNamesList, _
            Start:=64 * (lngCounter - 1) + 1, Length:=64)
        intLength = VBA.InStr(Start:=1, String1:=strPaperName, String2:=Chr(0)) - 1
        strPaperName = Left(String:=strPaperName, Length:=intLength)
         
        ' Add a paper number and name to text string for the message box. 
        strMsg = strMsg & vbCrLf & aintNumPaper(lngCounter) _
            & vbTab & strPaperName
             
    Next lngCounter
         
    ' Show the paper names in a message box. 
    MsgBox Prompt:=strMsg
 
GetPaperList_End:
    Exit Sub
     
GetPaperList_Err:
    MsgBox Prompt:=Err.Description, Buttons:=vbCritical & vbOKOnly, _
        Title:="Error Number " & Err.Number & " Occurred"
    Resume GetPaperList_End
     
End Sub
投稿者 snowmansnow  (社会人) 投稿日時 2021/6/25 20:22:36
るきお様こんばんは、
お返事ありがとうございます。

やっぱりダメで、
「エラー438
オブジェクトは、このプロパティまたはメソッドをサポートしていません。」になってしまいます。

「利用可能な用紙:」一覧は、複合機のトレイ内容と一致してる複合機もありますし、
複合機のトレイ内容の一部のものもありますし、複合機のトレイ内容より多いものもあります。

「利用可能な用紙:」一覧の意味が知りたいですし、複合機本来の各トレイ用紙一覧が取得できて、
その中の用紙を設定したいです。(無人で、用紙変更・補給などせず印刷したいです。)

各メーカーの独自?ドライバーの中の、トレイ一覧も、ただトレイ番号一覧のものや、
用紙も表示してるもの、用紙の大きさや向きも表示してるものなど様々でした。

各メーカーのドライバーの中の表示項目の全部又は一部は、何かしらの規格に基づいていて、
それを、独自の表示方法で表示してるだけ、ではないのでしょうか?
それぞれ、完全に各メーカー独自の世界なのでしょうか?

規格があるのなら、表示してるものを、それを通信したり取り出す事ができるのでは?と思いました。
規格~の考えが間違っていて、取り出す事や設定する事は出来ない世界なのでしょうか?

VBNETやPOWERSHELLの用紙設定は、遠隔で設定できても、その紙があるかわからず、
パソコンで設定してから、プリンタに行って、紙を確認したり、変更・補充しないと、
印刷は完了せずに、エラーのままなのでしょうか?そのエラーはわかりますか?

お手数かけますが、よろしくお願いします。



投稿者 るきお  (社会人) 投稿日時 2021/6/25 19:38:52
ここにプリンターがサポートする用紙の一覧を取得するVBのサンプルプログラムがあります。

https://docs.microsoft.com/ja-jp/office/vba/access/concepts/printing/programmatically-retrieve-printer-capabilities

Office VBA リファレンスの一部なので、Excel VBAでも動作するのではないかと思いますがどうでしょうか?
投稿者 snowmansnow  (社会人) 投稿日時 2021/6/24 23:36:40
こんばんは、るきお様、皆様
PRINTERの用紙についてお聞きしたいです。
普段は、複数種類の複合機や、
PDFプリンターをプリンターとして利用する事が多いです。

Windowsの設定の、プリンターのキューを開いてプロパティを開くと
全般タブの、機能の中に「利用可能な用紙:」というボックスがあります。
ここに表示される用紙の一覧(1種類から4種類くらい)をVBAなどで取得する事は可能でしょうか?

POWERSHELLのTRAY一覧や(用紙サイズがわからない)
VBNETのPaperSize一覧(ものすごい種類の用紙が取得される)
とも違うのでは?と思っています。

複合機のトレイ設定数・内容と合致する事や、相違する事(トレイの一部のみ表示)
があるみたいでした。


①この「利用可能な用紙:」一覧の用紙の意味が知りたいのと、
②この一覧の取得や、
③できれば、複合機での、手差しトレイでない、
 標準的なトレイ一覧が取得できるといいなぁと思っています。

④「利用可能な用紙:」の下の「基本設定」ボタンを押した後で出てくる
  ダイアログの、「用紙サイズ」との違いがあれば知りたいです。

⑤VBNETのPaperSize一覧の用紙の意味も知りたいです。

⑥POWERSHELLの、set可能なPaperSizeの意味と、①や④との対応も知りたいです。
 POWERSHELLでPaperSizeをsetしても、①や④のダイアログには影響しないようですし、
  でも、プリンタによってはsetを受け付けないPaperSizeもあるようでした。
  windows10 64bit powershell7
  https://www.koskila.net/how-to-change-printer-paper-size-using-powershell/


⑦意味がわかり、一覧の取得ができましたら、VBAなどで、それぞれに影響する用紙設定も教えて欲しいです。


APIの例もエラーになってしまい、何の用紙を取得できてるのか、実行結果を確認できませんでした。
https://docs.microsoft.com/ja-jp/office/vba/access/concepts/printing/programmatically-retrieve-printer-capabilities

できればVBAで、またはVBNETやPOWERSHELLでもよろしいので、教えて頂ける方お願いします。
 Excel2016の32bitでも64bitでもよろしいです。

APIやVBNETやPOWERSHELLの説明を見たり、少し実行してみてもわからず、よろしくお願いします