hide cursor for kiosk application (powerpoint)

G

Guest

I'm developing a kiosk app in vb2005 and don't want the mouse pointer
to shown.

I'm using the webbrower control to display documents of various types.
eg PDF and Powerpoint. I simply pass the document location via
me.webbrowser.navigate("file://...") and the webbrowser works out which
application it then has to call (Acrobat or Powerpoint) to display
the document within the browser window.

This works well for PDFs, Powerpoint seems to be a law unto itself.
Firstly I needed to set a registry value to force it into windowed mode:

rkPPT = Registry.ClassesRoot.CreateSubKey("PowerPoint.Show.8")
rkPPT.SetValue("BrowserFlags", 0, RegistryValueKind.DWord)

Now I discover that powerpoint does not obey the cursor.hide settings.
(whereas acrobat does)

As a test I set a button.click event to set cursor.hide. But as soon as I
click
into the powerpoint slide the cursor comes back.

I've seen references to the low level ShowCursor function. Is this what I'll
have to do ?

As a side issue I've found Powerpoint to be a pain to work with.
Looking at the MSDN developer centre for Powerpoint I found
that it's appears to be centred around PP2003 not PP2007.
Whereas for Windows Media Player I found great documentation
explaining how to interop from .NET, for PP it's all VBA related.

M.
 
L

Linda Liu [MSFT]

Hi,

Based on my understanding, you use a WebBrowser control to display
PowerPoint document and you'd like to hide the cursor on the WebBrowser. If
I'm off base, please feel free to let me know.

I have spent several hours on this issue but haven't found a solution yet.

In my test, I call the Cursor.Hide method to hide the cursor. When I move
the pointer on the WebBrowser, I did see that the cursor comes back.

I use Spy++ to watch the application, and see that there're several child
windows in the WebBrowser. The class and window names of these child
windows are as follows:
Shell Embedding ""
Shell DocObject View ""
childClass ""
childClass "PowerPoint Slide
Show-[PPT1.ppt[Read-Only][Compatibility Mode]]"
paneClassDC "Slide Show"

I watch the messages of the paneClassDC "Slide Show" window. When I move
the pointer on the WebBrowser, Spy++ catches the WM_SETCURSOR and
WM_MOUSEMOVE messages.

It seems that it's the paneClassDC "Slide Show" window that captures the
cursor and show it.

I have managed to get the handle of the paneClassDC window, but I haven't
worked out how to prevent the paneClassDC window from showing the cursor
until now.

BTW, could you tell me why you'd like to hide the cursor on the WebBrowser
when displaying PowerPoint document?

I will go on the research and will get the result back to you ASAP.

I appreciate your patience!

Sincerely,
Linda Liu
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
G

Guest

Hi LL

The app is to run on a kiosk with a touchscreen. There is no mouse control.
When the user touches the screen this is equivalent to a mouse click.
It does not look good to have a mouse pointer on screen in this situation.

You do seem to have recreated the problem correctly.

I'm new to windows development and the associated tools.
I've just been experimenting with spy++ myself for a related problem
using powerpoint via interop instead of the webbrowser control.

You mention in your reply you were able to get the handle of the slideshow:
paneClassDC "Slide Show"

This is exactly what I've been trying for the last few hours so I can
then use the handle with SetForegroundWindow(hndl) to ensure the slideshow
is on top.

So far my attempts with hndl = FindWindow("paneClassDC", "Slide Show")
and other variations have failed. If you could explain how you got the
handle that would greatly help although I realize it's a differrent issue to
the one I've raised.

Thanks

M.

Linda Liu said:
Hi,

Based on my understanding, you use a WebBrowser control to display
PowerPoint document and you'd like to hide the cursor on the WebBrowser. If
I'm off base, please feel free to let me know.

I have spent several hours on this issue but haven't found a solution yet.

In my test, I call the Cursor.Hide method to hide the cursor. When I move
the pointer on the WebBrowser, I did see that the cursor comes back.

I use Spy++ to watch the application, and see that there're several child
windows in the WebBrowser. The class and window names of these child
windows are as follows:
Shell Embedding ""
Shell DocObject View ""
childClass ""
childClass "PowerPoint Slide
Show-[PPT1.ppt[Read-Only][Compatibility Mode]]"
paneClassDC "Slide Show"

