Visual Basic > Visual Basic Forums > Visual Basic Interop and Upgrade > how to pass hwnd from VB6 code in VB.net library using Interop
Ask a questionAsk a question
 

Answerhow to pass hwnd from VB6 code in VB.net library using Interop

  • Monday, November 02, 2009 10:41 PMnarsiCherukupalli Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    I need to pass a window handle (long) from VB6 client to a library written in VB.Net for some processing (using interop).  I don't know how to make use of the hwnd from VB6 and get the correct window handle in .Net.

    Please help!

    btw, Im on VS 2008 SP1 and .Net framework 3.5 SP1

Answers

  • Friday, November 06, 2009 5:57 AMRiquel_DongModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    Hi Narsi,

    We can fix this issue by PInvoke calling MessageBox in VB.NET component to implement same effect as VB6 API calling. The following is my modification to your code snippet. I tested it. It should satisfy your requirement.

     Public Shadows Function MessageBoxU(ByVal OwnerHandle As Integer, ByVal Prompt As String, ByVal Title As String, ByVal buttons As Integer) As Integer
            Try
                Dim iPtr As IntPtr
                iPtr = New IntPtr(OwnerHandle)
                MessageBox.Show(OwnerHandle.ToString())
                MessageBox.Show(iPtr.ToString)
                'MessageBox.Show(New CWindowWrapper(iPtr), Prompt, Title, buttons)
                NativeMethods.MessageBoxW(iPtr, Prompt, Title, buttons)
            Catch
                MsgBox(Err.Description)
            End Try
        End Function

    Partial Public Class NativeMethods

        '''Return Type: int
        '''hWnd: HWND->HWND__*
        '''lpText: LPCWSTR->WCHAR*
        '''lpCaption: LPCWSTR->WCHAR*
        '''uType: UINT->unsigned int
        <System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint:="MessageBoxW")> _
        Public Shared Function MessageBoxW(<System.Runtime.InteropServices.InAttribute()> ByVal hWnd As System.IntPtr, <System.Runtime.InteropServices.InAttribute(), System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)> ByVal lpText As String, <System.Runtime.InteropServices.InAttribute(), System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)> ByVal lpCaption As String, ByVal uType As UInteger) As Integer
        End Function
    End Class
    Best regards,
    Riquel
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
  • Friday, November 06, 2009 1:20 PMPaul P Clement IVMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    I should have checked for a unicode version of the API function call (MessageBoxIndirectW) which can be called directly from a VB 6.0 app.

    http://www.vbforums.com/showthread.php?t=541220


    Paul ~~~~ Microsoft MVP (Visual Basic)

All Replies

  • Tuesday, November 03, 2009 2:42 PMPaul P Clement IVMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Maybe you could provide a little more detail as to what the .NET component needs to do with the hwnd. I don't see a problem with passing it from the VB 6.0 app to a .NET component function. Is it the interop process that you're not sure how to do?
    Paul ~~~~ Microsoft MVP (Visual Basic)
  • Tuesday, November 03, 2009 3:48 PMnarsiCherukupalli Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi Paul
    Thanks for the response.  Here is what I am trying to do.

    This is part of a project where we are adding Unicode support to a fairly complex VB6 application.  We are doing Interop user controls to be used in VB6 forms.  As part of this project we also have to wrap MessageBox function so that we can display unicode strings in the message box.

    following are the two main objectives of the the Wrapper function for MessageBox
      a) to handle Unicode Strings
      b) take Window handle as parameter so that the message box can be set for a particular window.

    now, when calling this function from VB6 I can pass the window handle of any form that I want to this function as Integer but don't know how to properly convert it to "Iwin32Window" type so that MessageBox function can understand it.

    Thanks again.
  • Tuesday, November 03, 2009 5:28 PMPaul P Clement IVMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    See if the below link helps. It's written in C# but the example looks to be relatively simple and should be adaptable to Visual Basic:

    http://ryanfarley.com/blog/archive/2004/03/23/465.aspx


    Paul ~~~~ Microsoft MVP (Visual Basic)
  • Tuesday, November 03, 2009 5:32 PMnarsiCherukupalli Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    Paul
    I got this working by using a WrapperClass for IWin32Window.  But I have a new problem now

    When I pass the Window handle of an external window (not the VB6 Client from where I call this function) the message box displays modally on that window and my VB6 Form also stuck behind that same Message box.

    I don't want it to be Modal to my VB6 Form when I pass a handle to another window. 

    Please help!  I'm posting the code here so you can reproduce it very easily

    VB.Net 
    
    Imports System.Windows.Forms
    
    <ComClass(CTest.ClassId, CTest.InterfaceId, CTest.EventsId)> _
    Public Class CTest
    
    #Region "COM GUIDs"
        ' These  GUIDs provide the COM identity for this class 
        ' and its COM interfaces. If you change them, existing 
        ' clients will no longer be able to access the class.
        Public Const ClassId As String = "75bce494-cfa8-46fd-8f2e-550aee2ebd47"
        Public Const InterfaceId As String = "8c003cc9-e226-4f9e-87c4-54619a2b9482"
        Public Const EventsId As String = "bbbf2e72-74f9-4514-8970-5a73ec62d421"
    #End Region
    
        ' A creatable COM class must have a Public Sub New() 
        ' with no parameters, otherwise, the class will not be 
        ' registered in the COM registry and cannot be created 
        ' via CreateObject.
        Public Sub New()
            MyBase.New()
        End Sub
    
        Public Shadows Function MessageBoxU(ByVal Prompt As String, Optional ByVal Title As String = "", Optional ByVal OwnerHandle As Integer = 0) As Integer
            Try
                Dim iPtr As IntPtr
                Dim oWindWrapper As CWindowWrapper
    
                If OwnerHandle > 0 Then
                    iPtr = New IntPtr(OwnerHandle)
                    oWindWrapper = New CWindowWrapper(iPtr)
    
    
                    Return MessageBox.Show(oWindWrapper, Prompt, Title)
                Else
                    Return MessageBox.Show(Prompt, Title)
                End If
    
            Catch
    
            End Try
    
    
        End Function
    
    End Class
    
    Imports System.Windows.Forms
    
    Class CWindowWrapper
        Implements IWin32Window
    
        Private _hwnd As IntPtr
    
        Public Sub New(ByVal handle As IntPtr)
            _hwnd = handle
        End Sub
    
        Public ReadOnly Property Handle() As IntPtr Implements IWin32Window.Handle
            Get
                Return _hwnd
            End Get
        End Property
    
    End Class
    
    -----------------------------------------------------------------
    VB6 Client Code
    On a form add these three controls
    TextBox, CheckBox and Command Button
     and paste the following code
    
    Option Explicit
    
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
    
    Private Declare Function MessageBox Lib "user32" Alias "MessageBoxA" _
    (ByVal hWnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As Long) As Long
    
    
    Private Sub Command1_Click()
        Dim ot As New CTest
        Dim Whdl As Long
        
        'Text1.Text is any Window Caption
        Whdl = FindWindow(vbNullString, Text1.Text)
           
        If Check1.Value = vbChecked Then
            'use Win32 API
            MessageBox Whdl, "Hello on Window (" & Text1.Text & ")", "My Caption", vbInformation
        Else
            'use .Net MessageBox
            If Whdl > 0 Then
                ot.MessageBoxU "Hello on Window (" & Text1.Text & ")", "My Caption", Whdl
            Else
                ot.MessageBoxU "Window Not Found (" & Text1.Text & ")", "My Caption"
            End If
        End If
    End Sub
    
    
    
    
  • Tuesday, November 03, 2009 5:36 PMPaul P Clement IVMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code

    Example code converted to VB:

       MessageBox.Show(New WindowWrapper(hwnd), "Hello World!")
    
       Public Class WindowWrapper
            Implements System.Windows.Forms.IWin32Window
            Public Sub New(ByVal handle As IntPtr)
                _hwnd = handle
            End Sub
    
            Public ReadOnly Property Handle() As IntPtr Implements System.Windows.Forms.IWin32Window.Handle
                Get
                    Return _hwnd
                End Get
            End Property
    
            Private _hwnd As IntPtr
        End Class
    

    Paul ~~~~ Microsoft MVP (Visual Basic)
  • Tuesday, November 03, 2009 5:53 PMnarsiCherukupalli Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    It is still modal to both the windows! 

    I am trying to make the Message box appear modally on lets say Windows Explorer window by passing its hwnd, when I click the button on my
    VB6 Form.  But somehow it makes the displayed message box Modal to both my VB6 Form and Windows Explorer window.

    If I use the win32 MessageBox API it works correctly.  After I click the button I can proceed to work with my VB6 Form.  Messagebox is only Modal to windows explorer.


    Modified VB.Net code (based on ur sample above)

     

    Public Shadows Function MessageBoxU(ByVal Prompt As String, Optional ByVal Title As String = "", Optional ByVal OwnerHandle As Integer = 0) As Integer

     

    Dim iPtr As IntPtr

    iPtr =

    New IntPtr(OwnerHandle)

    MessageBox.Show(

    New CWindowWrapper(iPtr), Prompt, Title)

     

    End Function


    Modified VB6 Code
    Private Sub Command1_Click()
        Dim ot As New CTest
        Dim Whdl As Long
       
        'Text1.Text is any Window Caption
        Whdl = FindWindow(vbNullString, Text1.Text)
          
        If Check1.Value = vbChecked Then
            'use Win32 API
            MessageBox Whdl, "Hello on Window (" & Text1.Text & ")", "My Caption", vbInformation
        Else
            'use .Net MessageBox
            ot.MessageBoxU "Hello on Window (" & Text1.Text & ")", "My Caption", Whdl
    End Sub
  • Tuesday, November 03, 2009 5:57 PMPaul P Clement IVMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    The MessageBox method displays a modal window by design. I don't believe there is a parameter that will allow you to display a non-modal or mode-less window.

    Can't you just use a Windows Form instead instead of a MessageBox?
    Paul ~~~~ Microsoft MVP (Visual Basic)
  • Tuesday, November 03, 2009 6:06 PMnarsiCherukupalli Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I understand that Messagebox method is Modal and that is what I want it to be, but just to the Window whose handle I pass to it.

    But it seems to become modal to both my VB6 Form (from where I call this method) and to the actual window whose handle I pass to this method.

  • Tuesday, November 03, 2009 7:17 PMPaul P Clement IVMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    The MessageBox method is just a .NET wrapper for the MessageBox API function. I don't know why both the parent window and calling app are blocked but it could be a threading restriction with respect to VB 6.0. I would be curious to see whether this would function differently under a .NET client. 

    I would use the API function call if it's providing the desired results. You could always create your own .NET MessageBox Class (wrapper) if you are looking for something that is easier to use than the API. 
    Paul ~~~~ Microsoft MVP (Visual Basic)
  • Tuesday, November 03, 2009 7:45 PMnarsiCherukupalli Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I tried it using a .Net client, its the same behavious with MessageBox.Show method.  It becomes Modal on both, the form that this method is called from and the window whose handle is passed.

     

    Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Integer

     

    Private Sub Command1_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles Command1.Click

     

    Dim handle As Integer

    handle = FindWindow(vbNullString,

    "VB6 Test")

    MessageBox.Show(

    New WindowWrapper(New IntPtr(handle)), "My Message: " & TextBox1.Text, "My Caption - " & TextBox1.Text)

     

    'MessageBox(handle, "My Message: " & TextBox1.Text, "My Caption - " & TextBox1.Text, vbInformation)

     

    End Sub


    --------------------------
    If I use the MessageBox API it behaves correctly in this scenario also.  But the API is not able to display Unicode Text, which is kind of why I am trying to use .Net Message Box in the first place.

     

    Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal hWnd As Integer, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As Integer) As Integer

  • Tuesday, November 03, 2009 9:21 PMPaul P Clement IVMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Let me check to see if I can get an explanation for this behavior.

    Paul ~~~~ Microsoft MVP (Visual Basic)
  • Thursday, November 05, 2009 7:42 AMRiquel_DongModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi ,

    Could you send your VB.NET project and VB6 project to me so that I can reproduce your scenario for further discussion? If you can, please send it via email(you can find it in my profile). Thanks.

    Best regards,
    Riquel
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
  • Thursday, November 05, 2009 4:05 PMnarsiCherukupalli Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi Riquel
    I had just sent the Code to you in via email.  subject line

    Ref: "how to pass hwnd from VB6 code in VB.net library using Interop"

    Hope you can see what I am doing wrong here.

    Thank you in advance.
  • Friday, November 06, 2009 5:57 AMRiquel_DongModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    Hi Narsi,

    We can fix this issue by PInvoke calling MessageBox in VB.NET component to implement same effect as VB6 API calling. The following is my modification to your code snippet. I tested it. It should satisfy your requirement.

     Public Shadows Function MessageBoxU(ByVal OwnerHandle As Integer, ByVal Prompt As String, ByVal Title As String, ByVal buttons As Integer) As Integer
            Try
                Dim iPtr As IntPtr
                iPtr = New IntPtr(OwnerHandle)
                MessageBox.Show(OwnerHandle.ToString())
                MessageBox.Show(iPtr.ToString)
                'MessageBox.Show(New CWindowWrapper(iPtr), Prompt, Title, buttons)
                NativeMethods.MessageBoxW(iPtr, Prompt, Title, buttons)
            Catch
                MsgBox(Err.Description)
            End Try
        End Function

    Partial Public Class NativeMethods

        '''Return Type: int
        '''hWnd: HWND->HWND__*
        '''lpText: LPCWSTR->WCHAR*
        '''lpCaption: LPCWSTR->WCHAR*
        '''uType: UINT->unsigned int
        <System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint:="MessageBoxW")> _
        Public Shared Function MessageBoxW(<System.Runtime.InteropServices.InAttribute()> ByVal hWnd As System.IntPtr, <System.Runtime.InteropServices.InAttribute(), System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)> ByVal lpText As String, <System.Runtime.InteropServices.InAttribute(), System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)> ByVal lpCaption As String, ByVal uType As UInteger) As Integer
        End Function
    End Class
    Best regards,
    Riquel
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
  • Friday, November 06, 2009 1:20 PMPaul P Clement IVMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    I should have checked for a unicode version of the API function call (MessageBoxIndirectW) which can be called directly from a VB 6.0 app.

    http://www.vbforums.com/showthread.php?t=541220


    Paul ~~~~ Microsoft MVP (Visual Basic)
  • Friday, November 06, 2009 2:20 PMnarsiCherukupalli Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Riquel
    This worked!  Thank you so much for your help.  The solution Paul gave below is also good and works for me.  Now I have a choice!

    Thank you for your help, I appreciate it very much.

    Narsi

  • Friday, November 06, 2009 2:21 PMnarsiCherukupalli Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Paul
    This worked!  Thank you so much for your help.  Riquel's solution above is also works.  Now I have a choice!

    Thank you for your help, I appreciate it very much.

    Narsi