path_frompathname...
Via caveman's/poor man's debugger method, I isolated the line which causes to crash my standalone on Mac:
path_frompathname(a, &b, "");
where a is the char* fullpath and b is the short newpath.
Why is this causing
EXC_BAD_ACCESS
KERN_PROTECTION_FAILURE
?
The same code works when applied on Windows.
What I'm trying to do is to access a folder near the app. So I _getapppath(), then turn it _topathname(), then strcat with the "/subfolder" and input this fullpath to _frompathname. Is there another way to do this?
Any idea why the crash is happening?
If you want help with this, you gotta post your code complete with how you declare any local variables and how you call the path_frompathname() function.
@LUIGI CASTELLI Thanks, here is the entire C source (I made it extremely short anyway) that can also be instantly built.
Note: I declared the variables path and newpath globally, because in the actual code I need them in several functions.
As it is apparent in the code, just connect it to a [bang] to try. Since ...getapppath() is used to begin with, "theSubfolder" should be created near Max.app. Or save a standalone and create the subfolder near it. When there is no valid path with the exact name (".../theSubfolder"), newpath gets the value 0, everything goes as expected. But when the path is valid, it crashes.
#include "ext.h"
#include "ext_obex.h"
#include "ext_strings.h"
#include "ext_sysmem.h"
#include "ext_path.h"
#include "ext_sysfile.h"
typedef struct _toquery
{
t_object ob;
void* out1;
} t_toquery;
void* toquery_new(t_symbol* s, long argc, t_atom* argv);
void toquery_free(t_toquery* x);
void toquery_getp(t_toquery* x);
void* toquery_class;
short path;
short newpath;
void ext_main(void* r)
{
t_class* c;
c = class_new("toquery", (method)toquery_new, (method)toquery_free, (long)sizeof(t_toquery), 0L, A_GIMME, 0);
class_addmethod(c, (method)toquery_getp, "bang", 0);
class_register(CLASS_BOX, c);
toquery_class = c;
}
void toquery_free(t_toquery* x)
{
;
}
void* toquery_new(t_symbol* s, long argc, t_atom* argv)
{
t_toquery* x = NULL;
if ((x = (t_toquery*)object_alloc(toquery_class)))
{
x->out1 = outlet_new(x, NULL);
}
return (x);
}
void toquery_getp(t_toquery* x)
{
path = path_getapppath();
char additional[] = "/theSubfolder";
additional[13] = '\0';
char fullpath[MAX_PATH_CHARS];
path_topathname(path, "", fullpath);
post("%s", fullpath);
strncat_zero(fullpath, additional, MAX_PATH_CHARS);
post("%s", fullpath);
path_frompathname(fullpath, &newpath, "");
//post("%d", newpath);
}
Ok great. Now I can clearly see what is going on...
So, first of all, you don't want to declare path and newpath as global variables.
If you do that, then they will become part of global scope and will be shared by all instances of your external. From what I can tell looking at your code, you want a specific value of path and newpath for every instance of your external. Turn path and newpath into struct fields.
Second, do not use double quotes ("") as argument to any of the path functions. You need to pass valid memory either statically or dynamically allocated. The fullpath is a combination of volume hierarchies AND file name. You need to specify the file name. You "could" pass a NULL pointer IF and only IF the function is designed to accept NULL pointers as arguments which I don't know, but I doubt. If you are lucky it will be documented in the ext_path.h header file.
Third, you should also check the return value of all the path_xxx functions to see if any errors have occurred. That way you can create branches in the control flow and post error messages to the Max window rather than simply crashing.
Other than these points, the code looks right to me.
I haven't compiled your code because I am a little pressed for time at the moment, but feel free to write back if none of my suggestions help you.
- Luigi
@LUIGI CASTELLI Thanks for your detailed comments, I tried all combinations about your second point (adding a filename at the end of the path for the first argument, passing a filename for the third argument, and doing both) but none of them worked.
The code I extracted to ask actually passes only momentarily within the loading sequence of my app, in preparation to check a code from a file in that particular subfolder. So there aren't meant to be multiple instances of that external. But still, the possibility of the user's opening the app simultaneously from different folders isn't zero, so I'll keep your advice (of turning path variables into struct fields) in my mind. Thank you! (I may come up with a question about its exact way of usage though.)
I can't apply your third suggestion because I don't know where the list of those error codes are and how to use them. Will be happy if you can show.
About using "" as arguments, path_topathname() evidently accepts that, and if they weren't allowed, those functions wouldn't be able to cover most situations I guess. Also see the second to last message under the following post:
https://www.facebook.com/groups/maxmspjitter/permalink/10159376881569392
And as I mentioned in my first post, this code already can work on Windows.
On Mac, I don't use code-signing and that is what I suspect as the cause, so perhaps you might run it without problems. Actually it would give a good idea for the issue if you try it whenever you can find the time.
Thanks again, I believe the problem is close to be determined...
You can find a list of Max error codes in ext_obex.h.
However in the path functions you can simply use shorts.
If the result != 0, then something went wrong.
As far as using double quotes I stand corrected. You are right.
You may use the double quotes as arguments to the path functions.
Ok, I will try to run your code as soon as I can.
Cheers
- Luigi
About checking errors to see if the functions have failed:
It is impossible to make use of that here, because the situation is not like I call the function and then it crashes when I'm later doing something depending on it. It just crashes at the moment the function is executed, so we don't even get a chance to check any returned errors.
Thanks in advance for the test. Can't wait to learn how it goes with you.
@Luigi Castelli So,what happened? Were you able to run it?
Hi, thanks for your patience...
Yes, I run your code. So your code 'kind of works', but it is not entirely correct.
If what you are trying to do is to just retrieve a path of a folder in the same directory as your app, all you need is the following:
void toquery_getp(t_toquery* x)
{
short path = path_getapppath();
char additional[] = "/theSubfolder";
additional[13] = '\0';
char fullpath[MAX_PATH_CHARS];
path_topathname(path, "", fullpath);
object_post(NULL, "%s", fullpath);
strncat_zero(fullpath, additional, MAX_PATH_CHARS);
object_post(NULL, "%s", fullpath);
}
the fullpath array will contain the correct absolute path of your folder named 'theSubfolder'.
Note that with this method you are just getting the folder.
There is no actual file associated with it.
There is no need to call path_frompathname().
In case you ever need to call path_frompathname() you cannot pass a double quote as 3rd argument because it is expecting an array of chars to fill upon return. That was probably the reason for your crash. The path_frompathname() function was accessing invalid memory, that is the 3rd argument you passed was invalid.
That's how you call path_frompathname().
Specify a char array filename and pass it anyway even if you only need the path.
{
char filename[MAX_FILENAME_CHARS];
short newpath;
path_frompathname(fullpath, &newpath, filename);}
Hope this makes sense.
- Luigi
That trick with the filename worked like a charm, thank you very much!
Of course I had to use the path_frompathname() function because as I mentioned, this was a preparation to read a file under that subfolder and for the function for opening the file, the path id (short newpath) was required.
It's strange that passing double quote results differently on Windows and on Mac.
I am glad that I was able to help...
It is strange indeed. I agree.
In a perfect world path_frompathname() should detect if some of the arguments are invalid and only act on the valid ones. That way if you only need the path you could pass NULL as a filename argument and vice versa.
However we are not living in a perfect world...
Cheers
P.S.
Actually, you should test if passing NULL leads to a crash or correctly ignores the filename argument... When I ran your code, I didn't specifically test that.