|
-
Jun 24th, 2013, 02:17 AM
#1
[RESOLVED] Trying to implement IShellItem - where'd I go wrong?
Just on the off chance anyone around here is familiar with typelibs and interfaces...
Code:
[
odl,
uuid(43826D1E-E718-42EE-BC55-A1E261C37BFE)
]
interface IShellItem : stdole.IUnknown {
HRESULT BindToHandler(
[in] IBindCtx *pbc,
[in] UUID rbhid,
[in] UUID riid,
[out] void *ppvOut);
HRESULT Compare(
[in] IShellItem *psi,
[in] LONG hint,
[out, retval] LONG *piOrder);
HRESULT GetAttributes(
[in] SFGAO_Flags sfgaoMask,
[out, retval] SFGAO_Flags *psfgaoAttribs);
HRESULT GetDisplayName(
[in] SIGDN sigdnName,
[out, retval] LPWSTR *ppszName);
HRESULT GetParent(
[out] IShellItem *ppsi);
};
Public Declare Function SHCreateShellItem Lib "shell32" (ByVal pidlParent As Long, psfParent As Any, pidl As Long, ppsi As IShellItem)
Dim hRes As Long
Dim isi As IShellItem
hRes = SHCreateShellItem(0, ByVal 0&, ByVal GetPIDLFromPath(Me.hwnd, "C:\temp"), isi)
'lpw = isi.GetDisplayName(0)
n = isi.GetAttributes(SFGAO_CANRENAME Or SFGAO_CANDELETE)
MsgBox hRes & "|||" & CStr(n)
All the types are correctly defined (long, uuid, IBindCtx, IShellFolder)... because they're all parts of working implementations from the same typelib of IShellFolder et al. that I use in my project. I know maybe psfParent should be explicitly declared as IShellFolder, but MSDN says I can use either that or pass a fully-qualified pidl, which is what I was doing. The typelib compiles without error.
SHCreateShellItem is returning S_OK but fails to create the object (isi Is Nothing = True). If I pass a valid IShellFolder, VB crashes.
My eventual goal is getting to IThumbnailProvider, which I can't bind to from IShellFolder2.
For reference, this is the source from shodjidl.idl I translated from:
Code:
cpp_quote(" HRESULT (WINAPI *BindToHandler)(IShellItem *This,IBindCtx *pbc,REFGUID rbhid,REFIID riid,void **ppvOut);")
cpp_quote(" HRESULT (WINAPI *GetParent)(IShellItem *This,IShellItem **ppsi);")
cpp_quote(" HRESULT (WINAPI *GetDisplayName)(IShellItem *This,SIGDN sigdnName,LPOLESTR *ppszName);")
cpp_quote(" HRESULT (WINAPI *GetAttributes)(IShellItem *This,SFGAOF sfgaoMask,SFGAOF *psfgaoAttribs);")
cpp_quote(" HRESULT (WINAPI *Compare)(IShellItem *This,IShellItem *psi,SICHINTF hint,int *piOrder);")
-
Jun 24th, 2013, 03:53 AM
#2
Re: Trying to implement IShellItem - where'd I go wrong?
Your
Code:
ByVal GetPIDLFromPath(Me.hwnd, "C:\temp")
looks suspicious to me. Could you elaborate on this function?
What i could find is, that this function is used as a wrapper for "SHSimpleIDListFromPath"-API, and that one only has one argument
Last edited by Zvoni; Tomorrow at 31:69 PM.
----------------------------------------------------------------------------------------
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------------------
People call me crazy because i'm jumping out of perfectly fine airplanes.
---------------------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad
-
Jun 24th, 2013, 11:11 AM
#3
Re: Trying to implement IShellItem - where'd I go wrong?
 Originally Posted by Zvoni
