I have a problem with a program I have done with Embarcadero C++ Builder.
Part one:
I want to make stand-alone .exe file, and according to that i have disabled "link with dynamic RTL" and disabled "link with runtime packages". But, every time i end my program, I get an error "Abnormal program termination". I've invstigated that and found a partial solution. When I enable link with runtime packages, that error does not appear, but .exe file won't run on PC which don't have C++ builder installed, because some .bpl files missing. So, for now, I have two possibilities: to have "Abnormal program termination" error, or not to have, functional .exe file for all PCs. Except for that error, my program works perfectly.
Part two:
I made fully functional program with 3 units, and I want to translate it on english language using resource DLL wizard. I can preview translated form, but I can't build it, because it say Error but I can't see any more details about error. I really don't know how is it possible to have working program with no errors, and localized version to have an error. I got the advice to do "clean all" and then "build all" but I can't even clean all because of that error.
I really don't know what to do anymore, I am struggling with those problems for a week now and I really hope that someone can help me. Thanks :)
Localization
Not sure if your localization tool is suited for Borland/Embarcadero apps (it might be just for MSVC++ apps which might be the problem).
I do localization on my own (as I needed custom stuff like internal messages and tables translation. So I coded a ~55 KByte parser that takes *.h,*.cpp,*.dfm of all windows as input and creates *.ini file and *.h that will read the selected language ini and translate all forms to it (Caption,Hint,...) then just for each new language I copy the ini and translate the strings. This way I can switch languages on the run without any DLL or restart needed and customers can add new languages on their own without the need for source code or programming skills.
Can not share the code however as it uses some non sharable (corporate) libs of mine for strings,dynamic lists and fast ini file access (they are included in the 55 KByte estimate) but I can share the executable (win32 standalone compiled in BDS2006 and for VCL BDS2006 formatting of code):
Form decode tool v2.70
Use the slow download (it is free without registration). I even add one window in the formulars for testing. You just run the exe and when done use the files from output. However the outputed source files use the fast ini file access (I can not share) so you would need to encode that (or replace with your style access). Here slow winapi based workaround for my ini.h that shoudl work:
#ifndef _ini_h
#define _ini_h
class inifile
{
public:
AnsiString section,filename;
// inline
inifile() {close();}
inifile(inifile& a) { *this=a; }
~inifile() {close();}
inifile* operator = (const inifile *a) { *this=*a; return this; }
//inifile* operator = (const inifile &a) { ...copy... return this; }
int open(AnsiString name) // open ini file
{
close();
if (FileExists(name)) { filename=name; return 1; }
return 0;
}
void close() // close ini file
{
filename="";
section="";
}
int sec_getid(const AnsiString &sec, bool _add) // select section to work with ignoring the returned ID
{
section=sec;
return 0;
}
AnsiString key_get(int section_id,const AnsiString &key,const AnsiString &def) // read key from section of ini with default value
{
if (filename=="") return def;
char dest[1024]; AnsiString val;
GetPrivateProfileString(section.c_str(),key.c_str(),def.c_str(),dest,1024,filename.c_str());
val=dest; return val;
}
};
#endif
So the app has 3 folders:
formulars
put all the *.h,*.cpp;*.dfm files of your forms here. The parsing of *.h is stopping on public: (skip user declarations) to prevent collisions with user code. You can also add comments to end of component line:
// LANGUAGE_SKIP_CAPT
// LANGUAGE_SKIP_TEXT
// LANGUAGE_SKIP_HINT
or any combination to prevent translation is messing with it.
messages
Ignore this it is for *.cpp source files with internal messages _messages[]to translate must be properly encoded using markers in comments like this:
AnsiString _messages[_msg_enum_end+1]=
{
// LANGUAGE_MSG000_TEXT_BEG
"Error: message id out of range !!!",
"OpenGL FBO support is missing !!! ... switching to simplified graphics",
"OpenGL VBO support is missing !!! ... switching to simplified graphics",
"Sorry: ATI graphic card detected !!! ... switching to simplified graphics",
"Sorry: ATI Radeon HD 5450 graphic card detected !!! ... disabling preview features",
"Done.",
// LANGUAGE_MSG000_TEXT_END
"",
""
};
output
the app here outputs 4 files
init.h can ignore this it is just init/exit load/stroing of window state
init.ini ini file for the init can ignore this
into app ini.
language.h this is what you want it is the translation routine
english.ini translation ini file using current window texts
Here example of generated language.h:
#include <math.h>
#include "ini.h" // use the ini.h above
void language_init(AnsiString name)
{
int sec;
inifile ini;
ini.open(name);
sec=ini.sec_getid("Twin_EditorSetup",true);
win_EditorSetup->ck_specchar->Caption=ini.key_get(sec,"C_ck_specchar","Special chars.");
win_EditorSetup->ck_specchar->Hint =ini.key_get(sec,"H_ck_specchar","View special characters on/off");
win_EditorSetup->cb_colors->Hint =ini.key_get(sec,"H_cb_colors","Select color for editing");
win_EditorSetup->ed_tabs->Text =ini.key_get(sec,"T_ed_tabs","1");
win_EditorSetup->ed_tabs->Hint =ini.key_get(sec,"H_ed_tabs","Tabulator size");
win_EditorSetup->ck_smart->Caption=ini.key_get(sec,"C_ck_smart","Smart eol");
win_EditorSetup->ck_smart->Hint =ini.key_get(sec,"H_ck_smart","Delete empty space at end of edited line");
win_EditorSetup->RadioGroup1->Caption=ini.key_get(sec,"C_RadioGroup1","");
win_EditorSetup->RadioGroup1->Hint =ini.key_get(sec,"H_RadioGroup1","");
win_EditorSetup->rb_eol1310->Caption=ini.key_get(sec,"C_rb_eol1310","CR LF");
win_EditorSetup->rb_eol1310->Hint =ini.key_get(sec,"H_rb_eol1310","EOL CR LF 13 10");
win_EditorSetup->rb_eol13->Caption=ini.key_get(sec,"C_rb_eol13","CR");
win_EditorSetup->rb_eol13->Hint =ini.key_get(sec,"H_rb_eol13","EOL CR 13");
win_EditorSetup->rb_eol10->Caption=ini.key_get(sec,"C_rb_eol10","LF");
win_EditorSetup->rb_eol10->Hint =ini.key_get(sec,"H_rb_eol10","EOL LF 10");
win_EditorSetup->txt_tabs->Caption=ini.key_get(sec,"C_txt_tabs","TAB:");
win_EditorSetup->txt_tabs->Hint =ini.key_get(sec,"H_txt_tabs","");
win_EditorSetup->pan_color->Caption=ini.key_get(sec,"C_pan_color","");
win_EditorSetup->pan_color->Hint =ini.key_get(sec,"H_pan_color","Pick color");
sec=ini.sec_getid("Global",true);
ini.close();
}
so in your main app form cpp include language.h but after its pointer is declared (after line like TForm1 *Form1; ) otherwise you will get access violation erros. Now call language_init with selected language ini file on any language change or Apps init like this:
if (Form1->Visible) language_init(ExtractFilePath(Application->ExeName)+"english.ini");
you need to specify full path if winapi is used for the ini.h !!!
Packages
Hard to say without MCVE. You most likely use non standard 3th party package and or have a bug in the code. My bet is you got memory leaks which tend to make a such errors time to time (especially on exit of app) try to use CodeGuard in the Project options which will show you runtime errors and allow you to debug them much better but it will not work for huge projects. Also if you are on older compiler or need to debug the memory leaks see these:
traceback a pointer in c++ code
BDS 2006 C++ hidden memory manager conflicts
They might solve your issues (even those you did not know of yet)
P.S.
If you're interested here is the main source for this tool (but without the supporting *.h files it will not work but you can see how it work in case you want to encode this on your own:
//--- Formular decoder storage class ver 2.40 -------------------------------
#ifndef _frm_decode_h
#define _frm_decode_h
//---------------------------------------------------------------------------
const int _frm_cmp_prop_none =0x00000000;
// language properties
const int _frm_cmp_prop_capt =0x00000001;
const int _frm_cmp_prop_text =0x00000002;
const int _frm_cmp_prop_hint =0x00000004;
// ini properties
const int _frm_cmp_prop_align =0x00010000;
const int _frm_cmp_prop_pos =0x00020000;
const int _frm_cmp_prop_check =0x00040000;
const int _frm_cmp_prop_down =0x00080000;
const int _frm_cmp_prop_page =0x00100000;
class _frm_component
{
public:
AnsiString name; // name in C++
int prop; // component class enum
int action; // if true do not save/load by language
};
_frm_component frm_component[]=
{
// standard
"TMenuItem" ,_frm_cmp_prop_capt|_frm_cmp_prop_hint|_frm_cmp_prop_align|_frm_cmp_prop_check,0,
"TLabel" ,_frm_cmp_prop_capt|_frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
"TEdit" ,_frm_cmp_prop_text|_frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
"TMemo" , _frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
"TButton" ,_frm_cmp_prop_capt|_frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
"TCheckBox" ,_frm_cmp_prop_capt|_frm_cmp_prop_hint|_frm_cmp_prop_align|_frm_cmp_prop_check,0,
"TRadioButton" ,_frm_cmp_prop_capt|_frm_cmp_prop_hint|_frm_cmp_prop_align|_frm_cmp_prop_check,0,
"TListBox" , _frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
"TComboBox" , _frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
"TScrollBar" , _frm_cmp_prop_hint|_frm_cmp_prop_align|_frm_cmp_prop_pos ,0,
"TGroupBox" ,_frm_cmp_prop_capt|_frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
"TRadioGroup" ,_frm_cmp_prop_capt|_frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
"TPanel" ,_frm_cmp_prop_capt|_frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
"TAction" ,_frm_cmp_prop_capt|_frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
// additional
"TBitBtn" ,_frm_cmp_prop_capt|_frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
"TSpeedButton" ,_frm_cmp_prop_capt|_frm_cmp_prop_hint|_frm_cmp_prop_align|_frm_cmp_prop_down ,0,
"TStringGrid" , _frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
"TCategoryButtons" , _frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
// win32
"TProgressBar" , _frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
// system
"TPaintBox" , _frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
// win3.1
"TTabbedNotebook" , _frm_cmp_prop_hint|_frm_cmp_prop_align|_frm_cmp_prop_page ,0,
"TFileListBox" , _frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
"TDirectoryListBox" , _frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
"TDriveComboBox" , _frm_cmp_prop_hint|_frm_cmp_prop_align ,0,
"",0,0
};
//---------------------------------------------------------------------------
class frm_decode
{
public:
AnsiString form; // formular class name in C++
struct _component:_frm_component
{
// language properties
AnsiString text,hint;
// init properties
AnsiString align; // -only for internal init purposes
AnsiString checked,down,position,left,top,width,height;
int skip; // znacky
void reset()
{
text="";
hint="";
align="alNone";
checked="False";
down="False";
position="1";
left="0";
top="0";
width="1";
height="1";
skip=0;
}
};
struct _message { AnsiString id,txt; };
List<_component> cmp;
List<_message> msg;
frm_decode(){ reset(); }
void reset() { form=""; cmp.reset(); msg.reset(); }
void load_form(AnsiString file); // load formular (file.h, file.dfm)
void load_msg(AnsiString file); // load messages (file.h, file.cpp)
void save_lang_ini(inifile &ini); // save language to ini
void save_init_ini(inifile &ini); // save initialisation to ini
void save_lang_cpp(AnsiString &txt,bool end=true); // save language loader (C++) to txt
void save_init_cpp(AnsiString &txt,bool end=true); // save init loader (C++) to txt
void save_exit_cpp(AnsiString &txt,bool end=true); // save exit saver (C++) to txt
};
//---------------------------------------------------------------------------
void frm_decode::load_form(AnsiString file)
{
// nepodporuje "/* ... */"
int hnd,adr,siz,i,j,l,e,_skip;
AnsiString lin,s,s0,ss;
_component a;
BYTE *txt;
reset();
// load list of components from: file.h
hnd=FileOpen(file+".h",fmOpenRead);
if (hnd<0) return;
siz=FileSeek(hnd,0,2);
FileSeek(hnd,0,0);
txt=new BYTE[siz];
if (txt==NULL) { FileClose(hnd); return; }
FileRead(hnd,txt,siz);
FileClose(hnd);
for (adr=0;adr<siz;)
{
lin=txt_load_lin(txt,siz,adr,true);
l=lin.Length();
for (_skip=0;i<=l;) // osetri zacky este pred odrezanim pokecu
{
s=str_load_str(lin,i,true);
if (s=="LANGUAGE_SKIP_CAPT") _skip|=_frm_cmp_prop_capt;
if (s=="LANGUAGE_SKIP_TEXT") _skip|=_frm_cmp_prop_text;
if (s=="LANGUAGE_SKIP_HINT") _skip|=_frm_cmp_prop_hint;
}
if (str_is_mask(lin,"*//*")) { s=lin; lin=""; for (i=1;i<l;i++) if ((s[i]=='/')&&(s[i+1]=='/')) break; else lin+=char(s[i]); l=lin.Length(); }
s=""; s0=""; e=0;
for (i=1;i<=l;)
{
s0=s;
s=str_load_str(lin,i,true);
if (s=="private:") // stop processing for non formular symbols
{
adr=siz; break;
}
if (!e) { e=1; continue; }
if (s0=="class")
{
form=s;
continue;
}
if (s!="")
for (j=0;;j++)
{
if (frm_component[j].name=="") break;
if (frm_component[j].name==s0)
{
a.reset();
a.name="";
if (s[1]!='*') s=" "+s;
for (int q=2;q<=s.Length();q++) a.name+=s[q];
a.prop=frm_component[j].prop;
a.action=0;
a.skip=_skip;
cmp.add(a);
break;
}
}
}
}
delete txt;
// load component properties from: file.dfm
int cmpid=-1;
hnd=FileOpen(file+".dfm",fmOpenRead);
if (hnd<0) return;
siz=FileSeek(hnd,0,2);
FileSeek(hnd,0,0);
txt=new BYTE[siz];
if (txt==NULL) { FileClose(hnd); return; }
FileRead(hnd,txt,siz);
FileClose(hnd);
for (adr=0;adr<siz;)
{
lin=txt_load_lin(txt,siz,adr,true);
l=lin.Length();
for (i=1;i<=l;i++) // if cutted '' line then continue
if (lin[i]>32)
{
if (lin[i]!='\'') i=-1;
break;
}
if (i<0) { s=""; s0=""; e=0; } // else new line
for (i=1;i<=l;)
{
int q=1;
if (s!="")
if (s[1]=='\'')
{
if (s[s.Length()]!='\'')
{
s+=" "+str_load_str(lin,i,true);
if (s[s.Length()]!='\'') continue;
}
q=0;
}
if (q)
{
int qqq=0; // handle cutted '' line
ss=str_load_str(lin,i,true);
if ((s0!="")&&((ss!="")&&(ss[1]=='\''))) { qqq=1; s+=ss; }
if ((s0!="")&&(ss=="+")) { qqq=1; }
if (!qqq) { s0=s; s=ss; }
if (s!="")
if (s[1]=='\'')
if (s[s.Length()]!='\'')
e=0;
}
/* // odstran ''
if (s!="")
if (s[1]=='\'')
if (s[s.Length()]=='\'')
{ ss=""; for (q=2;q<s.Length();q++) ss+=s[q]; s=ss; }
*/
if (!e) { e=1; continue; }
if (s!="")
if (s0=="object")
{
cmpid=-1;
if (s[s.Length()]==':')
{
AnsiString t=s;
s=""; for (j=1;j<t.Length();j++) s+=char(t[j]);
}
for (j=0;j<cmp.num;j++)
if (cmp[j].name==s)
{
cmpid=j;
break;
}
continue;
}
if (cmpid<0) continue;
char aa;
bool tt;
int ii,cc,ll=s.Length();
if (ll>0)
{
ss="";
tt=false;
for (ii=1;ii<=ll;ii++)
{
aa=s[ii];
if (aa=='\'') { tt=!tt; continue; }
if (tt) { ss+=aa; continue; }
if (aa!='#'){ ss+=aa; continue; }
cc=0;
for (ii++;ii<=ll;ii++)
{
aa=s[ii];
if (aa=='\'') { ii--; break; }
if (aa< '0') { ii--; break; }
if (aa> '9') { ii--; break; }
cc*=10; cc+=aa-'0';
}
ss+=char(cc);
}
s=ss;
}
if (s0=="Action" ) cmp[cmpid].action=1;
if (s0=="Caption" ) cmp[cmpid].text=s;
if (s0=="Text" ) cmp[cmpid].text=s;
if (s0=="Hint" ) cmp[cmpid].hint=s;
if (s0=="Align" ) cmp[cmpid].align=s;
if (s0=="Checked" ) cmp[cmpid].checked=s;
if (s0=="Down" ) cmp[cmpid].down=s;
if (s0=="Position") cmp[cmpid].position=s;
if (s0=="Left" ) cmp[cmpid].left=s;
if (s0=="Top" ) cmp[cmpid].top=s;
if (s0=="Width" ) cmp[cmpid].width=s;
if (s0=="Height" ) cmp[cmpid].height=s;
}
}
delete txt;
// load list of messages from: file.cpp
load_msg(file+".cpp");
}
//---------------------------------------------------------------------------
void frm_decode::load_msg(AnsiString file)
{
// nepodporuje "/* ... */"
int hnd,adr,siz,i,l,e;
AnsiString lin,s;
BYTE *txt;
hnd=FileOpen(file,fmOpenRead);
if (hnd<0) return;
siz=FileSeek(hnd,0,2);
FileSeek(hnd,0,0);
txt=new BYTE[siz];
if (txt==NULL) { FileClose(hnd); return; }
FileRead(hnd,txt,siz);
FileClose(hnd);
e=msg.num;
for (adr=0;adr<siz;)
{
lin=txt_load_lin(txt,siz,adr,true);
l=lin.Length();
for (i=1;i<=l;)
{
s=str_load_str(lin,i,true);
if (s=="_msg_enum_beg")
for (;adr<siz;)
{
lin=txt_load_lin(txt,siz,adr,true);
l=lin.Length();
for (i=1;i<=l;)
{
s=str_load_str(lin,i,true);
if (s=="_msg_enum_end") { adr=siz; i=l+1; break; }
if (str_is_mask(s,"_msg*"))
{
_message m;
m.id=s;
m.txt="";
msg.add(m);
}
}
}
}
}
for (adr=0;adr<siz;)
{
lin=txt_load_lin(txt,siz,adr,true);
l=lin.Length();
for (i=1;i<=l;)
{
s=str_load_str(lin,i,true);
if (s=="_messages[_msg_enum_end+1]")
for (;adr<siz;)
{
if (e>=msg.num) break;
s="";
for (;adr<siz;adr++)
if (txt[adr]=='"')
{
adr++;
for (;adr<siz;adr++)
if (txt[adr]=='"')
{
adr++;
msg[e].txt=s;
e++;
break;
}
else s+=char(txt[adr]);
break;
}
}
}
}
delete txt;
}
//---------------------------------------------------------------------------
void frm_decode::save_lang_ini(inifile &ini)
{
int i,p,s,sec;
AnsiString key,val;
sec=ini.sec_getid(form,true);
for (i=0;i<msg.num;i++) ini.key_set(sec,msg[i].id,msg[i].txt);
for (i=0;i<cmp.num;i++)
{
if (cmp[i].action) continue;
key=cmp[i].name;
p=cmp[i].prop;
s=cmp[i].skip;
if ((int(p&_frm_cmp_prop_capt)!=0)&&(int(s&_frm_cmp_prop_capt)==0)) ini.key_set(sec,"C_"+key,cmp[i].text);
if ((int(p&_frm_cmp_prop_text)!=0)&&(int(s&_frm_cmp_prop_text)==0)) ini.key_set(sec,"T_"+key,cmp[i].text);
if ((int(p&_frm_cmp_prop_hint)!=0)&&(int(s&_frm_cmp_prop_hint)==0)) ini.key_set(sec,"H_"+key,cmp[i].hint);
}
}
//---------------------------------------------------------------------------
void frm_decode::save_init_ini(inifile &ini)
{
int i,p,sec;
AnsiString key,val;
sec=ini.sec_getid(form,true);
for (i=0;i<cmp.num;i++)
{
if (cmp[i].action) continue;
key=cmp[i].name;
p=cmp[i].prop;
if (cmp[i].align=="alLeft") ini.key_set(sec,"XS_"+key,cmp[i].width);
if (cmp[i].align=="alRight") ini.key_set(sec,"XS_"+key,cmp[i].width);
if (cmp[i].align=="alTop") ini.key_set(sec,"YS_"+key,cmp[i].height);
if (cmp[i].align=="alBottom") ini.key_set(sec,"YS_"+key,cmp[i].height);
if (cmp[i].align=="alClient")
{
}
if (cmp[i].align=="alNone")
{
/*
ini.key_set(sec,"X0_"+key,cmp[i].left);
ini.key_set(sec,"Y0_"+key,cmp[i].top);
ini.key_set(sec,"XS_"+key,cmp[i].width);
ini.key_set(sec,"YS_"+key,cmp[i].height);
*/
}
if (int(p&_frm_cmp_prop_check )!=0) ini.key_set(sec,"CHK_"+key,(cmp[i].checked=="True")?"1":"0");
if (int(p&_frm_cmp_prop_down )!=0) ini.key_set(sec,"DWN_"+key,(cmp[i].down =="True")?"1":"0");
if (int(p&_frm_cmp_prop_pos )!=0) ini.key_set(sec,"POS_"+key, cmp[i].position );
}
}
//---------------------------------------------------------------------------
void frm_decode::save_lang_cpp(AnsiString &txt,bool end)
{
int i,p,s;
AnsiString cpp,lin,key,frm,tabl,endl;
tabl=char(9); endl=char(13); endl+=char(10);
cpp="";
if (txt=="")
{
lin="";
lin+="#include <math.h>"+endl;
lin+="#include \"ini.h\""+endl+endl;
lin+="void language_init(AnsiString name)"+endl;
lin+=tabl+"{"+endl;
lin+=tabl+"int sec;"+endl;
lin+=tabl+"inifile ini;"+endl;
lin+=tabl+"ini.open(name);"+endl;
cpp+=lin;
}
frm=""; for (i=2;i<=form.Length();i++) frm+=form[i];
cpp+=endl;
cpp+=tabl+"sec=ini.sec_getid(\""+form+"\",true);"+endl;
for (i=0;i<msg.num;i++)
{
lin=tabl+"_messages[";
if (i<100) lin+=" ";
if (i<10) lin+=" ";
lin+=i;
lin+="]=ini.key_get(sec,\""+msg[i].id+"\",\""+msg[i].txt+"\");"+endl;
cpp+=lin;
}
for (i=0;i<cmp.num;i++)
{
if (cmp[i].action) continue;
key=cmp[i].name;
p=cmp[i].prop;
s=cmp[i].skip;
lin="";
if ((int(p&_frm_cmp_prop_capt)!=0)&&(int(s&_frm_cmp_prop_capt)==0)) lin+=tabl+frm+"->"+key+"->Caption=ini.key_get(sec,\"C_"+key+"\",\""+cmp[i].text+"\");"+endl;
if ((int(p&_frm_cmp_prop_text)!=0)&&(int(s&_frm_cmp_prop_text)==0)) lin+=tabl+frm+"->"+key+"->Text =ini.key_get(sec,\"T_"+key+"\",\""+cmp[i].text+"\");"+endl;
if ((int(p&_frm_cmp_prop_hint)!=0)&&(int(s&_frm_cmp_prop_hint)==0)) lin+=tabl+frm+"->"+key+"->Hint =ini.key_get(sec,\"H_"+key+"\",\""+cmp[i].hint+"\");"+endl;
cpp+=lin;
}
if (end)
{
lin=endl;
lin+=tabl+"ini.close();"+endl;
lin+=tabl+"}"+endl;
cpp+=lin;
}
txt+=cpp;
}
//---------------------------------------------------------------------------
void frm_decode::save_init_cpp(AnsiString &txt,bool end)
{
int i,p;
AnsiString cpp,lin,key,frm,tabl,endl,endlx;
tabl=char(9); endl=char(13); endl+=char(10); endlx="\\"+endl;
cpp="";
if (txt=="")
{
lin="";
lin+="//---------------------------------------------------------------------------"+endl;
lin+="#include <math.h>"+endl;
lin+="#include \"ini.h\""+endl;
lin+="//---------------------------------------------------------------------------"+endl;
lin+="#define _def_program_init(name) for(;;)"+endlx;
lin+=tabl+"{"+endlx;
lin+=tabl+"int sec;"+endlx;
lin+=tabl+"inifile ini;"+endlx;
lin+=tabl+"ini.open(name);"+endlx;
// lin+=tabl+"if (!ini.open(name)) break;"+endlx;
cpp+=lin;
}
frm=""; for (i=2;i<=form.Length();i++) frm+=form[i];
cpp+=tabl+"sec=ini.sec_getid(\""+form+"\",true);"+endlx;
for (i=0;i<cmp.num;i++)
{
if (cmp[i].action) continue;
key=cmp[i].name;
p=cmp[i].prop;
lin="";
if (cmp[i].align=="alLeft") lin+=tabl+frm+"->"+key+"->Width =ini.key_geti(sec,\"XS_"+key+"\","+cmp[i].width +");"+endlx;
if (cmp[i].align=="alRight") lin+=tabl+frm+"->"+key+"->Width =ini.key_geti(sec,\"XS_"+key+"\","+cmp[i].width +");"+endlx;
if (cmp[i].align=="alTop") lin+=tabl+frm+"->"+key+"->Height=ini.key_geti(sec,\"YS_"+key+"\","+cmp[i].height+");"+endlx;
if (cmp[i].align=="alBottom") lin+=tabl+frm+"->"+key+"->Height=ini.key_geti(sec,\"YS_"+key+"\","+cmp[i].height+");"+endlx;
if (cmp[i].align=="alClient")
{
}
if (cmp[i].align=="alNone")
{
/*
lin+=tabl+frm+"->"+key+"->Left =ini.key_geti(sec,\"X0_"+key+"\","+cmp[i].left +");"+endlx;
lin+=tabl+frm+"->"+key+"->Top =ini.key_geti(sec,\"Y0_"+key+"\","+cmp[i].top +");"+endlx;
lin+=tabl+frm+"->"+key+"->Width =ini.key_geti(sec,\"XS_"+key+"\","+cmp[i].width +");"+endlx;
lin+=tabl+frm+"->"+key+"->Height=ini.key_geti(sec,\"YS_"+key+"\","+cmp[i].height+");"+endlx;
*/
}
if (int(p&_frm_cmp_prop_check )!=0) lin+=tabl+frm+"->"+key+"->Checked =ini.key_geti(sec,\"CHK_"+key+"\","+cmp[i].checked +");"+endlx;
if (int(p&_frm_cmp_prop_down )!=0) lin+=tabl+frm+"->"+key+"->Down =ini.key_geti(sec,\"DWN_"+key+"\","+cmp[i].down +");"+endlx;
if (int(p&_frm_cmp_prop_pos )!=0) lin+=tabl+frm+"->"+key+"->Position=ini.key_geti(sec,\"POS_"+key+"\","+cmp[i].position+");"+endlx;
cpp+=lin;
}
if (end)
{
lin+=tabl+"ini.close();"+endlx;
lin+=tabl+"break;"+endlx;
lin+=tabl+"}"+endl;
cpp+=lin;
}
txt+=cpp;
}
//---------------------------------------------------------------------------
void frm_decode::save_exit_cpp(AnsiString &txt,bool end)
{
int i,p;
AnsiString cpp,lin,key,frm,tabl,endl,endlx;
tabl=char(9); endl=char(13); endl+=char(10); endlx="\\"+endl;
cpp="";
if (txt=="")
{
lin+="//---------------------------------------------------------------------------"+endl;
lin+="#define _def_program_exit(name) for(;;)"+endlx;
lin+=tabl+"{"+endlx;
lin+=tabl+"int sec;"+endlx;
lin+=tabl+"inifile ini;"+endlx;
lin+=tabl+"ini.open(name);"+endlx;
cpp+=lin;
}
frm=""; for (i=2;i<=form.Length();i++) frm+=form[i];
cpp+=tabl+"sec=ini.sec_getid(\""+form+"\",true);"+endlx;
for (i=0;i<cmp.num;i++)
{
if (cmp[i].action) continue;
key=cmp[i].name;
p=cmp[i].prop;
lin="";
if (cmp[i].align=="alLeft") lin+=tabl+"ini.key_set(sec,\"XS_"+key+"\","+frm+"->"+key+"->Width );"+endlx;
if (cmp[i].align=="alRight") lin+=tabl+"ini.key_set(sec,\"XS_"+key+"\","+frm+"->"+key+"->Width );"+endlx;
if (cmp[i].align=="alTop") lin+=tabl+"ini.key_set(sec,\"YS_"+key+"\","+frm+"->"+key+"->Height);"+endlx;
if (cmp[i].align=="alBottom") lin+=tabl+"ini.key_set(sec,\"YS_"+key+"\","+frm+"->"+key+"->Height);"+endlx;
if (cmp[i].align=="alClient")
{
}
if (cmp[i].align=="alNone")
{
/*
lin+=tabl+"ini.key_set(sec,\"X0_"+key+"\","+frm+"->"+key+"->Left );"+endlx;
lin+=tabl+"ini.key_set(sec,\"Y0_"+key+"\","+frm+"->"+key+"->Top );"+endlx;
lin+=tabl+"ini.key_set(sec,\"XS_"+key+"\","+frm+"->"+key+"->Width );"+endlx;
lin+=tabl+"ini.key_set(sec,\"YS_"+key+"\","+frm+"->"+key+"->Height);"+endlx;
*/
}
if (int(p&_frm_cmp_prop_check )!=0) lin+=tabl+"ini.key_set(sec,\"CHK_"+key+"\",int("+frm+"->"+key+"->Checked));"+endlx;
if (int(p&_frm_cmp_prop_down )!=0) lin+=tabl+"ini.key_set(sec,\"DWN_"+key+"\",int("+frm+"->"+key+"->Down ));"+endlx;
if (int(p&_frm_cmp_prop_pos )!=0) lin+=tabl+"ini.key_set(sec,\"POS_"+key+"\", "+frm+"->"+key+"->Position);"+endlx;
cpp+=lin;
}
if (end)
{
lin+=tabl+"ini.save(name);"+endlx;
lin+=tabl+"ini.close();"+endlx;
lin+=tabl+"break;"+endlx;
lin+=tabl+"}"+endl;
lin+="//---------------------------------------------------------------------------"+endl;
cpp+=lin;
}
txt+=cpp;
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
I am trying to call the method ReadLocalBdAddrReq and receive its signal ReadLocalBdAddrCfm on dbus using the dbus low level APIs.
I have written the following code with the help of some forum posts and a dbus tutorial.
The thing is, I am not able to receive the signals back. The code is incomplete at some places as I didn't know what should be done.
So please help me so that I can receive the signals for the methods called.
Here the code I have written. Please correct any mistakes I've made.
#include <stdlib.h>
#include <stdio.h>
#include <dbus/dbus.h>
#define OBJ_PATH "/bt/cm"
static dbus_bool_t add_watch(DBusWatch *watch, void *data)
{
if (!dbus_watch_get_enabled(watch))
return TRUE;
int fd = dbus_watch_get_unix_fd(watch);
unsigned int flags = dbus_watch_get_flags(watch);
int f = 0;;
if (flags & DBUS_WATCH_READABLE) {
f |= DBUS_WATCH_READABLE;
printf("Readable\n");
}
if (flags & DBUS_WATCH_WRITABLE) {
printf("Writeable\n");
f |= DBUS_WATCH_WRITABLE;
}
/* this should not be here */
if (dbus_watch_handle(watch, f) == FALSE)
printf("dbus_watch_handle() failed\n");
return TRUE;
}
static void remove_watch(DBusWatch *watch, void *data)
{
printf("In remove watch with fd = [%d]\n",dbus_watch_get_unix_fd(watch));
}
static void toggel_watch(DBusWatch *watch, void *data)
{
printf("In toggel watch\n");
/*
if (dbus_watch_get_enabled(watch))
add_watch(watch, data);
else
remove_watch(watch, data);
*/
}
/* timeout functions */
static dbus_bool_t add_time(DBusTimeout *timeout, void *data)
{
/* Incomplete */
printf("In add_time\n");
if (!dbus_timeout_get_enabled(timeout))
return TRUE;
//dbus_timeout_handle(timeout);
return 0;
}
static void remove_time(DBusTimeout *timeout, void *data)
{
/* Incomplete */
printf("In remove_time\n");
}
static void toggel_time(DBusTimeout *timeout, void *data)
{
/* Incomplete */
printf("In toggel_time\n");
/*
if (dbus_timeout_get_enabled(timeout))
add_timeout(timeout, data);
else
remove_timeout(timeout, data);
*/
}
/* message filter -- handlers to run on all incoming messages*/
static DBusHandlerResult filter (DBusConnection *connection, DBusMessage *message, void *user_data)
{
printf("In filter\n");
char *deviceaddr;
if (dbus_message_is_signal(message, "com.bluegiga.v2.bt.cm", "ReadLocalBdAddrCfm")) {
printf("Signal received is ReadLocalBdAddrCfm\n");
if ((dbus_message_get_args(message,NULL,DBUS_TYPE_STRING, &deviceaddr,DBUS_TYPE_INVALID) == FALSE))
{
printf("Could not get the arguments from the message received\n");
return -2;
}
printf("Got Signal and device address is [%s]\n", deviceaddr);
}
return 0;
}
/* dispatch function-- simply save an indication that messages should be dispatched later, when the main loop is re-entered*/
static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_status, void *data)
{
printf("In dispatch_status\n");
if (new_status == DBUS_DISPATCH_DATA_REMAINS)
{
printf("new dbus dispatch status: DBUS_DISPATCH_DATA_REMAINS [%d]",new_status);
}
}
/* unregister function */
void unregister_func(DBusConnection *connection, void *user_data)
{
}
/* message function - Called when a message is sent to a registered object path. */
static DBusHandlerResult message_func(DBusConnection *connection, DBusMessage *message, void *data)
{
printf("Message [%s] is sent to [%s] from interface [%s] on path [%s] \n",dbus_message_get_member(message),dbus_message_get_destination(message),
dbus_message_get_interface(message),dbus_message_get_path(message)); return 0;
}
DBusObjectPathVTable table = {
.unregister_function = unregister_func,
.message_function = message_func,
};
int main(void) {
DBusMessage* msg;
DBusMessageIter args;
DBusConnection* conn;
DBusError err;
DBusPendingCall* pending;
int ret;
//unsigned int level;
char* appHandle = NULL;
//int *context;
int msg_serial;
int open;
char *deviceaddr;
dbus_error_init(&err);
// connect to the system bus and check for errors
conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
if (dbus_error_is_set(&err)) {
fprintf(stderr, "Connection Error (%s)\n", err.message);
dbus_error_free(&err);
}
if (NULL == conn) {
exit(1);
}
if (!dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggel_watch, NULL, NULL))
{
printf("Error in dbus_set_watch_functions\n");
dbus_connection_unref(conn);
return -1;
}
/* These functions are responsible for making the application's main loop aware of timeouts */
if (!dbus_connection_set_timeout_functions(conn, add_time, remove_time, toggel_time, NULL, NULL))
{
printf("Error in dbus_set_timeout_functions\n");
dbus_connection_unref(conn);
return -1;
}
/* Used to register the handler functions run on incoming messages*/
if (!dbus_connection_add_filter(conn, filter, NULL, NULL))
{
printf("Error in adding filter\n");
dbus_connection_unref(conn);
return -1;
}
/* Filter added for incoming messages */
/* Set a function to be invoked when the dispatch status changes */
dbus_connection_set_dispatch_status_function(conn, dispatch_status, NULL ,NULL);
/* Register a handler for messages sent to a given path */
if(!dbus_connection_register_object_path(conn, OBJ_PATH, &table, NULL))
{
printf("Error in registering object\n");
return -1;
}
/* sending messages to the outgoing queue */
msg = dbus_message_new_method_call("com.bluegiga.v2.bt.cm", // target for the method call
OBJ_PATH, // object to call on
"com.bluegiga.v2.bt.cm", // interface to call on
"ReadLocalBdAddrReq"); // method name
if (NULL == msg) {
fprintf(stderr, "Message Null\n");
exit(1);
}
dbus_message_iter_init_append(msg, &args);
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT16,&appHandle)) {
fprintf(stderr, "Out Of Memory!\n");
exit(1);
}
fprintf(stderr, "Sending the connections\n");
// send message and get a handle for a reply
if (!dbus_connection_send (conn, msg, &msg_serial)) {
fprintf(stderr, "Out Of Memory!\n");
exit(1);
}
fprintf(stderr, "Connection sent and the msg serial is %d\n",msg_serial);
/* Message sent over */
/* not sure whether this should be here or above watch */
while (dbus_connection_get_dispatch_status(conn) == DBUS_DISPATCH_DATA_REMAINS)
{
//printf("Entered in dispatch\n");
/* Processes any incoming data. will call the filters registered by add_filer*/
dbus_connection_dispatch(conn);
}
return 0;
}
After I run this program it has the following output:
Readable
Sending the connections
Connection sent and the msg serial is
2(DBUS_MESSAGE_TYPE_METHOD_RETURN)
If the connection was sent to the object path then message_func should have been called correctly, but it never is called. Have I made any mistake in sending the method call?
You are missing the event loop which is otherwise available by default if you choose to go with one of the bindings. When you get a call to add_watch, libdbus expects that the application will attach an IO handler to it. The IOHandler added by application will watch for an activity on the fd (filedescriptor) queried for the watch. Whenever there is an activity on that file descriptor, the IOHandler will trigger a callback with appropriate flags that you need to convert to DBUS flags before calling dbus_watch_handle.
Suggest that you use glib if you don't know how to use event loops. I am able to get it if I use libUV or libEV as low footprint event loop.