从接口指针获取接口/对象的正确方法?
Correct way to get an interface / object from an interface pointer?
直到现在我对接口一无所知,我很难尝试实际实施给 的解决方案。
我认为我正确地获得了指向 IShellFolder 接口的指针,但我似乎无法实际使用该指针。有了那个指针,我如何到达可用的界面?
下面的代码显示了我在做什么以及我怀疑哪里有问题。
首先,我的界面声明:
' I have wrapped the interfaces in their own namespace, "BinarusShell".
' I have declared every interface's functions / subs in the same order as they are in the .idl files of the Windows Platform SDK 7.1.
' Every function / sub has the <PreserveSig()> attribute so that I can see the native return values when debugging.
' I'd like to keep the question as lean as possible, so I am showing only a few functions per interface.
' Of course, I have implemented all of them (as they are in the respective .idl file).
Namespace BinarusShell
<ComImport()> _
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
<Guid("000214FA-0000-0000-C000-000000000046")> _
Public Interface IExtractIcon
' The above GUID is the GUID for the UNICODE version of the interface, i.e. IExtractIconW.
' Therefore, the UNICODE variant of strings is used in the parameters of the following functions.
' See also the include files of the Windows SDK where this is done exactly that way as well.
<PreserveSig()> Function GetIconLocation(ByVal uFlags As UInteger,
<MarshalAs(UnmanagedType.LPWStr, SizeParamIndex:=2)> ByVal pszIconFile As String,
ByVal cchmax As UInteger,
ByRef piIndex As Integer,
ByRef pwFlags As UInteger) As Integer
' ...
' Here comes the second function (IExtractIcon has only two) ...
' ...
End Interface
<ComImport()> _
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
<Guid("000214E6-0000-0000-C000-000000000046")> _
Public Interface IShellFolder
<PreserveSig()> Function GetUIObjectOf(<[In]()> ByVal hwndOwner As IntPtr,
<[In]()> ByVal cidl As UInteger,
<[In]()> <MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> ByVal apidl() As IntPtr,
<[In]()> <MarshalAs(UnmanagedType.LPStruct)> ByVal riid As Guid,
<[In](), Out()> ByRef rgfReserved As UInteger,
<Out()> ByRef ppv As IntPtr) As Integer
' ...
' Here come the other functions ...
' ...
End Interface
End Namespace
其次,我的 Windows API 声明,以及
三、我要实现的功能(这是我卡的地方):
' The Windows API declarations are in class Win32Api.
' This class is not wrapped in a certain namespace.
' To keep this question as lean as possible, I am leaving out my declarations of constants or GUIDs.
' I have taken all of them literally from the Windows Platform SDK 7.1.
Public Class Win32Api
<DllImport("shell32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SHBindToParent(<[In]()> ByVal pidl As IntPtr,
<[In]()> <MarshalAs(UnmanagedType.LPStruct)> ByVal riid As Guid,
<Out()> ByRef ppv As IntPtr,
<Out()> ByRef ppidlLast As IntPtr) As Integer
End Function
<DllImport("shell32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SHGetKnownFolderIDList(<[In]()> <MarshalAs(UnmanagedType.LPStruct)> ByVal rfid As Guid,
<[In]()> ByVal dwFlags As UInteger,
<[In]()> ByVal hToken As IntPtr,
<Out()> ByRef ppidl As IntPtr) As Integer
End Function
' -----------------------------------------------------
' The function I would like to implement is in class Win32Api as well.
' The function at this stage does nothing which is useful for the outer world;
' It is just meant for code which I am stepping through with the debugger.
' The comments below show the result of every step and my understanding about what has happened.
Public Shared Function IconTest() As Boolean
Dim ui_ReturnFlags As UInteger
Dim i_Result As Integer
Dim intptr_CurrentPidlAbsolute As IntPtr,
intptr_ParentIShellFolder As IntPtr,
intptr_CurrentIExtractIcon As IntPtr
Dim arintptr_RelPidList(0) As IntPtr
Dim ishellfolder_Parent As BinarusShell.IShellFolder
Dim iextracticon_Extractor As BinarusShell.IExtractIcon
i_Result = SHGetKnownFolderIDList(FOLDERID_System, 0, 0, intptr_CurrentPidlAbsolute)
' The above seems to work. i_Result is 0 which means success, and
' intptr_CurrentPidlAbsolute has a reasonable value now.
i_Result = SHBindToParent(intptr_CurrentPidlAbsolute, IID_IShellFolder, intptr_ParentIShellFolder, arintptr_RelPidList(0))
' The above seems to work. i_Result is 0 which means success, and
' intptr_ParentIShellFolder and arintptr_RelPidList(0) have reasonable values now.
' Now the PROBLEMS begin. I have no clue how to get an interface / object
' from intptr_ParentIShellFolder which I can use from within VB.net.
' Therefore, I just have tried the following (but believing that it is wrong):
ishellfolder_Parent = DirectCast(Marshal.GetObjectForIUnknown(intptr_ParentIShellFolder), BinarusShell.IShellFolder)
' This does not throw an error, but I have no clue what actually is happening.
' Marshal.GetObjectForIUnknown, as the name implies, should return an object which
' encapsulates an IUnknown interface, but I am giving a pointer to an IShellFolder
' interface, so I really wonder why it doesn't freak out. The original idea
' to try it that way was that I believe that IShellFolder inherits IUnknown,
' so it *eventually* could work that way. Could somebody comment about that?
i_Result = ishellfolder_Parent.GetUIObjectOf(0, 1, arintptr_RelPidList, IID_IExtractIcon, ui_ReturnFlags, intptr_CurrentIExtractIcon)
' Now this is the point where I am completely puzzled. i_Result is still 0,
' which means success, but intptr_CurrentIExtractIcon is 0 as well, meaning
' that something has gone horribly wrong. According to my understanding of
' the MSDN documentation, GetUIObjectOf MUST NOT return S_OK if something
' has gone wrong; in other words, if it returns S_OK, then intptr_CurrentIExtractIcon
' MUST be set to a reasonable value. Could somebody explain what's happening?
' I have left out the rest of the function because it wouldn't make any sense
' to use the NULL pointer for any further action.
End Function
End Class
知道我做错了什么吗?从文档中,我得到的印象是 每个 文件夹项目(虚拟与否)和文件公开 IExtractIcon。
回答我自己的问题:在 VB.net 中定义 IShellFolder
接口时,我忘记包含 BindToFolder()
函数(你真的不应该尝试在 03:00 a.m.,特别是如果你在 1 小时前才知道这些事情的话)。
当然,正如随处可见的那样,这使得 IShellFolder
接口产生了错误的结果(再次不知道我在说什么,因为 BindToFolder()
在 GetUIObjectOf()
之前接口定义,后者依次出现在 GetDisplayNameOf()
之前,我可以想象当我的代码调用 GetUIObjectOf()
时实际上已经调用了 GetDisplayNameOf()
,这可以解释奇怪的结果)。
现在我已经更正了我的 IShellFolder
定义(即我添加了缺失的函数)并仔细检查了我的其他接口定义(那些没有错误),一切都按预期工作。
直到现在我对接口一无所知,我很难尝试实际实施给
我认为我正确地获得了指向 IShellFolder 接口的指针,但我似乎无法实际使用该指针。有了那个指针,我如何到达可用的界面?
下面的代码显示了我在做什么以及我怀疑哪里有问题。
首先,我的界面声明:
' I have wrapped the interfaces in their own namespace, "BinarusShell".
' I have declared every interface's functions / subs in the same order as they are in the .idl files of the Windows Platform SDK 7.1.
' Every function / sub has the <PreserveSig()> attribute so that I can see the native return values when debugging.
' I'd like to keep the question as lean as possible, so I am showing only a few functions per interface.
' Of course, I have implemented all of them (as they are in the respective .idl file).
Namespace BinarusShell
<ComImport()> _
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
<Guid("000214FA-0000-0000-C000-000000000046")> _
Public Interface IExtractIcon
' The above GUID is the GUID for the UNICODE version of the interface, i.e. IExtractIconW.
' Therefore, the UNICODE variant of strings is used in the parameters of the following functions.
' See also the include files of the Windows SDK where this is done exactly that way as well.
<PreserveSig()> Function GetIconLocation(ByVal uFlags As UInteger,
<MarshalAs(UnmanagedType.LPWStr, SizeParamIndex:=2)> ByVal pszIconFile As String,
ByVal cchmax As UInteger,
ByRef piIndex As Integer,
ByRef pwFlags As UInteger) As Integer
' ...
' Here comes the second function (IExtractIcon has only two) ...
' ...
End Interface
<ComImport()> _
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
<Guid("000214E6-0000-0000-C000-000000000046")> _
Public Interface IShellFolder
<PreserveSig()> Function GetUIObjectOf(<[In]()> ByVal hwndOwner As IntPtr,
<[In]()> ByVal cidl As UInteger,
<[In]()> <MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> ByVal apidl() As IntPtr,
<[In]()> <MarshalAs(UnmanagedType.LPStruct)> ByVal riid As Guid,
<[In](), Out()> ByRef rgfReserved As UInteger,
<Out()> ByRef ppv As IntPtr) As Integer
' ...
' Here come the other functions ...
' ...
End Interface
End Namespace
其次,我的 Windows API 声明,以及 三、我要实现的功能(这是我卡的地方):
' The Windows API declarations are in class Win32Api.
' This class is not wrapped in a certain namespace.
' To keep this question as lean as possible, I am leaving out my declarations of constants or GUIDs.
' I have taken all of them literally from the Windows Platform SDK 7.1.
Public Class Win32Api
<DllImport("shell32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SHBindToParent(<[In]()> ByVal pidl As IntPtr,
<[In]()> <MarshalAs(UnmanagedType.LPStruct)> ByVal riid As Guid,
<Out()> ByRef ppv As IntPtr,
<Out()> ByRef ppidlLast As IntPtr) As Integer
End Function
<DllImport("shell32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SHGetKnownFolderIDList(<[In]()> <MarshalAs(UnmanagedType.LPStruct)> ByVal rfid As Guid,
<[In]()> ByVal dwFlags As UInteger,
<[In]()> ByVal hToken As IntPtr,
<Out()> ByRef ppidl As IntPtr) As Integer
End Function
' -----------------------------------------------------
' The function I would like to implement is in class Win32Api as well.
' The function at this stage does nothing which is useful for the outer world;
' It is just meant for code which I am stepping through with the debugger.
' The comments below show the result of every step and my understanding about what has happened.
Public Shared Function IconTest() As Boolean
Dim ui_ReturnFlags As UInteger
Dim i_Result As Integer
Dim intptr_CurrentPidlAbsolute As IntPtr,
intptr_ParentIShellFolder As IntPtr,
intptr_CurrentIExtractIcon As IntPtr
Dim arintptr_RelPidList(0) As IntPtr
Dim ishellfolder_Parent As BinarusShell.IShellFolder
Dim iextracticon_Extractor As BinarusShell.IExtractIcon
i_Result = SHGetKnownFolderIDList(FOLDERID_System, 0, 0, intptr_CurrentPidlAbsolute)
' The above seems to work. i_Result is 0 which means success, and
' intptr_CurrentPidlAbsolute has a reasonable value now.
i_Result = SHBindToParent(intptr_CurrentPidlAbsolute, IID_IShellFolder, intptr_ParentIShellFolder, arintptr_RelPidList(0))
' The above seems to work. i_Result is 0 which means success, and
' intptr_ParentIShellFolder and arintptr_RelPidList(0) have reasonable values now.
' Now the PROBLEMS begin. I have no clue how to get an interface / object
' from intptr_ParentIShellFolder which I can use from within VB.net.
' Therefore, I just have tried the following (but believing that it is wrong):
ishellfolder_Parent = DirectCast(Marshal.GetObjectForIUnknown(intptr_ParentIShellFolder), BinarusShell.IShellFolder)
' This does not throw an error, but I have no clue what actually is happening.
' Marshal.GetObjectForIUnknown, as the name implies, should return an object which
' encapsulates an IUnknown interface, but I am giving a pointer to an IShellFolder
' interface, so I really wonder why it doesn't freak out. The original idea
' to try it that way was that I believe that IShellFolder inherits IUnknown,
' so it *eventually* could work that way. Could somebody comment about that?
i_Result = ishellfolder_Parent.GetUIObjectOf(0, 1, arintptr_RelPidList, IID_IExtractIcon, ui_ReturnFlags, intptr_CurrentIExtractIcon)
' Now this is the point where I am completely puzzled. i_Result is still 0,
' which means success, but intptr_CurrentIExtractIcon is 0 as well, meaning
' that something has gone horribly wrong. According to my understanding of
' the MSDN documentation, GetUIObjectOf MUST NOT return S_OK if something
' has gone wrong; in other words, if it returns S_OK, then intptr_CurrentIExtractIcon
' MUST be set to a reasonable value. Could somebody explain what's happening?
' I have left out the rest of the function because it wouldn't make any sense
' to use the NULL pointer for any further action.
End Function
End Class
知道我做错了什么吗?从文档中,我得到的印象是 每个 文件夹项目(虚拟与否)和文件公开 IExtractIcon。
回答我自己的问题:在 VB.net 中定义 IShellFolder
接口时,我忘记包含 BindToFolder()
函数(你真的不应该尝试在 03:00 a.m.,特别是如果你在 1 小时前才知道这些事情的话)。
当然,正如随处可见的那样,这使得 IShellFolder
接口产生了错误的结果(再次不知道我在说什么,因为 BindToFolder()
在 GetUIObjectOf()
之前接口定义,后者依次出现在 GetDisplayNameOf()
之前,我可以想象当我的代码调用 GetUIObjectOf()
时实际上已经调用了 GetDisplayNameOf()
,这可以解释奇怪的结果)。
现在我已经更正了我的 IShellFolder
定义(即我添加了缺失的函数)并仔细检查了我的其他接口定义(那些没有错误),一切都按预期工作。