Your
Code:
ByVal GetPIDLFromPath(Me.hwnd, "C:\temp")
looks suspicious to me. Could you elaborate on this function?
What i could find is, that this function is used as a wrapper for "SHSimpleIDListFromPath"-API, and that one only has one argument
It returns a fully qualified pidl from a path. It works fine, I use it hundreds of times throughout the project.
-
Jun 24th, 2013, 08:11 PM
#4
Re: Trying to implement IShellItem - where'd I go wrong?
I see a few things, which are wrong.
- the VTable-Order is incorrect (please stick with the order of the C++-original)
- the Declare returns a Variant (not the usual 32Bit-Long for the HResult).
- only potentially a problem: make sure, you pass an absolute PIDL in case the two leading (Parent-) Parameters remain at zero.
(not sure what your GetPIDLPath - function wraps under the hood)
To keep the VTable-order of the method-signatures correct, you cannot rely on the MSDN - they sort alphabetically, which is almost always the wrong order.
I usually take a good look into e.g. the Wine-Implementations (or -Documentation), where the real VTable-Order can be seen:
http://fossies.org/dox/wine-1.4.1/in...ShellItem.html
usage of the cShellItem-Class I've put together (class-code comes further below):
vb Code:
Option Explicit Private Sub Form_Load() Dim SI1 As New cShellItem, SI2 As New cShellItem SI1.InitFromPath "D:\Test" SI2.InitFromPath "D:\Test" Debug.Print SI1.GetDisplayName() 'Test Debug.Print SI1.GetParent.GetDisplayName() 'Data (D:) Debug.Print SI1.GetParent.GetParent.GetDisplayName() 'Computer Debug.Print SI1.GetParent.GetParent.GetParent.GetDisplayName() 'Desktop Debug.Print SI1.GetAttributes(SFGAO_CANCOPY) = SFGAO_CANCOPY Debug.Print SI1.Compare(SI2, SICHINT_ALLFIELDS) = 0 '0 means the Items are equal End Sub
as said, now the demo-code for a small cShellItem-Class, which doesn't use a TypeLib, but is directly VTable-bound
vb Code:
'Into a Class, named: cShellItem Public Enum enmSIGDN SIGDN_NORMALDISPLAY = &H0 SIGDN_PARENTRELATIVEPARSING = &H80018001 SIGDN_DESKTOPABSOLUTEPARSING = &H80028000 SIGDN_PARENTRELATIVEEDITING = &H80031001 SIGDN_DESKTOPABSOLUTEEDITING = &H8004C000 SIGDN_FILESYSPATH = &H80058000 SIGDN_URL = &H80068000 SIGDN_PARENTRELATIVEFORADDRESSBAR = &H8007C001 SIGDN_PARENTRELATIVE = &H80080001 SIGDN_PARENTRELATIVEFORUI = &H80094001 End Enum Public Enum enmSICHINTF SICHINT_DISPLAY = &H0 SICHINT_ALLFIELDS = &H80000000 SICHINT_CANONICAL = &H10000000 SICHINT_TEST_FILESYSPATH_IF_NOT_EQUAL = &H20000000 End Enum Public Enum enmSFGAO SFGAO_CANCOPY = &H1 SFGAO_CANMOVE = &H2 SFGAO_CANLINK = &H4 SFGAO_STORAGE = &H8 SFGAO_CANRENAME = &H10 SFGAO_CANDELETE = &H20 SFGAO_HASPROPSHEET = &H40 SFGAO_DROPTARGET = &H100 SFGAO_CAPABILITYMASK = &H177 SFGAO_SYSTEM = &H1000 SFGAO_ENCRYPTED = &H2000 SFGAO_ISSLOW = &H4000 SFGAO_LINK = &H10000 SFGAO_SHARE = &H20000 SFGAO_READONLY = &H40000 SFGAO_GHOSTED = &H8000 SFGAO_HIDDEN = &H80000 SFGAO_DISPLAYATTRMASK = &HFC000 SFGAO_NONENUMERATED = &H100000 SFGAO_NEWCONTENT = &H200000 SFGAO_STREAM = &H400000 SFGAO_VALIDATE = &H1000000 SFGAO_REMOVABLE = &H2000000 SFGAO_COMPRESSED = &H4000000 SFGAO_BROWSABLE = &H8000000 SFGAO_FILESYSANCESTOR = &H10000000 SFGAO_STORAGEANCESTOR = &H800000 SFGAO_FOLDER = &H20000000 SFGAO_FILESYSTEM = &H40000000 SFGAO_HASSUBFOLDER = &H80000000 SFGAO_CONTENTSMASK = &H80000000 SFGAO_STORAGECAPMASK = &H70C50008 SFGAO_PKEYSFGAOMASK = &H81044000 End Enum Private Enum enmIShellItem 'this is the VTbl-Idx-Enum, used in the Private Helper-Func vtblcall, further below 'IUnknown isi_QueryInterface isi_AddRef isi_Release 'IShellItem ...definitions and vtable-order from here: [url]http://fossies.org/dox/wine-1.4.1/interfaceIShellItem.html[/url] isi_BindToHandler '([in] IBindCtx *pbc,[in] REFGUID rbhid,[in] REFIID riid,[out, iid_is(riid)] void **ppvOut) isi_GetParent '([out] IShellItem **ppsi) isi_GetDisplayName '([in] SIGDN sigdnName,[out] LPWSTR *ppszName) isi_GetAttributes '([in] SFGAOF sfgaoMask,[out] SFGAOF *psfgaoAttribs) isi_Compare '([in] IShellItem *psi,[in] SICHINTF hint,[out] int *piOrder) End Enum Private Declare Function SysReAllocString Lib "oleaut32" (ByVal pBSTR&, ByVal lpWStr&) As Long Private Declare Function DispCallFunc& Lib "oleaut32" (ByVal ppv&, ByVal oVft&, _ ByVal CC&, ByVal rtTYP%, ByVal paCount&, paTypes%, paValues&, fuReturn) Private Declare Sub CoTaskMemFree Lib "ole32" (ByVal pv As Long) Private Declare Function ILCreateFromPathW Lib "shell32" (ByVal pszPath As Long) As Long Private Declare Sub ILFree Lib "shell32" (ByVal pIDL As Long) Private Declare Function SHCreateShellItem Lib "shell32" (ByVal pidlParent As Long, _ ByVal psfParent As Long, ByVal pIDL As Long, ppsi As stdole.IUnknown) As Long Private mUnk As stdole.IUnknown, pUnk As Long, mPathName As String, pIDLAbs As Long, HRes As Long Public Function InitFromPath(PathName As String) Cleanup mPathName = PathName pIDLAbs = ILCreateFromPathW(ByVal StrPtr(mPathName)) HRes = SHCreateShellItem(0, 0, pIDLAbs, mUnk) pUnk = ObjPtr(mUnk) If HRes Then Err.Raise HRes End Function Friend Sub InitFromParentItem(ParentShellItem As stdole.IUnknown) Cleanup Set mUnk = ParentShellItem pUnk = ObjPtr(mUnk) End Sub Public Property Get PathName() As String PathName = mPathName End Property Public Property Get ShellItemUnk() As stdole.IUnknown Set ShellItemUnk = mUnk End Property 'after the few additional Public Props above, 'now the real IShellItem interface-implementation ' 'Public Function BindToHandler(...) '<- not yet implemented Public Function GetParent() As cShellItem Dim ParentUnk As stdole.IUnknown HRes = vtblCall(isi_GetParent, VarPtr(ParentUnk)) If HRes Then Err.Raise HRes If ParentUnk Is Nothing Then Exit Function Set GetParent = New cShellItem GetParent.InitFromParentItem ParentUnk End Function Public Function GetDisplayName(Optional ByVal SIGDN As enmSIGDN) As String Dim pS As Long HRes = vtblCall(isi_GetDisplayName, SIGDN, VarPtr(pS)) If HRes Then Err.Raise HRes SysReAllocString VarPtr(GetDisplayName), pS If pS Then CoTaskMemFree pS End Function Public Function GetAttributes(ByVal SFGAO As enmSFGAO) As Long HRes = vtblCall(isi_GetAttributes, SFGAO, VarPtr(GetAttributes)) If HRes Then Err.Raise HRes End Function Public Function Compare(OtherItem As cShellItem, Optional ByVal SICHINTF As enmSICHINTF) As Long HRes = vtblCall(isi_Compare, ObjPtr(OtherItem.ShellItemUnk), SICHINTF, VarPtr(Compare)) If HRes <> 0 And HRes <> 1 Then Err.Raise HRes End Function 'vtblcall-by-index-> Helper-function Private Function vtblCall(ByVal vtblIdx As enmIShellItem, ParamArray P()) Static VType(0 To 5) As Integer, VPtr(0 To 5) As Long Const CC_CDECL& = 1, CC_STDCALL& = 4 Dim i As Long, V(), HResDisp As Long If pUnk = 0 Then Exit Function V = P 'make a copy of the params, to prevent problems with VT_ByRef-Members in the ParamArray For i = 0 To UBound(V) VType(i) = VarType(V(i)) VPtr(i) = VarPtr(V(i)) Next i HResDisp = DispCallFunc(pUnk, vtblIdx * 4, CC_STDCALL, vbLong, i, VType(0), VPtr(0), vtblCall) If HResDisp Then Err.Raise HResDisp, , "Error in DispCallFunc" End Function Private Sub Cleanup() Set mUnk = Nothing: pUnk = 0 If pIDLAbs Then ILFree pIDLAbs: pIDLAbs = 0 End Sub Private Sub Class_Terminate() Cleanup End Sub
Olaf
Last edited by Schmidt; Jun 25th, 2013 at 06:07 AM.
Reason: small correction in cShellItem (forgot to cleanup a String-Handle)
-
Jun 24th, 2013, 09:31 PM
#5
Re: Trying to implement IShellItem - where'd I go wrong?
Thanks, very informative! I didn't know the VTable order mattered/was different from MSDN. I'm definitely on the right track now. It is successfully creating the item, and GetAttributes works fine, and now I'm on to display name.
So I can't pass the string directly for GetDisplayName? It seems to work ok,
Code:
Call isi.GetDisplayName(SIGDN_FILESYSPATH, lpw)
MsgBox lpw
Displays the correct name, but then VB crashes after I click OK. I only attempt doing it this way because it's most similar to how I use all the other interfaces.
Most importantly- BindToHandler requires IBindCtx? Do you know anything about what I have to set in that? MSDN says it's just for optional things, would it be needed for IThumbnailProvider or IEnumShellItems?
Also for reference,
Code:
Public Function GetPIDLFromPath(hwnd As Long, sPath As String) As Long
Dim pchEaten As Long
Dim pidl As Long
' Doesn't check the validity of the path as SUCCEEDED
' handles a failure from ParseDisplayName...
If SUCCEEDED(isfDesktop.ParseDisplayName(hwnd, 0, StrConv(sPath, vbUnicode), pchEaten, pidl, 0)) Then
GetPIDLFromPath = pidl
End If
End Function
Just for reference, since it's creating the correct IShellItem it works, as with many other places it's used.
Last edited by fafalone; Jun 24th, 2013 at 09:34 PM.
-
Jun 25th, 2013, 04:42 AM
#6
Re: Trying to implement IShellItem - where'd I go wrong?
As for the GetDisplayName-crash.
The MSDN states, that this is not a BSTR which is returned, so you can't "map" or "pass" it into the VB-String-Type directly.
They also make clear that:
"It is the responsibility of the caller to free the string pointed to by ppszName when it is no longer needed. Call CoTaskMemFree on *ppszName to free the memory."
Best to redefine your Typelib-Definition to a VB-Long - and handle it similar to what I do in the cShellItem-Class.
Maybe rip-out the code and make a small function from it:
Function BStrFromLPWStr(lpWStr As Long, Optional ByVal CleanupLPWStr As Boolean = True) As String
SysReAllocString VarPtr(BStrFromLPWStr), lpWStr
If CleanupLPWStr Then CoTaskMemFree lpWStr
End Function
As for the BindToHandler method - never used that - and also no experience with IBindCtx.
What do you want to achieve in the end, a Thumbnail-Preview?
Olaf
-
Jun 25th, 2013, 12:36 PM
#7
Re: Trying to implement IShellItem - where'd I go wrong?
Thanks, that worked without the crash. I'm working on thumbnail preview yeah, but I specifically want to try using IThumbnailProvider and IShellImageFactory.
-
Jun 26th, 2013, 10:38 PM
#8
Re: Trying to implement IShellItem - where'd I go wrong?
Ok, maybe you can help with this.
Code:
[
odl,
uuid(BCC18B79-BA16-442F-80C4-8A59C30C463B)
]
interface IShellItemImageFactory : stdole.IUnknown {
HRESULT GetImage(
[in] SIZE *size,
[in] SIIGBF flags,
[in, out] LONG* phbm);
};
Public Declare Function SHCreateItemFromIDList Lib "shell32" (ByVal pidl As Long, REFIID As UUID, ppv As Any) As Long
pidlFQ = GetPIDLFromPath(Me.hwnd, "C:\temp")
hRes = SHCreateItemFromIDList(pidlFQ, IID_IShellItemImageFactory(), isif)
It works up to this point. hRes is S_OK and isif Is Nothing =False.
But when I call isif.GetImage, VB immediately crashes. I tried SIZE* size and SIZE *size, same crash, and without a *, how I thought it should be, I get 'can't pass user type byval'. I also tried making it just [out] LONG* phbm and no effect. Also tried explicitly setting the return type instead of Any.
Last edited by fafalone; Jun 26th, 2013 at 11:03 PM.
-
Jun 26th, 2013, 11:33 PM
#9
Re: Trying to implement IShellItem - where'd I go wrong?
Is IID_IShellItemImageFactory() an array? Shouldn't it be a UUID UDT?
On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)
-
Jun 27th, 2013, 04:02 PM
#10
Re: Trying to implement IShellItem - where'd I go wrong?
 Originally Posted by Bonnie West
