To be quite frank, after 3 months of ASP.NET development, the opportunity to do some down and dirty API programming really appealled to me today. So, the question (as indicated by the title) involved a member wanting to disable the right-click context menu from the Flash ActiveX component when being used in a WinForms application. (See previous blog entries for examples of Flash in VB.NET and communication between the two such as http://blogs.vbcity.com/drydo/archive/2005/10/18/5586.aspx).
The simple answer is to capture the right-click of the Flash Window itself and prevent it from being processed. A little play with Spy++ quickly came up with the answers and the rest was all about good old fashioned subclassing. I must admit it pinched the API declarations from Pinvoke.net and smartened them up a tad and that is pretty much it.
For the record, this is something I've been meaning to do for a while + I can leave work not feeling like some CSS floozy :)
Note: to recreate, create one form, one button and add one Flash Active X control. Copy and Paste as necessary. Tested in VS2005. If you get the dreaded 'Failed to import the ActiveX control' message, check out this workaround
Imports System.Runtime.InteropServices
Public Class Form1
#Region "API Routines"
<DllImport("User32.dll")> _
Private Shared Function EnumChildWindows _
(ByVal WindowHandle As IntPtr, ByVal Callback As EnumWindowProcess, _
ByVal lParam As IntPtr) As Boolean
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Sub GetClassName(ByVal hWnd As System.IntPtr, _
ByVal lpClassName As System.Text.StringBuilder, ByVal nMaxCount As Integer)
End Sub
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Overloads Shared Function SetWindowLong(ByVal hWnd As IntPtr, ByVal
nIndex As Integer, ByVal dwNewLong As Integer) As Integer
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Overloads Shared Function SetWindowLong(ByVal hWnd As IntPtr, ByVal
nIndex As Integer, ByVal dwNewLong As FlashCaptureRoutine) As Integer
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function CallWindowProc(ByVal lpPrevWndFunc As Integer, ByVal
hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
End Function
#End Region
#Region "Delegates"
' Used for processing Flash Window messages
Public Delegate Function FlashCaptureRoutine(ByVal hwnd As Integer, ByVal
Msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
' Used for enumerating child windows
Public Delegate Function EnumWindowProcess(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
#End Region
' Constants
Public Const GWL_WNDPROC As Integer = (-4)
Public Const WM_ENTERMENULOOP As Integer = &H211
Public Const WM_RBUTTONDOWN As Integer = &H204
Public Const WM_INITMENU = &H116
' Private vars
Private mFlashWindowHandle As IntPtr
Private mPreviousHandle As Integer
Private isCapturing As Boolean
Private Sub Form1_FormClosing(ByVal sender As Object, ByVal
e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
' Cleanup if appropriate
If isCapturing Then CleanupCapture()
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal
e As System.EventArgs) Handles MyBase.Load
' Load the test movie...
Me.AxShockwaveFlash1.Movie = "MyTestMovie.swf"
Me.AxShockwaveFlash1.Menu = True
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal
e As System.EventArgs) Handles Button1.Click
' Capture / Reset Flash Window Processing as appropriate
' React as approrpiate
If isCapturing Then
' Resume
CleanupCapture()
Else
' Capture events
CaptureRightClick()
End If
' Switch over the flag
isCapturing = Not isCapturing
End Sub
Private Sub CaptureRightClick()
' Attempt to obtain the Flash Window
EnumChildWindows(Me.Handle,
AddressOf EnumWindow, IntPtr.Zero)
' Subclass using new routine storing the old result
mPreviousHandle = SetWindowLong(mFlashWindowHandle, GWL_WNDPROC, AddressOf FlashWindowCapture)
End Sub
Private Sub CleanupCapture()
' Reset back to the original handle...
SetWindowLong(mFlashWindowHandle, GWL_WNDPROC, mPreviousHandle)
End Sub
Private Function FlashWindowCapture(ByVal hwnd As Integer, ByVal Msg As Integer,
ByVal wParam As Integer, ByVal lParam As Integer) As Integer
' Capture the appropriate message and prevent it from being processed
Select Case Msg
Case WM_RBUTTONDOWN
' Exit
Exit Function
End Select
' Carry on...
Return CallWindowProc(mPreviousHandle, hwnd, Msg, wParam, lParam)
End Function
Private Function EnumWindow(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
' Obtain the class name of the child window
Dim ClassName As New System.Text.StringBuilder("", 255)
GetClassName(Handle, ClassName, ClassName.MaxCapacity)
' Is this the Flash Window?
If ClassName.ToString = "MacromediaFlashPlayerActiveX" Then
mFlashWindowHandle = Handle
End If
Return True
End Function
End Class