Click here to Skip to main content
15,861,125 members
Articles / Desktop Programming / ATL
Article

How to transfer a picture from an MFC client to an ATL ActiveX Ctrl

Rate me:
Please Sign up or sign in to vote.
4.60/5 (5 votes)
12 Mar 20025 min read 181.9K   1.8K   51   33
How to pass a metafile to an ATL server directly or using streams

Sample Image

Introduction:

Every book, and every single tutorial, teaches you how to use the basic things of COM with very good C samples. That's pretty good to begin with, but the problem comes when you want to pass from your client to your COM server/control something more than one numeric value, or one string, or... for example a Metafile, or a Bitmap... then you start having problems with the Variant type...

That was my nightmare during some days. I wanted to pass a metafile from my MFC client to my ATL ActiveX control. Here I show you how I did it (in the same process space and in different ones).

I have added some basic info about how to make both projects, if are used to it you can skip it, if you need more info about them, you can check out some basic tutorials that you can find in codeproject as well.

Create the ATL Control:

The ATL control that we are going to create is a full ActiveX control, with one interface that will allow us assign one metafile to the it ( then that metafile will be shown, in the space of the ActiveX control):

  • Go to "File --> new"
  • Select "ATL Com Appwizard"
  • Click next...
  • When the project is created, just go to the Workspace docking and select the tab "Class View"
  • Then go to the root of the control shown in that view, show the context menu and select the option "New Atl Object" 
  • From the wizard select the type of control "Full Control" ( and name the control "PicShower").
  • Now go to the ClassView tab ( in the workSpace docking), and select the interface IPicShower, press with the right button, and select the option "Add Method", then add a method called "SetDirectMeta" with the param "LPUNKNOWN Picture", and repeat the same operation to add a new method, this time called "SetMeta", with the param "LPUNKNOWN Stream".

Ok, we will add now a member variable to our brand new object, that will hold the metafile, so go to "PicShower.H", and add this variable:

// Better to use smart pointers, just forget about AddRef, Release, QInterface...
CComQIPtr<IPICTURE>  _Pict;
The "CComQIPtr" thing, it's one of the smart pointers that ATL offers to us. They make it a lot easier to handle COM objects as it makes the code look like Visual Basic (it wraps all the QueryInterface, AddRef, Release...). And the IPicture? It´s the interface to a standard component that allows you to display Metafiles, Bitmaps ( jpg, gif, tiff...). Cool isn´t it ?.

Now let's code a little. Go to the implementation of the method "SetDirectMeta" (the wizard implemented an empty skeleton for us, in the file "PicShower.cpp", the name of the method is

CPicShower::SetDirectMeta(LPUNKNOWN 
Picture)
and add this code:

/* -------------------------------------------------------------------------
      This only works, if it´s in the same space process
   ------------------------------------------------------------------------- */

STDMETHODIMP CPicShower::SetDirectMeta(LPUNKNOWN Picture)
{
   _Pict = Picture;        // Assign the picture
   FireViewChange();    // Force to redraw the ActiveX

   return S_OK;    
}

This method call will allow us to assign the metafile, but this only will work if we use the component in the same space process ( you can not share a DC, between different process spaces), so if you use this code in an EXE server, this method won't work, or if for example you paste the ActiveX in an automated Word instance and you use it from your application.

To solve this we can use this other method: go to the implementation, of the method "SetMeta",

CPicShower::SetMeta(LPUNKNOWN 
Picture)
and add this code:

/* -------------------------------------------------------------------------
      Ok, this method works in all the places ( in proccess, out of
   process, ...). The Picture is saved in memory in one stream, then 
   we open that stream and load the picture
   ------------------------------------------------------------------------- */

