2020년 11월 27일 금요일

C# 빅엔디안 처리 - C# Convert big-endian to variable

버퍼에 빅엔디안으로 데이터가 있고 이것을 변수에 담아야 할때 아래 코드를 사용하면 된다.


 private uint ConvertBigEndianUInt(byte[] buffer, int offset)  
 {  
   uint ret = (uint)(buffer[offset] << 24);  
   ret |= (uint)(buffer[offset + 1] << 16);  
   ret |= (uint)(buffer[offset + 2] << 8);  
   ret |= (uint)(buffer[offset + 3]);  
   return ret;  
 }  
   
 private ushort ConvertBigEndianUShort(byte[] buffer, int offset)  
 {  
   ushort ret = (ushort)(buffer[offset] << 8);  
   ret |= (ushort)(buffer[offset + 1]);  
   return ret;  
 }  
 
 private long ConvertBigEndianLong(byte[] buffer, int offset)
 {
   long ret = (long)(buffer[offset] << 56);
   ret |= (long)(buffer[offset + 1] << 48);
   ret |= (long)(buffer[offset + 2] << 40);
   ret |= (long)(buffer[offset + 3] << 32);
   ret |= (long)(buffer[offset + 4] << 24);
   ret |= (long)(buffer[offset + 5] << 16);
   ret |= (long)(buffer[offset + 6] << 8);
   ret |= (long)(buffer[offset + 7]);
   return ret;
 }

2020년 11월 16일 월요일

QT moveToThread 방식으로 쓰레드 생성시 QThread wait 함수 사용

 QThread 상속으로 쓰레드를 생성하지않고 QObject의 moveToThread 함수로 쓰레드를 생성할때 QThread의 wait 함수를 사용하면 무한 블러킹에 걸린다.

이때 

connect(worker, SIGNAL(finished()), thread, SLOT(quit()));

와 같은 방법으로 worker 에서 finished 를 emit 하고 thread 가 받아서 quit 하게 하여도 마찬가지로 wait 에서는 무한 블러킹에 걸린다

방법은 오직 thread->exit() 과 같이 QThread의 exit 함수를 호출하는 방법 밖에 없었다.

따라서 다음과 같이 구현하면 Worker 쓰레드가 종료할때까지 wait 할 수 있다.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QThread>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QThread *thread);
    virtual ~Worker();

public slots:
    void doWork();

private:
    QThread*    m_pThread;
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_btnStart_clicked();
    void on_btnWait_clicked();

private:
    Ui::MainWindow *ui;

    QThread*    m_pThread;
    Worker*     m_pWorker;
};
#endif // MAINWINDOW_H



#include "mainwindow.h"
#include "ui_mainwindow.h"

Worker::Worker(QThread *thread)
{
    m_pThread = thread;
}
Worker::~Worker()
{
}
void Worker::doWork()
{
    int count = 0;
    while (count++ < 10) {
        printf("do work\n");
        QThread::sleep(1);
    }

    m_pThread->exit();    // 인수로 받은 QThread의 exit을 호출해야 함
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m_pThread = new QThread();
    m_pWorker = new Worker(m_pThread);
    m_pWorker->moveToThread(m_pThread);

    connect(m_pThread, SIGNAL(started()), m_pWorker, SLOT(doWork()));
}
MainWindow::~MainWindow()
{
    delete m_pThread;
    delete m_pWorker;
    delete ui;
}

void MainWindow::on_btnStart_clicked()
{
    if (m_pThread->isRunning()) {
        return;
    }

    m_pThread->start();
}

void MainWindow::on_btnWait_clicked()
{
    printf("wait start\n");
    m_pThread->wait();
    printf("wait finished\n");
}

위와 같이 Worker는 QThread를 인수로 받아서 쓰레드 종료시 exit() 을 호출하면 wait() 함수에서 
종료시점에 동기화를 맞출수 있다

2020년 9월 1일 화요일

