16
votes

I am automating a Powerpoint scenario using Coded UI & VSTO. In my powerpoint presentation I have created an 'Action' setting on a shape to launch notepad. During slideshow I need to invoke this action by clicking on the 'text/shape' so that it will open notepad.exe. Could anyone help me how to achieve this. I wrote the following code.

//To launch Powepoint
PowerPoint.Application objPPT = new PowerPoint.Application();
objPPT.Visible = Office.MsoTriState.msoTrue;

//Add new presentation
PowerPoint.Presentations oPresSet = objPPT.Presentations;
PowerPoint.Presentation oPres = oPresSet.Add(Office.MsoTriState.msoTrue);

//Add a slide
 PowerPoint.Slides oSlides = oPres.Slides;
PowerPoint.Slide oSlide = oSlides.Add(1, PowerPoint.PpSlideLayout.ppLayoutTitleOnly);

//Add text
 PowerPoint.TextRange tr = oSlide.Shapes[1].TextFrame.TextRange;
tr.Text = "Launch notepad";
tr.Select();

//Add Action settings on the shape
oSlide.Shapes[1].ActionSettings[PowerPoint.PpMouseActivation.ppMouseClick].Action = PowerPoint.PpActionType.ppActionRunProgram;
oSlide.Shapes[1].ActionSettings[PowerPoint.PpMouseActivation.ppMouseClick].Run = "c:\\windows\\notepad.exe";

//start slideshow
objPPT.ActivePresentation.SlideShowSettings.Run();

This will launch the slideshow for the presentation and the 1st slide 'where the action settings are defined on the shape' will be displayed. Now how can I launch notepad.exe automatically thru APIs? unfortunately coded UI cannot detect objects in a slide. So a UI mouse click option may not be possible.

[Edit] Able to make little bit progress. I have got shape object during slide show. This is extension to the above code.

PowerPoint.SlideShowWindow oSsWnd = objPPT.ActivePresentation.SlideShowWindow;
PowerPoint.Shape oShape = oSsWnd.View.Slide.Shapes[1];
2
I'm unclear on what you're trying to accomplish. If you want to create a presentation, launch it in slide show view and then start notepad, why do it through PowerPoint? Have your code launch notepad after having created and launched the PPT show.Steve Rindsberg
This is an automation scenario to verify action is working properly. Hence I have to do this way onlysatya
I see. I don't know of any way to automate a click on any particular shape or point on the screen.Steve Rindsberg
If you only want to text if the run command is correct you could enumerate that shapes and Shell(ActionSettings[Click].Run) if Action = ppActionRunProgramPaul B.
Paul B, I couldn't figure out how to use the Shell function here. Could me elaborate it further.satya

2 Answers

7
votes

Don't ask me why C# behaves like this but it does!

You have to issue the command twice for it to work...

Tried and Tested

    private void button1_Click(object sender, EventArgs e)
    {
        //To launch Powepoint
        PowerPoint.Application objPPT = new PowerPoint.Application();
        objPPT.Visible = Microsoft.Office.Core.MsoTriState.msoTrue;

        //Add new presentation
        PowerPoint.Presentations oPresSet = objPPT.Presentations;
        PowerPoint.Presentation oPres = oPresSet.Add(Microsoft.Office.Core.MsoTriState.msoTrue);

        //Add a slide
        PowerPoint.Slides oSlides = oPres.Slides;
        PowerPoint.Slide oSlide = oSlides.Add(1, PowerPoint.PpSlideLayout.ppLayoutTitleOnly);

        //Add text
        PowerPoint.TextRange tr = oSlide.Shapes[1].TextFrame.TextRange;
        tr.Text = "Launch notepad";
        //tr.Select();

        //Add Action settings on the shape
        oSlide.Shapes[1].ActionSettings[PowerPoint.PpMouseActivation.ppMouseClick].Action = PowerPoint.PpActionType.ppActionRunProgram;
        oSlide.Shapes[1].ActionSettings[PowerPoint.PpMouseActivation.ppMouseClick].Run = @"C:\WINDOWS\system32\notepad.exe";
        oSlide.Shapes[1].ActionSettings[PowerPoint.PpMouseActivation.ppMouseClick].Action = PowerPoint.PpActionType.ppActionRunProgram;
        oSlide.Shapes[1].ActionSettings[PowerPoint.PpMouseActivation.ppMouseClick].Run = @"C:\WINDOWS\system32\notepad.exe";
        //start slideshow
        objPPT.ActivePresentation.SlideShowSettings.Run();

    }

HTH

Sid

5
votes

This might be a more complicated solution than what you were hoping for, but if you could somehow determine the X and Y coordinate of your "text/shape" object on the screen (perhaps with the Coded UI and VSTO Libraries?), you could use the User32 "SendInput" method to emulate moving the mouse to the location of the object and then emulate a mouse click.

Here is the code to emulate the user's input:

int x, y;
// ...  First obtain the X and Y coordinate of the "text/shape" object from APIs

//
InputEmulator inputEmulator = new InputEmulator();
inputEmulator.MoveMouse(x, y);
inputEmulator.ClickMouse();

And here is a stripped down version of an InputEmulator class I use to emulate Windows UI actions:

class InputEmulator
{
    private const int INPUT_MOUSE = 0;
    private const uint MOUSEEVENTF_MOVE = 0x0001;
    private const uint MOUSEEVENTF_ABSOLUTE = 0x8000;
    private const uint MOUSEEVENTF_LEFTDOWN = 0x0002;
    private const uint MOUSEEVENTF_LEFTUP = 0x0004;

    public void MoveMouse(int x, int y)
    {
        INPUT[] inp = new INPUT[1];
        inp[0].type = INPUT_MOUSE;
        inp[0].mi = createMouseInput(x, y, 0, 0, MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE);


        SendInput((uint)1, inp, Marshal.SizeOf(inp[0].GetType()));
    }

    public void ClickMouse()
    {
        INPUT[] inp = new INPUT[2];
        inp[0].type = INPUT_MOUSE;
        inp[0].mi = createMouseInput(0, 0, 0, 0, MOUSEEVENTF_LEFTDOWN);
        inp[1].type = INPUT_MOUSE;
        inp[1].mi = createMouseInput(0, 0, 0, 0, MOUSEEVENTF_LEFTUP);
        SendInput((uint)inp.Length, inp, Marshal.SizeOf(inp[0].GetType()));
    }

    [DllImport("user32.dll", SetLastError = true)]
    private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);

    private static MOUSEINPUT createMouseInput(int x, int y, uint data, uint t, uint flag)
    {
        MOUSEINPUT mi = new MOUSEINPUT();
        mi.dx = x;
        mi.dy = y;
        mi.mouseData = data;
        mi.time = t;
        //mi.dwFlags = MOUSEEVENTF_ABSOLUTE| MOUSEEVENTF_MOVE;
        mi.dwFlags = flag;
        return mi;
    }

    [StructLayout(LayoutKind.Explicit)]
    private struct INPUT
    {
        [FieldOffset(0)]
        public int type;
        [FieldOffset(sizeof(int))] //[FieldOffset(8)] for x64
        public MOUSEINPUT mi;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct MOUSEINPUT
    {
        public int dx;
        public int dy;
        public uint mouseData;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }
}