(C) 2009 - 2021 by Mourad Louha · Alle Rechte vorbehalten

Drucker und Papierformate in VBA ermitteln und verwenden

Im Rahmen unseres Projektes zur Generierung von Seriennummern stellte sich das Problem, ein Papierformat eines speziell zum Ausdruck von Typenschildern installierten Druckers anzusprechen. Das dauerhafte Umstellen des Standarddruckers in Windows war keine Lösung, denn zum Einen nutzen die Benutzer unsere Anwendung parallel mit weiteren Excel Arbeitsmappen und zum Anderen sich verschiedene Größen von Typenschildern zu drucken. Auch das ständige Auswählen des Druckers wäre nicht praktikabel.

Im Folgenden werden wir unsere Windows API Lösung vorstellen, die einerseits das Auswählen eines Druckers ermöglicht und andererseits alle verfügbaren Papierformate eines Druckers anbietet. Der Code hierzu den wir an dieser Stelle freigeben ist nur ein Auszug aus der Echtanwendung. Letztere merkt sich pro einer Maschine assoziiertes Typenschild das passende Format und den passenden Drucker. Somit braucht der User nur noch kurz die Angaben prüfen und kann sich auf das Wesentliche konzentrieren.

In der Beispielanwendung – die sie auch herunterladen können – haben wir eine Userform erstellt, die zwei Kombinbationsfelder enthält. Erstere nimmt die Liste der installierten Drucker auf, die zweite Combobox enthält nach Auswahl eines Druckers die passenden Papierformate.

Drucker und Papierformate in VBA ermitteln und verwenden

Um nun die Liste der Drucker zu ermitteln, benötigen wir die Windows API Funktion GetProfileString(), welche uns die Liste der verfügbaren Drucker zurückliefert.
Folgend der Code, in welchem wir den Aufruf der API Funktion durchführen.

Da wir die Funktion später in der Initialisierungsfunktion der Userform aufrufen werden, übergeben wir mlfpPrinters() das Handle zur Userform, den Namen des Steuerelementes sowie einen String für den Fall dass keine Auswahl getroffen wurde. Nach dem Aufruf der API Funktion enthält der Parameter r eine durch Nullzeichen separierte Zeichenkette, die die Liste der Drucker darstellt. Wir übergeben r an die Funktion mlfhNames(), die r aufteilt und die gefundenen Drucker im Array mlvhNames ablegt. Um einen Drucker in Excel über die globale Zuweisung Application.ActivePrinter wechseln zu können, muss auch ein Druckerport angegeben werden. Um diese zu ermitteln, rufen wir unsere Funktion mlfhPorts zu, die wiederum einen erneuten Aufruf der API Funktion GetProfileString() ausführt, allerdings mit veränderten Übergabeparametern. Auch hier erhalten wir pro Aufruf einen String, welcher jedoch diesmal die Elemente durch Kommata trennt. Wir zerlegen diesen String und füllen unsere Arrays mlvhPorts und mlvhDrivers, wobei letzteres nur der Vollständigkeit halber gefüllt wird.

Wie bereits erwähnt, verwenden wir ein Kombinationsfeld, um unsere Drucker aufzulisten. Im Code zu mlfpPrinters() löschen wir nach den Aufruf von mlfhPorts zunächst den Inhalt der Combobox und stellen diese auf 5 Spalten ein, wobei alle Spalten ausser der zweiten ausgeblendet werden. Anschließend füllen wir jede Spalte mit den Inhalten unserer Arrays. Besondere Beachtung findet die dritte Spalte. Um den Drucker mit ActivePrinter zu setzen muss auf das Wort auf übergeben werden. Eine gültige Anweisung wäre beispielsweise:

Application.ActivePrinter = "Adobe PDF auf Ne03:"

Nun sind unsere Anwendungen in der Regel auf Systemen mit unterschiedlichen Sprachen installiert, d.h. wir müssen berücksichtigen, dass auf einem englischen System der Zuweisungsstring „Adobe PDF on Ne03:“ und in Französisch „Adobe PDF sur Ne03:“ heißt. Da wir jedoch keine API Funktion gefunden hatten, die uns diesen String ermittelt und um zu vermeiden, dass eine sprachabhängige Liste in der Anwendung mitgeführt wird, bedienen wir uns eines kleinen Tricks. Zunächst suchen wir den in Excel aktiven Drucker aus unserer Gesamtliste heraus und reduzieren dann diesen String, indem wir Port und Druckername entfernen. Übrig bleibt der lokalisierte String, hier also auf. Abschließend füllen wir unser Kombinationsfeld. Folgend der Code zu unserer Userform.

Bei der Initialisierung rufen wir mlfpPrinters(Me, CMB_0001.Name, „?“) auf und setzen den Index von CMB_0001 auf 0. Wählt ein User nun einen Drucker aus CMB_0001, wird mlfpPapersizes(Me, CMB_0001.Name, CMB_0002.Name, „?“) aufgerufen. Diese Funktion ist ähnlich mlfpPrinters() aufgebaut, nur dass zusätzlich der Name einer zweiten Combobox übergeben wird. Die Funktion sieht wie folgt aus:

