Skip to content

Commit

Permalink
Add SHBrowseForFolder().
Browse files Browse the repository at this point in the history
Really, folder browse dialogs without an edit box should be outlawed. I mean,
all you have to do is to set that one flag. But sure, I get why you'd like to
be compatible to the first versions of Windows 95 that didn't ship with
Internet Explorer 4... (no, not really).

There's also no reason you wouldn't want the dialog to be resizable. Well,
believe it or not, specifying BIF_USENEWUI actually deadlocks the process if
COM happened to be initialized using the COINIT_MULTITHREADED flag. It's not
like we've even had to bother with initializing COM (despite what MSDN says),
and the Wine source code confirms that SHBrowseForFolder() does that itself
anyway.

So, OK. Can we retrieve that COINIT flag somehow?

Well, there's CoGetApartmentType() on Windows 7 and later, and luckily, you
can get the same functionality on older systems by mumbling some COM
incantation to query this deeply encapsulated piece of information *that
should have been public in the first place*. And to top it all off, MSDN
mentions the sli~ght potential of a race condition in all this querying, so
you better call CoGetApartmentType() if you can, which doesn't have that
problem.

Fuck yeah, OOP. And I'm trying way too hard, aren't I?

Also starting to re-#define the structures to default to the A version for
perfect transparency regardless of whether UNICODE is defined or not. Making
thcrap_configure compatible to this change is left as an exercise to its
future developers. Hint: It only takes one byte.
  • Loading branch information
nmlgc committed Dec 5, 2014
1 parent db836ce commit 9390ff5
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 2 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ In addition, this library also adds new useful functionality to some original Wi
* `CreateDirectoryU()` works recursively - the function creates all necessary directories to form the given path.
* `GetModuleFileNameU()` returns the necessary length of a buffer to hold the module file name if NULL is passed for `nSize` or `lpFilename`, similar to what `GetCurrentDirectory()` can do by default.

###### shell32.dll ######

* `SHBrowseForFolderU()` always displays an edit box and shows a resizable window if the active thread's COM settings allow it.

###### shlwapi.dll ######

* `PathRemoveFileSpecU()` correctly works as intended for paths containing forward slashes
75 changes: 74 additions & 1 deletion src/shell32_dll.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
#include "wrappers.h"

const w32u8_pair_t shell32_pairs[] = {
{"SHGetPathFromIDListA", SHGetPathFromIDListU},
{"DragQueryFileA", DragQueryFileU},
{"SHBrowseForFolderA", SHBrowseForFolderU},
{"SHGetPathFromIDListA", SHGetPathFromIDListU},
NULL
};

Expand Down Expand Up @@ -54,6 +55,78 @@ UINT WINAPI DragQueryFileU(
return ret;
}

// CoGetApartmentType() is not available prior to Windows 7, but luckily,
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd542641%28v=vs.85%29.aspx
// describes a way how to get the same functionality on previous systems.
static HRESULT CoGetApartmentTypeCompat(
_Out_ APTTYPE *apttype
)
{
int ret = S_FALSE;
APTTYPEQUALIFIER apttype_qualifier;
typedef HRESULT WINAPI CoGetApartmentType_t(
_Out_ APTTYPE *pAptType,
_Out_ APTTYPEQUALIFIER *pAptQualifier
);
// GetVersionEx() is deprecated with Windows 8.1, not everybody will
// have VersionHelpers.h, and this is a lot more fault-tolerant anyway.
HMODULE ole32 = LoadLibrary("ole32.dll");
CoGetApartmentType_t *cgat = NULL;

*apttype = APTTYPE_MTA;
if(!ole32) {
return S_FALSE;
}
cgat = (CoGetApartmentType_t*)GetProcAddress(ole32, "CoGetApartmentType");
if(cgat) {
ret = cgat(apttype, &apttype_qualifier);
} else {
IUnknown *ctx_token = NULL;
ret = CoGetContextToken((ULONG_PTR*)&ctx_token);
if(ret == S_OK) {
IComThreadingInfo *cti = NULL;
ret = IUnknown_QueryInterface(
ctx_token, &IID_IComThreadingInfo, (void**)&cti
);
if(ret == S_OK) {
ret = IComThreadingInfo_GetCurrentApartmentType(cti, apttype);
IUnknown_Release(cti);
}
} else if(ret == CO_E_NOTINITIALIZED) {
*apttype = APTTYPE_CURRENT;
}
}
FreeLibrary(ole32);
return ret;
}

PIDLIST_ABSOLUTE WINAPI SHBrowseForFolderU(
__in LPBROWSEINFOA lpbi
)
{
APTTYPE apttype;
PIDLIST_ABSOLUTE ret;
wchar_t pszDisplayName_w[MAX_PATH];
const char *lpszTitle = lpbi->lpszTitle;
BROWSEINFOW lpbi_w = *((BROWSEINFOW*)lpbi);
WCHAR_T_DEC(lpszTitle);
WCHAR_T_CONV(lpszTitle);

// Use the new UI if we can
CoGetApartmentTypeCompat(&apttype);
if(apttype != APTTYPE_MTA) {
lpbi_w.ulFlags |= BIF_USENEWUI;
}
// Really, folder browse dialogs without edit box should be outlawed.
lpbi_w.ulFlags |= BIF_EDITBOX;
lpbi_w.pszDisplayName = pszDisplayName_w;
lpbi_w.lpszTitle = lpszTitle_w;
ret = SHBrowseForFolderW(&lpbi_w);
StringToUTF8(lpbi->pszDisplayName, pszDisplayName_w, MAX_PATH);
WCHAR_T_FREE(lpszTitle);
return ret;
}

BOOL WINAPI SHGetPathFromIDListU(
__in PCIDLIST_ABSOLUTE pidl,
__out_ecount(MAX_PATH) LPSTR pszPath
Expand Down
12 changes: 12 additions & 0 deletions src/shell32_dll.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ UINT WINAPI DragQueryFileU(
#undef DragQueryFile
#define DragQueryFile DragQueryFileU

PIDLIST_ABSOLUTE WINAPI SHBrowseForFolderU(
__in LPBROWSEINFOA lpbi
);
#undef SHBrowseForFolder
#define SHBrowseForFolder SHBrowseForFolderU
#undef BROWSEINFO
#undef PBROWSEINFO
#undef LPBROWSEINFO
#define BROWSEINFO BROWSEINFOA
#define PBROWSEINFO PBROWSEINFOA
#define LPBROWSEINFO LPBROWSEINFOA

BOOL WINAPI SHGetPathFromIDListU(
__in PCIDLIST_ABSOLUTE pidl,
__out_ecount(MAX_PATH) LPSTR pszPath
Expand Down
1 change: 1 addition & 0 deletions win32_utf8.def
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ EXPORTS
; shell32.dll
; -----------
DragQueryFileU
SHBrowseForFolderU
SHGetPathFromIDListU

; shlwapi.dll
Expand Down
2 changes: 1 addition & 1 deletion win32_utf8.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>PSAPI_VERSION=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>PSAPI_VERSION=1;CINTERFACE;COBJMACROS;%(PreprocessorDefinitions)</PreprocessorDefinitions>

This comment has been minimized.

Copy link
@nmlgc

nmlgc Dec 5, 2014

Author Contributor

@MuffinPimp, if you still want to switch to CMake, be sure to add these two new #defines to your CMakeLists.

<AdditionalIncludeDirectories>$(ProjectDir)..\src\win32_utf8;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
</ClCompile>
Expand Down

0 comments on commit 9390ff5

Please sign in to comment.