Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate

COM QueryInterface


  • Please log in to reply
46 replies to this topic
Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

Now you are talking about something new to me. Do you mean ComObjQuery() by saying QueryInterface()?

The documentation for ComObjQuery quite clearly links it to IUnknown::QueryInterface.

What I'm looking for is an instruction for the IShellLink interface usage in AutoHotkey. Hopefully, it reads the target path of a specified lnk file and detect the icon location of it.

Were you thinking of using IShellLink::GetIconLocation? There's already an easy to use wrapper for that. It's called FileGetShortcut. :roll:

So I'm guessing that SetPath is the 18th method in the IShellLinkW interface. Therefore, shouldn't it be NumGet(link + 0)+17*A_PtrSize?

No. NumGet(link+0) retrieves a pointer to the interface's virtual function table, or vtable for short. This vtable contains pointers to all of the methods which the interface supports, including those inherited from other interfaces. This is why the C-style definition (i.e. IShellLinkWVtbl) includes inherited methods - it is a complete description of the interface's vtable.

Also Is it the only way we can do to use Win32 Programmer's Reference to find out which nth number is applied to which method in Vtable?

No. You can refer to the actual interface or vtable definition in the SDK header files.

  • Guests
  • Last active:
  • Joined: --

Personally, I like this page. Just find your header file on the side and click to bring it up. Search for typedef struct InterfaceNameVtbl to get the Virtual Table, or IID_InterfaceName to get the IID. (in this case it would be typedef struct IShellLinkWVtbl & IID_IShellLinkW)

Thanks for the information. But I still I cannot find CLSID. This is what I found from this page, http://www.koders.co...9E3BE416E0.aspx
EXTERN_C const IID IID_IShellLinkW;

#if defined(__cplusplus) && !defined(CINTERFACE)
    
    MIDL_INTERFACE("000214F9-0000-0000-C000-000000000046")
    IShellLinkW : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetPath( 
            /* [size_is][out] */ LPWSTR pszFile,
            /* [in] */ int cch,
            /* [full][out][in] */ WIN32_FIND_DATAW *pfd,
            /* [in] */ DWORD fFlags) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetIDList( 
            /* [out] */ LPITEMIDLIST *ppidl) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE SetIDList( 
            /* [in] */ LPCITEMIDLIST pidl) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetDescription( 
            /* [size_is][out] */ LPWSTR pszName,
            int cch) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE SetDescription( 
            /* [in] */ LPCWSTR pszName) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetWorkingDirectory( 
            /* [size_is][out] */ LPWSTR pszDir,
            int cch) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE SetWorkingDirectory( 
            /* [in] */ LPCWSTR pszDir) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetArguments( 
            /* [size_is][out] */ LPWSTR pszArgs,
            int cch) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE SetArguments( 
            /* [in] */ LPCWSTR pszArgs) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetHotkey( 
            /* [out] */ WORD *pwHotkey) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE SetHotkey( 
            /* [in] */ WORD wHotkey) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetShowCmd( 
            /* [out] */ int *piShowCmd) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE SetShowCmd( 
            /* [in] */ int iShowCmd) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetIconLocation( 
            /* [size_is][out] */ LPWSTR pszIconPath,
            /* [in] */ int cch,
            /* [out] */ int *piIcon) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE SetIconLocation( 
            /* [in] */ LPCWSTR pszIconPath,
            /* [in] */ int iIcon) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE SetRelativePath( 
            /* [in] */ LPCWSTR pszPathRel,
            /* [in] */ DWORD dwReserved) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE Resolve( 
            /* [in] */ HWND hwnd,
            /* [in] */ DWORD fFlags) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE SetPath( 
            /* [in] */ LPCWSTR pszFile) = 0;
        
    };

The documentation for ComObjQuery quite clearly links it to IUnknown::QueryInterface

However, IUnknown::QueryInterface does not have parenthesis. Plus I don't know what it means by first form of this function. Does it mean the first parameter?

The first form of this function is equivalent to IUnknown::QueryInterface.

Were you thinking of using IShellLink::GetIconLocation? There's already an easy to use wrapper for that. It's called FileGetShortcut. :roll:

Yes and several other interfaces inluding the IExtractIcon interface, the IExtractImageinterface, and the IFolderViewinterface. Is FileGetShortcut the wrapper for the IShellLink interface? The reason I'm looking for a way of using IShellLink interfase is that this doesn't work; OutIcon returns empty for some files.
path := A_Desktop "\test.lnk"
FileCreateShortcut, ::{21EC2020-3AEA-1069-A2DD-08002B30309D}  , % path 
FileGetShortcut, % path, OutTarget,,,, OutIcon
msgbox % OutTarget "`n" OutIcon

