Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win


300 Points.  Dll to change default printer paper size

Posted on 2001-06-14
Medium Priority
Last Modified: 2013-11-20
I've come to the end of my rope.  I am needing a dll that changes the default printer paper size to legal. and the returns to the previous state.I think it needs to be a dll so that it can be used in Access 97.  Access doesn't allow any changing of printer sizes programmically unless you leave the program in an MBD state.   Up on entering Access MDE it would store the present papersize and change it to legal. After exiting Access it would then restore the previous papersize.

Any Help, Any Place,
Any Time Please.....

Question by:chasferr
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 3
  • 2
  • +2
LVL 32

Accepted Solution

jhance earned 400 total points
ID: 6193201
1) Use GetDefaultPrinter() to get the name of the default printer.

2) Use OpenPrinter() to open the printer and get a handle to it.

3) Use DocumentProperties() to get the current page size and to set the desired one.

4) Use ClosePrinter() to close the handle to the printer.

From ACCESS you need a COM object not a standard DLL.  A COM in-process server is implemented as a DLL but it must be a COM DLL, not a "standard" DLL.

There are hooks in ACCESS for running VBA code when entering the application and when leaving so this is possible.  I'm just not sure how to do it since I'm not much of a VBA or ACCESS guru...

Assisted Solution

by:Chandra V
Chandra V earned 400 total points
ID: 6194628
Add a Function to your App Class like this

     PRINTDLG pd;
     BOOL bRet=GetPrinterDeviceDefaults(&pd);
      // protect memory handle with ::GlobalLock and ::GlobalUnlock
      DEVMODE FAR *pDevMode=(DEVMODE FAR *)::GlobalLock(m_hDevMode);
      // set orientation to landscape
  pDevMode->dmPaperSize = DMPAPER_LEGAL;

For Additional Settings seee 'CWinApp::GetPrinterDeviceDefaults' Documentation

Author Comment

ID: 6194681
  Thank you for the comment However, where do I find the documentation on
1) Use GetDefaultPrinter() to get the name of the default printer.

2) Use OpenPrinter() to open the printer and get a handle to it.

3) Use DocumentProperties() to get the current page size and to set the desired one.

4) Use ClosePrinter() to close the handle to the printer.
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.


Author Comment

ID: 6194695
To cvallabhaneni
  Thank you for your comment However...

How do I get to this from access. Any ideas.

Add a Function to your App Class like this

    PRINTDLG pd;
    BOOL bRet=GetPrinterDeviceDefaults(&pd);
     // protect memory handle with ::GlobalLock and ::GlobalUnlock
     DEVMODE FAR *pDevMode=(DEVMODE FAR *)::GlobalLock(m_hDevMode);
     // set orientation to landscape
 pDevMode->dmPaperSize = DMPAPER_LEGAL;

For Additional Settings seee 'CWinApp::GetPrinterDeviceDefaults' Documentation


Expert Comment

by:Chandra V
ID: 6194744
Hai chasferr,

The following article helps in your case

LVL 32

Expert Comment

ID: 6194750
Documentation for all Windows API functions is in the MSDN library.  If you have a Windows development tool it is surely included.  If not, you can download the Windows SDK from Microsoft free.  You can also access the MSDN library online free of charge at http://msdn.microsoft.com.

Expert Comment

by:Chandra V
ID: 6194765
A Sample Code for managing Access Printer Settings

Author Comment

ID: 6194797
To cvallabhaneni
THanks again, up early huh,
   I've used the routine, the problem is it doesn't work unless you keep your application as a mdb. which exposes your code.
  I forgot that I decided that I can't do it within access. I just need to change the default printer paper size using an exe file before actually loading the access application.
Any ideas.


Author Comment

ID: 6220298
 Can you put the whole piece together. It doesn't have to be a dll.  Just, a program the changes the default printer to legal. And a program to change the default printer to letter.  This can be run outside any app.  
Something to programmically take the place of manually going to settings, control panel, printers properties and doing it manually.  I feel like you are close  

Assisted Solution

christophm earned 400 total points
ID: 6501725
Hi chasferr,

