Mixed C++/CLI code with Berkeley DB - database

I'm traying to use Berkeley DB in C++/CLI with /clr mode. I wrote this code:
Edit:
// DB_test1.cpp : main project file.
#include "stdafx.h"
#pragma comment(lib,"libdb51")
using namespace System;
using namespace System::Runtime::InteropServices;
int main(array<System::String ^> ^args)
{
Db SigDb(0,0);
unsigned int oFlags= DB_CREATE;
SigDb.open(NULL,"SigDb.db",0,DB_BTREE,oFlags,0);
String^ HexSig="D8B1048900ABFF8B";
wchar_t* a=( wchar_t* )Marshal::StringToHGlobalUni(HexSig).ToPointer() ;
wchar_t* A=( wchar_t* )Marshal::StringToHGlobalUni(HexSig).ToPointer();;
Dbt key1(&a,100);
Dbt data1(&A,100);
Marshal::FreeHGlobal(IntPtr(A));
int ret= SigDb.put(NULL,&key1,&data1, DB_NOOVERWRITE);
if(ret==DB_KEYEXIST){
Console::WriteLine("You are trying to insert an exist key!");
}
wchar_t DDData[200];
Dbt getKey, getData;
getKey.set_data(&a);
getKey.set_size(100);
getData.set_data(DDData);
getData.set_ulen(200);
getData.set_flags(DB_DBT_USERMEM);
Marshal::FreeHGlobal(IntPtr(a));
if(SigDb.get(NULL,&getKey,&getData,0)==DB_NOTFOUND)
Console::WriteLine("Not Found !");
else
Console::WriteLine(" {0}",Marshal::PtrToStringUni((IntPtr)DDData));
return 0;
}
The code is compiled successfully but it shows wrong output. I am just traying to store String^ HexSig="D8B1048900ABFF8B"; in SigDb.db and then directly read the same string and print it!. The result does not appear like D8B1048900ABFF8B as it expected, but it appears as a random string. Any ideas?
After Editing:
This segment of code is always executed Console::WriteLine("Not Found !");

I can see two issues with your application:
1) The two calls to Marshal::FreeHGlobal are made before the contents of the buffers are used. You shouldn't free 'A' until after the put operation, and you shouldn't free 'a' until after both the put and get operations.
2) You are storing the pointers in Berkeley DB, rather than the strings themselves. That's due to the Dbt constructor calls. You're application is:
Dbt key1(&a,100);
It should be:
Dbt key1(a, 100);
Similarly for the getKey.set_data method - it should use the pointer, not a reference to the pointer.
Once I made the above changes to your application, it ran as expected.
Regards,
Alex Gorrod
Oracle Berkeley DB

You use Marshal::StringToHGlobalUni(), the converted string is a wchar_t*, not a char*. A wide string with the Unicode codepoints encoded in utf16. To get a char* you need StringToHGlobalAnsi().
Do consider that this is a lossy conversion, dbase engines have been Unicode enabled for well over a decade now. Another serious problem is that you don't release the memory allocated for this string, calling Marshal::FreeHGlobal() in a finally block is required. You also should technically use GlobalLock() to convert the returned HGLOBAL to a pointer, consider Marshal::StringToCoTaskMemXxx.

Related

Username as a Unicode/wchar_t string in systemd service?

