How to use JNI to call a "main" function that takes an array of character pointers? - android-ndk

hi i have an c function
int main(int argc, char *argv[])
and my wrapper.c has this function
JNIEXPORT jint JNICALL Java_package_Class_lameMain(JNIEnv *env, jclass class, jint argc, jcharArray argv) {
return main(argc, argv);
}
and in java i've defined it like this
private native int lameMain(int argc, char[] argv);
but i think i'm doing something wrong with the argv-argument... it's not an char-array, but a array of char-pointers.
can anyone help?
when i run it my app crashes with
03-20 23:26:23.487: A/libc(30436): Fatal signal 11 (SIGSEGV) at 0xfd90001d (code=1), thread 30436 (package)

On the Java side, convert the array to an array of strings (i. e. String[]). Pass it like that. On the JNI side, go through the array and retrieve the characters of each string. The declarations would go like this:
private native int lameMain(String[] argv);
And in C:
JNIEXPORT jint JNICALL Java_package_Class_lameMain(JNIEnv *env, jclass class, jobjectArray argv )
There's no need to pass argc, because Java arrays store their own size.
That said, you're probably doing something very wrong. Typical Android programs don't start with main and don't take command line arguments - they have activities instead. A C main() function is a starting point of a program, but since you're calling it from the Java side, it's not the first thing in the program.
EDIT: Okay, but I still think you're doing this wrong on more than one count. I take it, the encoder takes a file - right? So you save the wave from memory into a file just to be read again? That's lame (pun intended).
Also, do you really need to pass an arbitrary sized array from the Java side? If you know the number of arguments at design time, and it's small (say, two), it's much, much easier to just pass two jstrings.
Anyway, here goes the array stuff. This assumes the sources of your JNI library are C++, not C. For C, the invokation of JNI functions would be slightly different, and you'd have to use malloc/free instead of new/delete.
JNIEXPORT jint JNICALL Java_package_Class_lameMain(JNIEnv *env, jclass class, jobjectArray jargv)
{ //jargv is a Java array of Java strings
int argc = env->GetArrayLength(jargv);
typedef char *pchar;
pchar *argv = new pchar[argc];
int i;
for(i=0; i<argc; i++)
{
jstring js = env->GetObjectArrayElement(jargv, i); //A Java string
const char *pjc = env->GetStringUTFChars(js); //A pointer to a Java-managed char buffer
size_t jslen = strlen(pjc);
argv[i] = new char[jslen+1]; //Extra char for the terminating null
strcpy(argv[i], pjc); //Copy to *our* buffer. We could omit that, but IMHO this is cleaner. Also, const correctness.
env->ReleaseStringUTFChars(js, pjc);
}
//Call main
main(argc, argv);
//Now free the array
for(i=0;i<argc;i++)
delete [] argv[i];
delete [] argv;
}

Related

Difference between char* argv[] and string argv[] in C

What's the difference between:
int main(int argc, char* argv[])
and
int main(int argc, string argv[])
How does char* argv[] behave when compared to string argv[] and why do we need string argv[] ?
Why is string argv[] not a pointer variable in the argument here:
int main(int argc, string argv[])
{
string str = argv[1];
printf("%s\n",str);
}
where as if I use char I must use a pointer:
int main(int argc, char* argv[])
{
string str = argv[1];
printf("%s\n",str);
}
I found out what char* argv[] means and string argv[] means. They both mean the same.
To illustrate why, Let's look into the below example:
string S = "Hi!"
what exactly happens in the background while using strings?
Let's say for example:
char* S = "Hi!";
Here we create a pointer variable S that's gonna store the address of a char.
"Hi!" is stored somewhere in memory that we do not know. The address of the first character of "Hi!" is stored in S(which is the same as saying variable S points to H). In simple terms, S is storing an address(the address of character 'H'). Thus we can access all the elements one by one till we find '/0' since we know that a string ends when it finds a '/0'
If I abstract away all the details the above code can also be written as:
string S = "Hi!"
which is the same as saying char* S = "Hi!"
What if I wanna access each character then:
printf("%c", *(S)); // dereferencing the first character which is H
printf("%c", *(S+1)); // dereferencing the second character which is i
printf("%c", *(S+2)); // dereferencing the third character which is !
If I were to access each character in strings then I would say:
printf("%c", S[0]);
printf("%c", S[1]);
printf("%c", S[2]);
where both methods do the same thing. The latter is more readable than the former which is why it is commonly used.
So we can conclude that char* S is the same as string S. Since when we use strings, in the background, it uses a char pointer to access each element of the string that's stored in memory.
They both do the same thing. Using String abstracts all the details.
This solves my doubt as to why char argv[] the same as string argv[].
String argv[] does the same thing as char argv[] does but it hides the implementation and makes it easier for readability.

