Page 1 of 1

GetFieldByName/SetFieldByName not working?

Posted: Fri Apr 03, 2009 10:56 am
by Janschenkel
Hi all,

Plodding along with my external development, and though I seem to have conquered my daemons, I am now bumping into a strange problem: I can get and set field context using GetFieldByNum/SetFieldByNum and GetFieldById/SetFieldById - but the GetFieldByName/SetFieldByName pair always results in EXTERNAL_FAILURE.

In all cases I'm interacting with the same field, so I'm not sure why the aforementioned would fail.

Thanks in advance for any ideas!

Jan Schenkel.

Posted: Fri Apr 03, 2009 10:03 pm
by mwieder
Hmmm... the "byName" functions should be the easiest of the three types to deal with since you don't have to do any type conversions. If your string is formatted properly (trailing null C-string) then it should work. At least it used to work. Example from the docs:

Code: Select all

char *strContents;
int r_error;

/*
   Get the contents of field "Field 1",
   looking only in the foreground for the field,
   and place the contents in a string.
   Then place a copy of the contents in field "Field2" in the
   background.
*/
strContents = GetFieldByName("true", "Field 1", &r_error);
if (!r_error)
{
  SetFieldByName("false", "Field 2", strContents, &r_error);
}
free (strContents);  // free the buffer when we're done

Posted: Sat Apr 04, 2009 7:01 am
by Janschenkel
That's why I'm so surprised it didn't work for me. I created a simple test stack with a single field named "testfield" (number: 1, id: 1006) and a button with the following script:

Code: Select all

on mouseUp
   answer "qrttest_getfieldbyname:" && qrttest_getfieldbyname()
   qrttest_setfieldbyname
   answer the result
   answer "qrttest_getfieldbynum:" && qrttest_getfieldbynum()
   qrttest_setfieldbynum
   answer the result
   answer "qrttest_getfieldbyid:" && qrttest_getfieldbyid()
   qrttest_setfieldbyid
   answer the result
end mouseUp
for an external with the following code:

Code: Select all

///////////////////////////////////////////////////////////////////////////////
//
// Revolution external main entry point for 'qrttestbed'
//
// Generated by External Creator V1.00
//
// For language: C++
//

#include <revolution/external.h>

///////////////////////////////////////////////////////////////////////////////
//
// BEGIN USER DEFINITIONS

void qrttest_GetFieldByName(char *p_arguments[], int p_argument_count, char **r_result, Bool *r_pass, Bool *r_error) {
	int t_success;
	char *fieldtext;
	fieldtext = GetFieldByName(NULL, "testfield", &t_success);
	if (t_success == EXTERNAL_SUCCESS) {
		*r_result = strdup("success");
	} else {
		*r_result = strdup("failure");
	}
	free(fieldtext);
	// If we got to here it means there were no errors
	//
	*r_pass = False;
	*r_error = False;
}

void qrttest_SetFieldByName(char *p_arguments[], int p_argument_count, char **r_result, Bool *r_pass, Bool *r_error) {
	int t_success;
	char *fieldtext = "set by qrttestbed";
	SetFieldByName(NULL, "testfield", fieldtext, &t_success);
	if (t_success == EXTERNAL_SUCCESS) {
		*r_result = strdup("success");
	} else {
		*r_result = strdup("failure");
	}
	// If we got to here it means there were no errors
	//
	*r_pass = False;
	*r_error = False;
}

void qrttest_GetFieldByNum(char *p_arguments[], int p_argument_count, char **r_result, Bool *r_pass, Bool *r_error) {
	int t_success;
	char *fieldtext;
	fieldtext = GetFieldByNum(NULL, 1, &t_success);
	if (t_success == EXTERNAL_SUCCESS) {
		*r_result = strdup("success");
	} else {
		*r_result = strdup("failure");
	}
	free(fieldtext);
	// If we got to here it means there were no errors
	//
	*r_pass = False;
	*r_error = False;
}

void qrttest_SetFieldByNum(char *p_arguments[], int p_argument_count, char **r_result, Bool *r_pass, Bool *r_error) {
	int t_success;
	char *fieldtext = "set by qrttestbed";
	SetFieldByNum(NULL, 1, fieldtext, &t_success);
	if (t_success == EXTERNAL_SUCCESS) {
		*r_result = strdup("success");
	} else {
		*r_result = strdup("failure");
	}
	// If we got to here it means there were no errors
	//
	*r_pass = False;
	*r_error = False;
}

