Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Shell32.SHGetKnownFolderPath and corresponding constants #334

Merged
merged 3 commits into from
May 26, 2014
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Features
* Added Winspool monitor sample and updated Kernel32, WinBase, Winspool - [@wolftobias](https://github.com/wolftobias).
* Added Some minor changes to MS Office samples Test and small changes to the MS Office samples Bug Fixes - [@wolftobias](https://github.com/wolftobias).
* [#333](https://github.com/twall/jna/pull/333): Added `CoTaskMemAlloc`, `CoTaskMemRealloc` and `CoTaskMemFree` to `com.sun.jna.platform.win32.Ole32` - [@msteiger](https://github.com/msteiger).
* [#334](https://github.com/twall/jna/pull/334): Added `com.sun.jna.platform.win32.Shell32.SHGetKnownFolderPath` and `KnownFolders` GUID constants - [@msteiger](https://github.com/msteiger).

Bug Fixes
---------
Expand Down
329 changes: 329 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/KnownFolders.java

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/Shell32.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package com.sun.jna.platform.win32;

import com.sun.jna.Native;
import com.sun.jna.platform.win32.Guid.GUID;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.INT_PTR;
Expand Down Expand Up @@ -65,6 +66,36 @@ public interface Shell32 extends ShellAPI, StdCallLibrary {
HRESULT SHGetFolderPath(HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags,
char[] pszPath);

/**
* Retrieves the full path of a known folder identified by the folder's KNOWNFOLDERID. This function replaces
* {@link #SHGetFolderPath}. That older function is now simply a wrapper for SHGetKnownFolderPath.
* @param rfid A reference to the KNOWNFOLDERID (in {@link KnownFolders}) that identifies the folder.
* @param dwFlags Flags that specify special retrieval options. This value can be 0; otherwise, one or more of the
* {@link ShlObj.KNOWN_FOLDER_FLAG} values.
* @param hToken Type: HANDLE An access token that represents a particular user. If this parameter is NULL, which is
* the most common usage, the function requests the known folder for the current user. Request a specific user's
* folder by passing the hToken of that user. This is typically done in the context of a service that has sufficient
* privileges to retrieve the token of a given user. That token must be opened with TOKEN_QUERY and
* TOKEN_IMPERSONATE rights. In some cases, you also need to include TOKEN_DUPLICATE. In addition to passing the
* user's hToken, the registry hive of that specific user must be mounted. See Access Control for further discussion
* of access control issues. Assigning the hToken parameter a value of -1 indicates the Default User. This allows
* clients of SHGetKnownFolderPath to find folder locations (such as the Desktop folder) for the Default User. The
* Default User user profile is duplicated when any new user account is created, and includes special folders such
* as Documents and Desktop. Any items added to the Default User folder also appear in any new user account. Note
* that access to the Default User folders requires administrator privileges.
* @param ppszPath When this method returns, contains the address of a pointer to a null-terminated
* Unicode string that specifies the path of the known folder. The calling process is responsible for freeing this
* resource once it is no longer needed by calling {@link Ole32#CoTaskMemFree}. The returned path does not include a trailing
* backslash. For example, "C:\Users" is returned rather than "C:\Users\".
* @return Returns S_OK if successful, or an error value otherwise, including the following:
* <li>E_FAIL Among other things, this value can indicate that the rfid parameter references a KNOWNFOLDERID which
* does not have a path (such as a folder marked as KF_CATEGORY_VIRTUAL).</li>
* <li>E_INVALIDARG Among other things, this value can indicate that the rfid parameter references a KNOWNFOLDERID
* that is not present on the system. Not all KNOWNFOLDERID values are present on all systems. Use
* IKnownFolderManager::GetFolderIds to retrieve the set of KNOWNFOLDERID values for the current system.</li>
*/
HRESULT SHGetKnownFolderPath(GUID rfid, int dwFlags, HANDLE hToken, PointerByReference ppszPath);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to use LPSTR.ByReference here, but the returned string was garbage. Can't really explain why ..


/**
* Retrieves the IShellFolder interface for the desktop folder, which is the root of the Shell's namespace.
* The retrieved COM interface pointer can be used via Com4JNA's ComObject.wrapNativeInterface call
Expand Down
32 changes: 32 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/Shell32Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@
package com.sun.jna.platform.win32;

import com.sun.jna.Native;
import com.sun.jna.platform.win32.Guid.GUID;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPVOID;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.ptr.PointerByReference;

/**
* Shell32 Utility API.
Expand Down Expand Up @@ -56,6 +60,34 @@ public static String getFolderPath(HWND hwnd, int nFolder, DWORD dwFlags) {
public static String getFolderPath(int nFolder) {
return getFolderPath(null, nFolder, ShlObj.SHGFP_TYPE_CURRENT);
}

/**
* Retrieves the full path of a known folder identified by the folder's KNOWNFOLDERID. This function replaces
* {@link #getFolderPath}. That older function is now simply a wrapper for getKnownFolderPath
* @param guid the KNOWNFOLDERS GUID as defined in {@link KnownFolders}
* @return the path of the known folder. The returned path does not include a trailing backslash. For example,
* "C:\Users" is returned rather than "C:\Users\".
* @throws Win32Exception if the guid references a KNOWNFOLDERID which does not have a path (such as a folder marked
* as KF_CATEGORY_VIRTUAL) or that the KNOWNFOLDERID is not present on the system. Not all KNOWNFOLDERID values are
* present on all systems.
*/
public static String getKnownFolderPath(GUID guid) throws Win32Exception
{
int flags = ShlObj.KNOWN_FOLDER_FLAG.NONE.getFlag();
PointerByReference outPath = new PointerByReference();
HANDLE token = null;
HRESULT hr = Shell32.INSTANCE.SHGetKnownFolderPath(guid, flags, token, outPath);

if (!W32Errors.SUCCEEDED(hr.intValue()))
{
throw new Win32Exception(hr);
}

String result = outPath.getValue().getWideString(0);
Ole32.INSTANCE.CoTaskMemFree(new LPVOID(outPath.getPointer().getLong(0)));

return result;
}

/**
* Retrieves the path of a special folder, identified by its CSIDL.
Expand Down
98 changes: 98 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/ShlObj.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,102 @@ public interface ShlObj {
int CSIDL_COMMON_OEM_LINKS = 0x003a; // Links to All Users OEM specific apps
int CSIDL_CDBURN_AREA = 0x003b; // USERPROFILE\Local Settings\Application Data\Microsoft\CD Burning
int CSIDL_COMPUTERSNEARME = 0x003d; // Computers Near Me (computered from Workgroup membership)

/**
* KnownFolder flags as used by SHGetKnownFolderPath, SHGetKnownFolderIDList and others.
* Microsoft Windows SDK 7.0A.
*/
public enum KNOWN_FOLDER_FLAG
{
/**
* None
*/
NONE(0x00000000),

/**
* Build a simple IDList (PIDL) This value can be used when you want to retrieve the file system path but do not
* specify this value if you are retrieving the localized display name of the folder because it might not
* resolve correctly.
*/
SIMPLE_IDLIST(0x00000100),

/**
* Gets the folder's default path independent of the current location of its parent. KF_FLAG_DEFAULT_PATH must
* also be set.
*/
NOT_PARENT_RELATIVE(0x00000200),

/**
* Gets the default path for a known folder. If this flag is not set, the function retrieves the current-and
* possibly redirected-path of the folder. The execution of this flag includes a verification of the folder's
* existence unless KF_FLAG_DONT_VERIFY is set.
*/
DEFAULT_PATH(0x00000400),

/**
* Initializes the folder using its Desktop.ini settings. If the folder cannot be initialized, the function
* returns a failure code and no path is returned. This flag should always be combined with KF_FLAG_CREATE.
*/
INIT(0x00000800),

/**
* Gets the true system path for the folder, free of any aliased placeholders such as %USERPROFILE%, returned by
* SHGetKnownFolderIDList and IKnownFolder::GetIDList. This flag has no effect on paths returned by
* SHGetKnownFolderPath and IKnownFolder::GetPath. By default, known folder retrieval functions and methods
* return the aliased path if an alias exists.
*/
NO_ALIAS(0x00001000),

/**
* Stores the full path in the registry without using environment strings. If this flag is not set, portions of
* the path may be represented by environment strings such as %USERPROFILE%. This flag can only be used with
* SHSetKnownFolderPath and IKnownFolder::SetPath.
*/
DONT_UNEXPAND(0x00002000),

/**
* Do not verify the folder's existence before attempting to retrieve the path or IDList. If this flag is not
* set, an attempt is made to verify that the folder is truly present at the path. If that verification fails
* due to the folder being absent or inaccessible, the function returns a failure code and no path is returned.
* If the folder is located on a network, the function might take a longer time to execute. Setting this flag
* can reduce that lag time.
*/
DONT_VERIFY(0x00004000),

/**
* Forces the creation of the specified folder if that folder does not already exist. The security provisions
* predefined for that folder are applied. If the folder does not exist and cannot be created, the function
* returns a failure code and no path is returned. This value can be used only with the following functions and
* methods:
* <li>SHGetKnownFolderPath</li>
* <li>SHGetKnownFolderIDList</li>
* <li>IKnownFolder::GetIDList</li>
* <li>IKnownFolder::GetPath</li>
* <li>IKnownFolder::GetShellItem</li>
*/
CREATE(0x00008000),
/**
* Introduced in Windows 7: When running inside an app container, or when providing an app container token, this
* flag prevents redirection to app container folders. Instead, it retrieves the path that would be returned
* where it not running inside an app container.
*/
NO_APPCONTAINER_REDIRECTION(0x00010000),

/**
* Introduced in Windows 7. Return only aliased PIDLs. Do not use the file system path.
*/
ALIAS_ONLY(0x80000000);

private int flag;

KNOWN_FOLDER_FLAG(int flag)
{
this.flag = flag;
}

public int getFlag()
{
return flag;
}
}
}
16 changes: 16 additions & 0 deletions contrib/platform/test/com/sun/jna/platform/win32/Shell32Test.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@
import junit.framework.TestCase;

import com.sun.jna.Native;
import com.sun.jna.platform.win32.Guid.GUID;
import com.sun.jna.platform.win32.ShellAPI.APPBARDATA;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.LPVOID;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinDef.UINT_PTR;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.ptr.PointerByReference;


Expand Down Expand Up @@ -127,4 +131,16 @@ public void testResizeDesktopFromTop() throws InterruptedException {

}

public void testSHGetKnownFolderPath()
{
int flags = ShlObj.KNOWN_FOLDER_FLAG.NONE.getFlag();
PointerByReference outPath = new PointerByReference();
HANDLE token = null;
GUID guid = KnownFolders.FOLDERID_Fonts;
HRESULT hr = Shell32.INSTANCE.SHGetKnownFolderPath(guid, flags, token, outPath);

Ole32.INSTANCE.CoTaskMemFree(new LPVOID(outPath.getPointer().getLong(0)));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe LPVOID should have a LPVOID(Pointer p) constructor? Or maybe CoTaskMemFree should take a Pointer?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does declaring a CoTaskMemFree that takes Pointer work by itself? It's likely that JNA will marshal that appropriately.

Otherwise I would declare another version of CoTaskMemFree which takes a Pointer, wrapping the LPVOID one.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test could retrieve a well known path and should examine the string result. For example, FOLDERID_Cookies always ends with Cookies (even on non-English systems I believe).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About testing: Unfortunately, most folder names depend on the language. Also, many of them can be changed by the user (e.g. home folder), so we might have to rely on the HRESULT return value. I assumed that the focus of the testing should be on the correctness of the JNA mapping, not the actual Win32 API methods.

CoTaskMemFree: It also works with a plain Pointer. Would it make sense to convert the allocation method parameters from LPVOID to Pointer, too? It would be more consistent I guess..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the definition of LPVOID is incorrect. From WinDef.h, the definition is

typedef void *LPVOID;

Which means it should be of JNA type PointerType and not derived from LONG_PTR.

On May 25, 2014, at 4:02 AM, Martin Steiger notifications@github.com wrote:

In contrib/platform/test/com/sun/jna/platform/win32/Shell32Test.java:

@@ -127,4 +131,16 @@ public void testResizeDesktopFromTop() throws InterruptedException {

 }
  • public void testSHGetKnownFolderPath()
  • {
  •    int flags = ShlObj.KNOWN_FOLDER_FLAG.NONE.getFlag();
    
  •    PointerByReference outPath = new PointerByReference();
    
  •    HANDLE token = null;
    
  •    GUID guid = KnownFolders.FOLDERID_Fonts;
    
  •    HRESULT hr = Shell32.INSTANCE.SHGetKnownFolderPath(guid, flags, token, outPath);
    
  •    Ole32.INSTANCE.CoTaskMemFree(new LPVOID(outPath.getPointer().getLong(0)));
    

About testing: Unfortunately, most folder names depend on the language. Also, many of them can be changed by the user (e.g. home folder), so we might have to rely on the HRESULT return value. I assumed that the focus of the testing should be on the correctness of the JNA mapping, not the actual Win32 API methods.

CoTaskMemFree: It also works with a plain Pointer. Would it make sense to convert the allocation method parameters from LPVOID to Pointer, too? It would be more consistent I guess..


Reply to this email directly or view it on GitHub.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want me to change that or prefer doing that yourself?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can make the correction, I think it’s appropriate for your patch. It’ll fix your “initialize from pointer” issue, although it does introduce an incompatible binary change if someone happens to be doing anything with LPVOID other than passing it around.

On May 25, 2014, at 8:10 AM, Martin Steiger notifications@github.com wrote:

In contrib/platform/test/com/sun/jna/platform/win32/Shell32Test.java:

@@ -127,4 +131,16 @@ public void testResizeDesktopFromTop() throws InterruptedException {

 }
  • public void testSHGetKnownFolderPath()
  • {
  •    int flags = ShlObj.KNOWN_FOLDER_FLAG.NONE.getFlag();
    
  •    PointerByReference outPath = new PointerByReference();
    
  •    HANDLE token = null;
    
  •    GUID guid = KnownFolders.FOLDERID_Fonts;
    
  •    HRESULT hr = Shell32.INSTANCE.SHGetKnownFolderPath(guid, flags, token, outPath);
    
  •    Ole32.INSTANCE.CoTaskMemFree(new LPVOID(outPath.getPointer().getLong(0)));
    

Do you want me to change that or prefer doing that yourself?


Reply to this email directly or view it on GitHub.


assertTrue(W32Errors.SUCCEEDED(hr.intValue()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,42 @@ public void testGetFolderPath() {
public final void testGetSpecialFolderPath() {
assertFalse(Shell32Util.getSpecialFolderPath(ShlObj.CSIDL_APPDATA, false).isEmpty());
}

public void testGetKnownFolderPath()
{
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_Fonts));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_Desktop));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_Startup));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_Programs));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_StartMenu));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_Recent));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_SendTo));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_Documents));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_Favorites));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_NetHood));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_PrintHood));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_Templates));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_CommonStartup));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_CommonAdminTools));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_CDBurning));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_Music));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_SavedGames));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_SavedSearches));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_AdminTools));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_ProgramFiles));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_ProgramData));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_ProgramFilesCommon));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_ProgramFilesCommonX86));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_Programs));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_Windows));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_Public));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_PublicDesktop));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_Links));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_LocalAppData));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_Libraries));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_RoamingAppData));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_UserProfiles));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_UserProgramFiles));
assertNotNull(Shell32Util.getKnownFolderPath(KnownFolders.FOLDERID_UserProgramFilesCommon));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe there's a way to enumerate those via reflection? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but unfortunately, it won't help much. Many constants don't point a unique folder and getKnownFolderPath() fails for them. I manually picked those who work on Windows 7.

The constants were converted automatically using reg-exp search & replace, so I assume that either all of them are correct or none.

}