'Object doesn't support this property or method' - But it's there!


DevX Home    Today's Headlines   Articles Archive   Tip Bank   Forums   

Results 1 to 3 of 3

Thread: 'Object doesn't support this property or method' - But it's there!

  1. #1
    Damit Senanayake Guest

    'Object doesn't support this property or method' - But it's there!

    Firstly, it should be noted that I am using VB5 SP0, and it is not possible
    at all to upgrade to SP3 or any higher version of VB.

    As part of a requirement from 'on high', I've been told to dump all ActiveX
    controls that we are currently using and to replace them with UserControls.
    Fair enough, except that each ActiveX control has its own code modules, so
    I've tried to consolidate these modules into a single code module. The
    UserControls are subclassed, so I have a common WndProc in the code module
    that forwards to each UserControl's friend WndProc function.

    However, VB chokes while compiling or running the program on the indicated
    line, and if I attempt to debug and resume execution the IDE GPFs.

    Public Function WndProc (ByVal hWnd As Long, ByVal uMsg As Long, ByVal
    wParam As Long, ByVal lParam As Long) As Long
    Dim pObj As Object, lpPrevWndProc As Long
    Set pObj = PtrToCtl(GetProp(hWnd, "ControlPtr"))
    lpPrevWndProc = GetProp(hWnd, "OldWndProc")
    If Not (pObj Is Nothing) Then
    '// VB throws the error on the next line.
    WndProc = pObj.WndProc(hWnd, uMsg, wParam, lParam)
    Else
    If lpPrevWndProc <> 0 Then
    WndProc = CallWindowProc(lpPrevWndProc, hWnd, uMsg, wParam, lParam)
    Else
    WndProc = DefWindowProc(hWnd, uMsg, wParam, lParam)
    End If
    End If
    End Function

    Public Function PtrToCtl (ByVal ptr As Long) As Object
    If ptr = 0 Then
    Set PtrToCtl = Nothing
    Exit Function
    End If
    Dim oCtl As Object
    CopyMemory oCtl, ptr, 4
    Set PtrToCtl = oCtl
    CopyMemory oCtl, 0&, 4
    End Function

    In the UserControl's source code, however, there *is* a WndProc function,
    and it doesn't matter what (if anything) is inside the code. Making it a
    Public function results in an error message similar to the following:

    "Method WndProc of _TreeView failed."

    Friend Function WndProc (ByVal hWnd As Long, ByVal uMsg As Long, ByVal
    wParam As Long, ByVal lParam As Long) As Long
    '// ...
    End Function

    My intention is actually to write a generic WndProc forwarder for each
    UserControl. I have tried getting the UserControls to implement a callback
    interface, but that leads to the same issue.

    Additionally, I am using some vtable replacement code for the
    IOleInPlaceActiveObject interface, but the error and subsequent crash still
    occurs even if that code is commented out.

    Any help on this matter will be greatly appreciated.

    Thanks,
    Damit



  2. #2
    Matthew Curland Guest

    Re: 'Object doesn't support this property or method' - But it's there!

    Comments inline. Also, I'd strongly recommend that you use
    SubClass.bas/PushParamThunk.bas/DbgWProc.dll as described in my book
    (http://www.PowerVB.com), or at least DbgWProc.dll as described in Feb 97
    VBPJ or earlier. You'll save yourself all of the GetProp/SetProp/CopyMemory
    work, work correctly with Friend functions, and support debugging
    automatically. Subclassing with this technique requires a class variable + 2
    lines of code, your window proc, and a 3 line (including Function/End
    Function) redirection function in a standard module. Trust me, it is much
    easier than what you're trying here, and has a fraction of the per-message
    overhead.

    -Matt

    "Damit Senanayake" <damit@mvps.org> wrote in message
    news:3b1d0acf@news.devx.com...
    > Firstly, it should be noted that I am using VB5 SP0, and it is not

    possible
    > at all to upgrade to SP3 or any higher version of VB.


    Not a problem. Anything I mention below works equally well in all VB5/VB6
    versions.

    > As part of a requirement from 'on high', I've been told to dump all

    ActiveX
    > controls that we are currently using and to replace them with

    UserControls.
    > Fair enough, except that each ActiveX control has its own code modules, so
    > I've tried to consolidate these modules into a single code module. The
    > UserControls are subclassed, so I have a common WndProc in the code module
    > that forwards to each UserControl's friend WndProc function.


    You can't call a Friend function on multiple objects from a single function.
    A Friend function is not virtual, which means several things. First, it must
    be bound to a specific class type, not a specific interface type. Public
    functions are bound through an interface (read: vtable), not to a specific
    class type. This also means that a Friend function cannot be called than an
    'As Object' variable, as this implies a late-bound call, which will only
    work against Public functions. If you change your WndProc callbacks to
    Public, this would probably work. If you want to leave them as Friend, then
    you need to create a different .bas function for each type of UserControl
    you use. I would also recommend using a different PtrToCtl function for each
    UserControl type. Trying to use this code generically (with 'As Object'
    instead of 'As MyCtl' can lead to a lot of problems if you're not careful.

    > However, VB chokes while compiling or running the program on the indicated
    > line, and if I attempt to debug and resume execution the IDE GPFs.
    >
    > Public Function WndProc (ByVal hWnd As Long, ByVal uMsg As Long, ByVal
    > wParam As Long, ByVal lParam As Long) As Long
    > Dim pObj As Object, lpPrevWndProc As Long
    > Set pObj = PtrToCtl(GetProp(hWnd, "ControlPtr"))
    > lpPrevWndProc = GetProp(hWnd, "OldWndProc")


    Running GetProp against an Atom is about two orders of magnitude faster than
    using a string everytime. Also, if "ControlPtr" is actually set, then
    "OldWndProc" is redundant. The previous window procedure can be stored in
    the UserControl, which should be fully responsible for the forwarding calls.
    This code might seem more robust, but if the primary mechanism fails
    (because the property is not set), then so will the backup mechanism.

    I use the following code to get a shared Atom value I can use. You'll want
    to modify the string and create another class instance if you need more than
    one string.
    'In Atomizer.cls, Atomizer.
    Private m_Atom As Integer

    'Value should be set as the default property
    Public Property Get Value() As Long 'As Long for Get/SetProp calls
    Value = m_Atom And &HFFFF&
    End Property

    Private Sub Class_Initialize()
    'Get an atom unique to this instance and class
    m_Atom = GlobalAddAtom(CStr(App.hInstance) & CStr(ObjPtr(Me)))
    End Sub

    Private Sub Class_Terminate()
    GlobalDeleteAtom m_Atom
    End Sub

    'Declares
    Declare Function GlobalAddAtom Lib "kernel32" Alias "GlobalAddAtomA" (ByVal
    lpString As String) As Integer
    Declare Function GlobalDeleteAtom Lib "kernel32" (ByVal nAtom As Integer) As
    Integer
    Declare Function SetProp Lib "user32" Alias "SetPropA" (ByVal hWnd As Long,
    ByVal lpString As Long, ByVal hData As Long) As Long
    Declare Function GetProp Lib "user32" Alias "GetPropA" (ByVal hWnd As Long,
    ByVal lpString As Long) As Long
    Declare Function RemoveProp Lib "user32" Alias "RemovePropA" (ByVal hWnd As
    Long, ByVal lpString As Long) As Long

    'Usage
    Public Atomizer As New Atomizer

    pObj = GetProp(hWnd, Atomizer) 'etc


    > If Not (pObj Is Nothing) Then
    > '// VB throws the error on the next line.
    > WndProc = pObj.WndProc(hWnd, uMsg, wParam, lParam)


    This error is expected if WndProc is Friend. The function isn't there. Note
    that VB throws out of a .bas function if an error is not caught, and the
    windows is definitely not expecting a throw here. You should never let an
    error out of this procedure, which means not letting one out of your
    UserControl WndProc once you manage to call the function.

    > Else
    > If lpPrevWndProc <> 0 Then
    > WndProc = CallWindowProc(lpPrevWndProc, hWnd, uMsg, wParam, lParam)


    Once again, this is all overkill if "ControlPtr" actually works.

    > Else
    > WndProc = DefWindowProc(hWnd, uMsg, wParam, lParam)
    > End If
    > End If
    > End Function
    >
    > Public Function PtrToCtl (ByVal ptr As Long) As Object
    > If ptr = 0 Then
    > Set PtrToCtl = Nothing
    > Exit Function
    > End If
    > Dim oCtl As Object
    > CopyMemory oCtl, ptr, 4
    > Set PtrToCtl = oCtl
    > CopyMemory oCtl, 0&, 4
    > End Function
    >
    > In the UserControl's source code, however, there *is* a WndProc function,
    > and it doesn't matter what (if anything) is inside the code. Making it a
    > Public function results in an error message similar to the following:
    >
    > "Method WndProc of _TreeView failed."
    >
    > Friend Function WndProc (ByVal hWnd As Long, ByVal uMsg As Long, ByVal
    > wParam As Long, ByVal lParam As Long) As Long
    > '// ...
    > End Function
    >
    > My intention is actually to write a generic WndProc forwarder for each
    > UserControl. I have tried getting the UserControls to implement a callback
    > interface, but that leads to the same issue.


    The callback interface is possible, but is limited if you need more than one
    subclass per control (ie, subclass a textbox and the UserControl itself).
    You need to branch on the hWnd parameter for every message coming in. It is
    much easier to code window procedures if you have distinct procs for each
    subclassed item. If you did this, you should adjust your PtrToCtl function
    to return the interface directly (be sure the local variable and the return
    type have the same type), and make sure you use a Set statement to get the
    correct interface before the initial SetProp call (Dim pSubClass As
    ISubClass: Set pSubClass = Me: SetProp hWnd, Atomizer, ObjPtr(pSubClass)).

    > Additionally, I am using some vtable replacement code for the
    > IOleInPlaceActiveObject interface, but the error and subsequent crash

    still
    > occurs even if that code is commented out.


    Wrapping this interface and plugging it in with
    IOleInPlaceFrame.SetActiveObject is one thing (this can be done in response
    to WM_SETFOCUS as outlined in Chapter 16 in my book or the June 97 VBPJ),
    but doing vtable replacement on this interface is a very bad idea. You
    should only use vtable replacement on your primary or explicitly implemented
    vtables, and this should be a one-off replacement, not a toggle back and
    forth between the functions. The primary/implements vtables are shared with
    every instance of the class, but the VB-provided vtables (from IUnknown to
    IOleInPlaceActiveObject) are shared by every instance of every
    Class/Form/UserControl that happens to use them. You can't touch
    IOleInPlaceActiveObject without touching every user control in your Dll/Ocx
    and other Dll/Ocx/Exe instances in the same process. You get even more
    problems if you swap the vtable pointer back and forth to forward the call.
    I've been known to do some pretty hairy things in VB (including introducing
    vtable replacement, March 97 VBPJ), but I won't touch this application of
    the technique with a ten foot pole. What are you trying to accomplish with
    the vtable replacement?

    > Any help on this matter will be greatly appreciated.
    >
    > Thanks,
    > Damit
    >
    >




  3. #3
    Matthew Curland Guest

    Re: 'Object doesn't support this property or method' - But it's there!

    Comments inline. Also, I'd strongly recommend that you use
    SubClass.bas/PushParamThunk.bas/DbgWProc.dll as described in my book
    (http://www.PowerVB.com), or at least DbgWProc.dll as described in Feb 97
    VBPJ or earlier. You'll save yourself all of the GetProp/SetProp/CopyMemory
    work, work correctly with Friend functions, and support debugging
    automatically. Subclassing with this technique requires a class variable + 2
    lines of code, your window proc, and a 3 line (including Function/End
    Function) redirection function in a standard module. Trust me, it is much
    easier than what you're trying here, and has a fraction of the per-message
    overhead.

    -Matt

    "Damit Senanayake" <damit@mvps.org> wrote in message
    news:3b1d0acf@news.devx.com...
    > Firstly, it should be noted that I am using VB5 SP0, and it is not

    possible
    > at all to upgrade to SP3 or any higher version of VB.


    Not a problem. Anything I mention below works equally well in all VB5/VB6
    versions.

    > As part of a requirement from 'on high', I've been told to dump all

    ActiveX
    > controls that we are currently using and to replace them with

    UserControls.
    > Fair enough, except that each ActiveX control has its own code modules, so
    > I've tried to consolidate these modules into a single code module. The
    > UserControls are subclassed, so I have a common WndProc in the code module
    > that forwards to each UserControl's friend WndProc function.


    You can't call a Friend function on multiple objects from a single function.
    A Friend function is not virtual, which means several things. First, it must
    be bound to a specific class type, not a specific interface type. Public
    functions are bound through an interface (read: vtable), not to a specific
    class type. This also means that a Friend function cannot be called than an
    'As Object' variable, as this implies a late-bound call, which will only
    work against Public functions. If you change your WndProc callbacks to
    Public, this would probably work. If you want to leave them as Friend, then
    you need to create a different .bas function for each type of UserControl
    you use. I would also recommend using a different PtrToCtl function for each
    UserControl type. Trying to use this code generically (with 'As Object'
    instead of 'As MyCtl' can lead to a lot of problems if you're not careful.

    > However, VB chokes while compiling or running the program on the indicated
    > line, and if I attempt to debug and resume execution the IDE GPFs.
    >
    > Public Function WndProc (ByVal hWnd As Long, ByVal uMsg As Long, ByVal
    > wParam As Long, ByVal lParam As Long) As Long
    > Dim pObj As Object, lpPrevWndProc As Long
    > Set pObj = PtrToCtl(GetProp(hWnd, "ControlPtr"))
    > lpPrevWndProc = GetProp(hWnd, "OldWndProc")


    Running GetProp against an Atom is about two orders of magnitude faster than
    using a string everytime. Also, if "ControlPtr" is actually set, then
    "OldWndProc" is redundant. The previous window procedure can be stored in
    the UserControl, which should be fully responsible for the forwarding calls.
    This code might seem more robust, but if the primary mechanism fails
    (because the property is not set), then so will the backup mechanism.

    I use the following code to get a shared Atom value I can use. You'll want
    to modify the string and create another class instance if you need more than
    one string.
    'In Atomizer.cls, Atomizer.
    Private m_Atom As Integer

    'Value should be set as the default property
    Public Property Get Value() As Long 'As Long for Get/SetProp calls
    Value = m_Atom And &HFFFF&
    End Property

    Private Sub Class_Initialize()
    'Get an atom unique to this instance and class
    m_Atom = GlobalAddAtom(CStr(App.hInstance) & CStr(ObjPtr(Me)))
    End Sub

    Private Sub Class_Terminate()
    GlobalDeleteAtom m_Atom
    End Sub

    'Declares
    Declare Function GlobalAddAtom Lib "kernel32" Alias "GlobalAddAtomA" (ByVal
    lpString As String) As Integer
    Declare Function GlobalDeleteAtom Lib "kernel32" (ByVal nAtom As Integer) As
    Integer
    Declare Function SetProp Lib "user32" Alias "SetPropA" (ByVal hWnd As Long,
    ByVal lpString As Long, ByVal hData As Long) As Long
    Declare Function GetProp Lib "user32" Alias "GetPropA" (ByVal hWnd As Long,
    ByVal lpString As Long) As Long
    Declare Function RemoveProp Lib "user32" Alias "RemovePropA" (ByVal hWnd As
    Long, ByVal lpString As Long) As Long

    'Usage
    Public Atomizer As New Atomizer

    pObj = GetProp(hWnd, Atomizer) 'etc


    > If Not (pObj Is Nothing) Then
    > '// VB throws the error on the next line.
    > WndProc = pObj.WndProc(hWnd, uMsg, wParam, lParam)


    This error is expected if WndProc is Friend. The function isn't there. Note
    that VB throws out of a .bas function if an error is not caught, and the
    windows is definitely not expecting a throw here. You should never let an
    error out of this procedure, which means not letting one out of your
    UserControl WndProc once you manage to call the function.

    > Else
    > If lpPrevWndProc <> 0 Then
    > WndProc = CallWindowProc(lpPrevWndProc, hWnd, uMsg, wParam, lParam)


    Once again, this is all overkill if "ControlPtr" actually works.

    > Else
    > WndProc = DefWindowProc(hWnd, uMsg, wParam, lParam)
    > End If
    > End If
    > End Function
    >
    > Public Function PtrToCtl (ByVal ptr As Long) As Object
    > If ptr = 0 Then
    > Set PtrToCtl = Nothing
    > Exit Function
    > End If
    > Dim oCtl As Object
    > CopyMemory oCtl, ptr, 4
    > Set PtrToCtl = oCtl
    > CopyMemory oCtl, 0&, 4
    > End Function
    >
    > In the UserControl's source code, however, there *is* a WndProc function,
    > and it doesn't matter what (if anything) is inside the code. Making it a
    > Public function results in an error message similar to the following:
    >
    > "Method WndProc of _TreeView failed."
    >
    > Friend Function WndProc (ByVal hWnd As Long, ByVal uMsg As Long, ByVal
    > wParam As Long, ByVal lParam As Long) As Long
    > '// ...
    > End Function
    >
    > My intention is actually to write a generic WndProc forwarder for each
    > UserControl. I have tried getting the UserControls to implement a callback
    > interface, but that leads to the same issue.


    The callback interface is possible, but is limited if you need more than one
    subclass per control (ie, subclass a textbox and the UserControl itself).
    You need to branch on the hWnd parameter for every message coming in. It is
    much easier to code window procedures if you have distinct procs for each
    subclassed item. If you did this, you should adjust your PtrToCtl function
    to return the interface directly (be sure the local variable and the return
    type have the same type), and make sure you use a Set statement to get the
    correct interface before the initial SetProp call (Dim pSubClass As
    ISubClass: Set pSubClass = Me: SetProp hWnd, Atomizer, ObjPtr(pSubClass)).

    > Additionally, I am using some vtable replacement code for the
    > IOleInPlaceActiveObject interface, but the error and subsequent crash

    still
    > occurs even if that code is commented out.


    Wrapping this interface and plugging it in with
    IOleInPlaceFrame.SetActiveObject is one thing (this can be done in response
    to WM_SETFOCUS as outlined in Chapter 16 in my book or the June 97 VBPJ),
    but doing vtable replacement on this interface is a very bad idea. You
    should only use vtable replacement on your primary or explicitly implemented
    vtables, and this should be a one-off replacement, not a toggle back and
    forth between the functions. The primary/implements vtables are shared with
    every instance of the class, but the VB-provided vtables (from IUnknown to
    IOleInPlaceActiveObject) are shared by every instance of every
    Class/Form/UserControl that happens to use them. You can't touch
    IOleInPlaceActiveObject without touching every user control in your Dll/Ocx
    and other Dll/Ocx/Exe instances in the same process. You get even more
    problems if you swap the vtable pointer back and forth to forward the call.
    I've been known to do some pretty hairy things in VB (including introducing
    vtable replacement, March 97 VBPJ), but I won't touch this application of
    the technique with a ten foot pole. What are you trying to accomplish with
    the vtable replacement?

    > Any help on this matter will be greatly appreciated.
    >
    > Thanks,
    > Damit
    >
    >




Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center
 
 
FAQ
Latest Articles
Java
.NET
XML
Database
Enterprise
Questions? Contact us.
C++
Web Development
Wireless
Latest Tips
Open Source


   Development Centers

   -- Android Development Center
   -- Cloud Development Project Center
   -- HTML5 Development Center
   -- Windows Mobile Development Center