I watch the messages of the paneClassDC "Slide Show" window. When I move
the pointer on the WebBrowser, Spy++ catches the WM_SETCURSOR and
WM_MOUSEMOVE messages.

It seems that it's the paneClassDC "Slide Show" window that captures the
cursor and show it.

I have managed to get the handle of the paneClassDC window, but I haven't
worked out how to prevent the paneClassDC window from showing the cursor
until now.

BTW, could you tell me why you'd like to hide the cursor on the WebBrowser
when displaying PowerPoint document?

I will go on the research and will get the result back to you ASAP.

I appreciate your patience!

Sincerely,
Linda Liu
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
L

Linda Liu [MSFT]

Hi M,

Thank you for your prompt response.

The FindWindow function can only retrieves a handle to the top-level window
whose class name and window name match the specified strings. This function
does not search child windows.

To search child windows, use the FindWindowEx function. This function
searches the immediate child windows of the specified parent window.

The following is the code to retrieve the handle of the paneClassDC "Slide
Show" window.

using System.Runtime.InteropServices;

[DllImport("user32.dll")]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,
string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
static extern private IntPtr GetWindow(IntPtr hWnd, int uCmd);

const int GW_CHILD = 5;

IntPtr child1 = FindWindowEx(this.webBrowser1.Handle, IntPtr.Zero, "Shell
Embedding", "");
IntPtr child2 = FindWindowEx(child1, IntPtr.Zero, "Shell DocObject View",
"");
IntPtr child3 = FindWindowEx(child2, IntPtr.Zero, "childClass", "");
// because the 'childClass' window only has one child window and the child
window has a long window name, I use the GetWindow function to get the
child window for convenience
IntPtr child4 = GetWindow(child3, GW_CHILD);
IntPtr child5 = FindWindowEx(child4, IntPtr.Zero, "paneClassDC", "Slide
Show");

The variable child5 returns the handle of of the paneClassDC window.

Hope this helps.

Sincerely,
Linda Liu
Microsoft Online Community Support
 
G

Guest

LL

Sorry for the digression but FindWindowEx helps a great deal, now I get the
right handle. This gives me the option to construct a contol panel at the
bottom of the screen and run PP via interop, using SetForegroundWindow to
keep it on top.

But the original problem remains when using PP within the webbrowser.
(it's probably still there for the situation above, I haven't run a test on
the touchscreen yet ) I really dont want a mouse pointer appearing on the
screen.

But can this be avoided ?

Thanks again

M
 
L

Linda Liu [MSFT]

Hi M,

As for your original question to hide the cursor on WebBrowser, I am
consulting it in our inner discussion group.

As soon as I get an ansswer, I will get back to you.

BTW, do you really want this behaviour?

I appreciate your patience!

Sincerely,
Linda Liu
Microsoft Online Community Support
 
G

Guest

LL

The application will run on a kiosk with a touchscreen as the only user
interface.
There is no mouse or physical keyboard. The user will touch a button
or scrollbar to activate it.

In all the kiosk touchscreen apps I've seen there is no mouse pointer.

Thanks for your efforts so far

M
 
L

Linda Liu [MSFT]

Hi M,

After discussion, I get more information about displaying PowerPoint
document in WebBrowser control.

When we display a PowerPoint document in a WebBrowser on a form, a
PowerPoint process is created at background. You can see this process in
Task Manager.

However, cursor is specific to process, and this is why the cursor still
comes back on WebBrowser control when we hide the cursor for the WinForm
application and then move the pointer to the WebBrowser.

It is maybe impossible to hide the cursor on WebBrowser which is displaying
a PowerPoint document.

Sincerely,
Linda Liu
Microsoft Online Community Support
 
G

Guest

LL

I'm not sure how to react to that. I suppose I could view it as
good news and bad news. Its bad news that even Microsoft are
confounded by their products, but good news that my struggle with
controlling oher apps from .NET is justified.

So lets deal with what we've got. It sounds like I have to work with
the mouse pointer at a low level, as I had for key intercepts.

Presumably if I had low level control of the mounse pointer through
some API I could set its shape to anything ? including nothing at all ?

So my original question remains. How to hide the cursor. But am I
now asking it in the right forum ? Should I start looking for a
low level API group ?

Thanks

M.
 
L

Linda Liu [MSFT]

Hi M,

Thank you for your reply.

After doing more research, I found out a workaround for this issue, that is
to change the system cursor to a 'transparent' cursor when the application
is run and restore the previous system cursor when the application is
terminated. To set the system cursor, we could use the SetSystemCursor
Win32 function.

The following is a sample.

using System.Runtime.InteropServices;
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern bool SetSystemCursor(IntPtr hcur, int id);

[DllImport("user32.dll")]
static extern IntPtr LoadCursorFromFile(string lpFileName);

[DllImport("user32.dll")]
static extern IntPtr GetCursor();

[DllImport("user32.dll")]
static extern IntPtr CopyImage(IntPtr hImage,int uType,int
cxDesired,int cyDesired,int fuFlags);

int OCR_NORMAL = 32512;
int IMAGE_CURSOR = 2;
int LR_COPYDELETEORG = 0x0008;

void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
// restore the system cursor
SetSystemCursor(defaultCur, OCR_NORMAL);
}

private void Form1_Load(object sender, EventArgs e)
{
// get the handle of the current cursor
defaultCur = GetCursor();
// copy the default cursor to restore the system cursor when
the application exits
defaultCur = CopyImage(defaultCur, IMAGE_CURSOR, 0, 0,
LR_COPYDELETEORG);
// load a new transparent cursor
IntPtr hcur = LoadCursorFromFile(Application.StartupPath +
@"\transparent.cur");
// set the system cursor to the new transparent cursor
SetSystemCursor(hcur, OCR_NORMAL);
}
}