void qrttest_GetFieldById(char *p_arguments[], int p_argument_count, char **r_result, Bool *r_pass, Bool *r_error) {
	int t_success;
	char *fieldtext;
	fieldtext = GetFieldById(NULL, 1006L, &t_success);
	if (t_success == EXTERNAL_SUCCESS) {
		*r_result = strdup("success");
	} else {
		*r_result = strdup("failure");
	}
	free(fieldtext);
	// If we got to here it means there were no errors
	//
	*r_pass = False;
	*r_error = False;
}

void qrttest_SetFieldById(char *p_arguments[], int p_argument_count, char **r_result, Bool *r_pass, Bool *r_error) {
	int t_success;
	char *fieldtext = "set by qrttestbed";
	SetFieldById(NULL, 1006L, fieldtext, &t_success);
	if (t_success == EXTERNAL_SUCCESS) {
		*r_result = strdup("success");
	} else {
		*r_result = strdup("failure");
	}
	// If we got to here it means there were no errors
	//
	*r_pass = False;
	*r_error = False;
}

// END USER DEFINITIONS
//
///////////////////////////////////////////////////////////////////////////////


EXTERNAL_BEGIN_DECLARATIONS("qrttestbed")

// BEGIN USER DECLARATIONS

EXTERNAL_DECLARE_COMMAND("qrttest_SetFieldByName", qrttest_SetFieldByName)
EXTERNAL_DECLARE_FUNCTION("qrttest_GetFieldByName", qrttest_GetFieldByName)
EXTERNAL_DECLARE_COMMAND("qrttest_SetFieldByNum", qrttest_SetFieldByNum)
EXTERNAL_DECLARE_FUNCTION("qrttest_GetFieldByNum", qrttest_GetFieldByNum)
EXTERNAL_DECLARE_COMMAND("qrttest_SetFieldById", qrttest_SetFieldById)
EXTERNAL_DECLARE_FUNCTION("qrttest_GetFieldById", qrttest_GetFieldById)

// END USER DECLARATIONS

EXTERNAL_END_DECLARATIONS
Apart from missing the occasional 'free()' statement in this quick test code, I'm not sure what I'm doing wrong.

Thankis for thinking with me,

Jan Schenkel.

Posted: Sat Apr 04, 2009 7:50 am
by mwieder
I'm a bit surprised that the other calls work - I didn't realize you could set the background parameter to NULL. Assuming that you're working with a field on a card rather than in a background group, try

Code: Select all

fieldtext = GetFieldByName("true", "testfield", &t_success);
and set it to "false" if you're working with a background group field. You can set it to an empty string ("") to search both foreground and background, but that's different from a null string.

Posted: Sat Apr 04, 2009 3:41 pm
by Janschenkel
I'm just working from what the comments say in 'external.h'

Code: Select all

//   If p_group is "true" then it has the same effect as searching for
//   'card field'.
//   If p_group is "false" then it has the same effect as searching for
//   'background field'.
//   If p_group is NULL then no modifier is used.
Given that Revolution doesn't really have backgrounds like HyperCard, I figured it didn't need a modifier so I passed NULL. Just tried it with empty, but still no luck.

Even stranger: just to make sure I hadn't made a typo, I changed things slightly so that the short name of the field was passed as a parameter and used that as argument for GetFieldByName/SetFieldByName - but "true", "false", "" and NULL all resulted in failure.

Jan Schenkel.

Posted: Sat Apr 04, 2009 5:51 pm
by mwieder
Well, that *is* new. I looked in the header file and the comments do indeed say that. That didn't use to be the case. Previously the situation for the p_group argument was

"true" to look for the field on the card
"false" to look for the field on the background
"" to look on both the card and background (recursively)

If p_group is empty and the target stack is in HCaddress mode then the search is performed as if p_group is "true". Maybe the externals api has changed?

Posted: Tue Apr 07, 2009 9:26 pm
by Janschenkel
Thanks to the inimitable Mark Wieder, the source of the problem was tracked down to the 'externals.c' file which shipped with the ExternalsEnvironment v3 stack.
Relevant entry in the quality center, with workaround: http://quality.runrev.com/qacenter/show_bug.cgi?id=7913

Hope this helps someone stuck with the same problem. On to the next bug, indubitably hidden in my own code :-)

Jan Schenkel.