STDMETHODIMP CPicShower::SetMeta(LPUNKNOWN Stream)
{
   CComQIPtr<ISTREAM> pStream = Stream;
   if(pStream) {
      // Using one smart pointer to get the Picture Dispatch
      CComPtr<IPICTUREDISP> pic;    
      LARGE_INTEGER l;
      l.QuadPart  = 0;
      pStream->Seek(l, STREAM_SEEK_SET, NULL);                                 
   
      OleLoadPicture(pStream, l.LowPart, FALSE, IID_IPictureDisp, 
                     (void **) &pic);

      if(pic) {
         _Pict = pic;              // Ok, QInterface smart pointer...
      }
      
      FireViewChange();            // Force to redraw
   }
   return S_OK;
}

Here what we receive as a parameter is one stream, then we only have to load that stream ( normally it will be in memory), and load the picture.

Let´s modify the drawing code, in order to check if the picture variable is not empty and draw it then ( in the file "PicShower.H", the method HRESULT OnDraw(ATL_DRAWINFO& di)), we set it like this:

HRESULT OnDraw(ATL_DRAWINFO& di)
{
  RECT& rc = *(RECT*)di.prcBounds;
  if(_Pict) {
     RECT r = rc;
     long lPicWidth  = 0;
     long lPicHeight = 0;
     if(_Pict){
        _Pict->get_Width(&lPicWidth);_Pict->get_Height(&lPicHeight);
        HRESULT hres = _Pict->Render(di.hdcDraw, 0, 0, rc.right, rc.bottom,
                                  0, lPicHeight, lPicWidth, -(lPicHeight), &r);
     }
  } else {
     Rectangle(di.hdcDraw, rc.left, rc.top, rc.right, rc.bottom);

     SetTextAlign(di.hdcDraw, TA_CENTER|TA_BASELINE);
     LPCTSTR pszText = _T("PicShower: No picture assigned");
     TextOut(di.hdcDraw,
           (rc.left + rc.right) / 2,
           (rc.top + rc.bottom) / 2,
           pszText,
           lstrlen(pszText));
  }
  return S_OK;
}

In the Render we have to use negative value -(lPicHeight), because we are working in Himetric coordinates.

Ok, now it seems that we have completed all the code for our ATL ActiveX control, let´s go for the MFC part.

Create the MFC Client:

To use our control in our MFC client App, we can do it in several ways, the two that I like best are:

  • Use Smart pointers: quite powerful and easy to work with smart pointers, it wraps all the QueryInterface/AddRef/Release for you. You can find very good articles about that, for example you can search this one in the MSDN "Calling COM Objects with Smart Interface Pointers"
  • Just import the class: This method is quite EASY, just go to the main menu of Visual Studio, and select the option "Project >> Add to Project >> Components and Controls" and from there check the folder "Registered ActiveX Controls" ( in our case we would select then the PicShower class). It creates you a wrapper class that inherits from CWnd, and all the Dispatch methods are wrapped as well as normal methods that belongs to that class... that's pretty cool to use it in one example, but in real life, is better not to use it, because you will make all your call through the IDispatch interface, and that´s quite slow, and not very professional ( we are programming with VC++ not VB Script :-) ).

Well, in the sample I use the second method ( the EASY one), just to make the test I think it's OK, but if you want to have it with Smart Pointers, just write me a line and I will do that. I suposse that we are all more or less used to MFC, so I won't explain too much how the client code works ( again if you think it would be interesting to explain it more tell me). The sample is a Dialog based application, that shows one ActiveX, and when you press the "Show Metafile" button it pass a metafile ( that I generated with a drawing tool, and then stored in the resources, load it from a file would be possible too) to the ActiveX, and it is shown.

One thing, before try the MFC client, compile the ATL project ( then the ActiveX DLL will be autoregistered and all will work fine).

About this article:

To write this article I used MSDN to search Info, and one very good article, called "Using Picture Objects in ATL", found on vbpj April 1999.

