Call C function in DLL that returns char * from MS-Access VBA - ms-access

I have a DLL with a function declared like this:
char * __declspec(dllexport) WINAPI test(int x);
I want to call it in Access VBA like this:
Private Declare Function "test" Lib "MyDLL.dll"(ByVal x As Long) As String
Sub MySub
Dim s As String
s = test(1)
End Sub
Based on articles found in Google, I wrote a function in the DLL:
BSTR CStrToVBStr(char *str)
{
int wslen = MultiByteToWideChar(CP_ACP, 0, str, lstrlen(str), 0, 0);
BSTR bstr = SysAllocStringLen(0, wslen);
MultiByteToWideChar(CP_ACP, 0, str, strlen(str), bstr, wslen);
return bstr;
}
After the call to test(), s apparently contains Unicode characters. That is, a char * "test" is returned to VBA so that Mid(s, 1, 1) = "t", Mid(s, 2, 1) = Chr(0), Mid(s, 3, 1) = "e", etc. I decide to skip the conversion to Unicode and wrote CstrToVBStr as
BSTR CStrToVBStr(char *str)
{
return SysAllocString(str);
}
and ignored the warning that SysAllocString takes an OLECHAR * and not a char * parameter.
The returned string looks good now, but the null character terminator is included, so if the char * is "test", then in VBA len(s) = 5 and Right(s,1) = Chr(0).
What is the right way to do what I am doing? All examples that I have seen are about char * as parameters, not return values. I can change test() to be
void __declspec(dllexport) WINAPI test(int x, char *result);
but I would like to know if what I am trying to do is possible.
I am using Access 2007 on Windows 7 (64-bit).

A char is the same as an integer. In VBA that means a long (as VBA uses 16 bit terminology). There are no strings involved. To convert an integer or char to a string use chr(x).

Related

Send arguments to a function with argv and argc