No. NumGet(link+0) retrieves a pointer to the interface's virtual function table, or vtable for short. This vtable contains pointers to all of the methods which the interface supports, including those inherited from other interfaces. This is why the C-style definition (i.e. IShellLinkWVtbl) includes inherited methods - it is a complete description of the interface's vtable.

Thanks for the information.

No. You can refer to the actual interface or vtable definition in the SDK header files.

The problem is that I don't know where the SDK header files are.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

However, IUnknown::QueryInterface does not have parenthesis.

So what?

Plus I don't know what it means by first form of this function.

Originally I wrote the syntax as:
InterfacePointer := ComObjQuery(ComObject, IID)
InterfacePointer := ComObjQuery(ComObject, SID, IID)
... but I compacted it. I'll reword it:

In its two-parameter mode, this function is equivalent to IUnknown::QueryInterface. When SID and IID are both specified, it internally queries for the IServiceProvider interface, then calls IServiceProvider::QueryService. In either case, the return value is either zero or a pointer to the requested interface.

Is FileGetShortcut the wrapper for the IShellLink interface?

That's what I said.

The reason I'm looking for a way of using IShellLink interfase is that this doesn't work; OutIcon returns empty for some files.

You won't get any better results by using the IShellLink interface directly.

The problem is that I don't know where the SDK header files are.

In the SDK folder. You know how to do a file search, right? :roll:

  • Guests
  • Last active:
  • Joined: --

So what?

So they look different.

I'll reword it:

In its two-parameter mode, this function is equivalent to IUnknown::QueryInterface. When SID and IID are both specified, it internally queries for the IServiceProvider interface, then calls IServiceProvider::QueryService. In either case, the return value is either zero or a pointer to the requested interface.

Thanks.

That's what I said.

You won't get any better results by using the IShellLink interface directly.

Hmm. I was looking for an alternative since FileGetShortcut doesn't work. Is that a bug? By the way, I think this is a good opportunity for me to learn COM interfaces so it's OK that IShellLink doesn't work either at this point. I'd like to confirm it doesn't work with my eyes and I'd like to learn at least how to use it.

In the SDK folder. You know how to do a file search, right? :roll:

I found it in this location, thanks. C:\Program Files\Microsoft SDKs\Windows\v7.1\Include But how can I find the CLSID for IShellLinkW by myself?

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

But how can I find the CLSID for IShellLinkW by myself?

IShellLinkW is an interface, and therefore has an IID (interface ID) not a CLSID (class ID). At the beginning of the interface definition, there is a MIDL_INTERFACE(...) macro which defines the IID.

  • Guests
  • Last active:
  • Joined: --

IShellLinkW is an interface, and therefore has an IID (interface ID) not a CLSID (class ID). At the beginning of the interface definition, there is a MIDL_INTERFACE(...) macro which defines the IID.

Isn't CLSID necessary In order to create an object of COM interfaces? Fortunately, the CLSID for ISHellLinkW is defined in ShObjIdl.h.
EXTERN_C const CLSID CLSID_ShellLink;

#ifdef __cplusplus

class DECLSPEC_UUID("00021401-0000-0000-C000-000000000046")
ShellLink;
#endif
However, I cannot find the CLSID in ObjIdl.h for the IPersistFIle interface which seems to be required to be used with the IShellLink interface. So how can I find it?

nope
  • Guests
  • Last active:
  • Joined: --

class DECLSPEC_UUID("00021401-0000-0000-C000-000000000046")
ShellLink;

Class != interface. ShellLink != IShellLinkW.

  • Guests
  • Last active:
  • Joined: --
It is the same value that maul.esel posted though. Anyway, so how would you find out the CLSID for IPersistFile?

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Semantics: IPersistFile does not have a CLSID. It has an IID.

You cannot create an instance of IPersistFile or any other interface. You can only create an instance of an object which implements that interface. In this case, the ShellLink class implements IShellLinkW, IShellLinkA, IPersistFile and other interfaces. To get the IPersistFile interface of a ShellLink object, you need to call ComObjQuery with the IID of IPersistFile.

For your referece, the section of the source code for FileGetShortcut which deals with IShellLink and IPersistFile can be viewed online, here (this links to line 1272; it may take a while to load).

  • Guests
  • Last active:
  • Joined: --