Is IID_IShellItemImageFactory() an array? Shouldn't it be a UUID UDT?
It's a UUID, it's a function call defined the same way as IID_IShellFolder and a couple other interfaces I'm using.
Code:
Public Function IID_IShellItemImageFactory() As UUID
'{BCC18B79-BA16-442F-80C4-8A59C30C463B}
Static iid As UUID
If (iid.Data1 = 0) Then Call DEFINE_UUID(iid, &HBCC18B79, CInt(&HBA16), CInt(&H442F), &H80, &HC4, &H8A, &H59, &HC3, &HC, &H46, &H3B)
IID_IShellItemImageFactory = iid
End Function
Last edited by fafalone; Jun 27th, 2013 at 11:30 PM.
-
Jun 28th, 2013, 01:04 AM
#11
Re: Trying to implement IShellItem - where'd I go wrong?
So I tried using your vTable call method to see if I could get it to work that way, but alas VB immediately crashes again.
Code:
Private Enum enmIShellItemImageFactory
'IUnknown
isi_QueryInterface
isi_AddRef
isi_Release
isi_GetImage
End Enum
Public Function GetImage(aa As Long, bb As Long, ByVal flags As enmSIIGBF) As Long
Dim sz As sizeV
sz.cx = aa
sz.cy = bb
HRes = vtblCall(isi_GetImage, VarPtr(sz), flags, VarPtr(GetImage))
Debug.Print "vtbl hres=" & HRes
End Function
I might be passing the size type wrong, but I have no idea how to do it. Without varptr i get a compile error saying only public user types from public user modules can be passed. Even though it IS a public defined type in a public user module.
-
Jun 28th, 2013, 03:32 AM
#12
Re: Trying to implement IShellItem - where'd I go wrong?
If i understand the vtbCall-Function correctly you're passing a pointer to a pointer to DispCallFunc
Last edited by Zvoni; Tomorrow at 31:69 PM.
----------------------------------------------------------------------------------------
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------------------
People call me crazy because i'm jumping out of perfectly fine airplanes.
---------------------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad
-
Jun 28th, 2013, 07:09 AM
#13
Re: Trying to implement IShellItem - where'd I go wrong?
@fafalone
Before using the vtblCall, did you made sure, that the (class-level) Variables Unk and pUnk were filled?
Maybe it is better, to put the vtblCall-function into a *.bas-Module, so that it can be shared by more than one "Implementer-Class".
What would then be needed, is the passing of the formerly class-level Variable pUnk as a Parameter - but this'd make the
call-definition more "speaking" (what's needed), not hiding important things...
e.g. with a signature like this:
Public Function vtblCall(vtblIdx As Long, pUnk As Long, ParamArray P())
You could move it "out of the implementing class (and put it into a *.bas-Module).
Since ShellItem-wrapping (with all bells and whistles) is new to me (but interesting), maybe just wait
a bit more - I'm already playing with it and work with a small example, which exposes a bit more and
will wrap also IShellItemFactory in a small class - from a first view into the API-Def for its GetImage-
Method, it seems that the [Size sz] param needs to be passed ByValue - so you will have to copy it over
into an 8Byte-sized "ByVal transport-container" (as e.g. a Currency or a Double) - or just pass it
on the stack as two (isolated) Parameters (that will work too, since the Alignment of Stacked Params
for Windows-StdCalls is 4Byte).
@Zvoni
The DispCallFunc is basically the "generic and official Function-Caller by pointer", every VB-Dev was
always looking for (often resorting to CallWindowProc- or ASM-Hacks instead) - not sure who needs
to be given the credits for the first working VB-example (wasn't me, found it not that long ago
in a german forum the first time)... googling - ... - well, seems that the first "working VB-example"
is from 2003: http://www.activevb.de/tipps/vb6tipps/tipp0600.html
I was using Paul Catons stuff before - but since there was this nice explicit Parameter CC_STDCALL
I googled a bit more and found a whole Enum-List:
Public Enum CallingConventions
CC_FASTCALL
CC_CDECL
CC_PASCAL
CC_MACPASCAL
CC_STDCALL
CC_RESERVED
CC_SYSCALL
CC_MPWCDECL
CC_MPWPASCAL
End Enum
More googling and experimenting revealed, that the Function-Pointer is internally resolved by:
dereferencedResultOfParam_ppv + Vtbl_Offset
And (luckily) passing a Null to ppV - and the direct Function-Pointer in the Vtbl-Offset-Param,
one can do also all kind of "normal API-Calls" (not only Disp- or COM-Calls).
And in this "direct FuncPtr-Mode", the above Enum comes in very handy - and is working as
expected.
Here's a small Demo, which shows all 3 "Modes", in which DispCallFunc can be used now with
VB (StdCall-, CDECL- calls and of course also COM- (VTbl-) Calls.
http://www.vbRichClient.com/Download...InvokeDemo.zip
Olaf
-
Jun 29th, 2013, 04:13 AM
#14
Re: Trying to implement IShellItem - where'd I go wrong?
In the mean time, do you know any C++? I need a temporary workaround so was just going to make a dll to do it.
But I keep getting "unresolved external symbol" for ImageList_Add, even though windows.h and commctrl.h are included.
-
Jun 30th, 2013, 01:00 AM
#15
Re: Trying to implement IShellItem - where'd I go wrong?
Well I still can't get it working in VB. But I now have a workaround that works perfectly. It's actually pretty simple. I know absolutely nothing about C++ and was able to figure this out from an example in a couple hours.
First, need a DLL:
Code:
// RNThumb.cpp : Retrieves a thumbnail from IShellItemImageFactory and adds it to the specified imagelist
//
#include "stdafx.h"
#include "RNThumb.h"
#include <stdexcept>
//AddTohIML - Adds the thumbnail for a file to the given imagelist
//Inputs: Imagelist, absolute pidl for a file, and size of thumbnail
//Outputs: The position in the imagelist of the new icon
extern "C" RNTHUMB_API HRESULT __stdcall AddTohIML(HIMAGELIST himl, PCIDLIST_ABSOLUTE pidlSrc, int cxThumb, int cyThumb)
{
int hpos1 = -1;
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
// Getting the IShellItemImageFactory interface pointer for the file.
IShellItemImageFactory *pImageFactory;
hr = SHCreateItemFromIDList(pidlSrc, IID_PPV_ARGS(&pImageFactory));
if (SUCCEEDED(hr))
{
SIZE size = { cxThumb, cyThumb };
HBITMAP hbmp;
hr = pImageFactory->GetImage(size, SIIGBF_RESIZETOFIT, &hbmp);
if (SUCCEEDED(hr))
{
hpos1 = ImageList_Add(himl, hbmp, NULL);
return (HRESULT)hpos1;
DeleteObject(hbmp);
}
pImageFactory->Release();
}
}
CoUninitialize();
return (HRESULT)hpos1;
}
And then in VB,
Code:
Public Declare Function AddTohIML Lib "C:\rename\RNThumb.dll" (ByVal hIML As Long, ByVal pidlFile As Long, ByVal cx As Long, ByVal cy As Long) As Long
Dim lRtn As Long
lRtn = AddTohIML(hIML_Thumb, pidlfqChild, cxThumb, cyThumb)
lvi.iImage = lRtn
-
Jun 30th, 2013, 01:10 AM
#16
Re: Trying to implement IShellItem - where'd I go wrong?
Code:
Public Function GetImage(Byval aa As Long, Byval bb As Long, ByVal flags As enmSIIGBF) As Long
HRes = vtblCall(isi_GetImage, aa, bb, flags, VarPtr(GetImage))
Debug.Print "vtbl hres=" & HRes
End Function
Splitting up the 'SIZE sz' Single-Param into two separate Params didn't help?
Did you try it (because it works here).
Olaf
-
Jun 30th, 2013, 03:44 AM
#17
Re: Trying to implement IShellItem - where'd I go wrong?
HRes = vtblCall(isi_GetImage, aa, bb, Flags, VarPtr(GetImage))
That's what I tried, and VB still immediately crashes.
-
Jun 30th, 2013, 05:43 AM
#18
Re: Trying to implement IShellItem - where'd I go wrong?
 Originally Posted by fafalone
HRes = vtblCall(isi_GetImage, aa, bb, Flags, VarPtr(GetImage))
That's what I tried, and VB still immediately crashes.
Then there's something wrong elsewhere (no instance in mUnk, or not the correct instance)...
The call looks good - not different from what I have here (aside from, that I pass the pUnk now explicitely)...
Anyways - just did an upload of a Demo-Project, which does ShellItem-Rendering with different Fallbacks.
It is working also on XP (due to appropriate Fallbacks over IExtractImage, and IExtractIcon).
The following Interfaces are implemented completely now:
- IShellItem (in cShellItem)
- IExtractIcon (in cExtractIcon )
- IExtractImage (in cExtractImage )
- IShellItemImageFactory (in cShellItemImageFactory)
Still no TypeLib and not much code (the implementing classes share a module modImplHelpers.bas now).
One note though - a few small Routines in the Test-Form (and only there) use my Cairo-Graphics-Wrapper
to bring the different Alpha-hBmps or Alpha-hIcons onto the Screen (in a for me convenient way - since I have
PreMultiplying and a lot of other useful stuff there, which was time-saving).
As said, this is only in the Form - all the other Modules are directly usable with your own hBmp- and hIcon-
Handler-Routines and don't require the vbRichClient5.dll...
But for the example to be immediately usable, you would need to download the latest version from:
http://www.vbrichclient.com/#/en/Downloads.htm
and register it locally in a Folder on your Dev-Machine.
The Demo-Download for the ShellItem-stuff is here:
http://www.vbrichclient.com/Download...mRendering.zip
Hope this sheds some light on things - and that your crashes will vanish... ;-)
Olaf
-
Jul 5th, 2013, 01:44 AM
#19
Re: Trying to implement IShellItem - where'd I go wrong?
Thanks very much, this is all going very smoothly now. Just one general question; I thought IShellItemImageFactory returned a thumbnail if available? And only returned the icon if no thumbnail was available. Both your code (including an unmodified run of the sample) and mine are only showing the file type icons for image files instead of actual thumbnails. Is this dependent on the setting in Explorer? (I have thumbnails disabled in Explorer), or a problem with something elsewhere in my computer? I could have sworn it was showing them the other day... then something I did wiped my associations. Maybe the preview handlers didn't reset when I fixed the associations? Any way to check that manually? IExtractImage still works but I don't want to have to go through a fallback if I don't have to.
Last edited by fafalone; Jul 5th, 2013 at 02:08 AM.
-
Jul 5th, 2013, 03:08 AM
#20
Re: Trying to implement IShellItem - where'd I go wrong?
Ah, thanks for the feeedback - as said, it was my first attempt in wrapping those ShellItem-related Interfaces - so this is good to know...
That's what I experienced recently, after doing more and more "direct implementations" (using the DispCallFunc) instead of writing real typelibs for such "more exotic stuff" ...
It does make a bit more work in the beginning - but having a direct WrapperClass (cSomething) instead of an ISomething (which mayhap doesn't support the native VB-Types directly),
so far always payed off in the end.
As for your question ... well, there *is* some caching involved - and when you not force explicitely per (Optional) Flag, then the call might be using the first thing it finds in the thumbnail-cache.
I'd offer settings in some config-dialogue of your app for that - as e.g.:
[ ] Icon_first_then_Thumbnail
[ ] Thumbnail_first_then_Icon
and then call the Factory always with those potentially two stages - passing the explicit flags
in the Order as currently set in your Dialogue.
If the first (explicitely flagged) call fails, then proceed with the second -
if that fails too, then proceed with the IExtractIcon or other fallbacks...
Here's the possible flags again ... and in my tests those *did* have an effect:
Code:
Public Enum enmSIIGBF
SIIGBF_RESIZETOFIT = &H0
SIIGBF_BIGGERSIZEOK = &H1
SIIGBF_MEMORYONLY = &H2
SIIGBF_ICONONLY = &H4
SIIGBF_THUMBNAILONLY = &H8
SIIGBF_INCACHEONLY = &H10
SIIGBF_CROPTOSQUARE = &H20 'Introduced in Windows 8. If necessary, crop the bitmap to a square.
SIIGBF_WIDETHUMBNAILS = &H40 'Introduced in Windows 8. Stretch and crop the bitmap to a 0.7 aspect ratio.
SIIGBF_ICONBACKGROUND = &H80 'Introduced in Windows 8. If returning an icon, paint a background using the associated app's registered background color.
SIIGBF_SCALEUP = &H100 'Introduced in Windows 8. If necessary, stretch the bitmap so that the height and width fit the given size.
End Enum
Olaf
-
Jul 5th, 2013, 11:53 PM
#21
Re: Trying to implement IShellItem - where'd I go wrong?
It seems it will return the thumbnail if I use the SIIGBF_THUMBNAILONLY flag, but the file type icon with any other combination of flags. This seems a bit weird. It's probably related to how thoroughly I've disabled thumbnails in Explorer.
So it seems all I can do is first try THUMBNAILONLY and then if that fails ICONONLY.
-
Jul 6th, 2013, 12:05 AM
#22
Re: Trying to implement IShellItem - where'd I go wrong?
 Originally Posted by fafalone
It seems it will return the thumbnail if I use the SIIGBF_THUMBNAILONLY flag, but the file type icon with any other combination of flags. This seems a bit weird. It's probably related to how thoroughly I've disabled thumbnails in Explorer.
So it seems all I can do is first try THUMBNAILONLY and then if that fails ICONONLY.
Yep, that's how I'd have coded it in either case... with that two-stage-approach you simply avoid any "non-crystal-clear behavior in case the system interprets the default-flags in a funny way".
Olaf
-
Jun 12th, 2015, 01:33 AM
#23
Re: Trying to implement IShellItem - where'd I go wrong?
I wanted to revisit this thread since I finally found a solution to IShellItemImageFactory crashing.
MSDN, ShObjIdl.idl:
[in] SIZE* size,
MSDN:
Code:
typedef struct tagSIZE {
LONG cx;
LONG cy;
} SIZE, *PSIZE;
olelib:
Code:
typedef struct SIZE {
long cx;
long cy;
} SIZE;
Working C++ version i had been using:
Code:
SIZE size = { cxThumb, cyThumb };
HBITMAP hbmp;
hr = pImageFactory->GetImage(size, SIIGBF_RESIZETOFIT, &hbmp);
So what's the problem, everything looks good right? Well I was looking at Schmidt's code again, and noticed he sent them to the v-table as two separate args, so I tried redefining my interface:
Code:
interface IShellItemImageFactory : stdole.IUnknown {
long GetImage(
[in] LONG cx,
[in] LONG cy,
[in] SIIGBF flags,
[out] HBITMAP *phbm);
};
And it works! This all seems truly bizarre to me since I don't understand why adding an extra arg in isn't throwing the v-table off, not to mention the thousands of other structs that aren't passed argument by argument (i.e. every single one in all of olelib and oleexp except this instance). I had revisited this again while I was re-writing my C workaround for a new app, and gave it one more go. So anyway... resolved 2 years later. And dear sweet deity$ i posted this thread 2 years ago?
-
Jun 12th, 2015, 05:04 AM
#24
Re: Trying to implement IShellItem - where'd I go wrong?
 Originally Posted by fafalone
This all seems truly bizarre to me since I don't understand why adding an extra arg in isn't throwing the v-table off, not to mention the thousands of other structs that aren't passed argument by argument (i.e. every single one in all of olelib and oleexp except this instance).
I don't think a method's number of arguments has anything to do with its offset from the VTable. As you know, a VTable is just an array of pointers to the methods of an interface. It doesn't specify how many parameters a particular method expects.
Schmidt already pointed out that the IShellItemImageFactory::GetImage method's size parameter is supposed to be passed ByVal, but, as you have discovered, VB doesn't allow passing UDTs by value. The usual workaround for this, which you are now using, is to pass the UDT's members individually in the correct order and ensuring that the original alignment of the structure's members are preserved.
On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)
-
Jun 12th, 2015, 11:37 AM
#25
Re: [RESOLVED] Trying to implement IShellItem - where'd I go wrong?
Makes sense but I've written dozens of interfaces now and never experienced this.. Have always just passed a pointer if i couldn't pass byref, and certainly never got a full ide crash as a result.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|