Can someone help me to understand how i need to send the parameters to the function "lora_rf_config" ? Thank you so much !
I try with:
char cfgred[7][10]={'lora_rf_config','915000000','10','0','1','8','14'};
lora_rf_config(7,&cfgred);
The function that im trying to use is:
static void lora_rf_config(int argc, char *argv[])
{
if (argc == 1) {
e_printf("OK%d,%d,%d,%d,%d,%d\r\n", g_lora_config.lorap2p_param.Frequency,
g_lora_config.lorap2p_param.Spreadfact,
g_lora_config.lorap2p_param.Bandwidth,
g_lora_config.lorap2p_param.Codingrate,
g_lora_config.lorap2p_param.Preamlen,
g_lora_config.lorap2p_param.Powerdbm );
return;
} else {
if (argc != 7) {
out_error(RAK_ARG_ERR);
return;
}
if (!(CHECK_P2P_FREQ(atoi(argv[1])) &&
CHECK_P2P_SF(atoi(argv[2])) &&
CHECK_P2P_BDW(atoi(argv[3])) &&
CHECK_P2P_CR(atoi(argv[4])) &&
CHECK_P2P_PREMLEN(atoi(argv[5])) &&
CHECK_P2P_PWR(atoi(argv[6])))) {
out_error(RAK_ARG_ERR);
return;
}
if (read_partition(PARTITION_0, (char *)&g_lora_config, sizeof(g_lora_config)) < 0) {
out_error(RAK_RD_CFG_ERR);
return;
}
g_lora_config.lorap2p_param.Frequency = atoi(argv[1]);
g_lora_config.lorap2p_param.Spreadfact = atoi(argv[2]);
g_lora_config.lorap2p_param.Bandwidth = atoi(argv[3]);
g_lora_config.lorap2p_param.Codingrate = atoi(argv[4]);
g_lora_config.lorap2p_param.Preamlen = atoi(argv[5]);
g_lora_config.lorap2p_param.Powerdbm = atoi(argv[6]);
write_partition(PARTITION_0, (char *)&g_lora_config, sizeof(g_lora_config));
e_printf("OK\r\n");
}
return;
}
The error that i got is:
..\..\..\src\application\RAK811\app.c(107): error: #26: too many characters in character constant
char cfgred[7][10]={'lora_rf_config','915000000','10','0','1','8','14'};
I dont have experience with this kind of arguments.
Thank you for your time.
lora_rf_config expects same arguments than main function: array of pointers to strings, and its length.
Strings in C are pointers to char, where the char buffer they point to has terminating NUL character (if NUL char is missing, then it's not a string, just a character array). In other words, there is no string type in C, but stringiness is determined by the actual data in the char array or buffer. Using "" string literal creates a string, IOW it adds that terminating NUL char in addition to what you write.
// cfgred is array([) of 7 pointers(*) to char.
// Note: string literals are read-only, so you must not modify these
// strings. If you want a modifiable string, this would be a bit more complex,
// but I think this is out of the scope of your question.
char *cfgred[7] = { "lora_rf_config" , "915000000", "10","0", "1", "8", "14"};
// you can get the number of elements in array by dividing its sizeof size (bytes)
// with the size of it's elements in bytes. Just make sure cfgred here is array...
// in the function it is pointer already (arrays get converted to pointers, so
// you can't do this inside the function, you have to do it where you still have
// the original array
int cfgred_len = sizeof cfgred / sizeof(cfgred[0]);
// when you pass array to function, it is automatically converted to pointer,
// so you must not use & when passing an array like this, otherwise types don't
// match
lora_rf_config(cfgred_len, cfgred);
As a side note, always turn on compiler warnings... They help you a lot, fix them. For gcc and clagn, use -Wall -Wextra, for Visual Studio use /W3 or prefereably /W4. And then fix any warnings you get, because they are probably something that doesn't do what you expect.
Your initialization is not done correctly, try changing
char cfgred[7][10]={'lora_rf_config','915000000','10','0','1','8','14'};
into
char cfgred[7][16]={"lora_rf_config","915000000","10","0","1","8","14"};

convert from 'Platform::String ^' to 'const char *' when adding a const char *

I am porting a code (using C++ VS2015 in Windows 10) to a Universal Windows Application (UWP). It uses a macro (#define) that concatenates various const strings at compile time and sends a "const char *" as parameter to a function (not pointing to the first element). A simple example is as follows:
const char * s = ((const char *)("xxxx1234" "567" "89") + 4);
printf("[%s]", s );
in a common C++ program, it prints
[123456789] // <-- OK
The same, but for "Universal Windows", yields the error:
error C2440: 'initializing': cannot convert from 'Platform::String ^' to 'const char *'
If I omit the "+ 4" it works fine but I dont get the correct pointer offset. Prints
[xxxx123456789] <---- NG
How can I convert types or avoid defining the strings as Platform::String ? Preferably in one line, since it is a (#define) macro
Ok, got it. Changed it to
const char * s = (const_cast<char *>("xxxx1234" "567" "89") + 4);

Inserting variable in mysql with visual studio CLR / C

I used mysql connector and extract data from Database in visual studio 2010. Also inserted data successfully as value. But was fail to insert data with variable. Need a help, please.
this one worked.
mysql_query(connect,"INSERT INTO input VALUES(111,'Bangladesh','Khulna','Male','Muhammad Ashikuzzaman KUET','b+')");
But this are not working.
str="Muhammad Ashikuzzaman KUET";
mysql_query(connect,"INSERT INTO input VALUES(111,'Bangladesh','Khulna','Male','#str','b+')");
or
mysql_query(connect,"INSERT INTO input VALUES(111,'Bangladesh','Khulna','Male',#str,'b+')");
Please suggest a solution.
You have to create the string before calling mysql_query():
char statement[512], *str = "Muhammad Ashikuzzaman KUET";
snprintf(statement, sizeof statement, "INSERT INTO input VALUES(111,'Bangladesh','Khulna','Male','%s','b+')", str);
mysql_query(connect, statement);
Also, be careful when creating those query strings. Don't use functions like sprintf() if you cannot be sure how long the resulting string is. Don't write over the boundaries of the memory segment.
Edit
For precaution, You can use mysql_real_escape_string() additionally if the string usually comes from arbitrary sources:
int insertData(MYSQL *connect, char *str, int str_len) {
if (str_len < 0) {
str_len = strlen(str);
}
char esc[2 * str_len + 1];
unsigned long esclen = mysql_real_escape_string(connect, esc, str, str_len);
char statement[512];
snprintf(statement, sizeof statement, "INSERT INTO input VALUES(111,'Bangladesh','Khulna','Male','%s','b+')", esc);
return mysql_query(connect, statement);
}
Also here I've assumed your input string is small enough to fit into 512 characters string. Practically, it won't work. So declare statement length variable according to input string length plus some extra length to fit with the query string together.

Convert int to const char *

In this code:
this->_label = CCLabelTTF::labelWithString(number,"Artial", 32);
number is 5, but must be a const char *.
How can I convert number from an int to the required const char *?
The only three-argument call listed here is:
+ (id) labelWithString: (NSString *) string
fontName: (NSString *) name
fontSize: (CGFloat) size
That means number should not be an int but should indeed be a const char *.
If you want to populate it with the string "5" instead of the integer 5, you'll need to convert it to a string first.
Depending on the language you're using, this could be something like:
char buffer[20];
sprintf (buffer, "%d", number);
(for C) or using something like stringstreams in C++.

How to pass variable in mysql_query

I try to execute mysql query passing variable. Here is my code
char str[100] = "My String";
mysql_query(conn, printf("INSERT INTO table VALUES %s"), str);
I get that warning during compile process
warning: passing argument 2 of ‘mysql_query’ makes pointer from integer without a cast
What I miss ?
Extending #ckruse's answer, you should take care to use mysql_real_escape_string() if your string comes from arbitrary sources.
int insert_data(MYSQL * mysql, char * str, int len)
{
if (len < 0) {
len = strlen(str);
}
char esc[2*len+1];
unsigned long esclen = mysql_real_escape_string(mysql, esc, str, len);
char statement[512];
snprintf(statement, sizeof statement, "INSERT INTO table VALUES ('%s')", esc);
return mysql_query(mysql, statement);
}
(An alternative could be mysql_hex_string() if dealt with correctly.)
You cannot do that. printf() returns the number of characters printed. You have to create the string before calling mysql_query():
char statement[512], *my_str = "MyString";
snprintf(statement, 512, "INSERT INTO table VALUES ('%s')", str);
mysql_query(conn, statement);
Also, be careful when creating those query strings. Don't use functions like sprintf() if you cannot be sure how long the resulting string is. Don't write over the boundaries of the memory segment.
you should put "'' in front and after the string
like this
mysql_query(conn, printf("INSERT INTO table VALUES ('%s')"), str);