Semantics: IPersistFile does not have a CLSID. It has an IID.

You cannot create an instance of IPersistFile or any other interface. You can only create an instance of an object which implements that interface. In this case, the ShellLink class implements IShellLinkW, IShellLinkA, IPersistFile and other interfaces. To get the IPersistFile interface of a ShellLink object, you need to call ComObjQuery with the IID of IPersistFile.

I'm kind of getting the idea. IPersistFile belongs to IShellLink so I just need to ask the pointer of IPersistFile to the IShellLInk class with ComObjQuery(), don't I?

For your referece, the section of the source code for FileGetShortcut which deals with IShellLink and IPersistFile can be viewed online, here (this links to line 1272; it may take a while to load).

Hmm, indeed the source code uses IShelLink and IPersistFile. Thanks.

OK this is what I've tried. The function always returns after the line hresult := DllCall(pIPersistFile_Load... that indicates IPersistFile::Load failed. Could you point out why it fails? I'd like to see the message, IPersistFile::Load succeeded. I haven't finished off the rest of the code yet. I just need to get through the part which uses the IPersistFile::Load method.
lnkpath := A_ScriptDIr "\notepad.lnk"
FileCreateShortcut, %A_WIndir%\notepad.exe, % lnkpath

target := IShellLinkResolve(lnkpath)
if target
	msgbox % "result:`n" target
else
	msgbox not found
FileDelete, % lnkpath
Return

	
IShellLinkResolve(FilePath) {

	static	IID_IShellLinkW := "{000214F9-0000-0000-C000-000000000046}"
	static CLSID_ShellLink := "{00021401-0000-0000-C000-000000000046}"	
	static IID_IPersistFile := "{0000010b-0000-0000-C000-000000000046}"
			
	
	if !(pCOM_IShellLink := ComObjCreate(CLSID_ShellLink, IID_IShellLinkW))
        return False		
	pIShellLinkW := ComObjUnwrap(pCOM_IShellLink) ;make it usable in AutoHotkey
	vtIShellLink := NumGet(pIShellLinkW + 0) ;pointer to the vTable 
	pIShellLink_GetPath := NumGet(vtIShellLink + 03 * A_PtrSize)	
	pIShellLink_Resolve := NumGet(vtIShellLink + 19 * A_PtrSize)
	
	if !(pIPersistFile := ComObjQuery(pCOM_IShellLink, IID_IPersistFile)) 
		Return False
	vtPersistFile := NumGet(pIPersistFile+0)
	pIPersistFile_Load := NumGet(vtPersistFile + 1 * A_PtrSize)	;defined in ObjIdl.h

	msgbox IShellLink and IPersistFile are created

	;IPersistFile::Load http://msdn.microsoft.com/en-us/library/windows/desktop/ms687284%28v=vs.85%29.aspx
	;Flags http://msdn.microsoft.com/en-us/library/windows/desktop/aa380337%28v=vs.85%29.aspx
	hresult := DllCall(pIPersistFile_Load
		, "Str", FilePath
		, "Uint", 0x00000000L)	;STGM_Read

	if !(hresult = 0x00000000) ; S_OK
		return false

	msgbox IPersistFile::Load succeeded
	
	;http://msdn.microsoft.com/en-us/library/windows/desktop/bb774952%28v=vs.85%29.aspx
	hresult := DllCall(pIShellLink_Resolve
		, "Ptr", A_ScriptHwnd
		, "Uint", 0x0002)	;SLR_NO_UI
		
	if !(hresult = 0x00000000) ;S_OK
		return false
	
	msgbox IPersistFile::Resolve succeeded

	; http://msdn.microsoft.com/en-us/library/windows/desktop/bb774944%28v=VS.85%29.aspx
	VarSetCapacity(WIN32_FIND_DATA, 592)	;for unicode
	DllCall(pIShellLink_GetPath
		, "Str", FilePath
		, "Int", 260
		, "Ptr", &WIN32_FIND_DATA
		, "Uint", 0)
	Target := StrGet(&WIN32_FIND_DATA+44, 260)
	Return Target
	
}


jethrow
  • Moderators
  • 2854 posts
  • Last active: May 17 2017 01:57 AM
  • Joined: 24 May 2009
I've wanted to know this, & I still need to do more research, but ...

In this case, the ShellLink class implements IShellLinkW, IShellLinkA, IPersistFile and other interfaces. To get the IPersistFile interface of a ShellLink object, you need to call ComObjQuery with the IID of IPersistFile.

