Russian Qt Forum
Ноябрь 24, 2024, 08:32 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Цикл сообщений в DLL  (Прочитано 6229 раз)
Ground
Гость
« : Февраль 22, 2012, 15:23 »

Доброго времени суток!
У меня есть DLL, в ней есть мой самописный сетевой класс Client. Объект класса я создаю прямо в DLL. Чтобы класс работал, мне нужно в этой же DLL создать цикл сообщений.
Я пробовал делать вот например так:
Код
C++ (Qt)
bool DllMain( HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
   int argc = 0;
   switch (ul_reason_for_call)
   {
   case DLL_PROCESS_ATTACH:
       qDebug()<<"InDaDLL!";
       if (qApp == 0)
       {
           new QCoreApplication(argc, 0);
           client = new Client();
           CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)DllInit, NULL, 0, 0);
       }
       break;
   case DLL_PROCESS_DETACH:
       delete client;
       delete qApp;
       break;
   }
   return TRUE;
}
 
DWORD DllInit(LPVOID)
{
   return QCoreApplication::exec();
}
Т.е. в DLLMain создаю QCoreApplication (если его еще нет) и запускаю отдельный поток, запускает exec() для QCoreApplication. Результата - ноль.

Еще пробовал делать вместо QCoreApplication::exec() просто в отдельном потоке в бесконечном цикле гонять qApp->processEvents(); Тоже не работает. В чем может быть дело, не подскажете?

Кстати вот, я делаю как этот товарищ: http://www.prog.org.ru/topic_2148_0.html
Записан
Rem Norton
Гость
« Ответ #1 : Февраль 22, 2012, 16:21 »

Ну как бы цикл сообщений - это QEventLoop.

Только совсем не понятно зачем. В чем смысл жизни этой DLL-ки? Как я понимаю запустить некий поток.
Записан
Ground
Гость
« Ответ #2 : Февраль 22, 2012, 16:32 »

Смысл DLL в том, чтобы перенести функционал работы с сетью на приложение под VS2010. То есть из DLL торчит интерфейс, вроде такого:
Код
C++ (Qt)
   // Метод, выполняющий попытку подключения к серверу
   int CLIENTSHARED_EXPORT connectToServer();
   // Принудительное отключение клиента
   int CLIENTSHARED_EXPORT disconnectFromServer();
 
   // Установка сетевых настроек для клиента
   int CLIENTSHARED_EXPORT setParameters(char* serverAddress, int port);
 
   // Состояние клиента
   int CLIENTSHARED_EXPORT isConnected();
 
   // Отправка сообщения серверу о готовности клиента к получению данных
   int CLIENTSHARED_EXPORT sendClientIsReady();
   // Отправка пакета с информацией об имени отображаемого в память файла
   int CLIENTSHARED_EXPORT sendMMFPackage(char* MMFName);
   // Отправка данных об элементе для регистрации на сервере
   int CLIENTSHARED_EXPORT sendRegInfoPackage(int elementID, int count,
                          int* startBytes, int* bytesCount);
   // Отправка запроса на получение описания к элементу
   int CLIENTSHARED_EXPORT sendDBRequestPackage(int elementID, int startByte,
                            int startBit, int bitsCount);
   // Установка буфера для сброса полученной информации
   int CLIENTSHARED_EXPORT setBuffer(char* buffer);

Человек, работая с VS, подключает мою либу, вызывает эти функции и работает с сервером.

Либу я сделал, начинаю тестировать. После вызова метода connectToServer, сокет зависает в состоянии Connecting. Если бы был цикл обработки сообщений - сработал бы сигнал, сокет переключился в состояние Connected. Вот такая вот беда.
Записан
neversleep
Гость
« Ответ #3 : Февраль 22, 2012, 18:42 »

Пишите приложение под венду? Улыбающийся
Хотите работать с сетью? Веселый
Устали вставлять костыли? Злой

Тогда winsock - ваш выбор! Крутой
Записан
Rem Norton
Гость
« Ответ #4 : Февраль 22, 2012, 19:23 »

Смысл DLL в том, чтобы перенести функционал работы с сетью на приложение под VS2010. То есть из DLL торчит интерфейс, вроде такого:
Код
C++ (Qt)
   // Метод, выполняющий попытку подключения к серверу
   int CLIENTSHARED_EXPORT connectToServer();
   // Принудительное отключение клиента
   int CLIENTSHARED_EXPORT disconnectFromServer();
 
   // Установка сетевых настроек для клиента
   int CLIENTSHARED_EXPORT setParameters(char* serverAddress, int port);
 
   // Состояние клиента
   int CLIENTSHARED_EXPORT isConnected();
 
   // Отправка сообщения серверу о готовности клиента к получению данных
   int CLIENTSHARED_EXPORT sendClientIsReady();
   // Отправка пакета с информацией об имени отображаемого в память файла
   int CLIENTSHARED_EXPORT sendMMFPackage(char* MMFName);
   // Отправка данных об элементе для регистрации на сервере
   int CLIENTSHARED_EXPORT sendRegInfoPackage(int elementID, int count,
                          int* startBytes, int* bytesCount);
   // Отправка запроса на получение описания к элементу
   int CLIENTSHARED_EXPORT sendDBRequestPackage(int elementID, int startByte,
                            int startBit, int bitsCount);
   // Установка буфера для сброса полученной информации
   int CLIENTSHARED_EXPORT setBuffer(char* buffer);

