Did SHOpenWithDialog API change in Windows 10? - winapi

I have a function which allows the user to edit a file, either with their default program, or choose a program using SHOpenFileDialog().
I'm calling it by PInvoke in C# (https://www.pinvoke.net/default.aspx/shell32/SHOpenWithDialog.html):
[DllImport("shell32.dll", EntryPoint = "SHOpenWithDialog", CharSet = CharSet.Unicode)]
private static extern int SHOpenWithDialog(IntPtr hWndParent, ref tagOPENASINFO oOAI);
I call it with flags OAIF_HIDE_REGISTRATION | OAIF_EXEC.
Under Windows 7, it returns 1 on success. Under Windows 10, it returns 0 on success. The documentation claims it returns S_OK, which is 0, but that page was created on 2018/12/05, so I don't know what the previous documentation for it said.
What is the correct way to check the return code? Should I be checking for either 0 or 1 return value? Or should I be checking the Environment.OSVersion and checking for different return codes based on the OS version? Can it ever returns 1 under Windows 10 as an error condition?

Related

Enabling Closed-Display Mode w/o Meeting Apple's Requirements

EDIT:
I have heavily edited this question after making some significant new discoveries and the question not having any answers yet.
Historically/AFAIK, keeping your Mac awake while in closed-display mode and not meeting Apple's requirements, has only been possible with a kernel extension (kext), or a command run as root. Recently however, I have discovered that there must be another way. I could really use some help figuring out how to get this working for use in a (100% free, no IAP) sandboxed Mac App Store (MAS) compatible app.
I have confirmed that some other MAS apps are able to do this, and it looks like they might be writing YES to a key named clamshellSleepDisabled. Or perhaps there's some other trickery involved that causes the key value to be set to YES? I found the function in IOPMrootDomain.cpp:
void IOPMrootDomain::setDisableClamShellSleep( bool val )
{
if (gIOPMWorkLoop->inGate() == false) {
gIOPMWorkLoop->runAction(
OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::setDisableClamShellSleep),
(OSObject *)this,
(void *)val);
return;
}
else {
DLOG("setDisableClamShellSleep(%x)\n", (uint32_t) val);
if ( clamshellSleepDisabled != val )
{
clamshellSleepDisabled = val;
// If clamshellSleepDisabled is reset to 0, reevaluate if
// system need to go to sleep due to clamshell state
if ( !clamshellSleepDisabled && clamshellClosed)
handlePowerNotification(kLocalEvalClamshellCommand);
}
}
}
I'd like to give this a try and see if that's all it takes, but I don't really have any idea about how to go about calling this function. It's certainly not a part of the IOPMrootDomain documentation, and I can't seem to find any helpful example code for functions that are in the IOPMrootDomain documentation, such as setAggressiveness or setPMAssertionLevel. Here's some evidence of what's going on behind the scenes according to Console:
I've had a tiny bit of experience working with IOMProotDomain via adapting some of ControlPlane's source for another project, but I'm at a loss for how to get started on this. Any help would be greatly appreciated. Thank you!
EDIT:
With #pmdj's contribution/answer, this has been solved!
Full example project:
https://github.com/x74353/CDMManager
This ended up being surprisingly simple/straightforward:
1. Import header:
#import <IOKit/pwr_mgt/IOPMLib.h>
2. Add this function in your implementation file:
IOReturn RootDomain_SetDisableClamShellSleep (io_connect_t root_domain_connection, bool disable)
{
uint32_t num_outputs = 0;
uint32_t input_count = 1;
uint64_t input[input_count];
input[0] = (uint64_t) { disable ? 1 : 0 };
return IOConnectCallScalarMethod(root_domain_connection, kPMSetClamshellSleepState, input, input_count, NULL, &num_outputs);
}
3. Use the following to call the above function from somewhere else in your implementation:
io_connect_t connection = IO_OBJECT_NULL;
io_service_t pmRootDomain = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPMrootDomain"));
IOServiceOpen (pmRootDomain, current_task(), 0, &connection);
// 'enable' is a bool you should assign a YES or NO value to prior to making this call
RootDomain_SetDisableClamShellSleep(connection, enable);
IOServiceClose(connection);
I have no personal experience with the PM root domain, but I do have extensive experience with IOKit, so here goes:
You want IOPMrootDomain::setDisableClamShellSleep() to be called.
A code search for sites calling setDisableClamShellSleep() quickly reveals a location in RootDomainUserClient::externalMethod(), in the file iokit/Kernel/RootDomainUserClient.cpp. This is certainly promising, as externalMethod() is what gets called in response to user space programs calling the IOConnectCall*() family of functions.
Let's dig in:
IOReturn RootDomainUserClient::externalMethod(
uint32_t selector,
IOExternalMethodArguments * arguments,
IOExternalMethodDispatch * dispatch __unused,
OSObject * target __unused,
void * reference __unused )
{
IOReturn ret = kIOReturnBadArgument;
switch (selector)
{
…
…
…
case kPMSetClamshellSleepState:
fOwner->setDisableClamShellSleep(arguments->scalarInput[0] ? true : false);
ret = kIOReturnSuccess;
break;
…
So, to invoke setDisableClamShellSleep() you'll need to:
Open a user client connection to IOPMrootDomain. This looks straightforward, because:
Upon inspection, IOPMrootDomain has an IOUserClientClass property of RootDomainUserClient, so IOServiceOpen() from user space will by default create an RootDomainUserClient instance.
IOPMrootDomain does not override the newUserClient member function, so there are no access controls there.
RootDomainUserClient::initWithTask() does not appear to place any restrictions (e.g. root user, code signing) on the connecting user space process.
So it should simply be a case of running this code in your program:
io_connect_t connection = IO_OBJECT_NULL;
IOReturn ret = IOServiceOpen(
root_domain_service,
current_task(),
0, // user client type, ignored
&connection);
Call the appropriate external method.
From the code excerpt earlier on, we know that the selector must be kPMSetClamshellSleepState.
arguments->scalarInput[0] being zero will call setDisableClamShellSleep(false), while a nonzero value will call setDisableClamShellSleep(true).
This amounts to:
IOReturn RootDomain_SetDisableClamShellSleep(io_connect_t root_domain_connection, bool disable)
{
uint32_t num_outputs = 0;
uint64_t inputs[] = { disable ? 1 : 0 };
return IOConnectCallScalarMethod(
root_domain_connection, kPMSetClamshellSleepState,
&inputs, 1, // 1 = length of array 'inputs'
NULL, &num_outputs);
}
When you're done with your io_connect_t handle, don't forget to IOServiceClose() it.
This should let you toggle clamshell sleep on or off. Note that there does not appear to be any provision for automatically resetting the value to its original state, so if your program crashes or exits without cleaning up after itself, whatever state was last set will remain. This might not be great from a user experience perspective, so perhaps try to defend against it somehow, for example in a crash handler.

Ignoring exclusively locked files in SHFileOperation

I am using Windows 7 Professional and I am using SHFileOperation() to recursive copy one folder contents to another. But there is a locked file (opened exclusively by an application); I need to skip it, but SHFileOperation() returns 0x20 when tries to copy this file.
How can I skip this file during the file copy operation?
UPDATE: this is the code:
//
// CopyDirectory()
// рекурсивное копирование содержимого одной директории в другую средствами Windows
// lpszSource - исходная папка
// lpszDestination - папка назначения
//
BOOL CopyDirectory( LPSTR lpszSource, LPSTR lpszDestination )
{
LPSTR lpszNewSource = NULL;
// структура операции с файлами
SHFILEOPSTRUCT fileOP = { 0 };
// выделим память под новый путь
lpszNewSource = (LPSTR)calloc(strlen(lpszSource) + 50, 1);
// запишем новый путь с маской
wsprintf(lpszNewSource, "%s\\*", lpszSource);
// запишем параметры операции копирования
fileOP.wFunc = FO_COPY;
fileOP.pTo = lpszDestination;
fileOP.pFrom = lpszSource;
fileOP.fFlags = FOF_SILENT | FOF_NOCONFIRMMKDIR | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NO_UI;
// выполняем операцию
INT retVal = SHFileOperation( &fileOP );
// освободим память
FREE_NULL(lpszNewSource);
DebugPrint(DEBUG_INFO, "retVal = %d\n", retVal);
// возвращаем результат копирования
return retVal == 0;
}
The SHFileOperation() documentation says:
Return value
Type: int
Returns zero if successful; otherwise nonzero. Applications normally should simply check for zero or nonzero.
It is good practice to examine the value of the fAnyOperationsAborted member of the SHFILEOPSTRUCT. SHFileOperation can return 0 for success if the user cancels the operation. If you do not check fAnyOperationsAborted as well as the return value, you cannot know that the function accomplished the full task you asked of it and you might proceed under incorrect assumptions.
Do not use GetLastError with the return values of this function.
To examine the nonzero values for troubleshooting purposes, they largely map to those defined in Winerror.h. However, several of its possible return values are based on pre-Win32 error codes, which in some cases overlap the later Winerror.h values without matching their meaning. Those particular values are detailed here, and for these specific values only these meanings should be accepted over the Winerror.h codes.
In your case, 0x20 is not one of the pre-Win32 error codes, so it maps to a standard Win32 error code, specifically ERROR_SHARING_VIOLATION, which is appropriate for your situation as one of the files could not be accessed.
To skip the offending file, enable the FOF_NOERRORUI flag in the SHFILEOPSTRUCT::fFlags field. The SHFILEOPSTRUCT documentation says only the following about that flag:
FOF_NOERRORUI
Do not display a dialog to the user if an error occurs.
However, the documentation does also say this:
fAnyOperationsAborted
Type: BOOL
When the function returns, this member contains TRUE if any file operations were aborted before they were completed; otherwise, FALSE. An operation can be manually aborted by the user through UI or it can be silently aborted by the system if the FOF_NOERRORUI or FOF_NOCONFIRMATION flags were set.
The documentation for IFileOperation (which replaces SHFileOperation() on Vista and later) has more to say about the FOF_NOERRORUI flag:
FOF_NOERRORUI (0x0400)
Do not display a message to the user if an error occurs. If this flag is set without FOFX_EARLYFAILURE, any error is treated as if the user had chosen Ignore or Continue in a dialog box. It halts the current action, sets a flag to indicate that an action was aborted, and proceeds with the rest of the operation.
...
FOFX_EARLYFAILURE (0x00100000)
If FOFX_EARLYFAILURE is set together with FOF_NOERRORUI, the entire set of operations is stopped upon encountering any error in any operation. This flag is valid only when FOF_NOERRORUI is set.
So, with FOF_NOERRORUI enabled, the return value of ERROR_SHARING_VIOLATION, and also the SHFILEOPSTRUCT::fAnyOperationsAborted field being set to TRUE, will tell you that a file could not be accessed during the copy, but not which file specifically. It does not mean that the entire SHFileOperation() task failed completely.

Mac Sandbox: testing whether a file is accessible

Does anybody know whether there's a way of finding out whether a particular file system location is accessible under the sandbox?
I want to test whether a particular file is accessible under the normal Powerbox rules; that is has already been added to the power box using the open/ save dialog, etc.
Can I do this before triggering a sandbox exception?
Can I catch a sandbox exception?
Best regards,
Frank
You can use the OS access() system call for a quick and simple test, from man access:
#include <unistd.h>
int access(const char *path, int amode);
The access() function checks the accessibility of the file named by path
for the access permissions indicated by amode. The value of amode is the
bitwise inclusive OR of the access permissions to be checked (R_OK for
read permission, W_OK for write permission and X_OK for execute/search
permission) or the existence test, F_OK. All components of the pathname
path are checked for access permissions (including F_OK).
If path cannot be found or if any of the desired access modes would not
be granted, then a -1 value is returned and the global integer variable
errno is set to indicate the error. Otherwise, a 0 value is returned.
You could pretty this up for Objective-C using something like:
typedef enum
{
ReadAccess = R_OK,
WriteAccess = W_OK,
ExecuteAccess = X_OK,
PathExists = F_OK
} AccessKind;
BOOL isPathAccessible(NSString *path, AccessKind mode)
{
return access([path UTF8String], mode) == 0;
}
A few things. Always use fileSystemRepresentation when you need a path string. Also, R_OK is adequate if you just want to know if there is a hole in the sandbox for the specified path.
-(BOOL)isAccessibleFromSandbox:(NSString*)path
{
return( access( path.fileSystemRepresentation, R_OK) == 0 );
}

Unable to connect to ftp server

I'm trying to connect to an ftp server through a powerbuilder application using windows api functions. I manage to connect to the ftp server through Internet Explorer (so I don't think it's a permission problem) but the application fails for some unknown reason.
String ls_Null, &
ls_id
Integer li_rc
li_rc = 1
IF Not InternetAutodial(AUTODIAL_FORCE_UNATTENDED, 0) THEN
f_write_to_err_log('IMPORT Unable To Connect Internet - Dialup')
li_rc = -1
ELSE
SetNull(ls_Null)
ls_id = "Care_Dsend"
al_internet_handle = InternetOpen(ls_id, INTERNET_OPEN_TYPE_DIRECT, ls_Null, ls_Null, 0)
IF al_internet_handle > 0 THEN
al_ftp_connect_handle = InternetConnect(al_internet_handle, is_ftp_url, il_ftp_port, is_ftp_user, is_ftp_password, INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, al_ref)
END IF
END IF
Return li_rc
//al_internet_handle, al_ftp_connect_handle are by ref long parms
//al_ref is a by ref unsignedlong parm
//is_ftp_url, is_ftp_user, is_ftp_password are strings
//il_ftp_port is long
The function manages to return a handle from the InternetOpen api function, but returns 0 from the InternetConnect function.
Any ideas?
Ok, I found out what the problem was. I was missing ;Ansi at the end of my function declaration as in :
Function ulong InternetOpen (ref
string lpszAgent, ulong dwAccessType,
ref string lpszProxy, ref string
lpszProxyBypass, ulong dwFlags)
Library "WININET.DLL" Alias for
"InternetOpenA ;Ansi"
Can you post your Local External Functions or whatever you are using for InternetAutodial(), InternetOpen() etc? Those are not built-in functions (at least not in my Powerbuilder 11.2) and they don't look like PFC. Also, what version of Powerbuilder are you using.
Alternatively, you might want to look at these links:
Upload a file using FTP - Real's Powerbuilder HowTo (also uses a windows batch file)
Internet services example - PFC guide (uses PFC)

Determine the registered application for an extension

I've got a file extension and I'd like to get the name of the application (if there is one) that will be invoked when I ShellExecute a file of that type. This is a WTL/C++ app. Is there any sample code out there that does this?
Thanks!
twk,
You're probably looking for the Win32 AssocQueryStringByKey Function.
http://msdn.microsoft.com/en-us/library/bb773473(VS.85).aspx
The ASSOCSTR value that specifies the type of string that is to be returned:
typedef enum {
ASSOCSTR_COMMAND = 1,
ASSOCSTR_EXECUTABLE,
ASSOCSTR_FRIENDLYDOCNAME,
ASSOCSTR_FRIENDLYAPPNAME,
ASSOCSTR_NOOPEN,
ASSOCSTR_SHELLNEWVALUE,
ASSOCSTR_DDECOMMAND,
ASSOCSTR_DDEIFEXEC,
ASSOCSTR_DDEAPPLICATION,
ASSOCSTR_DDETOPIC,
ASSOCSTR_INFOTIP,
ASSOCSTR_QUICKTIP,
ASSOCSTR_TILEINFO,
ASSOCSTR_CONTENTTYPE,
ASSOCSTR_DEFAULTICON,
ASSOCSTR_SHELLEXTENSION,
ASSOCSTR_DROPTARGET,
ASSOCSTR_DELEGATEEXECUTE,
ASSOCSTR_MAX
} ASSOCSTR;
My guess is that you want ASSOCSTR_FRIENDLYAPPNAME.
DWORD dwSize = 255;
TCHAR sBuffer[MAX_PATH] = {0};
HRESULT hr = AssocQueryString(0, ASSOCSTR_EXECUTABLE, _T(".htm"), _T("Open"), sBuffer, &dwSize);
CString csExt;
csExt.Format(_T("%s"), sBuffer);
AfxMessageBox(csExt);
Sorry, no code, but some useful information. See this related question: how-does-vista-generate-the-icon-for-documents-associated-to-my-application
It asked about icons, but it turns out the program associated to an extension is stored in the same place in the registry as the icon for that extension.
It's a Win32 FAQ since 1995 (Shell, see Google Groups, Win32)

Resources