Using IXMLHttpRequest to fetch the data from the webservice.(actually, java servlets file). Using below code to send request and get response from webservice
IXMLHTTPRequestPtr pIXMLHTTPRequest = NULL;
CoInitialize(nullptr);
String usBuffer;
CATTry
{
hr = pIXMLHTTPRequest.CreateInstance("Msxml2.XMLHTTP.6.0");
if (SUCCEEDED(hr) && (pIXMLHTTPRequest != NULL))
{
hr = pIXMLHTTPRequest->open("POST", "URL", false);
if (SUCCEEDED(hr))
{
String lsusHeaderName;
lsusHeaderName.Append("Content-Type: application/x-www-form-urlencoded");
lsusHeaderName.Append("Accept: */*");
lsusHeaderName.Append("ticket: " + "ticketvalue");
lsusHeaderName.Append("Context: " + "securityvalue");
for (int i = 1; i <= ilsusHeaderName.Size(); i++)
{
BSTR bstrHeaderName;
BSTR bstrHeaderValue;
ilsusHeaderName[i].ConvertToBSTR(&bstrHeaderName);
hr = pIXMLHTTPRequest->setRequestHeader(bstrHeaderName, bstrHeaderValue);
}
String iusRequestParam = "Type=xxxx&Name=xxxxytr&Revision=09";
if (iusRequestParam != "")
{
hr = pIXMLHTTPRequest->send(iusRequestParam.ConvertToChar());
}
else
{
hr = pIXMLHTTPRequest->send();
}
{
struct __timeb64 t_stime;
struct __timeb64 t_ctime;
long lProcTime = 0;
memset(&t_stime, 0, sizeof(struct __timeb64));
memset(&t_ctime, 0, sizeof(struct __timeb64));
long nRedyState = READYSTATE_UNINITIALIZED;
_ftime64(&t_stime);
while (nRedyState != READYSTATE_COMPLETE)
{
_ftime64(&t_ctime);
lProcTime = (long)(1000 * (t_ctime.time - t_stime.time) + (t_ctime.millitm - t_stime.millitm));
if (lProcTime > HTTP_TIMEOUT)
{
break;
}
nRedyState = pIXMLHTTPRequest->readyState;
}
}
std::cout << "Request status : " << pIXMLHTTPRequest->status << std::endl;
if ((pIXMLHTTPRequest->status == 200))
{
_bstr_t spbstrResponse = pIXMLHTTPRequest->responseText;
BSTR bstrString = NULL;
bstrString = spbstrResponse.GetBSTR();
usBuffer.BuildFromBSTR(bstrString);
usOutput = usBuffer;
std::cout << "Output : " << usBuffer << std::endl;
bstrString = NULL;
}
else
{
std::cout << "Failed to send GET/POST Method." << std::endl;
}
}
else
{
hr = E_FAIL;
}
}
}
}
CATCatch(CATError, pError)
{
std::cout << "Failed to query XMLHTTP 6.0. Perhaps MSXML 6.0 is not exists." << std::endl;
}
CATEndTry;
This webservice call returns 200 status code, but response text is not good.It returns some html,javascript code as response text.
Expected result is one of the attribute value of the passed object. Example output:
{
"logy": "646F916E00005E78609AC53D0000974F"
}
Couldn't locate the issue in this code, why it returns html,javascript code.As I am new to WINAPI concepts, your help is highly appreciated.
Edit:
Checked webservice via postman and it returns expected result.
Header should be passed in this way. This solved the issue.
CATListOfCATUnicodeString lsusHeaderName;
CATListOfCATUnicodeString lsusHeaderValue;
lsusHeaderName.Append("Content-Type");
lsusHeaderValue.Append("application/x-www-form-urlencoded");
lsusHeaderName.Append("Login-ticket");
lsusHeaderValue.Append(usLoginTicket);
lsusHeaderName.Append("SecurityContext");
lsusHeaderValue.Append(usSecurityContext);
Related
I want to use Windows API to realize the line by line printing of the printer.
For example, if there is only one message at present, print one message. Then the printer waits and the paper stops printing. When there is information next time, continue printing on the same paper.
With windows API, I can only print by page in the following way. I don't know how to print by line. After one line is finished, the printing is suspended and the next line continues to be printed after waiting for a new task.
bool CMFCApplication1Dlg::printTicket(CString& szPrinter, CString&
szContent)
{
static DOCINFO di = { sizeof(DOCINFO), (LPTSTR)TEXT("printer"),NULL };
HDC hdcPrint = CreateDC(nullptr, szPrinter.GetBuffer(), nullptr, nullptr);
if (hdcPrint != 0)
{
if (StartDoc(hdcPrint, &di) > 0)
{
StartPage(hdcPrint);
SaveDC(hdcPrint);
int xDistance = 20;
int yDistance = 20;
LOGFONT logFont = { 0 };
logFont.lfCharSet = DEFAULT_CHARSET;
logFont.lfPitchAndFamily = DEFAULT_PITCH;
logFont.lfWeight = FW_NORMAL;
logFont.lfHeight = 60;
logFont.lfWeight = 36;
HFONT hFont = CreateFontIndirect(&logFont);
SelectObject(hdcPrint, hFont);
TextOut(hdcPrint, xDistance, yDistance, szContent.GetBuffer(), szContent.GetLength());
RestoreDC(hdcPrint, -1);
EndPage(hdcPrint);
EndDoc(hdcPrint);
}
else
{
cout << "StartDoc failed!" << endl;
string errorCode = to_string(GetLastError());
cout << "Error code is:" << errorCode << endl;
return false;
}
DeleteDC(hdcPrint);
}
else
{
cout << "CreateDC failed!" << endl;
string errorCode = to_string(GetLastError());
cout << "Error code is :" << errorCode << endl;
return false;
}
return true;
}
bool CMFCApplication1Dlg::SetPrinterParameters(CString& szPrinter)
{
HANDLE hPrinter = nullptr;
PRINTER_INFO_2* pi2 = nullptr;
DEVMODE* pDevMode = nullptr;
PRINTER_DEFAULTS pd;
DWORD dwNeeded = 0;
BOOL bFlag;
LONG lFlag;
WCHAR szDevName[MAX_PATH] = L"";
DWORD dwLength = MAX_PATH;
if (!GetDefaultPrinter(szDevName, &dwLength))
{
return false;
}
szPrinter = szDevName;
return true;
}
Maybe this has something to do with the printer driver?
I use C# winspool library and can't meet this requirement too, maybe I don't know how to use them.
Hope your help.
I am currently searching for a way to set a fully custom resolution on a Windows Server 2019 VM with GPU (T4, with grid licence, and virtual workstation grid drivers) with C++.
I have tried different way to achieve this, I can make this work on my laptop, but seems to have some limitations on GCP VMs (or Windows Server limitation).
I have tried to do this with ChangeDisplaySettings/ChangeDisplaySettingsEx (winuser.h), I can change to a known resolution, but can't make it works with a custom one (not even with CDS_ENABLE_UNSAFE_MODE).
DWORD deviceIndex = 0;
DISPLAY_DEVICE displayDevice = { 0 };
displayDevice.cb = sizeof(DISPLAY_DEVICE);
while (EnumDisplayDevices(NULL, deviceIndex, &displayDevice, 0)) {
deviceIndex++;
DEVMODE dm = { 0 };
dm.dmSize = sizeof(DEVMODE);
DEVMODE finalDm = { 0 };
finalDm.dmSize = sizeof(DEVMODE);
//Check if able to retrieve current settings
if (!EnumDisplaySettings(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) {
continue;
}
//Check if there is a difference in resolution list if UNSAFE_MODE is enabled or not (it seems to not change anything)
int result = ChangeDisplaySettingsEx(displayDevice.DeviceName, &dm, 0, CDS_DISABLE_UNSAFE_MODES, NULL);
std::cout << "CDS_DISABLE_UNSAFE_MODE" << std::endl;
if (result == DISP_CHANGE_SUCCESSFUL) {
for (int i = 0; EnumDisplaySettings(displayDevice.DeviceName, i, &dm) != 0; i++) {
if (dm.dmBitsPerPel == 32) {
std::cout << i << ". Found available resolution : " << dm.dmPelsWidth << " x " << dm.dmPelsHeight << " x " << dm.dmBitsPerPel << " # " << dm.dmDisplayFrequency << std::endl;
}
}
}
result = ChangeDisplaySettingsEx(displayDevice.DeviceName, &dm, 0, CDS_ENABLE_UNSAFE_MODES, NULL);
std::cout << "CDS_ENABLE_UNSAFE_MODE" << std::endl;
if (result == DISP_CHANGE_SUCCESSFUL) {
for (int i = 0; EnumDisplaySettings(displayDevice.DeviceName, i, &dm) != 0; i++) {
if (dm.dmBitsPerPel == 32) {
std::cout << i << ". Found available resolution : " << dm.dmPelsWidth << " x " << dm.dmPelsHeight << " x " << dm.dmBitsPerPel << " # " << dm.dmDisplayFrequency << std::endl;
}
}
}
std::cout << "Please enter width : ";
int width, height;
std::cin >> width;
std::cout << "Please enter height : ";
std::cin >> height;
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
if (width > height) {
dm.dmDisplayOrientation = DMDO_DEFAULT;
}
else {
dm.dmDisplayOrientation = DMDO_90;
}
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYORIENTATION;
//result = ChangeDisplaySettingsEx(displayDevice.DeviceName, &dm, NULL, CDS_TEST, NULL);
result = ChangeDisplaySettingsEx(displayDevice.DeviceName, &dm, NULL, 0, NULL);
if (result != DISP_CHANGE_SUCCESSFUL) {
std::cout << "Impossible to ChangeDisplaySettings" << endl;
}
else {
std::cout << "OK" << endl;
}
break;
}
I then take a look at NVAPI, and same here, I can make it works on my PC but still nothing on the GCP VMs... I have found a way to make NVAPI create and use custom resolution on my local PC, but can't make it works on GCP VM once again... (Code example found here)
NvAPI_Status result = NVAPI_ERROR;
NvU32 primaryDisplayId = 0;
//Testing resolution
int horizontal = 1920, vertical = 1090;
result = NvAPI_Initialize();
if (result != NVAPI_OK) {
printf("Could not initialize NvAPI");
return false;
}
MONITORINFOEX monInfo;
HMONITOR hMon;
const POINT ptZero = { 0, 0 };
// determine the location of the primary monitor
hMon = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
ZeroMemory(&monInfo, sizeof(monInfo));
monInfo.cbSize = sizeof(monInfo);
GetMonitorInfo(hMon, &monInfo);
result = NvAPI_DISP_GetGDIPrimaryDisplayId(&primaryDisplayId);
if (result != NVAPI_OK) {
printf("Could not get display ID from device");
NvAPI_Unload();
return false;
}
NvU32 deviceCount = 0;
NV_CUSTOM_DISPLAY cd[NVAPI_MAX_DISPLAYS] = { 0 };
float refreshRate = 60;
// timing computation (to get timing that suits the changes made)
NV_TIMING_FLAG flag = { 0 };
NV_TIMING_INPUT timing = { 0 };
timing.version = NV_TIMING_INPUT_VER;
timing.height = vertical;
timing.width = horizontal;
timing.rr = refreshRate;
timing.flag = flag;
timing.type = NV_TIMING_OVERRIDE_CVT_RB;
result = NvAPI_DISP_GetTiming(primaryDisplayId, &timing, &cd[0].timing);
if (result != NVAPI_OK) {
printf("Failed to get timing for display"); // failed to get custom display timing
NvAPI_Unload();
return false;
}
cd[0].width = horizontal;
cd[0].height = vertical;
cd[0].xRatio = 1;
cd[0].yRatio = 1;
cd[0].srcPartition = { 0, 0, 1.0, 1.0 };
cd[0].depth = 32;
cd[0].version = NV_CUSTOM_DISPLAY_VER;
cd[0].colorFormat = NV_FORMAT_A8R8G8B8;
//Returns NVAPI_ERROR on GCP but NVAPI_OK on my laptop
result = NvAPI_DISP_TryCustomDisplay(&primaryDisplayId, 1, cd);
if (result != NVAPI_OK) {
printf("Could not set custom resolution");
NvAPI_DISP_RevertCustomDisplayTrial(&primaryDisplayId, 1);
NvAPI_Unload();
return false;
}
else {
NvAPI_DISP_SaveCustomDisplay(&primaryDisplayId, 1, true, true);
}
This part works perfectly well on my laptop, I can use a new dynamic resolution (It works with 1920x400, 1920x500, 1920x600), but not on my GCP VM, this parts :
NvAPI_DISP_TryCustomDisplay(&primaryDisplayId, 1, cd);
always returns NVAPI_ERROR
I have found another trick, I can edit this registry entry : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Video{RANDOM_ID}\0001\NV_Modes
(Here is an old pdf, after some testing, it seems it is still working this way)
If I add some resolution using NVAPI, I can then set through ChangeDisplaySettingsEx function this resolution (it needs GPU driver restart, or Windows restart be able to change to a fresh new added resolution).
But I need to be able to rotate screen, playing with "dmDisplayOrientation", and it does not seem to work on GCP VM once again, if I authorize for example 1920x1090 I can set resolution to this, but cannot set 1090x1920 with a "dmDisplayOrientation = DMDO_90" (even if I authorize 1090x1920 too...)
So if anyone found a way, or have any idea on how to do this, it would be great, I am running out of idea right now...
My application populates some panels by means of incoming messages, using the SendStructMessage() function in Message.hpp.
SendStructMessage() need a valid windows handle to send to.
I have encapsulated the SendStrucMessage() in a function, like this:
bool sendAppMessage(ApplicationMessageEnum msgID, void* s)
{
if(!Application || !Application->MainForm || !Application->MainForm->Handle)
{
Log(lError) << "Failed to get a valid handle when trying to send application message";
return false;
}
HWND h = Application->MainForm->Handle;
AppMessageStruct data;
data.mMessageEnum = msgID;
data.mData = s;
LRESULT res = SendStructMessage(h, UWM_MESSAGE, 0, &data);
if(res)
{
Log(lError) << "Sending message: "<<msgID<<" was unsuccesful";
return false;
}
return true;
}
Trying to call this from either the MainForm's OnShow or OnCreate event doesn't work, as in either case the Application->MainForm->Handle is still NULL.
My question is, in a VCL application's startup phase, where can one be sure that the Application->MainForm->Handle is actually created?
Currently I kick off a Timer checking for a valid handle:
void __fastcall TMain::WaitForHandleTimerTimer(TObject *Sender)
{
if(Application->MainForm->Handle)
{
WaitForHandleTimer->Enabled = false;
//Send a message to main ui to update sequence shortcuts
if(sendAppMessage(abSequencerUpdate) != true)
{
Log(lDebug)<<"Sending sequencer update to UI was unsuccesful";
}
}
}
Is there a better way?
The TWinControl::Handle property getter creates a new HWND at the time the property is read, if an HWND hasn't already been created yet. If an error occurs while creating the HWND, an exception will be thrown.
So, your !Handle condition will always be false, because the Handle property can never return NULL (the WindowHandle property can, though).
bool sendAppMessage(ApplicationMessageEnum msgID, void* s)
{
if (!((Application) && (Application->MainForm)))
{
Log(lError) << "Failed to get a valid handle when trying to send application message";
return false;
}
AppMessageStruct data;
data.mMessageEnum = msgID;
data.mData = s;
LRESULT res = SendStructMessage(Application->MainForm->Handle, UWM_MESSAGE, 0, &data);
if (res)
{
Log(lError) << "Sending message: " << msgID << " was unsuccesful";
return false;
}
return true;
}
If you want to check if the Handle has been created without actually creating it, use the Form's HandleAllocated() method:
bool sendAppMessage(ApplicationMessageEnum msgID, void* s)
{
if (!((Application) && (Application->MainForm) && (Application->MainForm->HandleAllocated())))
{
Log(lError) << "Failed to get a valid handle when trying to send application message";
return false;
}
AppMessageStruct data;
data.mMessageEnum = msgID;
data.mData = s;
LRESULT res = SendStructMessage(Application->MainForm->Handle, UWM_MESSAGE, 0, &data);
if (res)
{
Log(lError) << "Sending message: " << msgID << " was unsuccesful";
return false;
}
return true;
}
Otherwise, don't use SendMessage()/SendStructMessage() at all. You can call the Form's Perform() method instead, which will deliver the message directly to the Form's assigned WindowProc without requiring any HWND at all:
bool sendAppMessage(ApplicationMessageEnum msgID, void* s)
{
if (!((Application) && (Application->MainForm))
{
Log(lError) << "Failed to get a valid handle when trying to send application message";
return false;
}
AppMessageStruct data;
data.mMessageEnum = msgID;
data.mData = s;
LRESULT res = Application->MainForm->Perform(UWM_MESSAGE, 0, (LPARAM)&data);
if (res)
{
Log(lError) << "Sending message: " << msgID << " was unsuccesful";
return false;
}
return true;
}
Alternatively, consider removing the MainForm dependency from sendAppMessage(). You can send to Application->Handle instead, and then have the MainForm register a callback method using Application->HookMainWindow().
bool sendAppMessage(ApplicationMessageEnum msgID, void* s)
{
if (!((Application) && (Application->Handle))
{
Log(lError) << "Failed to get a valid handle when trying to send application message";
return false;
}
AppMessageStruct data;
data.mMessageEnum = msgID;
data.mData = s;
LRESULT res = SendStructMessage(Application->Handle, UWM_MESSAGE, 0, &data);
if (res)
{
Log(lError) << "Sending message: " << msgID << " was unsuccesful";
return false;
}
return true;
}
__fastcall TMainForm::TMainForm(TComponent *Owner)
: TForm(Owner)
{
Application->HookMainWindow(&AppMessage);
}
__fastcall TMainForm::~TMainForm()
{
Application->UnhookMainWindow(&AppMessage);
}
bool __fastcall TMainForm::AppMessage(TMessage &Message)
{
if (Message.Msg == UWM_MESSAGE)
{
WindowProc(Message);
return true;
}
return false;
}
I'd like to write a c++ program(windows console application) to print receipts from a server (via websocket) to a locally connected(via rs-232) receipt printer(NCR 7197 rev 1.0).
I got both the websocket connection and the serial handle operating and ready.
My problem is, that I can't find any example to guide me through the process of printing, or how to even begin. I mean, do I have to write some bytes or read any before I start passing the "document" to the printer, or do I need to write any config bytes, or anything.
If someone has any suggestions to where to start, or any example preferably in c++, I will be thankful.
I came across the solution that I developed way back than, while cleaning my old projects folder. Seeing the multiple hundred of views on this question, I wanted to post an answer. Here is my solution, but it might be outdated:
The function to connect to a device through a COM port:
// Serial port connection handle connect method
HANDLE connect_h(wchar_t* s_port, int s_flowcontrol, int s_baudrate, int s_bytesize, int s_stopbits, int s_parity) {
wchar_t* port = s_port;
std::wstring ws(port);
std::string port_w(ws.begin(), ws.end());
// Open serial port
HANDLE hSerial;
hSerial = CreateFile(port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(hSerial == INVALID_HANDLE_VALUE) {
if(GetLastError() == ERROR_FILE_NOT_FOUND) {
// Serial port does not exist. Inform user.
std::cout << "Error: Serial port does not exists. Port: " << std::endl;
}
// Some other error occurred. Inform user.
std::cout << "Error: Cannot connect to port: " + port_w << std::endl;
std::cout << "Error code: " + GetLastError() << std::endl;
}
// Do some basic settings
DCB dcbSerialParams = { 0 };
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
if(!GetCommState(hSerial, &dcbSerialParams)) {
// Error getting state
std::cout << "Error: Cannot get port state. Port: " + port_w << std::endl;
std::cout << "Error code: " + GetLastError() << std::endl;
}
dcbSerialParams.BaudRate = s_baudrate;
dcbSerialParams.ByteSize = s_bytesize;
dcbSerialParams.StopBits = s_stopbits;
dcbSerialParams.Parity = s_parity;
// If flowcontrol set to XON/XOFF
if(s_flowcontrol == 1) {
dcbSerialParams.fDtrControl = DTR_CONTROL_DISABLE;
dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;
dcbSerialParams.fOutX = true;
dcbSerialParams.fInX = true;
dcbSerialParams.XonChar = 0x11;
dcbSerialParams.XoffChar = 0x16;
}
if(!SetCommState(hSerial, &dcbSerialParams)) {
// error setting serial port state
std::cout << "Error: Cannot set port state. Port: " + port_w << std::endl;
std::cout << "Error code: " + GetLastError() << std::endl;
}
// Set timeouts
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (!SetCommTimeouts(hSerial, &timeouts)) {
// Error occureed. Inform user
std::cout << "Error: Cannot set port timeouts. Port: " + port_w << std::endl;
std::cout << "Error code: " + GetLastError() << std::endl;
}
return hSerial;
}
Closing the serial port connection:
// Serial port connection handle close method
void close_h(const HANDLE &hSerial) {
CloseHandle(hSerial);
}
The function to print through the connected COM port:
// Printing recipe via serial port handle
std::string set_print(const HANDLE &hSerial, std::string &document) {
std::string result = "";
std::string printable = "";
// Format flags
bool _bold_flag = false;
bool _underline_flag = false;
bool _italic_flag = false;
// Process document for printing
for(int i = 0; i < (int)document.length(); ++i) {
if(document[i] == '\\') {
switch (document[i + 1]) {
// new line
case 'n':
printable.push_back((char)(0x0A));
i++;
break;
// underline format begin/end
case 'u':
if(!_underline_flag) {
printable.push_back((char)(0x1B));
printable.push_back((char)(0x2D));
printable.push_back('1');
_underline_flag = true;
}
else {
printable.push_back((char)(0x1B));
printable.push_back((char)(0x2D));
printable.push_back('0');
_underline_flag = false;
}
i++;
break;
// bold format begin/end
case 'b':
if (!_bold_flag) {
printable.push_back((char)(0x1B));
printable.push_back((char)(0x47));
printable.push_back('1');
_bold_flag = true;
}
else {
printable.push_back((char)(0x1B));
printable.push_back((char)(0x47));
printable.push_back('0');
_bold_flag = false;
}
i++;
break;
// italic format begin/end
case 'i':
if (!_italic_flag) {
printable.push_back((char)(0x1B));
printable.push_back((char)(0x49));
printable.push_back('1');
_italic_flag = true;
}
else {
printable.push_back((char)(0x1B));
printable.push_back((char)(0x49));
printable.push_back('0');
_italic_flag = false;
}
i++;
break;
// if not recognized
default:
printable.push_back(document[i]);
break;
}
}
else {
printable.push_back(document[i]);
}
}
// Additional push
printable.push_back((char)(0x0A));
printable.push_back((char)(0x0A));
printable.push_back((char)(0x0A));
printable.push_back((char)(0x0A));
printable.push_back((char)(0x0A));
printable.push_back((char)(0x0A));
printable.push_back((char)(0x0A));
printable.push_back((char)(0x0A));
// Add EOF bytes
printable.push_back((char)(0x03));
printable.push_back((char)(0x04));
// Add knife cut command
printable.push_back((char)(0x19));
printable.push_back((char)(0x1B));
printable.push_back((char)(0x76));
// Convert to hexadecimal
int* hex_print = new int[printable.length()];
for(int i = 0; i < (int)printable.length(); ++i) {
hex_print[i] = (int)printable[i];
}
// Print
for(int i = 0; i < (int)printable.length(); ++i) {
int rq[1] = { hex_print[i] };
DWORD rqBytesWritten = 0;
if(!WriteFile(hSerial, rq, 1, &rqBytesWritten, NULL)) {
// error occurred. Report to user.
std::cout << "Error: Can't write byte. written: " + rqBytesWritten << std::endl;
}
}
// return status
return result;
}
And a test main function, to demonstrate the usage of these functions:
// MAIN()
int main(int argc, char* argv[]) {
// Create connection on COM port 1
HANDLE sh_printer = connect_h(L"COM1", 0, 38400);
// Print a string povided in first argument
set_print(sh_printer, args[1]);
// Close the COM port connection
close_h(sh_printer);
return 0;
}
I hope I could be of any help to anyone, who tires to use this ancient way of printing a receipt. :)
I am writing a program that uses MySQLe as embedded backend. The database library is owned by an object called "Domain". This Domain object runs within the main thread.
The program launches another thread running a XML-RPC server (boost::thread and xmlrpc_c::serverAbyss). It is linked to the Domain object.
When the XML-RPC server makes the Domain object execute an SQL query the program crashes:
Program received signal: “EXC_BAD_ACCESS”.
[Switching to process 73191]
[Switching to process 73191]
Xcode could not locate source file: regex.cpp (line: 74)
When the master thread calls Domain object's method that executes SQL queries the program still runs.
/*
* Ports listening
*
* - create a Rpc_Server object
* - create a dedicated thread
*/
Rpc_Server server(&domain, &conf_params, &router);
boost::thread server_thread(boost::bind(&Rpc_Server::run, &server)); // This thread makes the server crash
/*
* Domain routine
*
* - Check for ready jobs every minute
*/
while (1) {
v_jobs jobs = domain.get_ready_jobs(conf_params.get_param("node_name")); // This method does NOT make the server crash
sleep(60);
}
Both the Domain object's methods and the Database object's methods lock a mutex to avoid multi access.
bool Mysql::execute(const std::string* query) {
MYSQL_RES* res;
MYSQL_ROW row;
if ( query == NULL )
return false;
this->updates_mutex.lock();
std::cout << query->c_str() << std::endl;
if ( mysql_query(this->mysql, query->c_str()) != 0 ) {
std::cerr << query << std::endl << mysql_error(this->mysql);
UNLOCK_MUTEX;
return false;
}
res = mysql_store_result(this->mysql);
if (res)
while ( ( row = mysql_fetch_row(res) ) )
for ( uint i=0 ; i < mysql_num_fields(res) ; i++ )
std::cout << row[i] << std::endl;
else
if ( mysql_field_count(this->mysql) != 0 ) {
std::cerr << "Erreur : " << mysql_error(this->mysql) << std::endl;
mysql_free_result(res);
this->updates_mutex.unlock();
return false;
}
mysql_free_result(res);
this->updates_mutex.unlock();
return true;
}
bool Domain::add_node(const std::string* running_node, const std::string* n, const int* w) {
std::string query;
this->updates_mutex.lock();
query = "START TRANSACTION;";
if ( this->database.execute(&query) == false ) {
this->updates_mutex.unlock();
return false;
}
query = "REPLACE INTO node (node_name,node_weight) VALUES ('";
query += n->c_str();
query += "','";
query += boost::lexical_cast<std::string>(*w);
query += "');";
if ( this->database.execute(&query) == false ) {
query = "ROLLBACK;";
this->database.execute(&query);
this->updates_mutex.unlock();
return false;
}
query = "COMMIT;"
if ( this->database.execute(&query) == false ) {
this->updates_mutex.unlock();
return false;
} else
this->updates_mutex.unlock();
return true;
}
The MySQLe is created there:
bool Mysql::prepare(const std::string* node_name, const std::string* db_skeleton) {
static char* server_args[] = {"this_program","--datadir=."};
static char* server_groups[] = {"embedded","server","this_program_SERVER",(char *)NULL};
std::string query("CREATE DATABASE IF NOT EXISTS ");
// DB init
if ( mysql_library_init(sizeof(server_args) / sizeof(char *), server_args, server_groups) )
std::cerr << "could not initialize MySQL library" << std::endl;
std::cout << "mysql init..." << std::endl;
if ( (this->mysql = mysql_init(NULL)) == NULL )
std::cerr << mysql_error(this->mysql) << std::endl;
if ( ! mysql_thread_safe() ) {
std::cerr << "MySQL is NOT theadsafe !" << std::endl;
return false;
}
mysql_options(this->mysql, MYSQL_READ_DEFAULT_GROUP, "embedded");
mysql_options(this->mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL);
mysql_real_connect(this->mysql, NULL, NULL, NULL, NULL, 0, NULL, 0);
// Creates the schema
query += this->translate_into_db(node_name);
query += ";";
if ( this->execute(&query) == false )
return false;
// Creates the schema
query = "CREATE SCHEMA IF NOT EXISTS ";
query += this->translate_into_db(node_name);
query += " DEFAULT CHARACTER SET latin1;";
this->execute(&query);
// Uses it
query = "USE " + this->translate_into_db(node_name) + ";";
this->execute(&query);
// Loads the skeleton from file
return this->load_file(db_skeleton->c_str());
}
Am I wrong somewhere?
Do you have an example to show me?
I found the solution to my problem. Each thread needs to initialize the MySQL environment. That is to say execute some mysql_* functions.
Here are the modified / new methods :
bool Mysql::atomic_execute(const std::string* query) {
MYSQL_RES* res;
MYSQL_ROW row;
boost::regex empty_string("^\\s+$", boost::regex::perl);
if ( query == NULL )
return false;
if ( query->empty() == true or boost::regex_match(*query, empty_string) == true ) {
std::cerr << "Error : query is empty !" << std::endl;
return false;
}
this->updates_mutex.lock();
if ( mysql_query(this->mysql, query->c_str()) != 0 ) {
std::cerr << query << std::endl << mysql_error(this->mysql);
this->updates_mutex.unlock();;
return false;
}
res = mysql_store_result(this->mysql);
if (res)
while ( ( row = mysql_fetch_row(res) ) )
for ( uint i=0 ; i < mysql_num_fields(res) ; i++ )
std::cout << row[i] << std::endl;
else
if ( mysql_field_count(this->mysql) != 0 ) {
std::cerr << "Erreur : " << mysql_error(this->mysql) << std::endl;
mysql_free_result(res);
this->updates_mutex.unlock();
return false;
}
mysql_free_result(res);
this->updates_mutex.unlock();
return true;
}
bool Mysql::standalone_execute(const v_queries* queries) {
MYSQL* local_mysql = this->init();
std::string query = "START TRANSACTION;";
if ( this->atomic_execute(&query) == false ) {
mysql_close(local_mysql);
return false;
}
BOOST_FOREACH(std::string q, *queries) {
std::cout << q.c_str() << std::endl;
if ( this->atomic_execute(&q) == false ) {
query = "ROLLBACK";
this->atomic_execute(&query);
mysql_close(local_mysql);
return false;
}
}
query = "COMMIT";
if ( this->atomic_execute(&query) == false ) {
mysql_close(local_mysql);
return false;
}
mysql_close(local_mysql);
return true;
}
MYSQL* Mysql::init() {
MYSQL* local_mysql;
local_mysql = mysql_init(this->mysql);
mysql_options(this->mysql, MYSQL_READ_DEFAULT_GROUP, "embedded");
mysql_options(this->mysql, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL);
mysql_real_connect(local_mysql, NULL, NULL, NULL, NULL, 0, NULL, 0);
return local_mysql;
}
The atomic_execute method is used to send single queries to the server.
The standalone_execute method initializes a connection and a transaction, then it sends the whole queries to the server using atomic_execute.
I do not know if a ROLLBACK is useful in case of COMMIT's failure...
The code might need some improvements but it works.