I believe your needs are answered by cvallabhaneni's code and jhance's suggestion that you put the code into an Automation component.  Automation components are the easiest thing for the VB/VBA code to tet to.  Creating an automation component is not difficult, I have included - it follows this note - a COMPREHENSIVE explanation of how to do that.  If this satisfies your needs do not give me any points for this - the others have really answered your question.


This is a writeup I did a while ago on how to create an automation
component and then use the exposed methods and properties from VB
(or VBScript or VBA).  If you follow this step by step you will create
a working automation component.  At the end of this writeup I will take
you through using the automation component from VBScript - (it will work
the same from VB and VBA.)

By The Way - I learned this from the Microsoft Press Book, "Inside Visual
C++", by  David Kruglinski.

To create an automation component you start with the the simplest
application that the wizard will support.  The way to get a simple
application is to use the wizard to create a dialog application then
delete all the code and all the files that you can from that
application.  This first section explains how to do that.

Use the the wizard to create a dialog application. (I named it
'Christophm' and put it in the 'Christophm' subdirectory).

I deselected (clicked to turn the check mark off) all the options in
Step 2 of the app wizard.

I checked the "As a statically linked library" box in Step 3.  (I do
this because some of my stuff runs all over North America, and some in
Europe.  I statically link so I won't have as many problems with installs
on PCs that are far away).

I made no other changes in the app wizard options.

Do not run the application yet!  We won't run this until we are further

Now you have to delete the files from the project and from the disk
that are not needed for an automation component.  The app is not going to
end up being a dialog application so you don't need either the
dialog.cpp or the dialog.h files.  Go to the "FileView" tab (in the
IDE) and delete  "CHRISTOPHMDlg.h"  from the 'Header Files' and
delete "CHRISTOPHMDlg.cpp" from the 'Source Files'.

You have removed the CHRISTOPHMDlg.h and CHRISTOPHMDlg.cpp files from
the project's list of files but the physical files are still in subdirectory.  
Go to Windows Explorer and delete those two files from the hard drive.