Человек, работая с VS, подключает мою либу, вызывает эти функции и работает с сервером.

Либу я сделал, начинаю тестировать. После вызова метода connectToServer, сокет зависает в состоянии Connecting. Если бы был цикл обработки сообщений - сработал бы сигнал, сокет переключился в состояние Connected. Вот такая вот беда.

А экспортнуть из DLL экземпляр класса и запустить в приложении не пробовал?
ИМХО: проблемму сам себе создал, причем совершенно на пустом месте.
Записан
Ground
Гость
« Ответ #5 : Февраль 23, 2012, 04:29 »

Смысл DLL в том, чтобы перенести функционал работы с сетью на приложение под VS2010. То есть из DLL торчит интерфейс, вроде такого:
Код
C++ (Qt)
   // Метод, выполняющий попытку подключения к серверу
   int CLIENTSHARED_EXPORT connectToServer();
   // Принудительное отключение клиента
   int CLIENTSHARED_EXPORT disconnectFromServer();
 
   // Установка сетевых настроек для клиента
   int CLIENTSHARED_EXPORT setParameters(char* serverAddress, int port);
 
   // Состояние клиента
   int CLIENTSHARED_EXPORT isConnected();
 
   // Отправка сообщения серверу о готовности клиента к получению данных
   int CLIENTSHARED_EXPORT sendClientIsReady();
   // Отправка пакета с информацией об имени отображаемого в память файла
   int CLIENTSHARED_EXPORT sendMMFPackage(char* MMFName);
   // Отправка данных об элементе для регистрации на сервере
   int CLIENTSHARED_EXPORT sendRegInfoPackage(int elementID, int count,
                          int* startBytes, int* bytesCount);
   // Отправка запроса на получение описания к элементу
   int CLIENTSHARED_EXPORT sendDBRequestPackage(int elementID, int startByte,
                            int startBit, int bitsCount);
   // Установка буфера для сброса полученной информации
   int CLIENTSHARED_EXPORT setBuffer(char* buffer);

Человек, работая с VS, подключает мою либу, вызывает эти функции и работает с сервером.

Либу я сделал, начинаю тестировать. После вызова метода connectToServer, сокет зависает в состоянии Connecting. Если бы был цикл обработки сообщений - сработал бы сигнал, сокет переключился в состояние Connected. Вот такая вот беда.

А экспортнуть из DLL экземпляр класса и запустить в приложении не пробовал?
ИМХО: проблемму сам себе создал, причем совершенно на пустом месте.

Ну т.е. в либе только описание, а в основном приложении создаю экземпляр класса, описанного в либе? Так делать пробовал, это был самый первый вариант, после чего и пошли такие косяки
Потом гуглил долго и тщательно, в итоге нашел, что народ предлагает создавать класс в самой DLL, и там же запускать цикл обработки сообщений. Только сколько я не пытаюсь это делать - ничего не получается.
Записан
neversleep
Гость
« Ответ #6 : Февраль 23, 2012, 05:48 »

Ну т.е. в либе только описание, а в основном приложении создаю экземпляр класса, описанного в либе? Так делать пробовал, это был самый первый вариант, после чего и пошли такие косяки
Потом гуглил долго и тщательно, в итоге нашел, что народ предлагает создавать класс в самой DLL, и там же запускать цикл обработки сообщений. Только сколько я не пытаюсь это делать - ничего не получается.
Надо запустить event loop в том потоке, в котором создан сокет.

Код
C++ (Qt)
DWORD __stdcall threadProc(LPVOID)
{
   int i = 0;
   QCoreApplication app(i, 0);
 
   MyObject *tmp = new MyObject;
   QTcpSocket *socket = new QTcpSocket;
   QObject::connect(socket, SIGNAL(connected()), tmp, SLOT(clientConnected()));
   socket->connectToHost("ya.ru", 80);
 
   return app.exec();
}
 
 
int main(int argc, char *argv[])
{
   DWORD d;
   CreateThread(0, 0, threadProc, 0, 0, &d);
 
   system("pause");
}
 
Записан
Ground
Гость
« Ответ #7 : Февраль 23, 2012, 06:11 »

Надо запустить event loop в том потоке, в котором создан сокет.

Вот сделал так в DLL:
Код
C++ (Qt)
int DLLStart()
{
   CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)DllInit, NULL, 0, 0);
   return 0;
}
 
DWORD DllInit(LPVOID)
{
   int argc = 0;
   QCoreApplication app(argc, 0);
   client = new Client();
   return app.exec();
}
 

