Here's how to use MFC in a Acrobat 8 plugin - Part 1

Ask a Question related to Adobe Acrobat SDK, Design and Development.

  1. #1

    Default Here's how to use MFC in a Acrobat 8 plugin - Part 1

    Since there is apparently no way to determine the currently selected pages in the Pages Navigation panel (see Leonard Rosenthol, "Determining currently selected page(s)?" #1, 3 Jan 2007 3:59 am </cgi-bin/webx?14@@.3bc2ba82/0>) I had to create my own Page Ranges dialog box. There also doesn't seem to be any explicit documentation on how to use MFC in a Acrobat 8 plug-in, so I'll explain what I did to implement this dialog using Visual Studio 2005.

    The easiest way to begin is to use the Acrobat 8 Plug-in Wizard mentioned on page 98 of the Guide to SDK Samples. You should read Developing Plug-ins and Applications (in particular Chapter 3, "Creating Plug-in and PDF Library Applications") but for some mysterious reason, that guide doesn't mention the wizard???

    The reason you should use the Wizard is that it has a checkbox for "Plug-in uses MFC"!

    Unfortunately I had already started my plug-in by following the instructions in the Developing Plug-ins doc. That recommends that you start off by copying the BasicPlugin sample which doesn't include any MFC support. So I used the Wizard to generate sample code with MFC support enabled and disabled. I then diffed the files to see what was required and retrofitted my Visual Studio solution to match.

    It turns out there is really very little difference between a MFC-enabled plug-in and a normal plug-in.

    The wizard will create stdafx.cpp and stdafx.h which you normally use for precompiling headers, but it doesn't bother to actually turn on their use.

    In any case you need these two files because anywhere you include the Acrobat plugin header file, you need to include stdafx.h before it:



    #include "stdafx.h"
    #ifndef MAC_PLATFORM
    #include "PIHeaders.h"
    #endif

    If you don't the compiler will complain that you can't include windows.h in a MFC dll (and PIHeaders.h includes windows.h if it hasn't been already).

    Then, in your project properties, "Configuration Properties | General | Use of MFC"
    should be set to "Use MFC in a Shared DLL" rather than the BasicPlugin's "Use Standard Windows Libraries".

    In the "C++ | Preprocessor" section add _AFXDLL to the list of Preprocessor definitions.

    That's basically it as far as project setup goes and like I said, if you start your plug-in by using the Acrobat 8 Plug-in Wizard it'll do all this for you. Unfortunately the wizard only creates a Debug configuration and not both a Debug and Release configuration which is standard on Visual Studio built projects. (I built a standard MFC dll and took a look at its Release configuration settings to figure out how to correctly generate a Release version of my plug-in).

    It should now be a snap to get an MFC dialog to display from an Acrobat 8 plug-in right? That's what I thought until I actually tried it.

    (continued in part 2)
    Jeff_Bowell@adobeforums.com Guest

  2. Similar Questions and Discussions

    1. Acrobat 7 plugin not working in Acrobat 8
      Hi, I have developped a plugin that run correctly in Acrobat 7. But if I use it in Acrobat 8 some fucntions are not working. Are there...
    2. anybody interested in maintaining acrobat plugin for use of itextwithin acrobat?
      Hi, Is anybody interested in hosting and maintaining the sourcecode of a acrobat plugin to extend acrobat using itext? A few years ago I wrote a...
    3. Does Acrobat Browser plugin allow full Adobe Acrobat 7.0 functionality
      Hello, currently I’m starting a new project. I have to design and implement a document retrieval system. The document format is PDF. The...
    4. Plugin Flash Player 7 in Linux has covered part of thescreen
      Some banners (Flash 7) cover part of the screen and I don't obtain to read what it was covered by banner. This problem occurs when use Linux. ...
    5. Can Acrobat suppress part of the pdf when printing???
      Being the complete pc novice that I am - can anyone tell me where the 'print only form fields' option is on Acrobat professional 7 pc version??? ...
  3. #2

    Default Re: Here's how to use MFC in a Acrobat 8 plugin - Part 1

    Here's how to use MFC in a Acrobat 8 plugin - Part 2

    I created a dialog box as usual using the Dialog Editor and generated a c++ class from it. (For some reason the .h file for the Visual Studio generated dialog class didn't include resource.h so I had to add that manually??).

    Ignoring what Developing Plug-ins and Applications says on page 43 in the section "Using modal dialog boxes". I then just tried doing:



    PageRangeDlg pd;
    INT_PTR nRet = pd.DoModal();

    which works fine in a normal MFC app.

    When I tried this in an Acrobat 8 plug-in however I got an assertion failure. Debugging shows me stopped at:



    _AFXWIN_INLINE HINSTANCE AFXAPI AfxGetResourceHandle()
    { ASSERT(afxCurrentResourceHandle != NULL);
    return afxCurrentResourceHandle; }

    It's something about not being able to load resources. Time to google! Luckily agaceff posted a message to the planetpdf.com forum about a similar problem and mentioned AFX_MANAGE_STATE. I dug into the MFC docs to figure out what that's all about and here's what I found in the MFC "Exported DLL Function Entry Points" section:

    If you have an exported function, such as one that launches a dialog box
    in your DLL, you need to add the following code to the beginning of the
    function:

    AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

    This swaps the current module state with the state returned from AfxGetStaticModuleState
    until the end of the current scope.

    Problems with resources in DLLs will occur if the AFX_MANAGE_STATE macro
    is not used. By default, MFC uses the resource handle of the main application
    to load the resource template. This template is actually stored in the
    DLL. The root cause is that MFC's module state information has not been
    switched by the AFX_MANAGE_STATE macro. The resource handle is recovered
    from MFC's module state. Not switching the module state causes the wrong
    resource handle to be used.

    AFX_MANAGE_STATE does not need to be put into every function in the DLL.
    For example, InitInstance can be called by the MFC code in the application
    without AFX_MANAGE_STATE because MFC automatically shifts the module state
    before InitInstance and then switches it back after InitInstance returns.
    The same is true for all message-map handlers. Regular DLLs actually have
    a special master window procedure that automatically switches the module
    state before routing any message.

    I don't claim to completely understand what that's saying but doing the following made my dialog box come up fine:



    AFX_MANAGE_STATE(AfxGetStaticModuleState( ))
    PageRangeDlg pd;
    INT_PTR nRet = pd.DoModal();

    (While composing this post I tried to duplicate the error by commenting out the AFX_MANAGE_STATE line and my dialog box still came up fine! But testing on a plugin newly created by the Acrobat 8 Plug-in Wizard showed the assertion error so I still think AFX_MANAGE_STATE is necessary??)

    (continued in part 3)
    Jeff_Bowell@adobeforums.com Guest

  4. #3

    Default Re: Here's how to use MFC in a Acrobat 8 plugin - Part 1

    Here's how to use MFC in a Acrobat 8 plugin - Part 3

    Just to be on the safe side I followed the instructions on page 43 of Developing Plug-Ins and Applications and my current code looks like this:



    AFX_MANAGE_STATE(AfxGetStaticModuleState( ))
    ...
    HWND capturehWnd, hParent;
    capturehWnd = GetCapture();
    if (capturehWnd != NULL)
    ReleaseCapture();
    hParent = WinAppGetModalParent(AVAppGetActiveDoc());

    CWnd cWnd;
    cWnd.Attach(hParent);
    PageRangeDlg pd(currentPage, currentPage, nPages, &cWnd);
    cWnd.Detach(); // since bad things might happen to hParent when cWnd
    disappears?

    INT_PTR nRet = pd.DoModal();
    if (capturehWnd != NULL)
    SetCapture(capturehWnd);

    However if I try to follow the instructions on page 44 and do the following in PageRangeDlg::OnInitDialog() I get an assertion exception when the dialog box is closed???



    AVWindow avWindow;
    avWindow = AVWindowNewFromPlatformThing(AVWLmodal, 0, NULL, gExtensionID,
    m_hWnd);
    AVAppBeginModal(avWindow);

    For now I'm just ignoring those instructions and my dialog box seems to work fine. Perhaps I'll start a separate topic to find out about that issue.

    Now that the Acrobat 8 SDK is free, other newbies like me will be trying it out. I hope this long post helps start a discussion on how to use MFC to show dialogs from Acrobat 8 plug-ins :)
    Jeff_Bowell@adobeforums.com Guest

  5. #4

    Default Re: Here's how to use MFC in a Acrobat 8 plugin - Part 1

    Thanks for the comprehensive write up!!

    I've forwarded it to our SDK folks.

    Leonard
    Leonard_Rosenthol@adobeforums.com Guest

  6. #5

    Default Re: Here's how to use MFC in a Acrobat 8 plugin - Part 1

    Ooops. I forgot to mention three other files the Acrobat 8 Plug-in Wizard makes for MFC plugins:



    plugin.clw
    pluginapp.cpp
    pluginapp.h

    The first .clw file is (as near as I can tell) obsolete in Visual Studio 2005. It is just a settings text file used for older versions of the MFC ClassWizard (at least I don't have any .clw files in my plugin directory and I used the ClassWizard to create a class for my PageRangeDlg dialog box.)

    The other two files define the CMFCApp class. I'm a bit surprised my plug-in seemed to worked fine without these! In "Regular DLLs Dynamically Linked to MFC" the Visual C++ doc states:

    A regular DLL, dynamically linked to MFC has the following requirements:

    * These DLLs are compiled with _AFXDLL defined, just like an executable
    that is dynamically linked to the MFC DLL. But _USRDLL is also defined,
    just like a regular DLL that is statically linked to MFC.
    * This type of DLL must instantiate a CWinApp-derived class.
    * This type of DLL uses the DllMain provided by MFC. Place all DLL-specific
    initialization code in the InitInstance member function and termination
    code in ExitInstance as in a normal MFC application.



    Because this kind of DLL uses the dynamic-link library version of MFC,
    you must explicitly set the current module state to the one for the DLL.
    To do this, use the AFX_MANAGE_STATE macro at the beginning of every function
    exported from the DLL.

    Regular DLLs must have a CWinApp-derived class and a single object of
    that application class, as does an MFC application. However, the CWinApp
    object of the DLL does not have a main message pump, as does the CWinApp
    object of an application.

    Note that the CWinApp::Run mechanism does not apply to a DLL, because
    the application owns the main message pump. If your DLL brings up modeless
    dialogs or has a main frame window of its own, your application's main
    message pump must call a DLL-exported routine that calls CWinApp::PreTranslateMessage.

    I looked at my plugin project's settings again after reading that and noticed that I didn't have _USRDLL set (the Wizard generated project doesn't set it either?). I now define _USRDLL in both my Debug and Release configurations. Unlike what I claimed in Part 1, it seems you don't have to define _AFXDLL once you say "Use MFC in a Shared DLL" (_AFXDLL then becomes one of the "inherited values" along with _WINDLL).

    I managed without a CWinApp-derived class in my plug-in for a day and a half and things worked okay but I feel better knowing pluginapp.cpp & pluginapp.h are in there now.

    So far I've only created a modal dialog box. Looks like creating a modeless one in a plug-in might have some issues.

    Some of this information is really only for people who have to add MFC support to an existing plug-in. If you use the Plug-in Wizard to create your initial project you're probably okay.
    Jeff_Bowell@adobeforums.com Guest

  7. #6

    Default Re: Here's how to use MFC in a Acrobat 8 plugin - Part 1

    BTW, I guess I should also mention that the project that the Acrobat 8 Plug-in Wizard makes isn't particularly ideal. For one thing it doesn't consistently use the name of your new project when generating output files. This is particularly annoying since the actual plug-in name will always be template.api!

    To fix this using Visual Studio 2005: After you generate your new plugin with the Wizard, select the project in the Solution Explorer pane, right click and choose Unload Project. The project will then become "(unavailable)". Right click it again and choose "Edit YourProjectName.vcproj". This lets you directly edit the .vcproj file which is just a text file with XML in it.

    Change all occurrences of PITemplate & Template to YourProjectName. Save and close the .vcproject window. Right click on your project again and choose Reload Project.

    (Or you could just close Visual Studio 2005 and edit YourProjectName.vcprog with any text editor)

    The other problem is the previously discussed omission of a Release Configuration in the project settings. Maybe the Wizard writers can fix this in the next release of the SDK?
    Jeff_Bowell@adobeforums.com Guest

  8. #7

    Default Re: Here's how to use MFC in a Acrobat 8 plugin - Part 1

    I think I figured out the cause of the assertion that happens when I follow the instructions on page 44 of Developing Plug-Ins and Applications in the "Using modal dialog boxes" section. The assertion is when I close my modal dialog box and is in CWnd::DestroyWindow():



    // Should have been detached by OnNcDestroy
    ASSERT(pMap->LookupPermanent(hWndOrig) == NULL);

    It looked to me like something wasn't being released correctly. So after putting breakpoints in CWnd::DestroyWindow() and stepping thru what happened a few times I decided to ignore just part of the instructions on page 44 :)

    Here's what my dialog class now does:



    BOOL PageRangeDlg::OnInitDialog()
    {
    CDialog::OnInitDialog();

    avWindow_ = AVWindowNewFromPlatformThing(AVWLmodal, 0, NULL, gExtensionID,
    m_hWnd);
    AVAppBeginModal(avWindow_);
    ...
    return TRUE; // return TRUE unless you set the focus to a control
    }

    void PageRangeDlg::OnDestroy()
    {
    CDialog::OnDestroy();

    // TODO: Add your message handler code here
    AVAppEndModal();
    AVWindowDestroy(avWindow_);
    }

    where I added AVWindow avWindow_ as a private member variable to my dialog class. Page 44 explicitly states:

    If you are using MFC to put up your dialog box, do not call AVWindowDestroy
    in the WM_DESTROY message (MFC will cause Acrobat or Adobe Reader to destroy
    the AVWindow automatically).

    But from my experience it seems that you still have to call AVWindowDestroy() even if you are using MFC?
    Jeff_Bowell@adobeforums.com Guest

Posting Permissions

  • You may not post new threads
  • You may post replies
  • You may not post attachments
  • You may not edit your posts

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139