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
maul.esel
  • Members
  • 790 posts
  • Last active: Jan 05 2013 09:26 PM
  • Joined: 28 Feb 2011
Sorry for my absence (no internet access). As you wanted an example, here it is: <!-- m -->https://github.com/m... ... ample1.ahk<!-- m --> (for use with CCF). It uses IShellLinkW + IPersistFile and creates + saves a shell link.

Btw: to ensure your IShellLinkW code works with ANSI builds, pass strings with the "wstr" type.
Join the discussion on The future of AutoHotkey
Posted Image Visit me on github Posted Image
Win7 HP SP1 64bit | AHK_L U 64bit

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

I just followed the maul.esel's tutorial.

Reading only the excerpt which you quoted, it seems to me that maul.esel was showing code which produces a similar result, not telling you to use that code. Note the following:

If an IID is specified, an interface pointer is returned. (...)
Otherwise, a wrapper object usable by script is returned.

You were passing an IID and therefore do not need to "unwrap" an object.

  • Guests
  • Last active:
  • Joined: --

Btw: to ensure your IShellLinkW code works with ANSI builds, pass strings with the "wstr" type.

Changing Str to WStr in the all DllCall lines just gives n in the result with the ANSI build.

By the way, although I appreciate your effort, there should be an example in here and there. They wouldn't be called a tutorial unless there is at least one working example. In fact, it caused lots of confusion when I was following it.

Reading only the excerpt which you quoted, it seems to me that maul.esel was showing code which produces a similar result, not telling you to use that code

There isn't a working example so it is hard to tell what he meant.

By the way, any thoughts why the code produces only a file name? I expected it returns a full path. I tried the other flags for the GetPath method, LGP_SHORTPATH and SLGP_UNCPRIORITY but the same. Also should I add ObjRelease(pIPersistFile) before returning from the function?

  • Guests
  • Last active:
  • Joined: --

Changing Str to WStr in the all DllCall lines just gives n in the result with the ANSI build.

In addition to it, in order to support the ANSI build, I found that the last line of the function had to be changed to:
Return StrGet(&WIN32_FIND_DATA + 44, A_IsUnicode ? "utf-16" : "cp1200")
By the way, If I put UTF-8 instead of UTF-16 in the Unicode builds, is started not returning the correct string. Does that mean IShellLinkW internally encodes strings as UTF-16?

  • Guests
  • Last active:
  • Joined: --
Return StrGet(&WIN32_FIND_DATA + 44, A_IsUnicode ? "[color=green]utf-16[/color]" : "[color=darkred]cp1200[/color]")

FileEncoding
[*:x7c4pj88]UTF-16: Unicode UTF-16, little endian byte order. This is equivalent to CP1200.



  • Guests
  • Last active:
  • Joined: --
Ah, so it's only enough to specify either utf-16 or cp-1200 for the ANSI build. Thanks.
Return StrGet(&WIN32_FIND_DATA + 44, "utf-16")

Any idea why the function only returns a file name, not the full path?

  • Guests
  • Last active:
  • Joined: --
HRESULT GetPath(

  [[color=red]out[/color]]      LPTSTR pszFile,

  [in]       int cchMaxPath,

  [in, out]  WIN32_FIND_DATA *pfd,

  [in]       DWORD fFlags

);


  • Guests
  • Last active:
  • Joined: --
Ah, thanks.