A lot of people helped me in the message board, to make this, special thanks to: Joao Vaz, Joaquín M López Muñoz, Mazdak, ...

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Spain Spain
Braulio is a developer specialized on Ms Technologies (Silverlight, ASP .net, SSRS, SSAS, SSIS). Currently he is working for Avanade in Málaga and as a journalist for the Spanish .net magazine Dotnetmania. He also is the webmaster of an specializad in .net technologies site Tipsdotnet.com, and Silverlight Based DB Schema Editor (www.dbschemaeditor.com).

Comments and Discussions

 
QuestionHow to ROTATE image, using an IPicture Pin
Alex Evans3-Jan-05 16:34
Alex Evans3-Jan-05 16:34 
Questionhow to export picture control into bmp file Pin
nnvidya2-Jun-04 20:37
nnvidya2-Jun-04 20:37 
GeneralCool sample Pin
Anonymous13-Oct-03 2:21
Anonymous13-Oct-03 2:21 
GeneralGetting bits pointer from IPicture Pin
CodeSilver22-May-03 15:14
sussCodeSilver22-May-03 15:14 
QuestionUsing Smart Pointers? Pin
CodeSilver21-May-03 0:28
sussCodeSilver21-May-03 0:28 
AnswerRe: Using Smart Pointers? Pin
Braulio Dez21-May-03 0:38
Braulio Dez21-May-03 0:38 
QuestionHow can I send picture from my ActiveX component into VB project ? Pin
alexangr13-May-03 23:21
professionalalexangr13-May-03 23:21 
Generalsend a bitmap Pin
Todd.Harvey9-Sep-02 6:19
Todd.Harvey9-Sep-02 6:19 
GeneralRe: send a bitmap Pin
Todd.Harvey9-Sep-02 6:22
Todd.Harvey9-Sep-02 6:22 
GeneralRe: send a bitmap Pin
Ramon80k17-Jan-03 0:28
Ramon80k17-Jan-03 0:28 
GeneralRe: send a bitmap Pin
Todd.Harvey17-Jan-03 3:39
Todd.Harvey17-Jan-03 3:39 
GeneralJavaScript or VBScript syntax question Pin
Todd.Harvey22-Aug-02 10:01
Todd.Harvey22-Aug-02 10:01 
GeneralRe: JavaScript or VBScript syntax question Pin
Braulio Dez22-Aug-02 21:36
Braulio Dez22-Aug-02 21:36 
GeneralMFC / Gypsy Kings Pin
Todd.Harvey21-Aug-02 16:06
Todd.Harvey21-Aug-02 16:06 
GeneralRe: MFC / Gypsy Kings Pin
Braulio Dez21-Aug-02 21:39
Braulio Dez21-Aug-02 21:39 
Generalloading a bmp file in CBitmap object Pin
saima shafiq12-Jul-02 20:15
susssaima shafiq12-Jul-02 20:15 
GeneralRe: loading a bmp file in CBitmap object Pin
Christian Graus12-Jul-02 21:30
protectorChristian Graus12-Jul-02 21:30 
QuestionHow knows Pin
2-Apr-02 23:55
suss2-Apr-02 23:55 
AnswerRe: How knows Pin
saima shafiq12-Jul-02 21:12
susssaima shafiq12-Jul-02 21:12 
GeneralThanks Crish Pin
17-Mar-02 19:35
suss17-Mar-02 19:35 
GeneralCrish ?? Pin
Braulio Dez18-Mar-02 3:43
Braulio Dez18-Mar-02 3:43 
GeneralRe: Crish ?? Pin
2-Apr-02 23:51
suss2-Apr-02 23:51 
Really?
GeneralIt's easy,but very useful, Thanks! Pin
13-Mar-02 17:29
suss13-Mar-02 17:29 
GeneralATL7+Dumb Question ;) Pin
Mazdak13-Mar-02 4:44
Mazdak13-Mar-02 4:44 
GeneralRe: ATL7+Dumb Question ;) Pin
Braulio Dez13-Mar-02 4:54
Braulio Dez13-Mar-02 4:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.