I searched the Internet and found a free cursor editor called RealWorld
Cursor Editor. You may use it to create a transparent cursor. You can
download it from the below link:

http://www.freedownloadscenter.com/Shell_and_Desktop/Cursor_Editing_Tools/Re
alWorld_Cursor_Editor.html

Hope this helps.

Sincerely,
Linda Liu
Microsoft Online Community Support
 
G

Guest

LL

I've just gone into Control Panel, Mouse, Pointers and set the mouse
to "NoCursor.cur". The mouser pointer then disappears.

I'll translate your code the VB and give that a go.

Strangley, while the test machine has the "noCursor.cur" option my
development machine does not. They both run XP-P !

I think this has finally solved the problem.
Thanks
 
L

Linda Liu [MSFT]

Hi M,

Thank you for your feedback.

The sample code I provided in my previous reply is to change the system
cursor programmatically. What's more, my sample code will restore the
previous cursor when the application is terminated.

I will translate the sample code in VB.NET for you.

Imports System.Runtime.InteropServices

Public Class Form1

Private Declare Auto Function SetSystemCursor Lib "user32.dll" (ByVal
hcur As IntPtr, ByVal id As Integer) As Boolean
Private Declare Auto Function LoadCursorFromFile Lib "user32.dll"
(ByVal lpFileName As String) As IntPtr
Private Declare Auto Function GetCursor Lib "user32.dll" () As IntPtr
Private Declare Auto Function CopyImage Lib "user32.dll" (ByVal hImage
As IntPtr, ByVal uType As Integer, ByVal cxDesired As Integer, ByVal
cyDesired As Integer, ByVal fuFlags As Integer) As IntPtr

Const OCR_NORMAL As Integer = 32512
Const IMAGE_CURSOR As Integer = 2
Const LR_COPYDELETEORG As Integer = &H8

Private defaultCur As IntPtr

Private Sub Form1_FormClosed(ByVal sender As System.Object, ByVal e As
System.Windows.Forms.FormClosedEventArgs) Handles MyBase.FormClosed
SetSystemCursor(defaultCur, OCR_NORMAL)
End Sub

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
defaultCur = GetCursor()
defaultCur = CopyImage(defaultCur, IMAGE_CURSOR, 0, 0,
LR_COPYDELETEORG)
Dim hcur As IntPtr = LoadCursorFromFile(Application.StartupPath +
"\transparent.cur")
SetSystemCursor(hcur, OCR_NORMAL)
End Sub

End Class

Hope this helps.


