QtSerialPort имеет недоступные данные, ошибка сегментации только в Windows 8

const QSerialPortInfo* serialPortInfo = nullptr;

bool PortManager::setPort(QString portName) {
    const QList<QSerialPortInfo> infoList = QSerialPortInfo::availablePorts();
    for (const QSerialPortInfo portInfo : infoList) {
        if (portInfo.portName() == portName && serialPortInfo != &portInfo) {
            serialPortInfo = &portInfo;
        }
    }
    if (serialPortInfo != nullptr) {
        if (portName != "" && serialPortInfo->isValid()) { //segmentation fault
            if (serialPort->isOpen()) {
                serialPort->close();
            }
            serialPort = new QSerialPort(*serialPortInfo, this);
            if (serialPort->open(QIODevice::ReadWrite)) {
                if (serialPort->clear()) {
                    if (serialPort->setBaudRate(QSerialPort::Baud38400, QSerialPort::AllDirections)
                            && serialPort->setFlowControl(QSerialPort::NoFlowControl)
                            && serialPort->setParity(QSerialPort::NoParity)) {
                        isPortSet = true;
                    }
                    .
                    .
                    .

Это мой код, который работает как в Linux, так и в Windows 7. Сейчас я тестирую его в Windows 8 и получаю ошибку сегментации в this->serialPortInfo->isValid() (и любой другой функции serialPortInfo). Все данные любого конкретного объекта QSerialPortInfo «недоступны» (как утверждает отладчик), что мне кажется, что у меня нет некоторых привилегий для их использования. В Linux я должен был быть членом группы uupc (если я правильно помню), чтобы не было таких ошибок, но в Windows 7 мне не нужно было ничего делать. Я запускаю Qt Creator "от имени администратора", но это не помогает; может быть, мне нужно как-то сказать ему, чтобы он запускал qmake от имени администратора? Но это только мое предположение, может причина в другом...


person smsware    schedule 21.07.2014    source источник
comment
Чтобы немного придраться, вы знаете, что в Windows нет ничего названного ошибки сегментации?   -  person Some programmer dude    schedule 21.07.2014
comment
@JoachimPileborg: что бы это ни было, оно называется SIGSEGV: ошибка сегментации от Qt Creator; но я работаю над MinGW, который по сути является Linux, так что... я мало что знаю о Windows. ;-) Это что-то меняет?   -  person smsware    schedule 21.07.2014


Ответы (2)


Это типичный случай, когда Qt рекомендует вам использовать ссылку на const в циклах for, когда вы не планируете изменять содержимое. Смотрите эту строку:

for (const QSerialPortInfo portInfo : infoList) {

Вы должны написать это, чтобы заставить его работать:

for (const QSerialPortInfo &portInfo : infoList) {
//                         ^

Причина очень проста: вы создаете временную копию, которая уничтожается при выходе за пределы области действия, а не используете исходный элемент списка, который переживет цикл, поскольку исходный список находится снаружи.

Вы должны быть счастливы, что это вообще работает на Linux и Windows 7. Я очень удивлен этому, и даже если это так, это может взорваться у вашего клиента в любой момент.

При этом ваша концепция кажется неправильной в целом. Вы запрашиваете все элементы, чтобы найти один выделенный порт. Было бы намного понятнее просто создать свой экземпляр QSerialPortInfo в первую очередь следующим образом:

serialPortInfo = new QSerialPortInfo(portName);

Более того, вам действительно следует использовать объект стека, а не кучу. Подобные информационные классы не предназначены для размещения в куче, особенно без интеллектуального управления указателями.

Также нет необходимости говорить, что создавать глобальные переменные, когда они являются злом, плохо, особенно в этом случае, когда вы можете избежать этого, поместив это в сам метод.

Вы также можете избавиться от следующей строки, если перестанете использовать указатель:

if (serialPortInfo != nullptr) {

Не говоря уже о том, что в приложении Qt вы все равно должны использовать Q_NULLPTR, так как это также будет работать без поддержки C++ 11, и та же причина относится к вашему исходному циклу for. Я бы использовал foreach из Qt, но опять же, я думаю, что общая концепция неверна.

Кажется, что вам даже не нужен экземпляр QSerialPortInfo, поскольку все, что вы используете, это просто имя, которое вы можете передать непосредственно объекту QSerialPort. Следовательно, вы можете избавиться даже от объекта QSerialPortInfo.

Поэтому я предлагаю просто отказаться от цикла for и экземпляра QSerialPortInfo и использовать QSerialPort непосредственно внутри метода.

person lpapp    schedule 21.07.2014
comment
Я сделал то, что вы сказали, поэтому не было экземпляра QSerialPort или какого-либо цикла, но это привело только к тому, что ошибка сегментации начала появляться при создании QSerialPort, а не в QSerialPortInfo... но когда я пошел дальше с вашими советами и изменил указатели на реальные объекты - создание в порядке, но вопрос о serialPort.portName() по-прежнему вызывает ошибки сегментации. Но это изменение показало, что есть еще одна ошибка для некоторого случайного сигнала timeout() дальше в коде... в основном я чувствую, что мне придется переписать все, что плохо, поскольку ВСЕ БЫЛО ХОРОШО в Windows 7. :/ - person smsware; 21.07.2014
comment
но я не понимаю причины этого... для меня этот код должен работать, и он работает прямо сейчас на моем ноутбуке... так что здесь нет ошибки, верно? - person smsware; 21.07.2014
comment
@smsware: существует неопределенное поведение при повторном использовании уже уничтоженного объекта. Как я написал в ответе, я удивлен, что это только сейчас взорвалось. - person lpapp; 21.07.2014

Проблема в этом цикле:

for (const QSerialPortInfo portInfo : infoList) {
    if (portInfo.portName() == portName && serialPortInfo != &portInfo) {
        serialPortInfo = &portInfo;
    }
}

Переменная portInfo имеет область видимости только внутри цикла и только для текущей итерации. Как только цикл повторяется, эта переменная уничтожается. Использование указателя на разрушенный объект является неопределенным поведением, и вероятная причиной ваших сбоев.

Мой совет вам, как это остановить, - не использовать указатель. Вместо этого скопируйте структуру.

person Some programmer dude    schedule 21.07.2014
comment
Почему не const & вместо portInfo? - person TemplateRex; 21.07.2014
comment
@TemplateRex Для этого вы должны спросить OP. Код, который я показываю, является цитатой из вопроса, чтобы показать, в чем проблема. - person Some programmer dude; 21.07.2014
comment
Ну, вы могли бы хотя бы предложить ему избегать двойных накладных расходов. - person TemplateRex; 21.07.2014
comment
@TemplateRex это что-то изменит? Я имею в виду, что я вижу, как это может быть константой без проблем, но чем это лучше? Что касается этой копии, это была моя попытка создать указатель и проверить, равен ли он указателю serialPortInfo (который теперь не является указателем), а не (*serialPortInfo != portInfo); Я не думал, что там есть разница. - person smsware; 23.07.2014
comment
@smsware Я думаю, что всегда лучше использовать for(auto const& element : range), чем for(auto const : range) - person TemplateRex; 23.07.2014