А в основной программе делаю так:
Код
C++ (Qt)
int main()
{
DLLinit();
 
char* serverAddress = "192.168.0.10";
char* MMFName = "Global\\DataFile_001.mkio";
setParameters(serverAddress, 5525);
^^^
// Тут программа начинает возмущаться: QObject: Cannot create children for a parent that is in a different thread.
// (Parent is QTcpSocket(0x1), parent's thread is QThread(0x2), current thread is QThread(0x3).
 
return 0;
}
 

В принципе понятно, сокет асинхронный, запускается в своем треде 1. Тред в DLL - это 2. Тред 3 - это main в основной программе. Но как это обойти?

Еще раз отмечу, в основном приложении не должно быть никакой информации о Qt.
« Последнее редактирование: Февраль 23, 2012, 06:14 от Ground » Записан
Rem Norton
Гость
« Ответ #8 : Февраль 23, 2012, 09:05 »

Цитировать
Еще раз отмечу, в основном приложении не должно быть никакой информации о Qt.

Тогда делай CALLBACK.
Записан
vregess
Гость
« Ответ #9 : Февраль 23, 2012, 10:13 »

Тебе нежен qtsolutions.qtwinmigrate QMfcApp, если я правильно понял задачу.
Ну а создавать GUI поток не рекомендуют, тк GUI должен крутиться в главном потоке приложения.
Записан
Ground
Гость
« Ответ #10 : Февраль 23, 2012, 10:22 »

Тебе нежен qtsolutions.qtwinmigrate QMfcApp, если я правильно понял задачу.
Ну а создавать GUI поток не рекомендуют, тк GUI должен крутиться в главном потоке приложения.
Да, это именно то, что мне нужно, спасибо за подсказку.
У меня конечно трудности с его сборкой, но я попробовал в его тестовом приложении развернуть свой код - и все заработало. Единственный минус - приходится тащить дополнительно QtGui4.dll, хотя мне достаточно было бы цикла из QtCore для консольки. Но после трех дней мучений - я и этому решению рад.

А вот по поводу сборки, я делаю вот так:
1. configure
2. qmake buildlib.pro CONFIG+=release
3. nmake
В итоге у меня 2 либы: qtwin.lib, qtwind.lib + .dll к ним + .pdb (инфо для отладчика). Все это добро я бросаю в папку с Qt, в Bin (dll + pdb) и в Lib (lib).
И еще есть заголовочные файлы, их я тоже в папку к стандартным файликам Qt бросаю.
В своем проекте подключаю только эти хедеры (в .pro ничего не меняю). Но в итоге у меня unresolved reference при компиляции. В чем тут дело?
Записан
vregess
Гость
« Ответ #11 : Февраль 23, 2012, 17:35 »

Чтобы не таскать с собой библиотеку solutions и QtGui можно сделать аналогичный функционал самому.
Исходник во вложении, дублирую тут.

DLLWrapper.h
Код
C++ (Qt)
#ifndef DLLWRAPPER_H
#define DLLWRAPPER_H
 
class DLLWrapper
{
public:
   static bool init(HINSTANCE inst = NULL);
   static void uninit();
 
private:
   DLLWrapper();
};
 
#endif // DLLWRAPPER_H
 

DLLWrapper.cpp
Код
C++ (Qt)
#include <QApplication>
#include "DLLWrapper.h"
 
HHOOK hook = 0;
 
LRESULT CALLBACK hook_callback(int code, WPARAM wParam, LPARAM lParam)
{
   if (qApp)
       qApp->sendPostedEvents();
   return CallNextHookEx(hook, code, wParam, lParam);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
 
 
bool DLLWrapper::init(HINSTANCE inst)
{
   // Do nothing if already
   if (qApp)
       return false;
 
   QT_WA({
       hook = SetWindowsHookExW(WH_GETMESSAGE, hook_callback, 0, GetCurrentThreadId());
   }, {
       hook = SetWindowsHookExA(WH_GETMESSAGE, hook_callback, 0, GetCurrentThreadId());
   });
 
   if (!hook)
      return false;
 
   int argc = 0;
   new QApplication(argc, 0);
 
   return true;
}
//------------------------------------------------------------------------------
 
void DLLWrapper::uninit()
{
   if (hook)
   {
       UnhookWindowsHookEx(hook);
       hook = 0;
   }
}
//------------------------------------------------------------------------------
 


Пример использования:
Код
C++ (Qt)
bool DllMain( HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
   switch (ul_reason_for_call)
   {
   case DLL_PROCESS_ATTACH:
       DLLWrapper::init(hModule);
       ...
       break;
 
   case DLL_PROCESS_DETACH:
       ...
       DLLWrapper::uninit();
       delete qApp;
       break;
   }
   return TRUE;
}
 
« Последнее редактирование: Февраль 23, 2012, 17:48 от ck » Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.242 секунд. Запросов: 21.