QTcpSocket 외부 쓰레드에서 write 하는 방법

 QTcpSocket 은 생성된 쓰레드 외에 다른 쓰레드에서 write 를 시도할 경우 에러를 리턴하며 전송이 되지않는다. 이럴 경우 아래와 같이 이벤트를 발생시켜 생성 쓰레드에서 write 처리를 하도록해서 문제를 해결할 수 있다.


 #ifndef TCPAUDIOSENDER_H  
 #define TCPAUDIOSENDER_H  
 #include <QThread>  
 #include <QTcpServer>  
 #include <QTcpSocket>  
 #include <QMutex>  
 #include "logwriter.h"  
 #include "bufferqueue.h"  
 class TcpAudioSender : public QThread  
 {  
   Q_OBJECT  
 public:  
   explicit TcpAudioSender(BufferQueue<AudioBuffer> *pSendQue, QThread *parent = 0);  
   virtual ~TcpAudioSender();  
 signals:  
   void socketWriteEvent(QTcpSocket *sock, AudioBuffer *buffer);  
 public slots:  
   void onNewConnection();  
   void onSocketStateChanged(QAbstractSocket::SocketState socketState);  
   void onReadyRead();  
   void onSocketWrite(QTcpSocket *sock, AudioBuffer *buffer);  
 public:  
   bool startServer(int port);  
   void stopServer();  
 protected:  
   virtual void run() override;  
 protected:  
   bool      m_bRun;  
   QTcpServer*     m_pServer;  
   int         m_nPort;  
   QList<QTcpSocket*> m_clientList;  
   QMutex       m_clientListMutex;  
   BufferQueue<AudioBuffer>*  m_pSendQue;  
   LogWriter*   m_pLogWriter;  
 };  
 #endif // TCPAUDIOSENDER_H  



 #include "tcpaudiosender.h"  
 TcpAudioSender::TcpAudioSender(BufferQueue<AudioBuffer> *pSendQue, QThread *parent) : QThread(parent)  
 {  
   m_bRun = false;  
   m_pServer = new QTcpServer(this);  
   m_pSendQue = pSendQue;  
   m_pLogWriter = LogWriter::getInstance();  
 }  
 TcpAudioSender::~TcpAudioSender()  
 {  
   delete m_pServer;  
 }  
 bool TcpAudioSender::startServer(int port)  
 {  
   if (!m_pServer->listen(QHostAddress::Any, port)) {  
     m_pLogWriter->writeLog(QString("TcpAudioSender cannot open %1 port").arg(port));  
     return false;  
   }  
   m_nPort = port;  
   connect(m_pServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));  
   connect(this, SIGNAL(socketWriteEvent(QTcpSocket*, AudioBuffer*)), this, SLOT(onSocketWrite(QTcpSocket*, AudioBuffer*)));  
   m_bRun = true;  
   QThread::start();  
   return true;  
 }  
 void TcpAudioSender::stopServer()  
 {  
   m_pServer->close();  
   m_bRun = false;  
   QThread::wait();  
 }  
 void TcpAudioSender::onNewConnection()  
 {  
   QTcpSocket *clientSocket = m_pServer->nextPendingConnection();  
   connect(clientSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));  
   connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));  
   m_clientListMutex.lock();  
   m_clientList.push_back(clientSocket);  
   m_clientListMutex.unlock();  
 }  
 void TcpAudioSender::onSocketStateChanged(QAbstractSocket::SocketState socketState)  
 {  
   if (socketState == QAbstractSocket::UnconnectedState)  
   {  
     QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());  
     m_clientListMutex.lock();  
     m_clientList.removeOne(sender);  
     m_clientListMutex.unlock();  
   }  
 }  
 void TcpAudioSender::onReadyRead()  
 {  
   /*  
   QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());  
   QByteArray datas = sender->readAll();  
   for (QTcpSocket* socket : m_clientList) {  
     if (socket != sender)  
       socket->write(QByteArray::fromStdString(sender->peerAddress().toString().toStdString() + ": " + datas.toStdString()));  
   }  
   */  
 }  
 void TcpAudioSender::onSocketWrite(QTcpSocket *sock, AudioBuffer *buffer)  
 {  
   int res = sock->write(buffer->data, buffer->size);  
   if (res != buffer->size) {  
     qDebug() << QString("TcpAudioSender(%1) cannot send client : %2, %3").arg(m_nPort).arg(res).arg(buffer->size);  
   }  
   delete buffer;  
   //qDebug() << "TcpAudioSender send :" << size << "bytes";  
 }  
 void TcpAudioSender::run()  
 {  
   while (m_bRun)  
   {  
     AudioBuffer *buffer = m_pSendQue->pop();  
     if (!buffer) {  
       usleep(10);  
       continue;  
     }  
     m_clientListMutex.lock();  
     for (QTcpSocket* socket : m_clientList) {  
       AudioBuffer *sendBuffer = new AudioBuffer(buffer->data, buffer->size);  
       emit socketWriteEvent(socket, sendBuffer);  
     }  
     m_clientListMutex.unlock();  
     delete buffer;  
   }  
 }  


2020년 7월 22일 수요일

간단한 Qt Thread 사용법

< MyThread.h>

class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QThread *parent = 0);
virtual ~MyThread();

void startThread();
void stopThread();

protected:
virtual void run();

signals:
void started();
void finished();

protected:
bool m_bRun;
}



< MyThread.cpp>

MyThread::MyThread(QThread *parent) : QThread(parent);
{
m_bRun = false;
}

MyThread::~MyThread()
{
}

void MyThread::startThread()
{
m_bRun = true;
QThread::start();
}

void MyThread::stopThread()
{
m_bRun = false;
QThread::wait();
}

void MyThread::run()
{
emit started();

while (m_bRun)
{
...
}

emit finished();
}

2020년 6월 24일 수요일

윈도우10 부팅시 관리자 권한으로 자동실행하는 법

윈도우 부팅시 프로그램을 자동실행하는 가장 쉬운 방법은 실행창에서 shell:startup 을 쳐서 
시작 프로그램에 실행 프로그램이나 바로가기를 복사해서 등록하는 방법이 있다.
하지만 이 방법을 사용할 경우 바로가기가 "관리자 권한으로 실행" 속성이 있는 경우 부팅을
해도 실행이 되지않는다. 
윈도우 부팅시 일반 프로그램을 관리자 권한으로 자동 실행하려면 아래와 같이 하면된다.

1. 윈도우의 UAC(User Access Control) 끄기
2. 바탕화면에 프로그램의 바로가기 생성
3. 바로가기 -> 속성 -> 바로가기의 고급 선택 -> 관리자 권한으로 실행 체크
4. cmd.exe 창 실행하여 c:\Users\<유저이름>\Desktop 아래에서 dir 명령으로 바로가기 이름 체크 -> MyApp Shortcut.lnk 라고 가정
5. 레지스트리 에디터 실행
6. 64비트 윈도우 기준 =>
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run 
아래에 문자열 속성으로 키 생성해서 이름 적당히 입력하고 값 데이터에 4번에서 알아낸 전체 경로 입력 => c:\Users\<유저이름>\Desktop\MyApp Shortcut.lnk

이렇게 하면 부팅할때마다 관리자 권한이 없는 일반 프로그램을 UAC 체크없이 자동으로 관리자 권한으로 실행할 수 있다.