I currently have a method that uses ShellExecute to open files passed in by the user. Recently it was discovered that when a filename contains an apostrophe (ALT+0146) or Mac simple quote, ShellExecute returns a File Not Found Error. Example filename: "C:\Users\AMS\Documents\te'st.txt"
Code:
void sys_ShellExecute( PA_PluginParameters params )
{
LONG_PTR returnValue = 0;
char returnText[255]; // MWD & Mark De Wever #12225
INT_PTR howToShow;
char *pChar;
char *operation = NULL;
char *file = NULL;
char *parameters = NULL;
char *directory = NULL;
//PA_Unistring *file; // AMS 2/24/14
// Get the function parameters.
operation = getTextParameter(params, 1);
file = getTextParameter(params, 2);
//file = PA_GetStringParameter(params, 2); // AMS 2/24/14
parameters = getTextParameter(params, 3);
directory = getTextParameter(params, 4);
howToShow = PA_GetLongParameter( params, 5 );
if ((strcmp(_strlwr(operation), "open") != 0) &&
(strcmp(_strlwr(operation), "explore") != 0) &&
(strcmp(_strlwr(operation), "print") != 0) &&
(file == NULL || strlen(file) == 0)) {
//strcpy(returnText, "Invalid Operation");
strncpy(returnText, message->InvalidOperation, 255); // Mark De Wever #12225 replaced the line above
}
else if (howToShow > 11) {
//strcpy(returnText, "Invalid HowToShow Constant");
strncpy(returnText, message->InvalidShowConstant, 255); // Mark De Wever #12225 replaced the line above
}
else
{
pChar = file; // added 10/28/02 shellExecute wants backslashes
do {
if (*pChar == '/') {
*pChar = '\\';
}
} while (*pChar++ != '\0') ;
pChar = directory;
do {
if (*pChar == '/') {
*pChar = '\\';
}
} while (*pChar++ != '\0');
returnValue = (LONG_PTR) ShellExecute(NULL, operation, file, parameters, directory, howToShow);
strcpy(returnText, "");
if (returnValue <= 32) { // error occurred
switch (returnValue)
{
case ERROR_FILE_NOT_FOUND :
//strcpy(returnText, "File Not Found");
strncpy(returnText, message->FileNotFound, 255); // Mark De Wever #12225 replaced line above
break;
case ERROR_PATH_NOT_FOUND :
//strcpy(returnText, "Path Not Found");
strncpy(returnText, message->PathNotFound, 255); // Mark De Wever #12225 Replaced line above
break;
case ERROR_BAD_FORMAT :
//strcpy(returnText, ".EXE File is Invalid");
strncpy(returnText, message->BadFormat, 255); // Mark De Wever #12225 Replaced line above
break;
case SE_ERR_ACCESSDENIED :
//strcpy(returnText, "OS Denied Access to File");
strncpy(returnText, message->AccessDenied, 255); // Mark De Wever #12225 Replaced line above
break;
case SE_ERR_ASSOCINCOMPLETE :
//strcpy(returnText, "File Name Association is Incomplete or Invalid");
strncpy(returnText, message->AssocIncomplete, 255); // Mark De Wever #12225 Replaced line above
break;
case SE_ERR_DDEBUSY :
case SE_ERR_DDEFAIL :
//strcpy(returnText, "DDE Transaction Could Not be Completed");
strncpy(returnText, message->DDEFail, 255); // Mark De Wever #12225 Replaced line above
break;
case SE_ERR_DDETIMEOUT :
//strcpy(returnText, "DDE Request Timed Out");
strncpy(returnText, message->DDETimeOut, 255); // Mark De Wever #12225 Replaced line above
break;
case SE_ERR_DLLNOTFOUND :
//strcpy(returnText, "DLL Libray Not Found");
strncpy(returnText, message->DLLNotFound, 255); // Mark De Wever #12225 Replaced line above
break;
case SE_ERR_NOASSOC :
//strcpy(returnText, "No Application Associated with File Extenstion");
strncpy(returnText, message->NoAssoc, 255); // Mark De Wever #12225 Replaced line above
break;
case SE_ERR_OOM :
//strcpy(returnText, "Insufficient Memory");
strncpy(returnText, message->OOM, 255); // Mark De Wever #12225 Replaced line above
break;
case SE_ERR_SHARE :
//strcpy(returnText, "Sharing Violation Occurred");
strncpy(returnText, message->ShareViolation, 255); // Mark De Wever #12225 Replaced line above
break;
default:
//strcpy(returnText, "Unknown error occurred");
strncpy(returnText, message->Default, 255); // Mark De Wever #12225 Replaced line above
break;
}
}
}
freeTextParameter(operation);
freeTextParameter(file);
freeTextParameter(parameters);
freeTextParameter(directory);
PA_ReturnText( params, returnText, strlen(returnText));
}
getTextParameter code:
char * getTextParameter(PA_PluginParameters param, short index)
{
char *textValue = NULL;
LONG_PTR textLength = 0;
textLength = PA_GetTextParameter( param, index, 0L ) + 1;
// modified by Mark de Wever on 20060721
// textLength was always > 0 due to the +1
// revert change since sys_ShellExecute
// fails if NULL pointer is returned...
// So if malloc fails it will crash 4D
// if(textLength > 1) {
if(textLength > 0) {
textValue = (char *)malloc(textLength);
if(textValue != NULL) {
memset(textValue, 0, textLength);
textLength = PA_GetTextParameter( param, index, textValue );
}
}
return textValue;
}
PA_GetTextParameter code:
LONG_PTR PA_GetTextParameter( PA_PluginParameters params, short index, char* text )
{
PA_Unistring *UnistringText;
LONG_PTR length;
char *textParameter;
UnistringText = PA_GetStringParameter( params, index);
if(text != 0L){
textParameter = malloc((UnistringText->fLength + 1) * sizeof(char));
memset(textParameter, 0, ((UnistringText->fLength + 1) * sizeof(char)));
wcstombs(textParameter, UnistringText->fString, UnistringText->fLength);
textParameter[strlen(textParameter)] = '\0';
strcpy(text, textParameter);
free (textParameter);
length = strlen(text);
}else{
length = UnistringText->fLength;
}
return length;
}
I noticed that wcstombs returns the filename without the apostrophe in textParameter and the full filename in UnistringText->fString.
I have tried using ShellExecuteW, ShellExecuteA, setting file to a unicode string, and many other variations and nothing seems to work. Am I missing something? Is there a way to work around this?
L"C:\\Users\\AMS\\Documents\\te'st.txt"
), does it work? If so, don't apply any conversion to the original string (UnistringText->fString
) and it should work fine. – arx