Consider this typical for Linux function (it returns the current process username):
char* currentUserName(void) {
struct passwd *p = getpwuid(getuid());
return (p? p->pw_name : NULL);
}
How to get it in Unicode (let's say wchar_t)? To be honest, I don't know what is the encoding of pw_name even (system? Which one - File System? Always UTF-8?).
Is there a way to get the username as wchar_t string? Maybe some function similar to Windows's GetUserNameW() (where W is for wide-chars) - to do it without to link with iconv library...
Maybe I can use mbstowcs() but which locale will be used? I plan to call this function from systemd service, so I have not idea what LC_CTYPE/LANG is there...

Problems with parsing a .csv file in C, I don't know how to tell my program that a ",,,," is equal to a "\n"

I'm currently working in tech support for a hosting bussiness, using primarly cPanel. Also, I'm learning how to code (using only C for the moment). The thing is that I'm having trouble parsing a csv file that is created by cPanel, a list with all the information of the domains (Ip, domain name, space avaible in disk, and so forth). Until now, all the files that I succesfully parsed (.txt, .csv, .bin, etc) they all have each new line separeted by a ¿endofline? (\n in C, I don't know how else is called :P), but in this particularly file the end of one line is separeted by a ",,,,", like this:
domain_name, ip, quota,,,,domain_name, ip, quota,,,,domain_name,ip, quota,,,,...
And I don't know how to tell my program that a ",,,," is equal to a "\n". So far I'am only able to read the first domain in the file, but that's the only record that it's returned. The code so far is as follows:
void parsear_archivo(ArrayList* lista)
{
FILE* archivo = abrir_archivo("archivo.csv");
eDominio* aux;
char aux_ip[100];
char aux_nombre_dominio[200];
char aux_user_name[50];
char aux_email[100];
char aux_quota[100];
char aux_server[100];
rewind(archivo);
fscanf(archivo, "%[^,],%[^,],%[^,],%[^\n]\n", aux_nombre_dominio, aux_ip, aux_email,aux_server);
while (!feof(archivo))
{
aux = constructor();
fscanf(archivo, "%[^,],%[^,],%[^,],%[^\n]\n", aux_nombre_dominio, aux_ip, aux_email,aux_server);
if (aux != NULL)
{
strcpy(aux->ip, aux_ip);
strcpy(aux->quota, aux_quota);
strcpy(aux->server, aux_server);
strcpy(aux->email, aux_email);
strcpy(aux->nombre_dominio, aux_nombre_dominio);
}
lista->add(lista, aux);
}
fclose(archivo);
}
A couple of comments on the code: I'm using an ArrayList to load the file in memory, that was already tested, so I know that it works properly; the functions constructor() and abrir_archivo() are not the problem here (I used them several times in others proyects with no issues -of course, the constructor() function is tailored to fit the specific structure that I'm currently using-).
I don't know much about regular expressions, I know that the ones I'm using right now read in the file until the first "," appears, but that is just about it.
Any help will be greatly appreciated!
Thank you and sorry for the bad english.

Test for device using udev sysname

