.NET Forum / Windows Forms / WinForm General / June 2007
hide cursor for kiosk application (powerpoint)
|
|
Thread rating:  |
lockness - 14 May 2007 15:39 GMT 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.
Linda Liu [MSFT] - 15 May 2007 12:17 GMT 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.
lockness - 15 May 2007 13:30 GMT 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.
> Hi, > [quoted text clipped - 58 lines] > > This posting is provided "AS IS" with no warranties, and confers no rights. Linda Liu [MSFT] - 16 May 2007 03:54 GMT 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
lockness - 16 May 2007 10:52 GMT 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
> Hi M, > [quoted text clipped - 39 lines] > Linda Liu > Microsoft Online Community Support Linda Liu [MSFT] - 18 May 2007 11:01 GMT 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
lockness - 20 May 2007 13:09 GMT 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
> Hi M, > [quoted text clipped - 10 lines] > Linda Liu > Microsoft Online Community Support Linda Liu [MSFT] - 22 May 2007 11:54 GMT 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
lockness - 24 May 2007 09:39 GMT 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.
> Hi M, > [quoted text clipped - 15 lines] > Linda Liu > Microsoft Online Community Support Linda Liu [MSFT] - 28 May 2007 03:24 GMT 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
lockness - 28 May 2007 17:58 GMT 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
> Hi M, > [quoted text clipped - 62 lines] > Linda Liu > Microsoft Online Community Support Linda Liu [MSFT] - 29 May 2007 04:28 GMT 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
Linda Liu [MSFT] - 31 May 2007 10:45 GMT 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
lockness - 01 Jun 2007 15:13 GMT 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.
> Hi M, > [quoted text clipped - 7 lines] > Linda Liu > Microsoft Online Community Support lockness - 03 Jun 2007 12:17 GMT 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
> LL > [quoted text clipped - 54 lines] > > Linda Liu > > Microsoft Online Community Support Linda Liu [MSFT] - 04 Jun 2007 10:50 GMT 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
Linda Liu [MSFT] - 06 Jun 2007 11:57 GMT 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
lockness - 08 Jun 2007 10:01 GMT 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
> Hi M, > [quoted text clipped - 7 lines] > Linda Liu > Microsoft Online Community Support Linda Liu [MSFT] - 11 Jun 2007 12:56 GMT 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
Linda Liu [MSFT] - 13 Jun 2007 13:14 GMT 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
lockness - 14 Jun 2007 09:44 GMT Hi LL
With regard to silent printing your suggestions did get rid of the print dialog but in all cases the acrobat reader program muscles its way to the top of the desktop. Setting topmost=true on my app helps, but then the taskbar flashes up. Its a kludge but finally I used ShowWindow(hndl,HIDE) to make the takbar invisible.
The next step is to check the printer is actually available for print before printing, and issue some message when printing complete. I know that .NET does not directly support the print spooler system and my previous attempts to get the printer status with WMI always returned a positive state even when the printer was turned off, so there could be a new question coming up soon ;-)
Thanks M
> Hi M, > [quoted text clipped - 7 lines] > Linda Liu > Microsoft Online Community Support
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|