Hier kommt unsere zweite API Funktion DeviceCapabilities ins Spiel, welche uns die Papierformate des gewählten Druckers zurückliefert. Der erste Aufruf der API Funktion liefert die verfügbare Anzahl der Papierformate zurück. Nach 2 weiteren Aufrufen der Funktion erhalten wir in r einen durch Nullzeichen separierten String, den wir wieder zerlegen und das Kombinationsfeld mit den einzelnen Elementen füllen können. Auch dieses Kombinationsfeld hat zwei Spalten, in erstere wird eine ID zum Papierformat abgelegt.

Schauen wir uns nun abschließend den Code zur Schaltfläche BTN_0009 an. In einem ersten Schritt wird der in Excel aktive Drucker in einer Variable abgelegt, danach die Zeichenkette zur Zuweisung an ActivePrinter aus den Elementen der ersten Combobox zusammengesetzt und zugewiesen. Weitere Einstellungen werden getroffen, das Papierformat übergeben (wichtig, die ID ist zu übergeben, nicht die Bezeichnung) und die Druckvorschau aufgerufen. Abschließend wird der Originaldrucker wieder zugewiesen.

Sie können den Code nach Belieben anpassen und in Ihren Projekten verwenden. Sollten Sie einen Fehler entdecken, würden wir uns über eine Nachricht freuen. Aber auch Anregungen und Kommentare sind willkommen.

Die Beispielanwendung können Sie hier Drucker und Papierformate in VBA ermitteln und verwenden herunterladen. Beachten Sie bitte, dass dieser Link nur angeklickt funktioniert und wenn Ihr Browser einen Referer sendet. Ausserdem übernehmen wir natürlich in keinster Weise irgendeine Haftung für die Richtigkeit der Angaben in der Datei.

 
Comments

Hello !

First of all, thanks of this tutorial. I searched a lot before find this !
I have „juste“ 3 questions :
-> I have different printer in my company and some have a lot of papersize available, like more than 50. Is that normal, because in the settings of the printer in windows, I find 2 or 3 papersize maximum (A3/A4/A5) ?
-> Some name of papersize are very strange like „Tabloid“ or „ARCH E1“, is that normal too ? (But maybe it’s special format for specific things)
-> In some printer, there is 2 or 3 time the papersize with differnent noun (ex : „A4 (210 x 297 mm)“, „A4“). Do you know why ?

If you have the code modification to have only the basic papersize (A3, A4 or A5) available on the printer selected, it would be amazing !

But still this little app help me a lot and is so nice. A big thanks for that. I’m not a begginer in VBA, I know a lot of things, but macro like that, it’s beyond my capabilities. Thank you !

I’m not German but I will keep an eyes on your very informative website !

Thanks and have a good day,

Baboutz

Hi Baboutz,

thank you very much! I am happy that the code helped you. However, please take into account, that the code is over 10 years old now, as the article is from 2009. This code may not work on e.g. 64 Bit Systems. In any way, I recommend to replace all declarations Private Declare by Private Declare PtrSafe, if you are using Excel 2010 or higher.

Regarding your questions: yes, I think, that’s normal, that so many entries are found. In Windows 10, if I select a printer from Settings / Printers & Scanner e.g. MS XPS Document Writer and then Manage, then Printer properties / General tab / Preferences, then Layout tab / Advanced, I get a lot of entries listed for the paper size, which correspond to the output of my tool. Includes also different formats, which I think, match different formats from several countries, even if it is A4, for example.

In my code, you can filter out the unneeded formats. Go into the module MLP_Maninweb and to the function mlfpPapersizes. There, you will find at the end of the function the following block:

  Handle.Controls(Target).AddItem
' Data...
 .List(.ListCount - 1, 0) = v(i)
 .List(.ListCount - 1, 1) = t

Replace that code by the following:

If Not UCase(t) <> "A3" Or Not UCase(t) <> "A4" Or Not UCase(t) <> "A5" Then
  Handle.Controls(Target).AddItem
 .List(.ListCount - 1, 0) = v(i)
 .List(.ListCount - 1, 1) = t
End If

That straightly takes only A3 … A5. It’s also possible to use InStr() and then get variants of A3 … A5.

Hope, that helps :-)

Best,
Mourad

Hi,

Thanks for your quick reply, and thanks by the way, for all the codes and tips your share for free !

I checked in this settings, and I have a lot of entries too. I looked in the wrong place before ;)
I apply your recommendation, it works as I want. It is so cool !

A huge thanks, that help me so much !!

Best,

Baboutz

Hi Baboutz,

great to hear, all worked fine for you. Thanks :-)

Best,
Mourad