I'm writing my first ever C program in Ubuntu and it involves detecting when the AC adapter or Battery on my laptop see events (through udev_monitor). So far, I'm able to log to the console when the power situation changes using something like this while snippet:
while (1) {
dev = udev_monitor_receive_device(mon);
if (dev) {
printf("\n[INFO] Got Device\n");
printf(" [INFO] Device Name: %s\n", udev_device_get_sysname(dev));
...
And that's great! When I plug or unplug my laptop I get that logged to the console. However, what I'm failing to grok is how to conditionally test using these same functions.
For instance, this is never true:
if (udev_device_get_sysname(dev) == "ADP1") { ... }
I'm unsure how to properly test this. Obviously it prints as a string (array of characters?) when printf() and %s are used.
EDIT: The values logged to the consle from the function called are ADP1 for the adapter and BAT0 for the battery - which correlates to their entries in the /sys/class/ directory.
In C you compare strings using one of the string comparison routines, such as strcmp or strncmp. If you compare them using == you're only comparing the values of the pointers, which will not be the same.
So for the code checking the sysname is incorrect, as the pointer comparison will never be true. It should be changed to something like:
if (strcmp(udev_device_get_sysname(dev), "ADP1") == 0) { … }
which causes it to compare the strings, rather than the pointer values.

How can I make the printer work in C in MS VC++ Express edition?

I am using VC++ 2008 express edition for C. When I try to run this:
/* Demonstrates printer output. */
#include <stdio.h>
main()
{
float f = 2.0134;
fprintf(stdprn, "This message is printed.\n\n");
fprintf(stdprn, "And now some numbers:\n\n");
fprintf(stdprn, "The square of %f is %f.", f, f*f);
/* Send a form feed */
fprintf(stdprn, "\f");
}
I get four of these errors: error C2065: 'stdprn' : undeclared identifier.
On this forum, they wrote that it works to define the printer as follows:
FILE *printer;
printer = fopen("PRN", "w");
EDIT
It builds with a warning that fopen is unsafe. When it runs the error appears:
Debug Assertion fails.
File: f:\dd\vctools\crt_bld\self_x86\crt\src\fprintf.c
Line: 55
Expression: (str != NULL)
The stdprn stream was an extension provided by Borland compilers - as far as I know, MS have never supported it. Regarding the use of fopen to open the printer device, I don't think this will work with any recent versions of Windows, but a couple of things to try:
use PRN: as the name instead of PRN (note the colon)
try opening the specific device using (for example) LPT1: (once again, note the colon). This will of course not work if you don't have a printer attached.
don't depend on a printer dialog coming up - you are not really using the WIndows printing system when you take this approach (and so it probably won't solve your problem, but is worth a try).
I do not have a printer attached, but I do have the Microsoft XPS document writer installed, s it shoulod at least bring up the standard Windows Print dialog from which one can choose the printer.
No. It wouldn't bring up a dialogue. This is because you are flushing data out to a file. And not going through the circuitous Win32 API.
The print doesn't work because the data is not proper PDL -- something that the printer could understand. For the print to work fine, you need to push in a PDL file, with language specific constructs. This varies from printer to printer, a PS printer will need you to push in a PostScript snippet, a PCL -- a PCL command-set and in case of MXDW you will have to write up XML based page description markup and create a zip file (with all resources embedded in it) i.e. an XPS file to get proper printout.
The PDL constructs are important because otherwise the printer doesn't know where to put the data, which color to print it on, what orientation to use, how many copies to print and so on and so forth.
Edit: I am curious why you are doing this. I understand portability is probably something you are trying to address. But apart from that, I'd like to know, there may be better alternatives available. Win32 Print Subsytem APIs are something that you ought to lookup if you are trying to print programmatically on Windows with any degree of fidelity.
Edit#2:
EDIT It builds with a warning that fopen is unsafe.
This is because MS suggests you use the safer versions nowadays fopen_s . See Security Enhancements in the CRT.
When it runs the error appears:
Debug Assertion fails. File: f:\dd\vctools\crt_bld\self_x86\crt\src\fprintf.c Line: 55
Expression: (str != NULL)
This is because fopen (whose return value you do not check) returns a NULL pointer. The file open failed. Also, if it did succeed a matching fclose call is called for.
There's no such thing as stdprn in ANSI C, it was a nonstandard extension provided by some compilers many years ago.
Today to print you have to use the specific APIs provided on your platform; to print on Windows you have to use the printing APIs to manage the printing of the document and obtain a DC to the printer and the GDI APIs to perform the actual drawing on the DC.
On UNIX-like OSes, instead, usually CUPS is used.
You can substitute the printer using this command with net use, see here on the MSDN kb
NET USE LPT1 \\server_name\printer_name
There is an excellent chapter on printing in DOS using the BIOS, ok, its a bit antiquated but interesting to read purely for nostalgic sake.
Onto your problem, you may need to use CreateFile to open the LPT1 port, see here for an example, I have it duplicated it here, for your benefit.
HANDLE hFile;
hFile = CreateFile("LPT1", GENERIC_WRITE, 0,NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
// handle error
}
OVERLAPPED ov = {};
ov.hEvent = CreateEvent(0, false, false, 0);
char szData[] = "1234567890";
DWORD p;
if (!WriteFile(hFile,szData, 10, &p, &ov))
{
if (GetLastError() != ERROR_IO_PENDING)
{
// handle error
}
}
// Wait for write op to complete (maximum 3 second)
DWORD dwWait = WaitForSingleObject(ov.hEvent, 3000);
if (dwWait == WAIT_TIMEOUT)
{
// it took more than 3 seconds
} else if (dwWait == WAIT_OBJECT_0)
{
// the write op completed,
// call GetOverlappedResult(...)
}
CloseHandle(ov.hEvent);
CloseHandle(hFile);
But if you insist on opening the LPT1 port directly, error checking is omitted...
FILE *prn = fopen("lpt1", "w");
fprintf(prn, "Hello World\n\f");
fclose(prn);
Hope this helps,
Best regards,
Tom.

How do you use Ruby/DL? Is this right?

I am trying to write an interface between RSPEC (ruby flavoured BDD) and a Windows application. The application itself is written in an obscure language, but it has a C API to provide access. I've gone with Ruby/DL but am having difficulties getting even the most basic call to a DLL method to work. Here is what I have so far, in a file called gt4r.rb:
require 'dl/import'
module Gt4r
extend DL::Importable
dlload 'c:\\gtdev\\r321\\bin\\gtvapi'
# GTD initialization/termination functions
extern 'int GTD_init(char *[], char *, char *)'
extern 'int GTD_initialize(char *, char *, char *)'
extern 'int GTD_done(void)'
extern 'int GTD_get_error_message(int, char **)'
end
My reading so far suggests that this is all I need to get going, so I wrote up a RSPEC example:
require 'gt4r'
##test_environment = "INCLUDE=C:\\graphtalk\\env\\aiadev\\config\\aiadev.ini"
##normal_user = "BMCHARGUE"
describe Gt4r do
it 'initializes' do
rv = Gt4r.gTD_initialize ##normal_user, ##normal_user, ##test_environment
rv.should == 0
end
end
And when run...
C:\code\GraphTalk>spec -fs -rgt4r gt4r_spec.rb
Gt4r
- initializes (FAILED - 1)
1)
'Gt4r initializes' FAILED
expected: 0,
got: 13 (using ==)
./gt4r_spec.rb:9:
Finished in 0.031 seconds
1 example, 1 failure
The return value (13) is an actual return code, meaning an error, but when I try to add the gTD_get_error_message call to my RSPEC, I can't get the parameters to work.
Am I heading in the right direction and can anyone point to the next thing I can try?
Thanks,
Brett
A follow up to this question, showing the part that fails when I try to get the error message from my target library:
require 'gt4r'
##test_environment = "INCLUDE=C:\\graphtalk\\env\\aiadev\\config\\aiadev.ini"
##normal_user = "BMCHARGUE"
describe Gt4r do
it 'initializes' do
rv = Gt4r.gTD_initialize ##normal_user, ##normal_user, ##test_environment
Gt4r.gTD_get_error_message rv, #msg
#msg.should == ""
rv.should == 0
end
end
I expect the error message to be returned in #msg, but when run I get the following:
Gt4r
(eval):5: [BUG] Segmentation fault
ruby 1.8.6 (2008-08-11) [i386-mswin32]
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
And this if I use a symbol (:msg) instead:
C:\code\GraphTalk\gt4r_dl>spec -fs -rgt4r gt4r_spec.rb
Gt4r
- initializes (ERROR - 1)
1)
NoMethodError in 'Gt4r initializes'
undefined method `to_ptr' for :msg:Symbol
(eval):5:in `call'
(eval):5:in `gTD_get_error_message'
./gt4r_spec.rb:9:
Finished in 0.046 seconds
1 example, 1 failure
Clearly I am missing something about passing parameters between ruby and C, but what?
The general consensus is you want to avoid DL as much as possible. The (english) documentation is quite sketchy and the interface is difficult to use for anything but trivial examples.
Ruby native C interface is MUCH easier to program against. Or you could use FFI, which fills a similiar niche to DL, originally comes from the rubinius project and has recently been ported to "normal" ruby. It has a nicer interface and is much less painful to use:
http://blog.headius.com/2008/10/ffi-for-ruby-now-available.html
The return value (13) is an actual
return code, meaning an error, but
when I try to add the
gTD_get_error_message call to my
RSPEC, I can't get the parameters to
work.
It might help posting the error instead of the code that worked :)
Basically, once you start having to deal with pointers as in (int, char **), things get ugly.
You need to allocate the data pointer for msg to be written to, since otherise C will have nowhere to write the error messages. Use DL.mallo.

Resources