Sincerely,
Linda Liu
Microsoft Online Community Support
 
L

Linda Liu [MSFT]

Hi M,

How about the problem now?

If you still need our further assistance, please feel free to let me know.

Thank you for using our MSDN Managed Newsgroup Support Service!

Sincerely,
Linda Liu
Microsoft Online Community Support
 
G

Guest

LL

I suppose the original problem of hiding the cursor is solved.
What I'm doing now relates to finding the handles of child windows
you explained in an earlier reply.

The goal of the kiosk program is to present documents (pdf,ppt,etc) in a
manner that does not expose the document viewer's native controls,
but instead offers a simple Next/Previous control of its own.

I'm showing Powerpoint slides within a webbrowser as you know.
Hitting the PageDown/PageUp (or Space key) progresses the slideshow.
But remember this is a Kiosk without a keyboard. I've tried Sendkeys but found
it unreliable ( ever other sendkeys work for some unknown reason).

I've abandonded sendkeys and am experimenting with the API Sendmessage.

child1 = Win32.FindWindowEx(Me.WebBrowser1.Handle, IntPtr.Zero, "Shell
Embedding", "")
child2 = Win32.FindWindowEx(child1, IntPtr.Zero, "Shell DocObject View", "")
child3 = Win32.FindWindowEx(child2, IntPtr.Zero, "childClass", "")
'because the 'childClass' window only has one child window and the child
'window has a long window name, I use the GetWindow function to get the
'child window for convenience
child4 = Win32.GetWindow(child3, GetWindow_Cmd.GW_CHILD)
child5 = Win32.FindWindowEx(child4, IntPtr.Zero, "paneClassDC", "Slide Show")

The above gets me the handle of the SlideShow.
I can then use sendmessage to simulate keystokes:

Win32.SendMessage(child5, WM_KEYDOWN, VK_NEXT, Nothing)
Win32.SendMessage(child5, WM_KEYUP, VK_NEXT, Nothing)

Using Spy++ to monitor SlideShow I can compare 'real' messages when
I hit the keyboard to my attempts with SendMessage.

My attempts are similar to the real messages but of course don't work.
Trouble is I can see VK_NEXT is in the real message but don't know
how to correctly format my own Sendmessage attempts.

If you could confirm what I'm doing is basically OK and point me
in the right direction for examples that would help.

Thanks and have a good weekend.

M.
 
G

Guest

LL

Using spy++ to carefully review messages I noticed they weren't
infact "sent" but "posted". So I switched from Sendmessage to Postmessage
and it now works. When I have time I'll looks in this whole area.

For now I want to achieve the same with a PDF loaded in the webbrowser.
With powerpoint the handle I wanted was 5 layers down, hence the use of
FindwindowEx 5 times. Acrobat appears to be 13 layers down ! But I
expect findwindowEx will get there in the end, unless there's a shortcut
to the right handle ?

M
 
L

Linda Liu [MSFT]

Hi M,

Glad to hear that the problem of hiding cursor on the WebBrowser is solved
: )

As for finding a specific child window, I do more reading and find that the
Win32 function EnumChildWindows is more efficient. The EnumChildWindows
function enumerates the child windows that belong to the specified parent
window and pass each child window's handle to an application-defined
callback function. If a child window has its own child windows,
EnumChildWindows enumerates those windows as well. EnumChildWindows
continues until the last child window is enumerated or the callback
function return false.

In the application-defined callback function, we need to determine whether
the child window associated with the handle is the window we look for. To
do this, we could use Win32 function GetClassName to retrieve the name of
the class to which the specified window belongs and use Win32 function
GetWindowText to get text of the specified window's title bar.

The following is a sample. It requires that you add a WebBrowser and a
Button on the form.

Imports System.Runtime.InteropServices

Public Class Form1

Declare Auto Function EnumChildWindows Lib "user32.dll" (ByVal
hWndParent As IntPtr, ByVal lpEnumFunc As MyDelegate, ByVal lParam As
WindowParam) As Boolean
Declare Auto Function GetClassName Lib "user32.dll" (ByVal hWnd As
IntPtr, ByVal lpClassName As System.Text.StringBuilder, ByVal nMaxCount As
Integer) As Integer
Declare Auto Function GetWindowText Lib "user32.dll" (ByVal hWnd As
IntPtr, ByVal lpString As System.Text.StringBuilder, ByVal nMaxCount As
Integer) As Integer

