1///////////////////////////////////////////////////////////////////////////// 2// Name: mac/corefoundation/utilsexc_base.cpp 3// Purpose: wxMacExecute 4// Author: Ryan Norton 5// Modified by: 6// Created: 2005-06-21 7// RCS-ID: $Id: utilsexc_base.cpp 40283 2006-07-24 18:01:39Z VZ $ 8// Copyright: (c) Ryan Norton 9// Licence: wxWindows licence 10// Notes: Source was originally in utilsexc_cf.cpp,1.6 then moved 11// to totally unrelated hid.cpp,1.8. 12///////////////////////////////////////////////////////////////////////////// 13 14//=========================================================================== 15// DECLARATIONS 16//=========================================================================== 17 18//--------------------------------------------------------------------------- 19// Pre-compiled header stuff 20//--------------------------------------------------------------------------- 21 22// For compilers that support precompilation, includes "wx.h". 23#include "wx/wxprec.h" 24 25// WX includes 26#ifndef WX_PRECOMP 27 #include "wx/string.h" 28 #include "wx/log.h" 29 #include "wx/intl.h" 30 #include "wx/utils.h" 31#endif // WX_PRECOMP 32 33// Mac Includes 34#include <CoreFoundation/CoreFoundation.h> 35#include <ApplicationServices/ApplicationServices.h> 36 37// More WX Includes 38#include "wx/filename.h" 39#include "wx/mac/corefoundation/cfstring.h" 40 41// Default path style 42#ifdef __WXMAC_OSX__ 43#define kDefaultPathStyle kCFURLPOSIXPathStyle 44#else 45#define kDefaultPathStyle kCFURLHFSPathStyle 46#endif 47 48//=========================================================================== 49// IMPLEMENTATION 50//=========================================================================== 51 52//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 53// 54// wxMacExecute 55// 56// argv is the command line split up, with the application path first 57// flags are the flags from wxExecute 58// process is the process passed from wxExecute for pipe streams etc. 59// returns -1 on error for wxEXEC_SYNC and 0 on error for wxEXEC_ASYNC 60//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 61long wxMacExecute(wxChar **argv, 62 int flags, 63 wxProcess *process) 64{ 65 // Semi-macros used for return value of wxMacExecute 66 const long errorCode = ((flags & wxEXEC_SYNC) ? -1 : 0); 67 const long successCode = ((flags & wxEXEC_SYNC) ? 0 : -1); // fake PID 68 69 // Obtains the number of arguments for determining the size of 70 // the CFArray used to hold them 71 CFIndex cfiCount = 0; 72 for(wxChar** argvcopy = argv; *argvcopy != NULL ; ++argvcopy) 73 { 74 ++cfiCount; 75 } 76 77 // If there is not a single argument then there is no application 78 // to launch 79 if(cfiCount == 0) 80 { 81 wxLogDebug(wxT("wxMacExecute No file to launch!")); 82 return errorCode ; 83 } 84 85 // Path to bundle 86 wxString path = *argv++; 87 88 // Create a CFURL for the application path 89 // Created this way because we are opening a bundle which is a directory 90 CFURLRef cfurlApp = 91 CFURLCreateWithFileSystemPath( 92 kCFAllocatorDefault, 93 wxMacCFStringHolder(path), 94 kDefaultPathStyle, 95 true); //false == not a directory 96 97 // Check for error from the CFURL 98 if(!cfurlApp) 99 { 100 wxLogDebug(wxT("wxMacExecute Can't open path: %s"), path.c_str()); 101 return errorCode ; 102 } 103 104 // Create a CFBundle from the CFURL created earlier 105 CFBundleRef cfbApp = CFBundleCreate(kCFAllocatorDefault, cfurlApp); 106 107 // Check to see if CFBundleCreate returned an error, 108 // and if it did this was an invalid bundle or not a bundle 109 // at all (maybe a simple directory etc.) 110 if(!cfbApp) 111 { 112 wxLogDebug(wxT("wxMacExecute Bad bundle: %s"), path.c_str()); 113 CFRelease(cfurlApp); 114 return errorCode ; 115 } 116 117 // Get the bundle type and make sure its an 'APPL' bundle 118 // Otherwise we're dealing with something else here... 119 UInt32 dwBundleType, dwBundleCreator; 120 CFBundleGetPackageInfo(cfbApp, &dwBundleType, &dwBundleCreator); 121 if(dwBundleType != 'APPL') 122 { 123 wxLogDebug(wxT("wxMacExecute Not an APPL bundle: %s"), path.c_str()); 124 CFRelease(cfbApp); 125 CFRelease(cfurlApp); 126 return errorCode ; 127 } 128 129 // Create a CFArray for dealing with the command line 130 // arguments to the bundle 131 CFMutableArrayRef cfaFiles = CFArrayCreateMutable(kCFAllocatorDefault, 132 cfiCount-1, &kCFTypeArrayCallBacks); 133 if(!cfaFiles) //This should never happen 134 { 135 wxLogDebug(wxT("wxMacExecute Could not create CFMutableArray")); 136 CFRelease(cfbApp); 137 CFRelease(cfurlApp); 138 return errorCode ; 139 } 140 141 // Loop through command line arguments to the bundle, 142 // turn them into CFURLs and then put them in cfaFiles 143 // For use to launch services call 144 for( ; *argv != NULL ; ++argv) 145 { 146 // Check for '<' as this will ring true for 147 // CFURLCreateWithString but is generally not considered 148 // typical on mac but is usually passed here from wxExecute 149 if (wxStrcmp(*argv, wxT("<")) == 0) 150 continue; 151 152 153 CFURLRef cfurlCurrentFile; // CFURL to hold file path 154 wxFileName argfn(*argv); // Filename for path 155 156 if(argfn.DirExists()) 157 { 158 // First, try creating as a directory 159 cfurlCurrentFile = CFURLCreateWithFileSystemPath( 160 kCFAllocatorDefault, 161 wxMacCFStringHolder(*argv), 162 kDefaultPathStyle, 163 true); //true == directory 164 } 165 else if(argfn.FileExists()) 166 { 167 // And if it isn't a directory try creating it 168 // as a regular file 169 cfurlCurrentFile = CFURLCreateWithFileSystemPath( 170 kCFAllocatorDefault, 171 wxMacCFStringHolder(*argv), 172 kDefaultPathStyle, 173 false); //false == regular file 174 } 175 else 176 { 177 // Argument did not refer to 178 // an entry in the local filesystem, 179 // so try creating it through CFURLCreateWithString 180 cfurlCurrentFile = CFURLCreateWithString( 181 kCFAllocatorDefault, 182 wxMacCFStringHolder(*argv), 183 NULL); 184 } 185 186 // Continue in the loop if the CFURL could not be created 187 if(!cfurlCurrentFile) 188 { 189 wxLogDebug( 190 wxT("wxMacExecute Could not create CFURL for argument:%s"), 191 *argv); 192 continue; 193 } 194 195 // Add the valid CFURL to the argument array and then 196 // release it as the CFArray adds a ref count to it 197 CFArrayAppendValue( 198 cfaFiles, 199 cfurlCurrentFile 200 ); 201 CFRelease(cfurlCurrentFile); // array has retained it 202 } 203 204 // Create a LSLaunchURLSpec for use with LSOpenFromURLSpec 205 // Note that there are several flag options (launchFlags) such 206 // as kLSLaunchDontSwitch etc. and maybe we could be more 207 // picky about the flags we choose 208 LSLaunchURLSpec launchspec; 209 launchspec.appURL = cfurlApp; 210 launchspec.itemURLs = cfaFiles; 211 launchspec.passThruParams = NULL; //AEDesc* 212 launchspec.launchFlags = kLSLaunchDefaults; 213 launchspec.asyncRefCon = NULL; 214 215 // Finally, call LSOpenFromURL spec with our arguments 216 // 2nd parameter is a pointer to a CFURL that gets 217 // the actual path launched by the function 218 OSStatus status = LSOpenFromURLSpec(&launchspec, NULL); 219 220 // Cleanup corefoundation references 221 CFRelease(cfbApp); 222 CFRelease(cfurlApp); 223 CFRelease(cfaFiles); 224 225 // Check for error from LSOpenFromURLSpec 226 if(status != noErr) 227 { 228 wxLogDebug(wxT("wxMacExecute LSOpenFromURLSpec Error: %d"), 229 (int)status); 230 return errorCode ; 231 } 232 233 // No error from LSOpenFromURLSpec, so app was launched 234 return successCode; 235} 236 237