There is still a reference (the  'include "CHRISTOPHMDlg.h"' at the top
of the "CHRISTOPHM.h" file.  Go to the "ClassView" tab, open the
"CHRISTOPHMApp.h" file and then delete the line near the top that says    
'include "CHRRISTOPHMDlg.h"

Now (using 'ClassView' again) open the function "InitInstance()".  You
see that "InitInstance()" has all the code to display the dialog
CHRISTOPHMDlg.  Delete everything in "InitInstance()" except these
three lines :

      return FALSE;

      Now "InitInstance() looks like this !

BOOL CJunkApp::InitInstance()
      return FALSE;


Next you have to enable your application for automation.  

Add the include "#include <afxdisp.h>" at the top after the other
includes that you find in stdafx.h in your project (you can use the
'FileView' tab to open stdafx.h and then type in that line.)

Now Change "InitInstance()" so it looks like this.

BOOL CJunkApp::InitInstance()
      if (RunEmbedded() || RunAutomated())
            // component started by COM
            return TRUE;
      // component is being run directly by the user
      ::AfxMessageBox("CHRISTOPHM server is registered");

      return FALSE;

This component is coded to register itself so that the registration
will not be a separate step.  (in an install procedure (install shield?)
this .EXE should be copied onto the target PC hard drive and also it
should be exectued to register the component on the target PC)

When it registers itself you will see the message from the
'AfxMessageBox()' in 'InitInstance()' above.  If you remove the
'AfxMessageBox()' the component will still register itself but won't
display anything.  


Now you have to add in your own application's special code.

This code is what makes your automation component application do
something for you.  I am going to give an example of a 'method' and a
'Get/Set' property.  Everything your 'server' program exposes to the
'client' should be either as a 'method' or as a 'Get/Set' property.  

(For your 'set printer to legal' and 'set printer to 'standard' actions
I suggest you use a method).

Automation components are a special class of COM components, they are
designed to interface easily with VB/VBA/VBScript and 'methods' and
'Get/Set' properties are the technique that will be most familiar to the
VB users of your component. The client in this example will be a VBScript
but the code needed in VBA is identical.

From the Class Wizard click the 'Add Class' button on any tab of the Wizard
Dialog to add a new class.  Choose 'New Class' and add a class named  
'SAMPLE'.    Choose 'CCmdTarget' for the Base Class and in the
"Automation" area click the radio button "Creatable by Type ID".  
You will see that the wizard has filled in  "CHRISTOPHM.Sample"  as
the Type ID.  You can make this name anything you want, it goes in the
registry and it is what you will use in the VBScript code that uses
this component.

To access this component from VBScript (or VB/VBA) the code will look like :    

There are complete VBScript code examples later on in this note.

Now Click "OK" in the "New Class" dialog box.
Now you have added a class   ("CHRISTOPHM.Sample") to the automation
component named "CHRISTOPHM".  We will go back to the wizard in a few
steps but close the wizard now and look at the ClassView tab.  You will
see the class "Sample" in the ClassView tree.
The next step is to add methods and properties to the component.  The
'properties' represent the state of the component - the properties often
(but not always!) have a one-to-one relationship with variables in the
component class.  The methods are ways to change the state(properties)
- the methods are functions in the component that change variables in the

Run the wizard again and select the 'Automation' tab.  The
project edit control has "CHRISTOPHM" and the class name edit control has
"Sample".  Click "Add Method" to add a method.
Add a method called "Hello".  Make both the 'External' and 'Internal'
name "Hello" - making them different would make it harder to keep up
with.  Make the "Return Type" void, and do not use any parameters.  
(I am making a simple example, for your needs you will be adding methods
'Set_Printer_to_Legal' and 'Set_Printer_Back' kinds of methods)
Click "OK", then close the wizard dialog.  In the "ClassView" tab you
will see the function "Hello" appear.  Double click "Hello" in
"ClassView" and put in a message box.
      void Sample::Hello()
        ::AfxMessageBox "Hello Computer !"
Go ahead and run the component.  You will know when it compiles, links
and runs successfully because you will see the message box from
'InitInstance()' displayed.

Now I am going to explain how to make a method do something
more than display a message box, and explain how to add a 'Get/Set'
property but first LET'S TEST THE COMPONENT.
Here is a test script file  "01.VBS".  Create this file somewhere
on your hard drive.  The 'associations' in Windows will associate the
.VBS extension with the scripting engine.  This means that you can
double click the 'KH01.VBS' file in Windows-Explorer and it will run.
      ' everything is a VARIANT in VBScript
      dim IMA_AutoComp
      ' create an instance of "CHRISTOPHM.Sample "
      Set IMA_AutoComp = CreateObject("CHRISTOPHM.Sample ")
      ' call/run the method 'Hello'
      ' cleanup
      set AutoComp = Nothing
Now if your PC has the scripting engine (if you can run VBScript then
it does) then you will see the "Hello Computer !" message box.
If you don't then (1) you have not followed my example exactly - you
should delete everything in the CHRISTOPHM subdirectory and try
again, (2) your PC doesn't have the 'Scripting Engine' - I understand
that scripting comes as a part of Win2000, IE5, and that there is a
patch to add it to W95 PCs that have IE4.
BTW - Remember that when you have your application's component done
The component needs to be run once as an .EXE (double click it in Explorer
or run it from the IDE) to register it on your PC.
So far all the component is doing for you is displaying a message box.  
The next two things to explain are; 1-making a method do something,
and, 2-illustrating Get/Set properties.  



I will describe adding a method that will change the message (from VBScript)
that the 'Sample::Hello()' method displays.  You have already added the
'Hello()' method so I'm going to shorten my explanations some.

Add a variable to the class definition of the 'Sample' class.  (this is
in the 'Sample.h' file) this variable will hold a message that the 'Hello()'
method will cause to display from VBScript - and  - I will add a method that
changes the message.

     CString     wwMessage;

Add this line to the constructor (the function "Sample::Sample()") of the Sample

     wwMessage = "Hello from Chris Miller";

Change the 'Hello()' function to this (now instead of displaying a
string literal it displays whatever is in 'wwMessage'

   void Sample::Hello()

Run the class wizard and pick the "Automation" tab.  Click the
"Add Method" button.

Type    ChangeMessage    in the "External Name" and in the
"Internal Name"  (it will automatically fill in the "Internal Name"
for you).

Select   'void'    for the "Return Type".  Add one parameter, make
the name   "varNewMessage"    and make it a "Variant" .   You have to
click around some under both columns in the "Parameter List" box, it's a
little tricky to get the focus in the 'Type' column.

Click "OK" and you will go back to the Class Wizard.  You will see that
both "ChangeMessage" and "Hello" are in the 'External Names' list of the
'Automation' tab.

Select "ChangeMessage" and click the "Edit Code" button.  You will see
that the function (method to the VB people) appears in the 'ClassView'
and it belongs to the 'Sample' class.  The edit window positions you on
this function.

   void Sample::ChangeMessage(const VARIANT FAR& varNewMessage)
         // TODO: Add your dispatch handler code here

Change the function to --

   void Sample::ChangeMessage(const VARIANT FAR& varNewMessage)
     wwMessage = (LPCWSTR)varNewMessage.bstrVal;

A warning here !   This function assumes that a string value has been
assigned to the parameter by the VB client.  The variable corresponding
to varNewMessage on the VB client side is a VARIANT and the VB client
could have set it to anything - an integer?, a float?, a reference to
a Recordset?, ..    In a production app you should check the
type of the VARIANT.  Explaining variants would take a lot of writing
so I'm not going to do that here, if you are using VB (VBA or VBScript)
and plan to use components written in C/C++ then you are going to
have to learn a little about VARIANTs.  Basically you can pass many
different data types and there is an easy way to tell what data type the
VARIANT happens to contain at the moment.

Time to test again.  Compile/link/run the component, when it is
successful you will get the registration message.  Then run the
following script

   ' everything is a VARIANT in VBScript
   dim IMA_AutoComp

   ' create an instance of "CHRISTOPHM.Sample"
   Set IMA_AutoComp = CreateObject("CHRISTOPHM.Sample")

   ' call/run the method 'Hello' (the initial message displays)

   ' change the message
   dim NewMessage
   NewMessage = "I like cookies !"

   ' call/run the method 'Hello' again (the new message displays)

   ' cleanup
   set AutoComp = Nothing

At this point you have created a component, it self registers, it has
two methods (Hello and ChangeMessage) and you can set the message
string that 'Hello()' displays by using 'ChangeMessage()'.  There is
really only one trick left in my bag and that is the 'Get/Set'

CHASFERR - this is really as far as you need to go, everything you
wanted can be done with a couple of methods and using the code
samples from cvallabhaneni.  I leave the rest of this stuff in here
in case you want to learn more about components.

The following notes will detail creating a Get/Set property.

The procedures for creating 'Get/Set' properties are much like what
you did with the two methods so I'm going to abbreviate some.

Run the wizard, go to the Automation tab, click "Add Property"

In the "Implementation" box near the center of the "Add Property" dialog
there are three radio buttons.  "Member Variable" is selected, change it
to select "Get/Set Methods".

Type  "AThing"  in the "External Name" - you will see some of the other
edit controls auto fill.  For the "Type" pick VARIANT.  Notice the names
of the "Get" and "Set" functions.  Click "OK"

Now in the "External Names" list box on the "Automation" tab you will see
"AThing" in addition to "ChangeMessage" and "Hello"

Double click on "Athing" in the list box and you will be positioned here
in the code.

   VARIANT Sample::GetAThing()
      VARIANT vaResult;
      // TODO: Add your property handler here

      return vaResult;

   void Sample::SetAThing(const VARIANT FAR& newValue)
     // TODO: Add your property handler here
Now - (We are almost finished !)  It is difficult for some people (it
was for me!) to understand that what Get/Set methods really do is
invisible to the VB side.  The VB side (client) is going to run the
Get/Set methods and to the VB side it will appear that a single
variable is changed in the component, this may or may not be true.  
What the C++ component is doing is invisible to the VB client.  As an
example maybe, for some particular pair of Get/Set properties, the Set
property sets international time zone, and the Get property returns the
current time in that time zone.  You see in the time zone example the
Get/Set properties are not just a simple single variable in the C++ server.  
You can make the C++ server do whatever you want (or even do nothing!)
in either one, or both, of the Get/Set functions.  If you want a property
to be 'Get' only then just remove all the active code from the 'Set'
function and the VB client cannot change the property.

Though the Get/Set can be much more than just a way for the VB client to
change and get a single variable for this example I am going to map
the Get/Set property to just a single variable in the C++ code.

Add a variable to the 'Sample' class

   CString    csAProperty;

Initialize the variable in the Sample constructor

   csAProperty = "XXXXXXXXX";

Go back to the functions that are the Get/Set properties (use the Wizard
or use Class View to get there) and change them to the following.

   VARIANT Sample::GetAThing()
     VARIANT vaResult;

     // TODO: Add your property handler here
     CString csWorkString;
     csWorkString = csAProperty;

     vaResult.vt = VT_BSTR;
     vaResult.bstrVal = csWorkString.AllocSysString();
     return vaResult;

   void Sample::SetAThing(const VARIANT FAR& newValue)
     // you see here that the VB user doesn't see how
     // we do it but when he changes a property with
     // the 'Set' the variable mysteriously gets a "XX"
     // added in front and in back !

     // TODO: Add your property handler here
     CString     csJunk("XX");
     csAProperty = csJunk + (LPCWSTR)newValue.bstrVal + csJunk;

You see that no matter how the client changes "AThing" with the Set
property the Get property returns it with an "XX" in front and in back.  
A Get/Set property can be associated with anything in the C++ code.  
You can remove all the code from either the Get or the Set property to
permit the client to only Set or only Get.

BTW - you also see some of the MFC/C++ supplied tools for converting
back and forth between a VARIANT containing a string (called a BSTR)
and a conventional CString.

Here is some VBScript that will use all the features in this example.

   ' everything is a VARIANT in VBScript
   dim IMA_AutoComp

   ' create an instance of "CHRISTOPHM.Sample"
   Set IMA_AutoComp = CreateObject("CHRISTOPHM.Sample")

   ' call/run the method 'Hello'

   ' change the message
   dim NewMessage
   NewMessage = "Happy New Year !!"

   ' call/run the method 'Hello' again

   ' example of the get/set
   dim AnotherVariable

   ' example of the GET function
   msgbox IMA_AutoComp.AThing

   ' example of the SET function
   IMA_AutoComp.AThing = "christophm"

   ' example of the GET function
   msgbox IMA_AutoComp.AThing

   ' cleanup
   set AutoComp = Nothing

And finally, ...

You need to be careful passing those VARIANTs back and forth - this
example program doesn't do any type checking to be sure that the VB client
isn't passing one variable type (maybe a float?) when the component
expects another type (a BSTR).  If you have those kinds of needs or
concerns you need to look up VARIANTs and how to type check them.

There is a (documented) protocol for passing arrays of VARIANTs, I
think a search for "SAFEARRAY" in MSDN will get you on the right track.

Components can be housed in DLLs - I prefer .EXEs because they are
self registering .  I recall that the .DLL will run in the same thread,
the .EXE will run in a separate thread but that is just my offhand
recollection - take it for what it's worth.  

Using automation as a server to VB works really well, if you want a
component to be a server to C++ it is suggested you use a COMponent and
the C-based VTABLE type of interface mechanism rather than the static
'IDispatch' based mechanism of Automation Components.  Automation components
can, however - and are - used from C++ clients.  All the MS-Office suite
products (Word, XL, PowerPoint, MS-Access) expose their methods and
properties through automation.  The 'file scripting object' is an
automation component, . . .

Good Luck with it !! - christophm
LVL 23

Expert Comment

by:Roshan Davis
ID: 9484699
No comment has been added lately, so it's time to clean up this TA.
I will leave a recommendation in the Cleanup topic area that this question is:

Answered by : jhance, cvallabhaneni, christophm (points to be split)

Please leave any comments here within the next seven days.


Roshan Davis
EE Cleanup Volunteer

Author Comment

ID: 11977793
Okay, it's about time to close this out.  I appreciate the input however, none worked.  It so Happened that Access 2002 had the function built in.

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction: Finishing the grid – keyboard support for arrow keys to manoeuvre, entering the numbers.  The PreTranslateMessage function is to be used to intercept and respond to keyboard events. Continuing from the fourth article about sudoku. …
Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
In this video, Percona Solution Engineer Dimitri Vanoverbeke discusses why you want to use at least three nodes in a database cluster. To discuss how Percona Consulting can help with your design and architecture needs for your database and infras…

609 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question