0

The Adobe AxAcroPDF control has no function to return the current page number. I'm just making a personal utility, so I had the luxury of settling on a hack method I thought I would share... It's patched together with bits and pieces from across the net. It uses Windows native functions from User32.dll to enumerate the children of the control and find the textbox in the toolbar that corresponds with page number. The text from that is then read using a SendMessage call.

TomEverin
  • 415
  • 1
  • 5
  • 12

1 Answers1

2

This method enumerates the children components of the adobe pdf viewer and finds the text boxes in the toolbar. One of the textboxes is the page number, one is the current zoom value. The text box without a '%' in its content is taken as the page number text box. The content of the text box is retrieved using the SendMessage function.

You might need to call SetToolbarVisible(true) on the viewer component first, to make sure the toolbar (and hence text boxes) are visible.

This is a terrible and hacky solution, and could easily break as Adobe update the viewer. It would be great if Adobe would add a "getCurrentPage" method so all of this could be avoided.

  //you can get the handle parameter for this method as: yourPDFControl.Handle
  public static string GetPageNumber(IntPtr adobeViewerHandle)
    {
        //get a list of all windows held by parent 
        List<IntPtr> childrenWindows = new List<IntPtr>();
        GCHandle listHandle = GCHandle.Alloc(childrenWindows);
        try
        {
            EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
            EnumChildWindows(adobeViewerHandle, childProc, GCHandle.ToIntPtr(listHandle));
        }
        finally
        {
            if (listHandle.IsAllocated)
                listHandle.Free();
        }

        //now have a list of the children, look for text boxes with class name "Edit"
        for (int i = 0; i < childrenWindows.Count; i++)
        {
            int nRet;
            // Pre-allocate 256 characters, the maximum class name length.
            StringBuilder ClassName = new StringBuilder(256);
            //Get the window class name
            nRet = GetClassName(childrenWindows.ElementAt(i), ClassName, ClassName.Capacity);

            if (ClassName.ToString().CompareTo("Edit") == 0)
            {
                IntPtr resultPointer = Marshal.AllocHGlobal(200);
                StringBuilder text = new StringBuilder(20);
                NativeMethods.SendMessage(childrenWindows.ElementAt(i), 0x000D, text.Capacity, text); //0x000D is WM_GETTEXT message
                if (text.ToString().Contains("%")) //we don't want the text box for the PDF scale (e.g. 66.7% zoomed etc.)
                {
                    continue;
                } else
                {
                    return text.ToString(); // the only other text box is the page number box
                }
            }
        }

        //Note I return as a string because PDF supports page labels, "I", "ii", "iv" etc. or even section labels "A", "B". So you're not guaranteed a numerical page number.
        return "0";

    }

    private static bool EnumWindow(IntPtr handle, IntPtr pointer)
    {
        GCHandle gch = GCHandle.FromIntPtr(pointer);
        List<IntPtr> list = gch.Target as List<IntPtr>;
        if (list == null)
            throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");

        list.Add(handle);
        return true;
    }


        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool EnumChildWindows(IntPtr window,
                                                        EnumWindowProc callback,
                                                        IntPtr i);

        internal delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern int GetWindowTextLength(IntPtr hWnd);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern int GetWindowText(IntPtr hWnd,
                                                   StringBuilder lpString,
                                                   int nMaxCount);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern bool SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);
TomEverin
  • 415
  • 1
  • 5
  • 12