Delegate Function MyDelegate(ByVal hwndChild As IntPtr, ByVal lParam As
WindowParam) As Boolean
Private expectedChild As IntPtr

Class WindowParam
Private _className As String
Private _windowName As String
Public Property ClassName() As String
Get
Return _className
End Get
Set(ByVal value As String)
_className = value
End Set
End Property
Public Property WindowName() As String
Get
Return _windowName
End Get
Set(ByVal value As String)
_windowName = value
End Set
End Property
Public Sub New(ByVal cn As String, ByVal wn As String)
_className = cn
_windowName = wn
End Sub
End Class

Private Function EnumChildProc(ByVal hwndChild As IntPtr, ByVal lParam
As WindowParam) As Boolean
Dim className As New System.Text.StringBuilder(255)
GetClassName(hwndChild, className, 255)

Dim windowName As New System.Text.StringBuilder(255)
GetWindowText(hwndChild, windowName, 255)

If (className.ToString().Equals(lParam.ClassName) And
windowName.ToString().Equals(lParam.WindowName)) Then
expectedChild = hwndChild
Return False
Else
Return True
End If
End Function

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
Me.WebBrowser1.Url = New Uri(Application.StartupPath + "\pdf1.pdf")
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
Dim result As Boolean
result = EnumChildWindows(Me.WebBrowser1.Handle, New
MyDelegate(AddressOf EnumChildProc), New WindowParam("AVL_AVView",
"AVPageView"))
If (result = False) Then
MessageBox.Show(Convert.ToString(expectedChild.ToInt32(), 16))
End If
End Sub
End Class

Hope this helps.
If you have anything unclear, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support
 
L

Linda Liu [MSFT]

Hi M,

How about the problem now?

If you have anything unclear, please feel free to let me know.

Thank you for using our MSND Managed Newsgroup Support Service!

Sincerely,
Linda Liu
Microsoft Online Community Support
 
G

Guest

LL

The original problem hiding the cursor has a workable solution (change
the cursor to "blank") and the other problem communicating with powerpoint
and acrobat have a solution with findwindowex ( although EnumChildWindows
may be a better solution)

Right now there are other issues I'm working through, but they're just plain
design and coding issues. But there is another outstanding technical issue:

"Silent printing" or printing without a print dialog popping up.
Powerpoint within the browser window is OK, the print request to
the browser generates "printing page 1,2,3,etc" thats OK.
But AcrobatReader comes up with a print dialog box.

One solution would be to pay Adobe for their full product that exposes
lots of useful methods, but of course we want to minimize the expense
of out product.

I've tried using acrobat activex directly (no browser window) that was
partly successful, but unreliable.

I suspect I may end up be using EnumChildWindows to intercept the dialog and
press the OK key.

When this problem becomes a priority should I start a new thread or carry on
here ???

Thanks

M
 
L

Linda Liu [MSFT]

Hi M,

Firstly, you'd better post a new thread for your new question for next time
: )

As for the question of sliently printing a PDF file, I searched the
Internet and found two solutions.

One solution is to use the Win32 API function ShellExecute to perform the
'print' verb on a PDF file. If you right click a PDF file in Windows
Explorer, and choose 'print' command, you should see that the PDF file is
printed sliently.

For more information on the ShellExecute function, you may refer to the
following document:

'ShellExecute Function'
http://msdn2.microsoft.com/en-us/library/ms647732.aspx

Another solution is to use /t command line to print a PDF file silently.
You can then launch the command line using the Process class in your
application. For more information on the /t command line, you may read the
followng article:

'How To: Reader command line printing'
http://support.adobe.com/devsup/devsup.nsf/docs/52080.htm

Hope this helps.


Sincerely,
Linda Liu
Microsoft Online Community Support
 
L

Linda Liu [MSFT]

Hi M,

How about the problem now?

If you need our further assistance, please feel free to let know know.

Thank you for using our MSDN Managed Newsgroup Support Service!

Sincerely,
Linda Liu
Microsoft Online Community Support
 
Top