... is there a way to determine all the interfaces an object implements?

  • Guests
  • Last active:
  • Joined: --
hresult := DllCall(pIPersistFile_Load

      , "Str", FilePath

      , "Uint", 0x00000000[color=red]L[/color])   ;STGM_Read


  • Guests
  • Last active:
  • Joined: --

STGM_READ
0x00000000L
Indicates that the object is read-only, meaning that modifications cannot be made.

What does L mean here?

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

IPersistFile belongs to IShellLink so I just need to ask the pointer of IPersistFile to the IShellLInk class with ComObjQuery(), don't I?

That is wrong on multiple levels, but I feel that repeating myself and/or explaining further would be a waste of time.

if !(pIPersistFile := ComObjQuery(pCOM_IShellLink, IID_IPersistFile))

That looks okay.

pIPersistFile_Load := NumGet(vtPersistFile + 1 * A_PtrSize) ;defined in ObjIdl.h

That has to be wrong since the first three functions in every COM interface vtable are QueryInterface, AddRef and Release, inherited from IUnknown. I suggest you look at the C style interface definition instead of the C++ interface definition.

pIShellLinkW := ComObjUnwrap(pCOM_IShellLink) ;make it usable in AutoHotkey

What is your intention here? pCOM_IShellLink is already an integer.

What does L mean here?

It is a suffix indicating that the type of the numeric constant is long int. Since it has no meaning to AutoHotkey, 0x00000000L is a variable name.

hresult := DllCall(pIPersistFile_Load
, "Str", FilePath
, "Uint", 0x00000000L) ;STGM_Read

The first parameter in every interface function call is always a pointer to the interface (pIPersistFile in this case). This is reflected in the C style definition.


... is there a way to determine all the interfaces an object implements?

Look into ITypeInfo and IProvideClassInfo.

  • Guests
  • Last active:
  • Joined: --

That is wrong on multiple levels, but I feel that repeating myself and/or explaining further would be a waste of time.

I'll reread the links you posted.

That has to be wrong since the first three functions in every COM interface vtable are QueryInterface, AddRef and Release, inherited from IUnknown. I suggest you look at the C style interface definition instead of the C++ interface definition.

Thanks. Indeed the Load method is the 6th method in the C style inteface code block defined in the header file. So the number for the VTable should be 5. By the way, this is confusing whether or not I should add 2 to it since IUnknown is inherited. But looking at maul.esel's code, the number for the SetPath method is 20 and the header file definition lists it in 21st. So 5 is ok I guess.
#else 	/* C style interface */

    typedef struct IPersistFileVtbl
    {
        BEGIN_INTERFACE
        
        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( 
            __RPC__in IPersistFile * This,
            /* [in] */ __RPC__in REFIID riid,
            /* [annotation][iid_is][out] */ 
            __RPC__deref_out  void **ppvObject);
        
        ULONG ( STDMETHODCALLTYPE *AddRef )( 
            __RPC__in IPersistFile * This);
        
        ULONG ( STDMETHODCALLTYPE *Release )( 
            __RPC__in IPersistFile * This);
        
        HRESULT ( STDMETHODCALLTYPE *GetClassID )( 
            __RPC__in IPersistFile * This,
            /* [out] */ __RPC__out CLSID *pClassID);
        
        HRESULT ( STDMETHODCALLTYPE *IsDirty )( 
            __RPC__in IPersistFile * This);
        
        HRESULT ( STDMETHODCALLTYPE *Load )( 
            __RPC__in IPersistFile * This,
            /* [in] */ __RPC__in LPCOLESTR pszFileName,
            /* [in] */ DWORD dwMode);
        
        HRESULT ( STDMETHODCALLTYPE *Save )( 
            __RPC__in IPersistFile * This,
            /* [unique][in] */ __RPC__in_opt LPCOLESTR pszFileName,
            /* [in] */ BOOL fRemember);
        
        HRESULT ( STDMETHODCALLTYPE *SaveCompleted )( 
            __RPC__in IPersistFile * This,
            /* [unique][in] */ __RPC__in_opt LPCOLESTR pszFileName);
        
        HRESULT ( STDMETHODCALLTYPE *GetCurFile )( 
            __RPC__in IPersistFile * This,
            /* [out] */ __RPC__deref_out_opt LPOLESTR *ppszFileName);
        
        END_INTERFACE
    } IPersistFileVtbl;


What is your intention here? pCOM_IShellLink is already an integer.