How to use parameter in exe

I want to use parameters in a program I made in C/C++ languages. Example:
MaxPayne2.exe -developer -developerkeys
I want to use a parameter like this in my exe file. How can I do it?
https://en.cppreference.com/w/cpp/language/main_function
While I would usually recommend adding more context this is your answer.
The standard c++ main has two additional parameters,
int main (int argc, char *argv[]) { }
To use these "arguments" (as they are called) you just reference their point in the argv array.
argc = the number of additional arguments supplied.
argv = the array of argument values supplied.
Example:
int main (int argc, char *argv[])
{
cout << argv[argc-1]; //Prints out the last argument supplied.
}
(note)If my syntax is wrong someone please correct me, my c++ is a bit rusty.

Would argc ever be passed less than 1

I'm developing my own version of getopt() in assembly and trying to get my head wrapped around this snippet, specifically line 476
if (argc < 1)
return -1;
As the normal calling convention would be something like this;
int c = getopt( argc, argv, "vm:drx:");
and assuming the programmer hasn't done anything with argc before hand, the only reason I can think it would be there is that some flavor of Linux, possibly non POSIX compliant would'nt pass argv[0] application path & name. Therefore, argc could be zero. Is there any credence to this conjecture?
Of the 12 times this variable is used in this procedure, it's only ever asserted or copied, never modified and not referenced at all in the two levels of procedure before this.
Consider this:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
execve("./testargc", NULL, NULL);
}
And this program:
#include <stdio.h>
int main (int argc, char* argv[])
{
printf("%d\n", argc);
}
The first one execs the 2nd one with no arguments. The pathname is not passed in and as a result argc is 0.

C++ argv indices larger than argc

So I've been trying to access command line arguments with indices larger than argc in C++ (because why not) and discovered that there are actually some parameters passed on to the program. The following code produces the following result in my Ubuntu 14.04:
#include <cstdio>
int main (int argc, char** argv) {
for (int i=argc+1; argv[i]!=0; i++)
printf("%3d %s\n", i, argv[i]);
}
This is the output:
2 XDG_VTNR=7
3 LC_PAPER=tr_TR.UTF-8
4 LC_ADDRESS=tr_TR.UTF-8
5 XDG_SESSION_ID=c2
6 XDG_GREETER_DATA_DIR=/var/lib/lightdm-data/kubuzetto
7 SELINUX_INIT=YES
8 LC_MONETARY=tr_TR.UTF-8
9 CLUTTER_IM_MODULE=xim
(and so on)
What is this and is it distro-dependent?
Don't run past argc, it's not defined.
In this case, we know what happened. You see, main() is declared and called as
extern int main(int argc, char **argv, char **envp);
and it just so happens that the the way argv and envp are built leaves envp right after argv, so running off the end encounters envp.
It might change someday, so don't depend on this working. If you wanna depend on the kernel's passing method, provide the kernel's entry point yourself (which must be done in asm [or possibly a naked function] as it doesn't look like a function call).

How to change the name of a haskell process under linux

I am trying to change the name of a running process under linux. In C, I would just modify argv[0] in-place, but how can I do that from haskell? I noticed that ghc has a primitive called getProgArgv:
foreign import ccall unsafe "getProgArgv"
getProgArgv :: Ptr CInt -> Ptr (Ptr CString) -> IO ()
but I tried with that and it didn't work. Also, I am aware of prctl(PR_SET_NAME,"...") but that only changes the current thread's name, and most tools (such as ps and htop) do not use that name.
Ok, so I came up with an ugly hack that seems to work. It based on a idea borrowed from here. We have to use an auxiliary c file:
#include <string.h>
#include <sys/prctl.h>
char *argv0 = 0;
static void capture_argv0(int argc, char *argv[]) {
argv0 = argv[0];
}
__attribute__((section(".init_array"))) void (*p_capture_argv0)(int, char*[]) = &capture_argv0;
void set_prog_name(char *name) {
if (!argv0) return;
size_t len = strlen(argv0);
strncpy(argv0, name, len);
prctl(PR_SET_NAME, name);
}
This relies on the section(".init_array") attribute that tells gcc to register capture_argv0 as an initialization function. This means that it will be executed before main. We use it to make a copy of the argv[0] pointer and store it as a global variable. Now we can call set_prog_name from haskell.

Resources