For the life of me I can't figure out why this is not working. Basically, I created the pipe with the inherit bit set to true, and created two child processes, and used the STARTUPINFO structure to set the input and output handles as one would need, but the pipe seems broken (the second process writes no output to the console, even though output is expected)
I know the problem does not lie in my test program (BitTwiddler.exe) because I performed the same operation using CMD.exe, and everything works as expected.
Below is a minimal reproduction of what I have. What have I done incorrectly?
#include "windows.h"
int main()
{
PROCESS_INFORMATION piSource, piDest;
HANDLE hPipeIn, hPipeOut;
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
STARTUPINFOW suSource, suDest;
ZeroMemory(&suSource, sizeof(suSource));
ZeroMemory(&suDest, sizeof(suDest));
suSource.cb = suDest.cb = sizeof(STARTUPINFOW);
suSource.dwFlags = suDest.dwFlags = STARTF_USESTDHANDLES;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = TRUE;
if (CreatePipe(&hPipeIn, &hPipeOut, &sa, 0) == 0)
{
return GetLastError();
}
suSource.hStdInput = hIn;
suSource.hStdError = suSource.hStdOutput = hPipeIn;
suDest.hStdInput = hPipeOut;
suDest.hStdError = suDest.hStdOutput = hOut;
std::wstring cmdLineA(L"BitTwiddler 1"), cmdLineB(L"BitTwiddler 0");
cmdLineA.push_back(0); cmdLineB.push_back(0);
if (CreateProcessW(0, &cmdLineA[0], 0, 0, TRUE, 0, 0, 0, &suSource, &piSource) == 0)
{
return GetLastError();
}
CloseHandle(piSource.hThread);
if (CreateProcessW(0, &cmdLineB[0], 0, 0, TRUE, 0, 0, 0, &suDest, &piDest) == 0)
{
return GetLastError();
}
CloseHandle(piDest.hThread);
HANDLE hArray[2];
hArray[0] = piSource.hProcess;
hArray[1] = piDest.hProcess;
WaitForMultipleObjects(2, hArray, TRUE, INFINITE);
CloseHandle(hArray[0]);
CloseHandle(hArray[1]);
return 0;
}
(In case anyone's interested, BitTwiddler is:
#include <windows.h>
#include <sstream>
#include <iostream>
#include <string>
int main(int argc, char *argv[])
{
std::size_t opt = 0;
argc--; argv++;
if (argc == 0)
{
return 0;
}
else
{
std::istringstream converter(*argv);
converter >> opt;
}
switch(opt)
{
case 0:
{
std::wstring currentLine;
while(std::getline(std::wcin, currentLine))
{
std::wcout << "Got somepin: " << currentLine << std::endl;
}
}
break;
case 1:
for (;;)
{
std::wcout << L"Hello World!" << std::endl;
Sleep(1000);
}
break;
case 2:
return -1;
default:
std::wcout << "Unknown option.";
return 0;
}
return 0;
}
), but I really don't think that matters.
You misplaced the read and write ends :)
CreatePipe has the prototype
BOOL CreatePipe(
PHANDLE hReadPipe, // can only read from this
PHANDLE hWritePipe, // can only write to this
LPSECURITY_ATTRIBUTES lpPipeAttributes,
DWORD nSize
);
You can't call ReadFile (or in your case std::getline) from a Write-only handle, and vice versa. If you replaced your std::getline calls with a simple ReadFile call, you'd get an ACCESS_DENIED error, confirming this fact, since your STD_INPUT_HANDLE in the child process was not opened for GENERIC_READ.
The fix is as follows:
suSource.hStdError = suSource.hStdOutput = hPipeOut; // must be the write pipe!
suDest.hStdInput = hPipeIn; // must be the read pipe.
Perhaps the names you assigned are confusing. If you called them as per the formal parameters, the error would be clearer:
suSource.hStdError = suSource.hStdOutput = hReadPipe; // clearly wrong.
suDest.hStdInput = hWritePipe; // as above -- expects a read-handle.
I upvoted your question as it is a very important one. Since I learnt Windows programming from FamTrinli's demos years ago, CreateProcess has been one of the harddest functions of the Win32 API. If not, the harddest.
CreateProcess has alot of arguments. Each argument requires careful reading of the documentation to properly use. Alot of arguments are complicated structures; such as SECURITY_ATTRIBUTES and STARTUPINFO. The names for the HANDLE(s) in STARTUPINFO structure can be confusing. As you were confused. The steps that are involved in using CreateProcess. whether or not you need to immediately close the handles(nb: you don't). And the actual communication between the processes can cause deadlocks.
After using CreateProcess for years I did an extensive research and testing of the API. I then designed a library that encapsulates all that knowledge. This library is apart of my Jav/win32 library. This library is similar to Java's Process class.
I am not able to post every last bit of source code here. If you are interested in the source code I can email it to you. Keeping Cpp a goto for programming.
Jav/win32/Process.h
#ifndef JAV_WIN32_PROCESS_HPP
#define JAV_WIN32_PROCESS_HPP
#include <Jav/File.h>
#include <Jav/string/cstring.h>
#include <Jav/error/error.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/container/flat_map.hpp>
#include <sstream>
#include <Jav/win32/win32.h>
namespace Jav { namespace win32 {
/** class Process makes handling win32 CreateProcess a bit easier.
Parent Process refers to the process that is calling CreateProcess.
Child Process refers to the process being created.
Note: CreateProcess uses the parent process's CurrentDirectory and
EnvironmentVariable when deducing relative filepaths used in AppName and CommandLine String.
Thus I have no idea the purpose of passing a current directory and environment to the CreateProcess.
Note: Rather than using Process::setCurrentDir and Process::addEnv and Process::addParentEnv; which
seems to make no sense at all as CreateProcess doesn't use these settings to deduce relative filepaths
passed to CreateProcess; instead use Process::addPathArg to pass absolute filepaths on the commandline.
Note: Using Process::addEnv or Process::addParentEnv will temporarily adjust parent process Environment,
and pass the adjusted Environment to the child process as well. This allows you to set Path variable
so that CreateProcess deduces relative paths. However using Process::addPath is probably better.
Note: Asynchronous read and write functions are not implemented.
Probably synchronous read and writes are good enough.
To properly read and write the child process, be sure to know when it makes read and write request.
If you sync read a process that never writes to stdout or stderror your program will deadlock.
If you write to a process that didn't read stdinput that makes no sense. You are just filling up the buffer.
Also ensure to read out data written by child process so that the buffer is not clogged up causing deadlock.
*/
class ProcessA
{
public:
enum { INVALID_EXIT_CODE = -1 };
enum { AUTO_KILL=1, WAIT=2, DEBUG=4, ADD_PARENT_ENV=8 };
struct Builder
{
struct ICompare { bool operator()(const std::string &l,const std::string &r)const; };
using EnvList = boost::container::flat_map<std::string,std::ostringstream,ICompare>;
std::ostringstream cmd;
EnvList env_list;
Jav::cstring app_name;
Jav::cstring current_dir;
STARTUPINFOA si = {sizeof(STARTUPINFOA)};
};
public:
ProcessA(const char *cmd=NULL,const char *current_dir=NULL,const char *app_name=NULL);
~ProcessA();
public:
bool launch();
int stop();
int wait(uint time=INFINITE);
void pause();
void resume();
bool isOpen();
bool isValid() { return info.hProcess; }
size_t getMsgSize();
size_t getErrorMsgSize();
size_t read(void*,size_t);
size_t readError(void*,size_t);
size_t write(const void*,size_t);
size_t write(const char*);
bool read_async(void*,size_t);
bool readError_async(void*,size_t);
bool write_async(const void*,size_t);
bool write_async(const char*);
/** Set up process for creation */
void setAppName(const char*);
void setCurrentDir(const char*);
void addArg(const char*);
void addQuotedArg(const char*);
void addPathArg(const char *parent_dir,const char *child_name);
void addEnv(const char *var,const char *val);
bool addParentEnv();
template<class ...ARGS>
void addArgEx(const char*,ARGS ...rest);
void addArgEx(const char*);
void setConsoleTitle(const char*);
void setConsoleWidth(int);
void setConsoleHeight(int);
void setConsoleTextAndFillColor(int);
void setWindowVisibility(int); //SW_SHOW, SW_HIDE, etc
void setWindowXpos(int);
void setWindowYpos(int);
void setWindowWidth(int);
void setWindowHeight(int);
void setWaitTime(uint);
void setParentMode(uint);
void addParentMode(uint);
void removeParentMode(uint);
Jav::cstring toString();
Jav::cstring getError() { return error; }
private:
Jav::cstring buildEnvironment();
Jav::cstring getCombinedEnvVar(const std::string &name);
private:
PROCESS_INFORMATION info = {};
Jav::rFile m_read_end;
Jav::rFile m_error_end;
Jav::wFile m_write_end;
OVERLAPPED async_struct;
uint flags = 0;
uint waitTime = INFINITE;
Builder *builder;
Jav::Error error = "";
};
Jav::cstring getEnvVarA(const char *name);
Jav::cstring FindFileExtExeA(const char *ext);
Jav::cstring FindFileNameExeA(const char *fname);
void setEnvironmentStringsA(const char *env);
class ProcessW
{
public:
enum { INVALID_EXIT_CODE = -1 };
enum { AUTO_KILL=1, WAIT=2, DEBUG=4, ADD_PARENT_ENV=8 };
struct Builder
{
struct ICompare { bool operator()(const std::wstring &l,const std::wstring &r)const; };
using EnvList = boost::container::flat_map<std::wstring,std::wostringstream,ICompare>;
std::wostringstream cmd;
EnvList env_list;
Jav::cstringw app_name;
Jav::cstringw current_dir;
STARTUPINFOW si = {sizeof(STARTUPINFOW)};
};
public:
ProcessW(const wchar_t *cmd=NULL,const wchar_t *current_dir=NULL,const wchar_t *app_name=NULL);
~ProcessW();
public:
bool launch();
int stop();
int wait(uint time=INFINITE);
void pause();
void resume();
bool isOpen();
bool isValid() { return info.hProcess; }
size_t getMsgSize();
size_t getErrorMsgSize();
size_t read(void*,size_t);
size_t readError(void*,size_t);
size_t write(const void*,size_t);
size_t write(const wchar_t*);
size_t write(const char*);
size_t read_async(void*,size_t);
size_t readError_async(void*,size_t);
size_t write_async(const void*,size_t);
size_t write_async(const wchar_t*);
size_t write_async(const char*);
void setAppName(const wchar_t*);
void setCurrentDir(const wchar_t*);
void addArg(const wchar_t*);
void addQuotedArg(const wchar_t*);
void addPathArg(const wchar_t *parent_dir,const wchar_t *child_name);
void addEnv(const wchar_t *var,const wchar_t *val);
bool addParentEnv();
void setAppName(const char*);
void setCurrentDir(const char*);
void addArg(const char*);
void addQuotedArg(const char*);
void addPathArg(const char *parent_dir,const char *child_name);
void addEnv(const char *var,const char *val);
template<class ...ARGS>
void addArgEx(const wchar_t*,ARGS ...rest);
template<class ...ARGS>
void addArgEx(const char*,ARGS ...rest);
void addArgEx(const wchar_t*);
void addArgEx(const char*);
void setConsoleTitle(const wchar_t*);
void setConsoleTitle(const char*);
void setConsoleWidth(int);
void setConsoleHeight(int);
void setConsoleTextAndFillColor(int);
void setWindowVisibility(int);
void setWindowXpos(int);
void setWindowYpos(int);
void setWindowWidth(int);
void setWindowHeight(int);
void setWaitTime(uint);
void setParentMode(uint);
void addParentMode(uint);
void removeParentMode(uint);
Jav::cstringw toString();
Jav::cstring getError() { return error; }
private:
Jav::cstringw buildEnvironment();
Jav::cstringw getCombinedEnvVar(const std::wstring &name);\
Jav::cstringw towstring(const char *s);
private:
PROCESS_INFORMATION info = {};
Jav::rFile m_read_end;
Jav::rFile m_error_end;
Jav::wFile m_write_end;
OVERLAPPED async_struct;
uint flags = 0;
uint waitTime = INFINITE;
Builder *builder;
Jav::Error error = "";
};
Jav::cstringw getEnvVarW(const wchar_t *name);
Jav::cstringw findFileExtExeW(const wchar_t *ext);
Jav::cstringw findFileNameExeW(const wchar_t *fname);
void setEnvironmentStringsW(const wchar_t *env);
///________________///___________
template<class ...ARGS>
void ProcessA::addArgEx(const char *first,ARGS ...rest)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << first;
addArgEx(rest...);
}
inline void ProcessA::addArgEx(const char *s)
{
builder->cmd << ' ' << s;
}
template<class ...ARGS>
void ProcessW::addArgEx(const wchar_t *first,ARGS ...rest)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << first;
addArgEx(rest...);
}
template<class ...ARGS>
void ProcessW::addArgEx(const char *first,ARGS ...rest)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << towstring(first);
addArgEx(rest...);
}
inline void ProcessW::addArgEx(const wchar_t *s)
{
builder->cmd << ' ' << s;
}
inline void ProcessW::addArgEx(const char *s)
{
builder->cmd << ' ' << towstring(s);
}
}}
#endif // JAV_WIN32_PROCESS_HPP
src/Jav/win32/Process.cpp
#include <Jav/error/error.h>
#include <Jav/string.h>
#include <Jav/win32/Process.h>
#include <Jav/win32/debug.h>
#include <Jav/win32/registry.h>
#include <shlwapi.h>
namespace Jav { namespace win32 {
ProcessA::ProcessA(const char *cmd,const char *current_dir,const char *app_name)
: builder(new Builder())
{
if(cmd) builder->cmd << cmd;
builder->app_name = app_name;
builder->current_dir = current_dir;
}
ProcessA::~ProcessA()
{
stop();
delete builder;
}
bool ProcessA::launch()
{
Jav::rFile child_read_end;
Jav::wFile child_error_end;
Jav::wFile child_write_end;
if(!Jav::createPipe(m_read_end,child_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
//Jav::createPipe(m_error_end,child_error_end);
if(!Jav::createPipe(child_read_end,m_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
builder->si.dwFlags |= STARTF_USESTDHANDLES;
builder->si.hStdInput = (HANDLE)child_read_end;
builder->si.hStdOutput = (HANDLE)child_write_end;
builder->si.hStdError = (HANDLE)child_write_end;
auto env = buildEnvironment();
auto cmd = builder->cmd.str();
if(flags & ADD_PARENT_ENV)
{
auto parent_env = GetEnvironmentStringsA();
for(auto &elem : builder->env_list)
{
auto val = getCombinedEnvVar(elem.first);
SetEnvironmentVariableA(elem.first.c_str(),val);
}
auto child_env = GetEnvironmentStringsA();
if( !CreateProcessA(builder->app_name,&cmd[0],NULL,NULL,true,0,child_env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
setEnvironmentStringsA(parent_env);
FreeEnvironmentStringsA(child_env);
FreeEnvironmentStringsA(parent_env);
}
else
{
if( !CreateProcessA(builder->app_name,&cmd[0],NULL,NULL,true,0,env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
}
if( !(flags & DEBUG) )
{
delete builder;
builder = NULL;
}
/*
if( WaitForInputIdle(info.hProcess,INFINITE) == WAIT_FAILED)
{
error = Jav::DefferedError("Error: Failed to wait for input idle\n");
return false;
}
if( WaitForSingleObject(info.hProcess,INFINITE) == WAIT_FAILED)
{
error = Jav::InternalError("InternalError: Failed to wait for object\n");
return false;
}
*/
return true;
}
int ProcessA::wait(uint time)
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
int ProcessA::stop()
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
if(flags & WAIT) WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
void ProcessA::pause()
{
SuspendThread(info.hThread);
}
void ProcessA::resume()
{
ResumeThread(info.hThread);
}
bool ProcessA::isOpen()
{
if(!info.hProcess) { return false; }
/*
DWORD exitCode;
if(!GetExitCodeProcess(info.hProcess,&exitCode))
throw Jav::InternalError("InternalError: Failed to GetExitCodeProcess\n");
return exitCode == STILL_ACTIVE;
*/
switch(WaitForSingleObject(info.hProcess,0))
{
case WAIT_TIMEOUT: return true;
case WAIT_FAILED: throw Jav::Error("Error: Failed to wait for object\n");
case WAIT_ABANDONED: throw Jav::Error("Error: wait abondoned\n");
case WAIT_OBJECT_0: rep("WAIT_OBJECT_0"); return false;
default: throw Jav::Error("Error: Invalid Error Code, %s\n",__func__);
}
}
size_t ProcessA::getMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_read_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessA::getErrorMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_error_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessA::read(void *buf,size_t sz)
{
return m_read_end.read(buf,sz);
}
size_t ProcessA::readError(void *buf,size_t sz)
{
return m_error_end.read(buf,sz);
}
size_t ProcessA::write(const void *data,size_t sz)
{
return m_write_end.write(data,sz);
}
size_t ProcessA::write(const char *data)
{
return m_write_end.write(data);
}
void ProcessA::setAppName(const char *name)
{
builder->app_name = name;
}
void ProcessA::setCurrentDir(const char *dir)
{
builder->current_dir = dir;
}
void ProcessA::addArg(const char *arg)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << arg;
}
void ProcessA::addQuotedArg(const char *arg)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << '\"' << arg << '\"';
}
void ProcessA::addPathArg(const char *parent_dir,const char *child_name)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << '\"'<< parent_dir << '\\' << child_name << '\"';
}
void ProcessA::addEnv(const char *var,const char *val)
{
auto &stream = builder->env_list[var];
llong pos = stream.tellp();
pos == 0 ? stream << val : stream << ';' << val;
}
/*
void ProcessA::addEnv(const char *var,const char *val)
{
auto &stream = builder->env_list[var];
llong pos = stream.tellp();
if(pos == 0) stream << '\"' << val << '\"';
else
{
stream.seekp(pos-1);
stream << ';' << val << '\"';
}
}
*/
bool ProcessA::addParentEnv()
{
flags |= ADD_PARENT_ENV;
}
/*
bool ProcessA::addParentEnv()
{
LPCH parent_env = GetEnvironmentStringsA();
if(parent_env == NULL)
{
error = Jav::Error("Error: GetEnvironmentStrings failed\n");
FreeEnvironmentStringsA(parent_env);
return false;
}
for(auto it = Jav::MultiStringIterator(parent_env); it.has(); it.next())
{
auto var_end = Jav::nextPosOf('=',it.get()).it;
if(!var_end)
{
error = Jav::Error("Error: Invalid EnvironmentString\n");
FreeEnvironmentStringsA(parent_env);
return false;
}
Jav::cstring var(it.get(),var_end++);
addEnv(var,var_end);
}
FreeEnvironmentStringsA(parent_env);
return true;
}
*/
void ProcessA::setConsoleTitle(const char *title)
{
builder->si.lpTitle = (char*)title;
}
void ProcessA::setConsoleWidth(int w)
{
builder->si.dwFlags |= STARTF_USECOUNTCHARS;
builder->si.dwXCountChars = w;
}
void ProcessA::setConsoleHeight(int h)
{
builder->si.dwFlags |= STARTF_USECOUNTCHARS;
builder->si.dwYCountChars = h;
}
void ProcessA::setConsoleTextAndFillColor(int color)
{
builder->si.dwFlags |= STARTF_USEFILLATTRIBUTE;
builder->si.dwFillAttribute = color;
}
void ProcessA::setWindowVisibility(int visibility)
{
builder->si.dwFlags |= STARTF_USESHOWWINDOW;
builder->si.wShowWindow = visibility;
}
void ProcessA::setWindowXpos(int x)
{
builder->si.dwFlags |= STARTF_USEPOSITION;
builder->si.dwX = x;
}
void ProcessA::setWindowYpos(int y)
{
builder->si.dwFlags |= STARTF_USEPOSITION;
builder->si.dwY = y;
}
void ProcessA::setWindowWidth(int w)
{
builder->si.dwFlags |= STARTF_USESIZE;
builder->si.dwXSize = w;
}
void ProcessA::setWindowHeight(int h)
{
builder->si.dwFlags |= STARTF_USESIZE;
builder->si.dwYSize = h;
}
void ProcessA::setWaitTime(uint time)
{
waitTime = time;
}
void ProcessA::setParentMode(uint mode)
{
flags = mode;
}
void ProcessA::addParentMode(uint mode)
{
Jav::bitOn(flags,mode);
}
void ProcessA::removeParentMode(uint mode)
{
Jav::bitOff(flags,mode);
}
Jav::cstring ProcessA::toString()
{
if(!builder) return "NOTHING TO DEBUG";
std::ostringstream s;
auto env = buildEnvironment();
s << "AppName: " << builder->app_name << '\n';
if(env) s << "Env: " << Jav::MultiString{env} << '\n';
else s << "Env: " << "NO ENVIRONMENT" << '\n';
s << "CurrentDir: " << builder->current_dir << '\n';
s << "cmd: " << builder->cmd.str() << '\n';
return s.str().c_str();
}
Jav::cstring ProcessA::buildEnvironment()
{
if(!builder->env_list.size()) return NULL;
std::string env;
for(auto &elem : builder->env_list)
{
env += elem.first + '=' + elem.second.str() + '\0';
}
env += '\0';
return Jav::cstring(&env[0],&env[env.size()]);
}
Jav::cstring ProcessA::getCombinedEnvVar(const std::string &name)
{
Jav::cstring parent_val;
std::string child_val;
auto elem = builder->env_list.find(name);
if(elem != builder->env_list.end()) child_val = elem->second.str();
SetLastError(0);
auto sz = GetEnvironmentVariableA(name.c_str(),NULL,0);
if(sz == 0)
{
parent_val = GetLastError() ? NULL : "";
return child_val.empty() ? parent_val : Jav::cstring(child_val.c_str());
}
GetEnvironmentVariableA(name.c_str(),parent_val,sz);
if(child_val.empty()) return parent_val;
Jav::cstring val( strlen(parent_val + child_val.size() + 2) );
sprintf(val,"%s;%s",parent_val.str(),child_val.c_str());
return val;
}
bool ProcessA::Builder::ICompare::operator()(const std::string &l,const std::string &r)const
{
return boost::ilexicographical_compare<std::string,std::string>(l,r);
}
Jav::cstring getEnvVarA(const char *name)
{
SetLastError(0);
auto sz = GetEnvironmentVariableA(name,NULL,0);
if(sz == 0) return GetLastError() ? NULL : "";
Jav::cstring val(sz);
GetEnvironmentVariableA(name,val,sz);
return val;
}
Jav::cstring FindFileNameExe(const char *fname)
{
Jav::cstring exe_name(MAX_PATH);
auto e = (int)FindExecutableA(fname,NULL,exe_name);
if(e <= 32) throw Jav::Error("Error: unable to find association\n");
return exe_name;
}
/*
Jav::cstring FindFileExtExe(const char *ext)
{
Jav::cstring exe_name;
Jav::win32::RegistryKeyReader key(".cpp",HKEY_CLASSES_ROOT);
if(!key || !key.getString(NULL,exe_name))
throw Jav::DefferedError("Error: No program Associated with file");
return exe_name;
}
*/
/* Note uses AssocQueryString which doesn't seem to be compatible on winxp*/
Jav::cstring FindFileExtExe(const char *ext)
{
DWORD sz=0;
auto hr = AssocQueryStringA(ASSOCF_INIT_IGNOREUNKNOWN,ASSOCSTR_EXECUTABLE,ext,NULL,NULL,&sz);
if( hr != S_FALSE)
throw Jav::InternalError("Error Failed to obtain extension association name length\n");
Jav::cstring exe_name(sz);
hr = AssocQueryStringA(ASSOCF_INIT_IGNOREUNKNOWN,ASSOCSTR_EXECUTABLE,ext,NULL,exe_name,&sz);
if( hr != S_OK)
throw Jav::InternalError("Error Failed to obtain extension association\n");
return exe_name;
}
void setEnvironmentStringsA(const char *env)
{
if(env == NULL) throw Jav::Error("Error: environment string is null\n");
for(auto it = Jav::MultiStringIterator(env); it.has(); )
{
auto begin = it.get();
auto end = it.next('\0');
auto var_end = Jav::nextPosOf('=',{begin,end});
if(!var_end) throw Jav::Error("Error: Invalid EnvironmentString\n");
Jav::cstring var(begin,var_end.it++);
SetEnvironmentVariableA(var,var_end);
}
}
ProcessW::ProcessW(const wchar_t *cmd,const wchar_t *current_dir,const wchar_t *app_name)
: builder(new Builder())
{
if(cmd) builder->cmd << cmd;
builder->app_name = app_name;
builder->current_dir = current_dir;
}
ProcessW::~ProcessW()
{
stop();
delete builder;
}
bool ProcessW::launch()
{
Jav::rFile child_read_end;
Jav::wFile child_error_end;
Jav::wFile child_write_end;
if(!Jav::createPipe(m_read_end,child_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
//Jav::createPipe(m_error_end,child_error_end);
if(!Jav::createPipe(child_read_end,m_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
builder->si.dwFlags |= STARTF_USESTDHANDLES;
builder->si.hStdInput = (HANDLE)child_read_end;
builder->si.hStdOutput = (HANDLE)child_write_end;
builder->si.hStdError = (HANDLE)child_write_end;
auto env = buildEnvironment();
auto cmd = builder->cmd.str();
if(flags & ADD_PARENT_ENV)
{
auto parent_env = GetEnvironmentStringsW();
for(auto &elem : builder->env_list)
{
auto val = getCombinedEnvVar(elem.first);
SetEnvironmentVariableW(elem.first.c_str(),val);
}
auto child_env = GetEnvironmentStringsW();
if( !CreateProcessW(builder->app_name,&cmd[0],NULL,NULL,false,0,child_env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
setEnvironmentStringsW(parent_env);
FreeEnvironmentStringsW(child_env);
FreeEnvironmentStringsW(parent_env);
}
else
{
if( !CreateProcessW(builder->app_name,&cmd[0],NULL,NULL,false,0,env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
}
if( !(flags & DEBUG) )
{
delete builder;
builder = NULL;
}
return true;
}
int ProcessW::wait(uint time)
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
int ProcessW::stop()
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
if(flags & WAIT) WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
void ProcessW::pause()
{
SuspendThread(info.hThread);
}
void ProcessW::resume()
{
ResumeThread(info.hThread);
}
bool ProcessW::isOpen()
{
if(!info.hProcess) return false;
DWORD exitCode;
if(!GetExitCodeProcess(info.hProcess,&exitCode))
throw Jav::InternalError("InternalError: Failed to GetExitCodeProcess\n");
return exitCode == STILL_ACTIVE;
}
size_t ProcessW::getMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_read_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessW::getErrorMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_error_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessW::read(void *buf,size_t sz)
{
return m_read_end.read(buf,sz);
}
...
...
...
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
i have some c++ project after a release of support c++20, i want to upgrade my makefile std support 17 to 20 after that point my compiler (gcc10.2) give me a error like this ;
Error
In file included from /usr/local/lib/gcc10/include/c++/bits/node_handle.h:39,
from /usr/local/lib/gcc10/include/c++/bits/stl_tree.h:72,
from /usr/local/lib/gcc10/include/c++/map:60,
from AsyncSQL.h:10,
from AsyncSQL.cpp:4:
/usr/local/lib/gcc10/include/c++/optional: In function 'constexpr std::strong_ordering std::operator<=>(const std::optional<_Tp>&, std::nullopt_t)':
/usr/local/lib/gcc10/include/c++/optional:1052:24: error: invalid operands of types 'bool' and 'int' to binary 'operator<=>'
1052 | { return bool(__x) <=> false; }
| ~~~~~~~~~ ^~~
| |
| bool
gmake[2]: *** [Makefile:23: AsyncSQL.o] Error 1
This is my AsyncSQL.cpp ;
#include <sys/time.h>
#include <cstdlib>
#include <cstring>
#include "AsyncSQL.h"
#define MUTEX_LOCK(mtx) pthread_mutex_lock(mtx)
#define MUTEX_UNLOCK(mtx) pthread_mutex_unlock(mtx)
CAsyncSQL::CAsyncSQL(): m_stHost (""), m_stUser (""), m_stPassword (""), m_stDB (""), m_stLocale (""), m_iMsgCount (0), m_iPort (0), m_bEnd (false), m_hThread (0), m_mtxQuery (NULL), m_mtxResult (NULL), m_iQueryFinished (0), m_ulThreadID (0), m_bConnected (false), m_iCopiedQuery (0)
{
memset (&m_hDB, 0, sizeof (m_hDB));
m_aiPipe[0] = 0;
m_aiPipe[1] = 0;
}
CAsyncSQL::~CAsyncSQL()
{
Quit();
Destroy();
}
void CAsyncSQL::Destroy()
{
if (m_hDB.host)
{
sys_log (0, "AsyncSQL: closing mysql connection.");
mysql_close (&m_hDB);
m_hDB.host = NULL;
}
if (m_mtxQuery)
{
pthread_mutex_destroy (m_mtxQuery);
delete m_mtxQuery;
m_mtxQuery = NULL;
}
if (m_mtxResult)
{
pthread_mutex_destroy (m_mtxResult);
delete m_mtxResult;
m_mtxQuery = NULL;
}
}
void* AsyncSQLThread (void* arg)
{
CAsyncSQL* pSQL = ((CAsyncSQL*) arg);
if (!pSQL->Connect())
{
return NULL;
}
pSQL->ChildLoop();
return NULL;
}
bool CAsyncSQL::QueryLocaleSet()
{
if (0 == m_stLocale.length())
{
sys_err ("m_stLocale == 0");
return true;
}
if (mysql_set_character_set (&m_hDB, m_stLocale.c_str()))
{
sys_err ("cannot set locale %s by 'mysql_set_character_set', errno %u %s", m_stLocale.c_str(), mysql_errno (&m_hDB) , mysql_error (&m_hDB));
return false;
}
sys_log (0, "\t--mysql_set_character_set(%s)", m_stLocale.c_str());
return true;
}
bool CAsyncSQL::Connect()
{
if (0 == mysql_init (&m_hDB))
{
fprintf (stderr, "mysql_init failed\n");
return false;
}
if (!m_stLocale.empty())
{
if (mysql_options (&m_hDB, MYSQL_SET_CHARSET_NAME, m_stLocale.c_str()) != 0)
{
fprintf (stderr, "mysql_option failed : MYSQL_SET_CHARSET_NAME %s ", mysql_error(&m_hDB));
}
}
if (!mysql_real_connect (&m_hDB, m_stHost.c_str(), m_stUser.c_str(), m_stPassword.c_str(), m_stDB.c_str(), m_iPort, NULL, CLIENT_MULTI_STATEMENTS))
{
fprintf (stderr, "mysql_real_connect: %s\n", mysql_error(&m_hDB));
return false;
}
my_bool reconnect = true;
if (0 != mysql_options (&m_hDB, MYSQL_OPT_RECONNECT, &reconnect))
{
fprintf (stderr, "mysql_option: %s\n", mysql_error(&m_hDB));
}
m_ulThreadID = mysql_thread_id (&m_hDB);
m_bConnected = true;
return true;
}
bool CAsyncSQL::Setup (CAsyncSQL* sql, bool bNoThread)
{
return Setup (sql->m_stHost.c_str(), sql->m_stUser.c_str(), sql->m_stPassword.c_str(), sql->m_stDB.c_str(), sql->m_stLocale.c_str(), bNoThread, sql->m_iPort);
}
bool CAsyncSQL::Setup (const char* c_pszHost, const char* c_pszUser, const char* c_pszPassword, const char* c_pszDB, const char* c_pszLocale, bool bNoThread, int iPort)
{
m_stHost = c_pszHost;
m_stUser = c_pszUser;
m_stPassword = c_pszPassword;
m_stDB = c_pszDB;
m_iPort = iPort;
if (c_pszLocale)
{
m_stLocale = c_pszLocale;
sys_log (0, "AsyncSQL: locale %s", m_stLocale.c_str());
}
if (!bNoThread)
{
m_mtxQuery = new pthread_mutex_t;
m_mtxResult = new pthread_mutex_t;
if (0 != pthread_mutex_init (m_mtxQuery, NULL))
{
perror ("pthread_mutex_init");
exit (0);
}
if (0 != pthread_mutex_init (m_mtxResult, NULL))
{
perror ("pthread_mutex_init");
exit (0);
}
pthread_create (&m_hThread, NULL, AsyncSQLThread, this);
return true;
}
else
{
return Connect();
}
}
void CAsyncSQL::Quit()
{
m_bEnd = true;
m_sem.Release();
if (m_hThread)
{
pthread_join (m_hThread, NULL);
m_hThread = NULL;
}
}
SQLMsg* CAsyncSQL::DirectQuery (const char* c_pszQuery)
{
if (m_ulThreadID != mysql_thread_id (&m_hDB))
{
sys_log (0, "MySQL connection was reconnected. querying locale set");
while (!QueryLocaleSet());
m_ulThreadID = mysql_thread_id (&m_hDB);
}
SQLMsg* p = new SQLMsg;
p->m_pkSQL = &m_hDB;
p->iID = ++m_iMsgCount;
p->stQuery = c_pszQuery;
if (mysql_real_query (&m_hDB, p->stQuery.c_str(), p->stQuery.length()))
{
char buf[1024];
snprintf (buf, sizeof(buf), "AsyncSQL::DirectQuery : mysql_query error: %s\nquery: %s", mysql_error (&m_hDB), p->stQuery.c_str());
sys_err (buf);
p->uiSQLErrno = mysql_errno (&m_hDB);
}
p->Store();
return p;
}
void CAsyncSQL::AsyncQuery (const char* c_pszQuery)
{
auto p = new SQLMsg;
p->m_pkSQL = &m_hDB;
p->iID = ++m_iMsgCount;
p->stQuery = c_pszQuery;
PushQuery (p);
}
void CAsyncSQL::ReturnQuery (const char* c_pszQuery, void* pvUserData)
{
auto p = new SQLMsg;
p->m_pkSQL = &m_hDB;
p->iID = ++m_iMsgCount;
p->stQuery = c_pszQuery;
p->bReturn = true;
p->pvUserData = pvUserData;
PushQuery (p);
}
void CAsyncSQL::PushResult (SQLMsg* p)
{
MUTEX_LOCK (m_mtxResult);
m_queue_result.push (p);
MUTEX_UNLOCK (m_mtxResult);
}
bool CAsyncSQL::PopResult(SQLMsg** pp)
{
MUTEX_LOCK (m_mtxResult);
if (m_queue_result.empty())
{
MUTEX_UNLOCK (m_mtxResult);
return false;
}
*pp = m_queue_result.front();
m_queue_result.pop();
MUTEX_UNLOCK (m_mtxResult);
return true;
}
void CAsyncSQL::PushQuery (SQLMsg* p)
{
MUTEX_LOCK (m_mtxQuery);
m_queue_query.push (p);
m_sem.Release();
MUTEX_UNLOCK (m_mtxQuery);
}
bool CAsyncSQL::PeekQuery (SQLMsg** pp)
{
MUTEX_LOCK (m_mtxQuery);
if (m_queue_query.empty())
{
MUTEX_UNLOCK (m_mtxQuery);
return false;
}
*pp = m_queue_query.front();
MUTEX_UNLOCK (m_mtxQuery);
return true;
}
bool CAsyncSQL::PopQuery (int iID)
{
MUTEX_LOCK (m_mtxQuery);
if (m_queue_query.empty())
{
MUTEX_UNLOCK (m_mtxQuery);
return false;
}
m_queue_query.pop();
MUTEX_UNLOCK (m_mtxQuery);
return true;
}
bool CAsyncSQL::PeekQueryFromCopyQueue (SQLMsg** pp)
{
if (m_queue_query_copy.empty())
{
return false;
}
*pp = m_queue_query_copy.front();
return true;
}
int CAsyncSQL::CopyQuery()
{
MUTEX_LOCK (m_mtxQuery);
if (m_queue_query.empty())
{
MUTEX_UNLOCK (m_mtxQuery);
return -1;
}
while (!m_queue_query.empty())
{
SQLMsg* p = m_queue_query.front();
m_queue_query_copy.push (p);
m_queue_query.pop();
}
int count = m_queue_query_copy.size();
MUTEX_UNLOCK (m_mtxQuery);
return count;
}
bool CAsyncSQL::PopQueryFromCopyQueue()
{
if (m_queue_query_copy.empty())
{
return false;
}
m_queue_query_copy.pop();
return true;
}
int CAsyncSQL::GetCopiedQueryCount()
{
return m_iCopiedQuery;
}
void CAsyncSQL::ResetCopiedQueryCount()
{
m_iCopiedQuery = 0;
}
void CAsyncSQL::AddCopiedQueryCount (int iCopiedQuery)
{
m_iCopiedQuery += iCopiedQuery;
}
DWORD CAsyncSQL::CountQuery()
{
return m_queue_query.size();
}
DWORD CAsyncSQL::CountResult()
{
return m_queue_result.size();
}
void __timediff (struct timeval* a, struct timeval* b, struct timeval* rslt)
{
if (a->tv_sec < b->tv_sec)
{
rslt->tv_sec = rslt->tv_usec = 0;
}
else if (a->tv_sec == b->tv_sec)
{
if (a->tv_usec < b->tv_usec)
{
rslt->tv_sec = rslt->tv_usec = 0;
}
else
{
rslt->tv_sec = 0;
rslt->tv_usec = a->tv_usec - b->tv_usec;
}
}
else
{
rslt->tv_sec = a->tv_sec - b->tv_sec;
if (a->tv_usec < b->tv_usec)
{
rslt->tv_usec = a->tv_usec + 1000000 - b->tv_usec;
rslt->tv_sec--;
}
else
{
rslt->tv_usec = a->tv_usec - b->tv_usec;
}
}
}
class cProfiler
{
public:
cProfiler()
{
m_nInterval = 0 ;
memset (&prev, 0, sizeof (prev));
memset (&now, 0, sizeof (now));
memset (&interval, 0, sizeof (interval));
Start();
}
cProfiler (int nInterval = 100000)
{
m_nInterval = nInterval;
memset (&prev, 0, sizeof (prev));
memset (&now, 0, sizeof (now));
memset (&interval, 0, sizeof (interval));
Start();
}
void Start()
{
gettimeofday (&prev , (struct timezone*) 0);
}
void Stop()
{
gettimeofday (&now, (struct timezone*) 0);
__timediff (&now, &prev, &interval);
}
bool IsOk()
{
if (interval.tv_sec > (m_nInterval / 1000000))
{
return false;
}
if (interval.tv_usec > m_nInterval)
{
return false;
}
return true;
}
struct timeval* GetResult()
{
return &interval;
}
long GetResultSec()
{
return interval.tv_sec;
}
long GetResultUSec()
{
return interval.tv_usec;
}
private:
int m_nInterval;
struct timeval prev;
struct timeval now;
struct timeval interval;
};
void CAsyncSQL::ChildLoop()
{
cProfiler profiler(500000);
while (!m_bEnd)
{
m_sem.Wait();
int count = CopyQuery();
if (count <= 0)
{
continue;
}
AddCopiedQueryCount (count);
SQLMsg* p;
while (count--)
{
profiler.Start();
if (!PeekQueryFromCopyQueue (&p))
{
continue;
}
if (m_ulThreadID != mysql_thread_id (&m_hDB))
{
sys_log (0, "MySQL connection was reconnected. querying locale set");
while (!QueryLocaleSet());
m_ulThreadID = mysql_thread_id (&m_hDB);
}
if (mysql_real_query (&m_hDB, p->stQuery.c_str(), p->stQuery.length()))
{
p->uiSQLErrno = mysql_errno (&m_hDB);
sys_err ("AsyncSQL: query failed: %s (query: %s errno: %d)", mysql_error (&m_hDB), p->stQuery.c_str(), p->uiSQLErrno);
switch (p->uiSQLErrno)
{
case CR_SOCKET_CREATE_ERROR:
case CR_CONNECTION_ERROR:
case CR_IPSOCK_ERROR:
case CR_UNKNOWN_HOST:
case CR_SERVER_GONE_ERROR:
case CR_CONN_HOST_ERROR:
case ER_NOT_KEYFILE:
case ER_CRASHED_ON_USAGE:
case ER_CANT_OPEN_FILE:
case ER_HOST_NOT_PRIVILEGED:
case ER_HOST_IS_BLOCKED:
case ER_PASSWORD_NOT_ALLOWED:
case ER_PASSWORD_NO_MATCH:
case ER_CANT_CREATE_THREAD:
case ER_INVALID_USE_OF_NULL:
m_sem.Release();
sys_err ("AsyncSQL: retrying");
continue;
}
}
profiler.Stop();
if (!profiler.IsOk())
{
sys_log (0, "[QUERY : LONG INTERVAL(OverSec %ld.%ld)] : %s", profiler.GetResultSec(), profiler.GetResultUSec(), p->stQuery.c_str());
}
PopQueryFromCopyQueue();
if (p->bReturn)
{
p->Store();
PushResult (p);
}
else
{
delete p;
}
++m_iQueryFinished;
}
}
SQLMsg* p;
while (PeekQuery (&p))
{
if (m_ulThreadID != mysql_thread_id (&m_hDB))
{
sys_log (0, "MySQL connection was reconnected. querying locale set");
while (!QueryLocaleSet());
m_ulThreadID = mysql_thread_id (&m_hDB);
}
if (mysql_real_query (&m_hDB, p->stQuery.c_str(), p->stQuery.length()))
{
p->uiSQLErrno = mysql_errno (&m_hDB);
sys_err ("AsyncSQL::ChildLoop : mysql_query error: %s:\nquery: %s", mysql_error (&m_hDB), p->stQuery.c_str());
switch (p->uiSQLErrno)
{
case CR_SOCKET_CREATE_ERROR:
case CR_CONNECTION_ERROR:
case CR_IPSOCK_ERROR:
case CR_UNKNOWN_HOST:
case CR_SERVER_GONE_ERROR:
case CR_CONN_HOST_ERROR:
case ER_NOT_KEYFILE:
case ER_CRASHED_ON_USAGE:
case ER_CANT_OPEN_FILE:
case ER_HOST_NOT_PRIVILEGED:
case ER_HOST_IS_BLOCKED:
case ER_PASSWORD_NOT_ALLOWED:
case ER_PASSWORD_NO_MATCH:
case ER_CANT_CREATE_THREAD:
case ER_INVALID_USE_OF_NULL:
continue;
}
}
sys_log (0, "QUERY_FLUSH: %s", p->stQuery.c_str());
PopQuery (p->iID);
if (p->bReturn)
{
p->Store();
PushResult (p);
}
else
{
delete p;
}
++m_iQueryFinished;
}
}
int CAsyncSQL::CountQueryFinished()
{
return m_iQueryFinished;
}
void CAsyncSQL::ResetQueryFinished()
{
m_iQueryFinished = 0;
}
MYSQL* CAsyncSQL::GetSQLHandle()
{
return &m_hDB;
}
size_t CAsyncSQL::EscapeString (char* dst, size_t dstSize, const char* src, size_t srcSize)
{
if (0 == srcSize)
{
memset (dst, 0, dstSize);
return 0;
}
if (0 == dstSize)
{
return 0;
}
if (dstSize < srcSize * 2 + 1)
{
char tmp[256];
size_t tmpLen = sizeof (tmp) > srcSize ? srcSize : sizeof (tmp);
strlcpy (tmp, src, tmpLen);
sys_err ("FATAL ERROR!! not enough buffer size (dstSize %u srcSize %u src%s: %s)", dstSize, srcSize, tmpLen != srcSize ? "(trimmed to 255 characters)" : "", tmp);
dst[0] = '\0';
return 0;
}
return mysql_real_escape_string (GetSQLHandle(), dst, src, srcSize);
}
void CAsyncSQL2::SetLocale (const std::string & stLocale)
{
m_stLocale = stLocale;
QueryLocaleSet();
}
This is my AsyncSQL.h
#ifndef __INC_METIN_II_ASYNCSQL_H__
#define __INC_METIN_II_ASYNCSQL_H__
#include "../../libthecore/src/stdafx.h"
#include "../../libthecore/src/log.h"
#include "../../Ayarlar.h"
#include <string>
#include <queue>
#include <vector>
#include <map>
#include <mysql/server/mysql.h>
#include <mysql/server/errmsg.h>
#include <mysql/server/mysqld_error.h>
#include "Semaphore.h"
typedef struct _SQLResult
{
_SQLResult(): pSQLResult (NULL), uiNumRows (0), uiAffectedRows (0), uiInsertID (0) {}
~_SQLResult()
{
if (pSQLResult)
{
mysql_free_result (pSQLResult);
pSQLResult = NULL;
}
}
MYSQL_RES* pSQLResult;
uint32_t uiNumRows;
uint32_t uiAffectedRows;
uint32_t uiInsertID;
} SQLResult;
typedef struct _SQLMsg
{
_SQLMsg() : m_pkSQL (NULL), iID (0), uiResultPos (0), pvUserData (NULL), bReturn (false), uiSQLErrno (0) {}
~_SQLMsg()
{
auto first = vec_pkResult.begin();
auto past = vec_pkResult.end();
while (first != past)
{
delete * (first++);
}
vec_pkResult.clear();
}
void Store()
{
do
{
SQLResult* pRes = new SQLResult;
pRes->pSQLResult = mysql_store_result (m_pkSQL);
pRes->uiInsertID = mysql_insert_id (m_pkSQL);
pRes->uiAffectedRows = mysql_affected_rows (m_pkSQL);
if (pRes->pSQLResult)
{
pRes->uiNumRows = mysql_num_rows (pRes->pSQLResult);
}
else
{
pRes->uiNumRows = 0;
}
vec_pkResult.push_back (pRes);
}
while (!mysql_next_result (m_pkSQL));
}
SQLResult* Get()
{
if (uiResultPos >= vec_pkResult.size())
{
return NULL;
}
return vec_pkResult[uiResultPos];
}
bool Next()
{
if (uiResultPos + 1 >= vec_pkResult.size())
{
return false;
}
++uiResultPos;
return true;
}
MYSQL* m_pkSQL;
int iID;
std::string stQuery;
std::vector<SQLResult *> vec_pkResult;
unsigned int uiResultPos;
void* pvUserData;
bool bReturn;
unsigned int uiSQLErrno;
} SQLMsg;
class CAsyncSQL
{
public:
CAsyncSQL();
virtual ~CAsyncSQL();
void Quit();
bool Setup (const char* c_pszHost, const char* c_pszUser, const char* c_pszPassword, const char* c_pszDB, const char* c_pszLocale, bool bNoThread = false, int iPort = 0);
bool Setup (CAsyncSQL* sql, bool bNoThread = false);
bool Connect();
bool IsConnected()
{
return m_bConnected;
}
bool QueryLocaleSet();
void AsyncQuery (const char* c_pszQuery);
void ReturnQuery (const char* c_pszQuery, void* pvUserData);
SQLMsg* DirectQuery (const char* c_pszQuery);
DWORD CountQuery();
DWORD CountResult();
void PushResult (SQLMsg* p);
bool PopResult (SQLMsg** pp);
void ChildLoop();
MYSQL* GetSQLHandle();
int CountQueryFinished();
void ResetQueryFinished();
size_t EscapeString (char* dst, size_t dstSize, const char* src, size_t srcSize);
protected:
void Destroy();
void PushQuery (SQLMsg* p);
bool PeekQuery (SQLMsg** pp);
bool PopQuery (int iID);
bool PeekQueryFromCopyQueue (SQLMsg** pp );
INT CopyQuery();
bool PopQueryFromCopyQueue();
public:
int GetCopiedQueryCount();
void ResetCopiedQueryCount();
void AddCopiedQueryCount (int iCopiedQuery);
protected:
MYSQL m_hDB;
std::string m_stHost;
std::string m_stUser;
std::string m_stPassword;
std::string m_stDB;
std::string m_stLocale;
int m_iMsgCount;
int m_aiPipe[2];
int m_iPort;
std::queue<SQLMsg*> m_queue_query;
std::queue<SQLMsg*> m_queue_query_copy;
std::queue<SQLMsg*> m_queue_result;
volatile bool m_bEnd;
pthread_t m_hThread;
pthread_mutex_t* m_mtxQuery;
pthread_mutex_t* m_mtxResult;
CSemaphore m_sem;
int m_iQueryFinished;
unsigned long m_ulThreadID;
bool m_bConnected;
int m_iCopiedQuery;
};
class CAsyncSQL2 : public CAsyncSQL
{
public:
void SetLocale (const std::string & stLocale);
};
#endif
And this is the function the reason of the error ;
optional:1052 ;
#ifdef __cpp_lib_three_way_comparison
template<typename _Tp>
constexpr strong_ordering
operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept
{ return bool(__x) <=> false; }
#else
After a see a document the microsoft release i'm gonna try <= > false; like this and take a error again..
Best Regards.
I ve no idea why it looks is getting bool(__x) <=> false as an bool and int comparison.
I would think you got some strange macro in your files included before to include the header that is going to break the standard code.
I would suggest you try to move above the standard headers and below them your 'user defined' headers.
#include <string>
#include <queue>
#include <vector>
#include <map>
#include <mysql/server/mysql.h>
#include <mysql/server/errmsg.h>
#include <mysql/server/mysqld_error.h>
#include "../../libthecore/src/stdafx.h"
#include "../../libthecore/src/log.h"
#include "../../Ayarlar.h"
#include "Semaphore.h"
EDIT:
i ve found the cause of the problem.
a macro defined in "libthrecore/stdafx.h" (i own the files that is using the author, they are public).
#ifndef false
#define false 0
#define true (!false)
#endif
it is causing false to be read as a int and is causing the spaceship operator to fails with the error shown by the author. Move up the standard headers or remove the macro to solve the error.
pdb file exists and works normally in Debug mode, but the problem occurs only in the Release version.
It is a function that prints the call stack. How can I solve it?
One of the strange things when debugging was that the address was different in Relese version and Debug mode.
std::string SymbolLookup::GetSymbolString(void* address) const{
if (address == nullptr)
{
return "";
}
DWORD displacement = 0;
DWORD addr = reinterpret_cast<DWORD>(address);
SymbolBuffer symbol;
char buffer[kStrBufferSize] = { 0, };
IMAGEHLP_LINE line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
SymFromAddr(handle_, addr, 0, &symbol);
DWORD Temp = GetLastError();
BOOL ret = SymGetLineFromAddr(handle_, addr, &displacement, &line);
if (ret)
{
sprintf_s(buffer, kStrBufferSize, "%s(%d) : %s",
line.FileName, line.LineNumber, symbol.Name);
}
else
{
sprintf_s(buffer, kStrBufferSize, "No line info : %s", symbol.Name);
}
return std::string(buffer);
}
void log_callstack()
{
SymbolLookup lookup;
CallStack stack;
for (size_t i = 0; i < stack.GetCount(); ++i)
{
const string& str = lookup.GetSymbolString(stack[i]);
print_log(str.c_str());
}
}
class SymbolLookup
{
public:
SymbolLookup() : handle_(GetCurrentProcess())
{
SymInitialize(handle_, nullptr, TRUE);
SymSetOptions(SYMOPT_LOAD_LINES);
}
std::string GetSymbolString(void* address) const;
private:
const static size_t kStrBufferSize = 1024U;
private:
HANDLE handle_;
};
class CallStack
{
public:
static const size_t kMaxStackDepth = 24U;
public:
CallStack()
{
count_ = CaptureStackBackTrace(0, kMaxStackDepth, addresses_, &hash_);
}
void* operator[] (const size_t index) const { return addresses_[index]; }
ULONG GetHash() const { return hash_; }
size_t GetCount() const { return count_; }
private:
void* addresses_[kMaxStackDepth];
ULONG hash_;
size_t count_;
};
I found that the addresses value obtained through CaptureStackBackTrace is different when run with f5 and when run with Relese executable.
GetLastError() value is 487
Is it possible to somehow create a template for WinAPI functions? For example, there are two similar functions (LookupPrivilegeName and LookupPrivilegeDisplayName), but with different set of parameters. I call both functions like this: the first call retrieves required buffer size, the second call returns desired value. If this is not possible, are there any alternative ways to make the code more compact?
You likely want something like:
template<typename CharT>
bool LookupPriviledgeDisplayName(CharT const *, CharT const *, DWORD *, std::basic_string<CharT> & strDisplayName);
template<>
bool LookupPriviledgeDisplayName<char>(char const * pszSystem, char const * pszName, DWORD * pdwLangID, std::basic_string <char> & strDisplayName)
{
DWORD dwLength = 0;
if(!LookupPrivilegeDisplayNameA(pszSystem, pszName, nullptr, &dwLength, pdwLangID))
return false;
std::vector<char> buffer(dwLength + 1);
if(!LookupPrivilegeDisplayNameA(pszSystem, pszName, &buffer[0], &dwLength, pdwLangID))
return false;
strDisplayName.assign(&buffer[0], &buffer[0] + dwLength);
return true;
}
template<>
bool LookupPriviledgeDisplayName<wchar_t>(wchar_t const * pszSystem, wchar_t const * pszName, DWORD * pdwLangID, std::basic_string <wchar_t> & strDisplayName)
{
DWORD dwLength = 0;
if(!LookupPrivilegeDisplayNameW(pszSystem, pszName, nullptr, &dwLength, pdwLangID))
return false;
std::vector<wchar_t> buffer(dwLength + 1);
if(!LookupPrivilegeDisplayNameW(pszSystem, pszName, &buffer[0], &dwLength, pdwLangID))
return false;
strDisplayName.assign(&buffer[0], &buffer[0] + dwLength);
return true;
}
Something like this. The main idea is to separate code by functionality avoiding duplication.
#include <memory>
#include <string>
#include <iostream>
#include <system_error>
#include <cassert>
// Error handler.
void
On_Error(const ::DWORD error_code)
{
throw ::std::system_error{static_cast<int>(error_code), ::std::system_category()};
}
// Error check.
void
Validate_LookupPrivilegeNameSuccess(const ::BOOL result, const bool expect_insufficient_buffer)
{
if(FALSE == result)
{
auto const error_code{::GetLastError()};
if((ERROR_INSUFFICIENT_BUFFER == error_code) && (!expect_insufficient_buffer))
{
On_Error(error_code);
}
}
}
enum class
t_PrivilegeNameCategoryId
{
name
, display_name
};
// Helper class calling WinAPI methods, checking input and output.
template<t_PrivilegeNameCategoryId> class
t_LookupPrivilegeNameImpl;
template<> class
t_LookupPrivilegeNameImpl<t_PrivilegeNameCategoryId::name> final
{
public: static ::std::size_t
Lookup(const ::LPCWSTR psz_system_name, ::LUID & luid, const ::LPWSTR p_buffer, ::DWORD buffer_capacity_items_count)
{
assert((0 == buffer_capacity_items_count) || (nullptr != p_buffer));
Validate_LookupPrivilegeNameSuccess
(
::LookupPrivilegeNameW
(
psz_system_name
, ::std::addressof(luid)
, p_buffer
, ::std::addressof(buffer_capacity_items_count)
)
, nullptr == p_buffer
);
return(buffer_capacity_items_count);
}
};
template<> class
t_LookupPrivilegeNameImpl<t_PrivilegeNameCategoryId::display_name> final
{
public: static ::std::size_t
Lookup(const ::LPCWSTR psz_system_name, const ::LPCWSTR psz_display_name, const ::LPWSTR p_buffer, ::DWORD buffer_capacity_items_count)
{
assert(psz_display_name);
assert(L'\0' != psz_display_name[0]);
assert((0 == buffer_capacity_items_count) || (nullptr != p_buffer));
::DWORD language_id{};
Validate_LookupPrivilegeNameSuccess
(
::LookupPrivilegeDisplayNameW
(
psz_system_name
, psz_display_name
, p_buffer
, ::std::addressof(buffer_capacity_items_count)
, ::std::addressof(language_id)
)
, nullptr == p_buffer
);
return(buffer_capacity_items_count);
}
};
// Lookup function implementing get size -> resize buffer -> get data algorithm.
template<t_PrivilegeNameCategoryId name_category_id, typename... TArgs> void
Lookup_PrivilegeName(::std::wstring & name, TArgs &&... args)
{
try
{
name.resize(t_LookupPrivilegeNameImpl<name_category_id>::Lookup(::std::forward<TArgs>(args)..., ::LPWSTR{}, ::DWORD{}));
t_LookupPrivilegeNameImpl<name_category_id>::Lookup(::std::forward<TArgs>(args)..., name.data(), static_cast<::DWORD>(name.size()));
if(name.empty() || (L'\0' != name.back()))
{
On_Error(ERROR_UNIDENTIFIED_ERROR);
}
name.pop_back();
}
catch(...)
{
name.clear();
throw;
}
}
int main()
{
::LPCWSTR psz_system_name{};
::LUID privilege_luid{5}; // a wild guess
::std::wstring privilege_name{};
Lookup_PrivilegeName<t_PrivilegeNameCategoryId::name>(privilege_name, psz_system_name, privilege_luid);
::std::wstring privilege_display_name{};
Lookup_PrivilegeName<t_PrivilegeNameCategoryId::display_name>(privilege_display_name, psz_system_name, privilege_name.c_str());
::std::wcout << privilege_name << L"\n" << privilege_display_name << ::std::endl;
return(0);
}
My Russian friend explained to me the limitations of templates. Instead of them, he suggested using functions with context switching. In my situation it will be something like this:
BOOL LookupData(DWORD context, PLUID luid, wstring input, wstring & output) {
BOOL status = TRUE;
DWORD buflen = 0, lang = 0;
switch (context) {
case 0: status = LookupPrivilegeName(NULL, luid, NULL, &buflen); break;
case 1: status = LookupPrivilegeDisplayName(NULL, input.c_str(), NULL, &buflen, &lang); break;
default: return FALSE;
}
if (!status && ERROR_INSUFFICIENT_BUFFER != GetLastError()) return status;
auto buffer = make_unique<wchar_t[]>(buflen);
switch (context) {
case 0: status = LookupPrivilegeName(NULL, luid, buffer.get(), &buflen); break;
case 1: status = LookupPrivilegeDispayName(NULL, input.c_str(), buffer.get(), &buflen, &lang); break;
}
if (!status) {
buf.release();
return status;
}
output = buf.get();
buf.release();
return status;
}
Now I can write in cycle after getting TOKEN_PRIVILEGES structures:
std::wstring name, desription;
if (!LookupData(0, tp->Privipeges[i].Luid, L"", name)) break;
if (!LookupData(1, NULL, name, description)) break;
std::wcout << name << L" - " << std::endl;
Of course, it's dirty trick but it works.
I have this structure in my C++ code:
struct sData
{
DWORD Number;
int CurrentNumber;
bool GameOver;
};
I need to save it to Clipboard as a structure from one process. And from other process I need to load it again as the structure. I can do it easy with Cstrings/strings but not with structures. What do you suggest to me?
This is my method for setting Cstring to Clipboard:
bool SetText(CString text)
{
CString source;
source = text;
//put your text in source
if (OpenClipboard(NULL))
{
HGLOBAL clipbuffer;
char * buffer;
EmptyClipboard();
clipbuffer = GlobalAlloc(GMEM_DDESHARE, source.GetLength() + 1);
buffer = (char*)GlobalLock(clipbuffer);
strcpy(buffer, LPCSTR(source));
GlobalUnlock(clipbuffer);
SetClipboardData(CF_TEXT, clipbuffer);
CloseClipboard();
return true;
}
else
{
return false;
}
}
And this is getter:
std::string GetText(void) const
{
return (const char*)GetClipboardData(CF_TEXT);
}
You need to register your own clipboard format, then you can store the struct data as-is.
static UINT CF_MYSTRUCTDATA = RegisterClipboardFormat(TEXT("MyStructData"));
#pragma pack(push, 1)
struct sData
{
DWORD Number;
int CurrentNumber;
bool GameOver;
};
#pragma pack(pop)
bool SetData(const sData &data)
{
if (CF_MYSTRUCTDATA == 0)
return false;
bool bOK = false;
if (OpenClipboard(NULL))
{
if (EmptyClipboard())
{
HGLOBAL clipbuffer = GlobalAlloc(GMEM_MOVEABLE, sizeof(sData));
if (clipbuffer)
{
sData *buffer = (sData*) GlobalLock(clipbuffer);
if (buffer)
{
*buffer = data;
GlobalUnlock(clipbuffer);
bOK = SetClipboardData(CF_MYSTRUCTDATA, clipbuffer);
}
if (!bOK)
GlobalFree(clipbuffer);
}
}
CloseClipboard();
}
return bOK;
}
bool GetData(sData &data) const
{
if (CF_MYSTRUCTDATA == 0)
return false;
bool bOk = false;
if (OpenClipboard(NULL))
{
HANDLE clipbuffer = GetClipboardData(CF_MYSTRUCTDATA);
if (clipbuffer)
{
sData *buffer = (sData*) GlobalLock(clipbuffer);
if (buffer)
{
data = *buffer;
GlobalUnlock(clipbuffer);
bOK = true;
}
}
CloseClipboard();
}
return bOK;
}
Alternatively, using some C++ RAII wrappers:
struct Clipboard
{
Clipboard(HWND hWnd = NULL)
{
if (!OpenClipboard(hWnd))
throw std::runtime_error("Error opening clipboard");
}
~Clipboard()
{
CloseClipboard();
}
void Empty()
{
if (!EmptyClipboard())
throw std::runtime_error("Error emptying clipboard");
}
template<typename T>
struct DataBuffer
{
HGLOBAL _hmem;
bool _free;
struct Lock
{
DataBuffer& _buffer;
T* _data;
Lock(DataBuffer &buffer)
: _buffer(buffer), _locked(false)
{
_data = (T*) GlobalLock(_buffer.Get());
if (!_data)
throw std::runtime_error("Error locking memory");
}
~Lock()
{
GlobalUnlock(_buffer.Get());
}
T& Data() { return *_data; }
};
DataBuffer(const T &data)
: _hmem(NULL), _free(true)
{
_hmem = GlobalAlloc(GMEM_MOVEABLE, sizeof(T));
if (!_hmem)
throw std::runtime_error("Error allocating memory");
Lock(*this).Data() = data;
}
DataBuffer(HGLOBAL hmem)
: _hmem(mem), _free(false)
{
if (GlobalSize(_hmem)) < sizeof(T))
throw std::runtime_error("Bad memory size");
}
~DataBuffer()
{
if ((_hmem) && (_free))
GlobalFree(_hmem);
}
HGLOBAL Release()
{
HGLOBAL tmp = _hmem;
_hmem = NULL;
return tmp;
}
HGLOBAL Get()
{
return _hmem;
}
void Copy(T &data)
{
data = Lock(*this).Data();
}
};
template<typename T>
void SetData(UINT format, const T &data)
{
DataBuffer<T> buffer(data);
if (!SetClipboardData(format, buffer.Get()))
throw std::runtime_error("Error setting clipboard data");
buffer.Release();
}
template<typename T>
void GetData(UINT format, T &data)
{
DataBuffer<T> buffer(GetClipboardData(format));
if (!buffer.Get())
throw std::runtime_error("Error getting clipboard data");
buffer.Copy(data);
}
};
bool SetData(const sData &data)
{
if (CF_MYSTRUCTDATA != 0)
{
try
{
Clipboard clipbrd;
clipbrd.Empty();
clipbrd.SetData(CF_MYSTRUCTDATA, data);
return true;
}
catch (const std::runtime_error&)
{
}
}
return false;
}
bool GetData(sData &data) const
{
if (CF_MYSTRUCTDATA != 0)
{
try
{
Clipboard clipbrd;
clipbrd.GetData(CF_MYSTRUCTDATA, data);
return true;
}
catch (const std::runtime_error&)
{
}
}
return false;
}
I'm having trouble for using opus with Port audio.
I need to read data audio from a stream using PortAudio, encoding data, decoding data and writing data. If I just read and write, everything works well. But when encoding and decoding, all I can hear is snow with my voice in background.
Here a part of my code:
I/O stream header:
#define NUM_CHANNELS (2)
#define PA_SAMPLE_TYPE paInt24
#define SAMPLE_RATE (48000)
#define FRAMES_PER_BUFFER (1024)
#define SAMPLE_SIZE (3)
#define FRAME_SIZE (960)
class SoundSystem
{
private:
PaStream *_stream;
int _readBufferSize;
PaStreamParameters _inputParam;
PaStreamParameters _outputParam;
unsigned char *_readBuffer;
public:
SoundSystem();
~SoundSystem();
// Init Stream
bool initPa();
bool openStream();
bool startStream();
bool initStream();
// Init params stream
bool initParams() const;
bool initInputParams();
bool initOutputParams();
bool initParams();
// I/O
bool writeOnStream(unsigned cha\
r *buff);
bool readFromStream();
// Utils
void cleanReadBuffer();
int getReadBufferSize() const;
unsigned char *getReadBuffer() const;
};
I/O stream .cpp:
SoundSystem::SoundSystem()
{
_stream = NULL;
_readBufferSize = FRAMES_PER_BUFFER * NUM_CHANNELS * SAMPLE_SIZE;
_readBuffer= new unsigned char [_readBufferSize];
}
SoundSystem::~SoundSystem()
{
}
bool SoundSystem::initPa()
{
if ((Pa_Initialize()) != paNoError)
return (false);
return (true);
}
bool SoundSystem::openStream()
{
if ((Pa_OpenStream(&_stream, &_inputParam, &_outputParam, SAMPLE_RATE,
FRAMES_PER_BUFFER, paClipOff, NULL, NULL)) != paNoError)
return (false);
return (true);
}
bool SoundSystem::startStream()
{
if ((Pa_StartStream(_stream)) != paNoError)
return (false);
return (true);
}
bool SoundSystem::initStream()
{
if ((openStream()) == false)
std::cerr << "can not open stream" << std::endl;
if ((startStream()) == false)
std::cerr << "cannot start stream" <<std::endl;
return (true);
}
bool SoundSystem::initParams()
{
if ((initPa()) == false)
std::cerr << "can not ijnit PA" << std::endl;
initInputParams();
initOutputParams();
return (true);
}
bool SoundSystem::initInputParams()
{
if ((_inputParam.device = Pa_GetDefaultInputDevice()) == paNoDevice)
return (false);
_inputParam.channelCount = 2;
_inputParam.sampleFormat = PA_SAMPLE_TYPE;
_inputParam.suggestedLatency = Pa_GetDeviceInfo(_inputParam.device)->defaultLowInputLatency;
_inputParam.hostApiSpecificStreamInfo = NULL;
return (true);
}
bool SoundSystem::initOutputParams()
{
if ((_outputParam.device = Pa_GetDefaultInputDevice()) == paNoDevice)
return (false);
_outputParam.channelCount = 2;
_outputParam.sampleFormat = PA_SAMPLE_TYPE;
_outputParam.suggestedLatency = Pa_GetDeviceInfo(_outputParam.device)->defaultLowInputLatency;
_outputParam.hostApiSpecificStreamInfo = NULL;
return (true);
}
bool SoundSystem::writeOnStream(unsigned char *buff)
{
if ((Pa_WriteStream(_stream, buff, FRAMES_PER_BUFFER)) != paNoError)
{
std::cout << "FAIL WRITE" <<std::endl;
return (false);
}
return (true);
}
bool SoundSystem::readFromStream()
{
if ((Pa_ReadStream(_stream, _readBuffer, FRAMES_PER_BUFFER)) != paNoError)
return (false);
return (true);
}
void SoundSystem::cleanReadBuffer()
{
for (int i = 0; i != _readBufferSize; i++)
_readBuffer[i] = 0;
}
int SoundSystem::getReadBufferSize() const
{enter code here
return (_readBufferSize);
}
unsigned char* SoundSystem::getReadBuffer() const { return (_readBuffer); }
Encode header:
#define FRAME_SIZE (960)
#define SAMPLE_RATE (48000)
#define CHANNELS (2)
#define APPLICATION OPUS_APPLICATION_VOIP
#define MAX_FRAME_SIZE (6*960)
class EncoderSystem
{
private:
OpusEncoder *_encode;
OpusDecoder *_decode;
opus_int16 _in[FRAME_SIZE*CHANNELS];
opus_int16 _out[MAX_FRAME_SIZE*CHANNELS];
int _nbBytes;
public:
EncoderSystem();
~EncoderSystem();
bool encoderCreate();
bool decoderCreate();
unsigned char* encode(unsigned char *, int);
unsigned char* decode(unsigned char *, int);
int getEncodeLen() const;
};
Encode .cpp:
EncoderSystem::EncoderSystem()
{
}
EncoderSystem::~EncoderSystem()
{
}
bool EncoderSystem::encoderCreate()
{
int error;
if ((_encode = opus_encoder_create(SAMPLE_RATE, CHANNELS, OPUS_APPLICATION_VOIP, &error)) == NU\
LL)
{
std::cerr << "Can not create encode" <<std::endl;
return (false);
}
return (true);
}
bool EncoderSystem::decoderCreate()
{
int error;
if ((_decode = opus_decoder_create(SAMPLE_RATE, CHANNELS, &error)) == NULL)
{
std::cerr << "Can not create decoder" <<std::endl;
return (false);
}
return (true);
}
unsigned char* EncoderSystem::encode(unsigned char *data, int size)
{
unsigned char *c_bits = new unsigned char [size];
memcpy(_in, data, size);
/* Encode the frame. */
_nbBytes = opus_encode(_encode, _in, FRAME_SIZE, c_bits, size);
if (_nbBytes<0)
{
std::cerr << "cannot decode" << std::endl;
return NULL;
}
return (c_bits);
}
unsigned char* EncoderSystem::decode(unsigned char *data, int size)
{
int frame_size = opus_decode(_decode, data, size, _out,
MAX_FRAME_SIZE * CHANNELS * 2, 0);
unsigned char *pcm_bytes = new unsigned char [MAX_FRAME_SIZE * CHANNELS * 2];
if (frame_size<0)
{
std::cerr << "cannot decode" << std::endl;
return (NULL);
}
memcpy(pcm_bytes, _out, size);
return (pcm_bytes);
}
int EncoderSystem::getEncodeLen() const { return (this->_nbBytes); }
I really need you, thanks a lot to take your time to help me.
#define PA_SAMPLE_TYPE paInt24
That's probably your problem. As far as I know the standard OPUS codecs take 16-bit integers or 32-bit floating point samples. These correspond to the PortAudio sample types paInt16 and paFloat32.
I recommend getting the types of all your sample buffers correct. Using unsigned char* for formatted sample data is asking for trouble. You need to understand what data types are expected by PortAudio functions and by the OPUS codec functions.