Results 1 to 13 of 13

Thread: [RESOLVED] Unique Scenario: Passing IUnknown as optional parameter

  1. #1

    Thread Starter
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Resolved [RESOLVED] Unique Scenario: Passing IUnknown as optional parameter

    I code a lot with VTable-only interfaces vs TLB usage. This question is more for personal knowledge than fixing some existing code.

    I have a scenario where I'd like to know if a user passed an optional parameter and if so, return an exact Interface reference to the user via that parameter. Just not quite getting what I want...

    1. If I declare the parameter: Optional oUnknown As IUnknown. Result is that I can't really tell if the user passed anything, since not passing it returns the same ObjPtr, & VarType as if it were passed. So, can't really test whether the parameter was skipped or provided. Was hoping some magic, kinda like testing a true null string with StrPtr() vs. comparing a string to vbNullString.

    These calls result in oUnknown testing identically within the TestRoutine where oUnknown is used:
    Call TestRoutine()
    Call TestRoutine(Nothing)
    Dim tmpUnk As IUnknown: Call TestRoutine(tmpUnk)

    2. If I declare the parameter: Optional oUnknown As Variant. This is much easier to test whether the parameter was passed a variable, passed Nothing, or skipped. However, the returned interface is coherced to its identity IUnknown, which is what I was hoping to avoid.

    For example, let's say I passed an IShellItem interface back to oUnknown (parameter declared as variant). When the routine returns to the caller, then the interface they get is not IShellItem rather its IUnknown identity. Testing ObjPtr() before the object is passed thru the variant and after, returns two different values. One would need to QueryInterface the returned IUnknown for the IShellItem in order to make calls to the IShellItem.

    3. In summary. The 1st example obviously returns the same interface reference but doesn't reliably allow testing whether or not the parameter was passed. The 2nd example makes it easy to test whether the parameter was passed, but doesn't result in the same interface reference.

    If you want to play. Here's sample code that can be used. Change the test file to any valid file, if needed.
    Code:
    Private Declare Function SHILCreateFromPath Lib "shell32.dll" (ByVal pszPath As Long, ByRef pidl As Long, ByVal pAttrs As Long) As Long
    Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal pv As Long)
    Private Declare Function SHCreateShellItemArrayFromIDLists Lib "shell32" (ByVal cidl As Long, ByVal rgpidl As Long, ppsiItemArray As IUnknown) As Long
    
    Private Sub TestMe(oReturn As Variant)
        
        Dim oUnk As IUnknown
        Dim pidl As Long
        
        SHILCreateFromPath StrPtr("C:\Windows\winhlp32.exe"), pidl, 0&
            Debug.Assert pidl <> 0
        SHCreateShellItemArrayFromIDLists 1, VarPtr(pidl), oUnk
        If pidl Then CoTaskMemFree pidl
            Debug.Assert Not oUnk Is Nothing
        
        Debug.Print "oUnk: "; ObjPtr(oUnk)
        Set oReturn = oUnk
        
    End Sub
    
    Private Sub Form_Load()
    
        Dim oReturn As IUnknown
        TestMe oReturn
        Debug.Print "oReturn: "; ObjPtr(oReturn)
        Unload Me
    
    End Sub
    P.S. I know a TLB should solve this. But as stated, no TLBs used.

    Edited: FYI. This IUnknown identity issue applies if assigning it to Variant or any variant based property/object, i.e., the Collection object. For example, adding it to a collection and returning it from the collection results in the identity IUnknown reference.
    Last edited by LaVolpe; Jan 14th, 2018 at 11:09 AM.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  2. #2
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Unique Scenario: Passing IUnknown as optional parameter

    Well, an ugly fix would be to just pass a second optional argument that indicates what you passed.

    However, I agree; it'd be cool to know if an optional argument was passed or not. I'm traveling today, but I'll definitely be poking at it to see if there's a way to figure this out.

    Best Regards,
    Elroy
    Last edited by Elroy; Jan 14th, 2018 at 10:37 AM.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  3. #3

    Thread Starter
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Unique Scenario: Passing IUnknown as optional parameter

    @Elroy. Yep thought about that but not an option simply due to its ugliness

    P.S. The reason for the optional parameter is that if it is passed, then another routine needs to be called to build the return value. Don't want to waste time/cpu cycles performing something that isn't wanted.

    Edited: And just FYI, don't want the caller to call yet another routine that passes that IUknown, performs the QueryInterface, and returns the proper reference. In fact, this should always be done when passing an interface reference, because one really has no way of knowing which reference was passed. It could be the identity IUnknown, another interface that implements the desired interface, etc. VB does this automatically for us when we use keyword SET for objects (IDispatch implemented) and when a TLB is used to define an interface and that interface is declared via the TLB. As stated in 1st post, this thread's goal is really just a learning opportunity for me.
    Last edited by LaVolpe; Jan 14th, 2018 at 10:49 AM.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  4. #4
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Unique Scenario: Passing IUnknown as optional parameter

    Also, just thinking this through a bit ... the called procedure must keep track of whether or not an optional argument was passed. Here's why I say this. It's always been my assumption that, if an argument is passed, it's just used. However, if it's not passed, the called procedure implicitly declares (Dim) a variable of the type specified in the procedure declaration. Now, if the called procedure declared the variable, then it's responsible for destroying it when the procedure terminates. If it was passed in, it's not destroyed. Therefore, there's somehow a difference. Whether it can be detected is another matter.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  5. #5
    PowerPoster
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    2,412

    Re: Unique Scenario: Passing IUnknown as optional parameter

    Maybe I've misunderstood, but doesn't IsMissing do what you need?

    Code:
    Private Declare Function SHILCreateFromPath Lib "shell32.dll" (ByVal pszPath As Long, ByRef pidl As Long, ByVal pAttrs As Long) As Long
    Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal pv As Long)
    Private Declare Function SHCreateShellItemArrayFromIDLists Lib "shell32" (ByVal cidl As Long, ByVal rgpidl As Long, ppsiItemArray As IUnknown) As Long
    
    Private Sub TestMe(Optional oReturn As Variant)
        
        Dim oUnk As IUnknown
        Dim pidl As Long
        
        If IsMissing(oReturn) Then Exit Sub
        
        SHILCreateFromPath StrPtr("C:\Windows\winhlp32.exe"), pidl, 0&
            Debug.Assert pidl <> 0
        SHCreateShellItemArrayFromIDLists 1, VarPtr(pidl), oUnk
        If pidl Then CoTaskMemFree pidl
            Debug.Assert Not oUnk Is Nothing
        
        Debug.Print "oUnk: "; ObjPtr(oUnk)
        Set oReturn = oUnk
        
    End Sub
    
    Sub Test()
        Dim oReturn As IUnknown
        TestMe 'oReturn
        Debug.Print "oReturn: "; ObjPtr(oReturn)
    End Sub
    For me that shortcircuits TestMe when no optional parameter is passed,.

  6. #6

    Thread Starter
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Unique Scenario: Passing IUnknown as optional parameter

    @jpbro. Yes, but if the interface was requested by the caller, then they wouldn't get the actual interface passed through that optional parameter; they would get a reference to its identity IUnknown instead.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  7. #7
    PowerPoster
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    2,412

    Re: Unique Scenario: Passing IUnknown as optional parameter

    Ahh that was point #2, I think I understand a bit better now..Probably out of my league, but I'll play with it and see if I can come up with anything.

  8. #8

    Thread Starter
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Unique Scenario: Passing IUnknown as optional parameter

    Quote Originally Posted by jpbro View Post
    ... but I'll play with it and see if I can come up with anything.
    Don't spend too much time messing with it. If dilettante or The Trick pops in, they may know the answer right off (if there's an answer).

    Any routines I write for VTable-only interfaces always perform a QueryInterface on the IUnknown passed to my routines to ensure it implements the interface that routine was designed for. In this case, I was more 'concerned' about the caller not realizing this and expecting something else. Would rather give them what they want vs an indirect reference to what they want. Also, since these interfaces are likely not to be directly accessed by the user, rather passed to APIs or other custom routines written by me, this 'problem' really isn't a problem. But would be nice to know if there is a solution to this specific scenario that gets me exactly what I want.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  9. #9
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Unique Scenario: Passing IUnknown as optional parameter

    Here's another idea, but it's also a bit kludgy. You could possibly user VarPtr and compare the proximity of two variables. Something along these lines:

    Code:
    
    Option Explicit
    
    Sub Test(Optional ByRef Maybe As Long, Optional ByRef NeverPassThis As Long)
        Debug.Print VarPtr(Maybe), VarPtr(NeverPassThis)
    End Sub
    
    
    If they're not precisely 4 apart, then "Maybe" should have been passed in.

    EDIT1: I'm also wondering if the same idea would work with a variable just declared at the top of the function, rather than a second Optional variable. Running out of time for now though. Good Luck.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  10. #10

    Thread Starter
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Unique Scenario: Passing IUnknown as optional parameter

    Well, here's some experimenting with Variant (as the optional parameter type)

    In this line: Set oReturn = oUnk
    I tested the objptr placed into the variant using CopyMemory to retrieve the far pointer referenced by the Variant. It's same as oUnk, not the identity IUnknown. However, once that reference is extracted from the Variant, that's when we get the identity IUnknown. I think what is happening is that VB is performing a QueryInterface on that reference, looking for IUnknown because: Dim oReturn As IUnknown

    So, looks like I'm not gonna get exactly what I'd like. No biggie, just a little disappointing.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  11. #11

    Thread Starter
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: [RESOLVED] Unique Scenario: Passing IUnknown as optional parameter

    Oh and for Elroy and anyone else that may be curious.

    I think I'll go this route: pass the optional parameter as a pointer. The modified code is shown below. Also note that I modified one of the API parameters from IUnknown to Long.
    Code:
    Private Declare Function SHILCreateFromPath Lib "shell32.dll" (ByVal pszPath As Long, ByRef pidl As Long, ByVal pAttrs As Long) As Long
    Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal pv As Long)
    Private Declare Function SHCreateShellItemArrayFromIDLists Lib "shell32" (ByVal cidl As Long, ByVal rgpidl As Long, ByVal ppsiItemArray As Long) As Long
    
    Private Sub TestMe(Optional ByVal oReturnPtr As Long)
        
        Debug.Assert oReturnPtr <> 0
        
        Dim pidl As Long
        
        SHILCreateFromPath StrPtr("C:\Windows\winhlp32.exe"), pidl, 0&
            Debug.Assert pidl <> 0
        SHCreateShellItemArrayFromIDLists 1, VarPtr(pidl), oReturnPtr
        CoTaskMemFree pidl
        
    End Sub
    
    Private Sub Form_Load()
    
        Dim oReturn As IUnknown
        TestMe VarPtr(oReturn)
        Debug.Print "oReturn: "; ObjPtr(oReturn)
        Unload Me
    
    End Sub
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  12. #12
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,673

    Re: [RESOLVED] Unique Scenario: Passing IUnknown as optional parameter

    Maybe pass a pointer to object variable?
    Code:
    Private Declare Function GetMem4 Lib "msvbvm60" (src As Any, dst As Any) As Long
    
    
    Private Sub TestMe(Optional ByVal ppObj As Long)
        Dim pObj    As IUnknown
    
        If ppObj = 0 Then
            Debug.Print "Not passed"
        Else
            
            GetMem4 ByVal ppObj, pObj
    
            If pObj Is Nothing Then
                Debug.Print "Passed Nothing or uninitialized variable"
            Else
                Debug.Print "Passed initialized variable"
                Set pObj = Nothing
                GetMem4 0&, ByVal ppObj
            End If
                
            Dim pidl As Long
        
            SHILCreateFromPath StrPtr("C:\Windows\winhlp32.exe"), pidl, 0&
                Debug.Assert pidl <> 0
            SHCreateShellItemArrayFromIDLists 1, VarPtr(pidl), pObj
            If pidl Then CoTaskMemFree pidl
                Debug.Assert Not pObj Is Nothing
        
            Debug.Print "pUnk: "; ObjPtr(pObj)
            
            GetMem4 pObj, ByVal ppObj
            GetMem4 0&, pObj
            
        End If
    
    End Sub
    
    Private Sub Form_Load()
    
        Dim oReturn As IUnknown
        
        Set oReturn = New Collection
        
        Debug.Print ObjPtr(oReturn)
        
        TestMe VarPtr(oReturn)
        
        Debug.Print ObjPtr(oReturn)
        
        TestMe
        
        TestMe VarPtr(Nothing)
        
    End Sub

  13. #13
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: [RESOLVED] Unique Scenario: Passing IUnknown as optional parameter

    this is how i do it. As you can see, I'm not too worried about whether or not the optional oReturn is passed. VB will clean up the references for us.

    code was equivalent to your option #1... interesting.


    yeah I don't see a way other than passing a pointer...

    Code:
    Private Declare Function SHILCreateFromPath Lib "shell32.dll" (ByVal pszPath As Long, ByRef pidl As Long, ByVal pAttrs As Long) As Long
    Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal pv As Long)
    Private Declare Function SHCreateShellItemArrayFromIDLists Lib "shell32" (ByVal cidl As Long, ByVal rgpidl As Long, ppsiItemArray As IUnknown) As Long
    Private Declare Function vbaObjSetAddref Lib "msvbvm60" Alias "__vbaObjSetAddref" (ByVal ObjPtr As Long, ByVal NewPtr As Long) As Long
    
    Private Sub TestMe(Optional pReturn As Long)
        
        Dim oUnk As IUnknown
        Dim pidl As Long
        
        SHILCreateFromPath StrPtr("C:\Windows\winhlp32.exe"), pidl, 0&
            Debug.Assert pidl <> 0
        SHCreateShellItemArrayFromIDLists 1, VarPtr(pidl), oUnk
        If pidl Then CoTaskMemFree pidl
            Debug.Assert Not oUnk Is Nothing
        
        
        Debug.Print "oUnk: "; ObjPtr(oUnk)
        
        If pReturn Then vbaObjSetAddref pReturn, ObjPtr(oUnk)
    End Sub
    
    Private Sub Form_Load()
    
        Dim oReturn As IUnknown
        TestMe VarPtr(oReturn)
        Debug.Print "oReturn: "; ObjPtr(oReturn)
        Unload Me
    
    End Sub
    I think vbaObjSetAddref already checks if you're passing a populated object pointer, and frees it automatically - but I'd have to double check.
    Last edited by DEXWERX; Jan 17th, 2018 at 08:17 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width