I just followed the maul.esel's tutorial. It says.

Pointers and the vTable

First we have to analyze what we got from this: We did not get an object you can use like "normal" objects in AutoHotkey_L or AutoHotkey v2. Instead we got a pointer: a number addressing a special location in memory. This is the same thing you get running this code:

[i]; AutoHotkey_L or AutoHotkey v2[/i]
ptr := ComObjUnwrap(ComObjCreate("Scripting.Dictionary"))

I removed it.

It is a suffix indicating that the type of the numeric constant is long int. Since it has no meaning to AutoHotkey, 0x00000000L is a variable name.

OK, I changed it to 0x00000000.

The first parameter in every interface function call is always a pointer to the interface (pIPersistFile in this case). This is reflected in the C style definition.

Thanks. I also noticed that while I was reviewing the manual example for ComObjQuery().

This is the update. Finally it seems to be working. However, it only returns the name of the file, notepad.exe. I'd like to see the full path though. Also the code only works with the Unicode builds. The ANSI build may have to use ISHellLinkA. I'm not sure.
lnkpath := A_ScriptDIr "\notepad.lnk"
FileCreateShortcut, %A_WIndir%\notepad.exe, % lnkpath

if target := IShellLinkResolve(lnkpath)
	msgbox % "result:`n" target
else
	msgbox not found
FileDelete, % lnkpath
Return

	
IShellLinkResolve(FilePath) {

	static	IID_IShellLinkW := "{000214F9-0000-0000-C000-000000000046}"
	static CLSID_ShellLink := "{00021401-0000-0000-C000-000000000046}"	
	static IID_IPersistFile := "{0000010b-0000-0000-C000-000000000046}"
	static def__WIN32_FIND_DATA
	
	if !(pCOM_IShellLink := ComObjCreate(CLSID_ShellLink, IID_IShellLinkW))
        return False		
	if !(pIPersistFile := ComObjQuery(pCOM_IShellLink, IID_IPersistFile)) 
		Return False
		
	; msgbox IShellLink and IPersistFile are created
	
	vtIShellLink := NumGet(pCOM_IShellLink + 0) ;pointer to the vTable 
	pIShellLink_GetPath := NumGet(vtIShellLink + 03 * A_PtrSize)	
	pIShellLink_Resolve := NumGet(vtIShellLink + 19 * A_PtrSize)	
	
	vtPersistFile := NumGet(pIPersistFile+0)
	pIPersistFile_Load := NumGet(vtPersistFile + 05 * A_PtrSize)	;defined in ObjIdl.h 

	;IPersistFile::Load http://msdn.microsoft.com/en-us/library/windows/desktop/ms687284%28v=vs.85%29.aspx
	;Flags http://msdn.microsoft.com/en-us/library/windows/desktop/aa380337%28v=vs.85%29.aspx
	hresult := DllCall(pIPersistFile_Load
		, "Ptr", pIPersistFile
		, "Str", FilePath
		, "Uint", 0x00000000)	;STGM_Read

	if !(hresult = 0x00000000) ; S_OK
		return false

	; msgbox IPersistFile::Load succeeded

	;http://msdn.microsoft.com/en-us/library/windows/desktop/bb774952%28v=vs.85%29.aspx
	hresult := DllCall(pIShellLink_Resolve
		, "ptr", pCOM_IShellLink
		, "Ptr", A_ScriptHwnd
		, "Uint", 0x0001)	;SLR_NO_UI
		
	if !(hresult = 0x00000000) ;S_OK
		return false
	
	; msgbox IPersistFile::Resolve succeeded

	; IShellLink::GetPath http://msdn.microsoft.com/en-us/library/windows/desktop/bb774944%28v=VS.85%29.aspx
	VarSetCapacity(WIN32_FIND_DATA, 592)	;for unicode. for ANSI, 320.
	hresult := DllCall(pIShellLink_GetPath
		, "ptr", pCOM_IShellLink
		, "Str", FilePath
		, "Int", 260
		, "Ptr", &WIN32_FIND_DATA ;_WIN32_FIND_DATA.GetPtr()	;&WIN32_FIND_DATA
		, "Uint", 0x4)	; SLGP_SHORTPATH = 0x1 SLGP_UNCPRIORITY = 0x2 SLGP_RAWPATH = 0x4
		
	if !(hresult = 0x00000000) ;S_OK
		return false
	; msgbox IPersistFile::GetPath succeeded
	
	Return StrGet(&WIN32_FIND_DATA + 44)
	
}