Here is the update. It works well.
lnkpath := A_ScriptDIr "\notepad.lnk"
FileCreateShortcut, %A_WIndir%\notepad.exe, % lnkpath
if target := IShellLinkResolve(lnkpath)
	msgbox % 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		
	if !(pIPersistFile := ComObjQuery(pCOM_IShellLink, IID_IPersistFile)) 
		Return False
	
	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
	if hresult := DllCall(pIPersistFile_Load
		, "Ptr", pIPersistFile
		, "wStr", FilePath
		, "Uint", 0x00000000)	;STGM_Read
		return false

	;http://msdn.microsoft.com/en-us/library/windows/desktop/bb774952%28v=vs.85%29.aspx
	if hresult := DllCall(pIShellLink_Resolve
		, "ptr", pCOM_IShellLink
		, "Ptr", A_ScriptHwnd
		, "Uint", 0x0001)	;SLR_NO_UI
		return false
	
	; IShellLink::GetPath http://msdn.microsoft.com/en-us/library/windows/desktop/bb774944%28v=VS.85%29.aspx
	VarSetCapacity(WIN32_FIND_DATA, 592)
	VarSetCapacity(target, 260)
	if hresult := DllCall(pIShellLink_GetPath
		, "ptr", pCOM_IShellLink
		, "wStr", target
		, "Int", 260
		, "Ptr", &WIN32_FIND_DATA 
		, "Uint", 0x4)	; SLGP_SHORTPATH = 0x1 SLGP_UNCPRIORITY = 0x2 SLGP_RAWPATH = 0x4
		return false
	
	ObjRelease(pIPersistFile)
	ObjRelease(pCOM_IShellLink)
	Return target 	
	
}


  • Guests
  • Last active:
  • Joined: --
VarSetCapacity(target, 260 [color=red]* 2[/color])
It's unicode!

  • Guests
  • Last active:
  • Joined: --
Does that mean also the number in the second parameter for IShellLink::GetPath (the third parameter in DllCall) has to be 260*2 ?

By the way, I noticed that it didn't run in the ANSI build regardless the suggested change.

Here is the updated function which also runs with the ANSI build.
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		
	if !(pIPersistFile := ComObjQuery(pCOM_IShellLink, IID_IPersistFile)) 
		Return False
	

	;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
	if hresult := DllCall(NumGet(NumGet(pIPersistFile+0) + 05 * A_PtrSize)	;IPersistFile::Load
		, "Ptr", pIPersistFile
		, "wStr", FilePath
		, "Uint", 0x00000000)	;STGM_Read
		return false

	;http://msdn.microsoft.com/en-us/library/windows/desktop/bb774952%28v=vs.85%29.aspx
	if hresult := DllCall(NumGet(NumGet(pCOM_IShellLink + 0) + 19 * A_PtrSize)		;IShellLink:Resolve
		, "ptr", pCOM_IShellLink
		, "Ptr", A_ScriptHwnd
		, "Uint", 0x0001)	;SLR_NO_UI
		return false
	
	; IShellLink::GetPath http://msdn.microsoft.com/en-us/library/windows/desktop/bb774944%28v=VS.85%29.aspx
	VarSetCapacity(WIN32_FIND_DATA, 592)
	VarSetCapacity(target, 260 * 2)
	if hresult := DllCall(NumGet(NumGet(pCOM_IShellLink + 0) + 03 * A_PtrSize)		;IShellLink:GetPath
		, "ptr", pCOM_IShellLink
		, "ptr", &target
		, "Int", 260   ;[color=red]<-- not sure if it should be 260*2[/color]
		, "Ptr", &WIN32_FIND_DATA 
		, "Uint", 0x4)	; SLGP_SHORTPATH = 0x1 SLGP_UNCPRIORITY = 0x2 SLGP_RAWPATH = 0x4
		return false
	
	ObjRelease(pIPersistFile)
	ObjRelease(pCOM_IShellLink)
	Return StrGet(&target, "utf-16") 	
	
}


  • Guests
  • Last active:
  • Joined: --

VarSetCapacity()
Parameters
RequestedCapacity

Specify for RequestedCapacity the number of bytes that the variable should be able to hold after the adjustment. For Unicode strings, this should be the length times two.



  • Guests
  • Last active:
  • Joined: --
I'm talking about the part commented in red.

  • Guests
  • Last active:
  • Joined: --

IShellLink::GetPath method

cchMaxPath [in]
Type: int

The size, in characters, of the buffer pointed to by the pszFile parameter, including the terminating null character. The maximum path size that can be returned is MAX_PATH. This parameter is commonly set by calling ARRAYSIZE(pszFile). The ARRAYSIZE macro is defined in Winnt.h.



  • Guests
  • Last active:
  • Joined: --
OK, so it should be 260*2 as well. Thanks.

  • Guests
  • Last active:
  • Joined: --
No, it shouldn't: Unicode (UTF-16) -> 1 character = 2 bytes. :roll: