我正在尝试编写一个使用 DBus 与 BlueZ 交互的程序。根据我从 DBus 文档、BlueZ DBus API 文档和各种论坛收集的信息,我的思考过程如下:
hci0
)org.freedesktop.DBus.ObjectManager.GetManagedObjects()
查找可用设备。所以我写了以下代码:
#include <...>
#include <dbus-1.0/dbus/dbus.h>
struct DBusDeleter
{
void operator()(DBusConnection *connection) {dbus_connection_unref(connection);}
void operator()(DBusMessage *msg) {dbus_message_unref(msg);}
void operator()(DBusError *err) {dbus_error_free(err);}
};
void print_managed_objects(std::unique_ptr<DBusMessage, DBusDeleter> message)
{
DBusMessageIter message_iter;
dbus_message_iter_init(message.get(), &message_iter);
std::cout << (char) dbus_message_iter_get_arg_type(&message_iter) << '\n';
DBusMessageIter dict_iter;
dbus_message_iter_recurse(&message_iter, &dict_iter);
std::cout << (char) dbus_message_iter_get_arg_type(&dict_iter) << '\n';
while(dbus_message_iter_get_arg_type(&dict_iter) == DBUS_TYPE_DICT_ENTRY)
{
DBusMessageIter entry_iter;
dbus_message_iter_recurse(&dict_iter, &entry_iter);
char * object_path;
dbus_message_iter_get_basic(&entry_iter, object_path);
std::cout << object_path << "\n";
std::cout << (char) dbus_message_iter_get_arg_type(&entry_iter) << '\n';
dbus_message_iter_next(&dict_iter);
}
std::cout << std::flush;
}
int main()
{
std::unique_ptr<DBusError, DBusDeleter> dbus_error(new DBusError);
dbus_error_init(dbus_error.get());
std::shared_ptr<DBusConnection> conn(dbus_bus_get(DBUS_BUS_SYSTEM, dbus_error.get()), DBusDeleter());
if (dbus_error_is_set(dbus_error.get()))
{
std::cout << "DBus Error: " << dbus_error->message << std::endl;
return 1;
}
std::string service_name = "org.bluez";
std::unique_ptr<DBusMessage, DBusDeleter> msg, reply;
// Start Device Discovery
msg.reset(dbus_message_new_method_call(service_name.c_str(), "/org/bluez/hci0", "org.bluez.Adapter1", "StartDiscovery"));
reply.reset(nullptr);
uint32_t serial = 1;
dbus_connection_send(conn.get(), msg.get(), &serial);
if (dbus_error_is_set(dbus_error.get()))
{
std::cout << "BlueZ Error (Failed to start discovery): " << dbus_error->message << std::endl;
return 1;
}
std::chrono::duration<int64_t> scan_duration = std::chrono::seconds(10);
std::chrono::_V2::system_clock::time_point start_time = std::chrono::system_clock::now();
msg.reset(dbus_message_new_method_call(service_name.c_str(), "/", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"));
while (start_time + scan_duration >= std::chrono::system_clock::now())
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
reply = std::unique_ptr<DBusMessage, DBusDeleter>(dbus_connection_send_with_reply_and_block(conn.get(), msg.get(), -1, dbus_error.get()));
if (dbus_error_is_set(dbus_error.get()))
{
std::cout << "error with reply: " << dbus_error->message << "\n";
break;
}
print_managed_objects(std::move(reply));
if (reply == nullptr) {
std::cout << "All Good!\n";
continue;
}
std::cout << "Something went wrong!\n";
break;
}
// more code...
}
问题发生在
print_managed_objects
函数调用周围。当函数返回时,我希望由于 print_managed_objects
函数已经消耗了消息,因为我已经赋予了它消息的所有权,所以一切都会好起来,但是对 nullptr
的检查在原始 unique_ptr
上失败了已移出。
这意味着
reply
指针仍然具有对print_managed_objects
返回后被销毁的消息的引用,并且在循环的下一次迭代中重置指针时,程序会崩溃,因为DBus会吐出以下错误:
dbus[15920]:dbus_message_unref() 的参数不正确,文件 dbus-message.c 第 1728 行中的断言“message-> Generation == _dbus_current_ Generation”失败
我的问题是为什么
reply
的内部指针在移动后没有设置为nullptr
?根据 另一个堆栈溢出问题,在唯一指针上调用 std::move()
会将该指针的内部指针设置为 nullptr
。
编辑:我在
print_managed_objects
函数中省略了一些行,我认为我可以省略这些行以获得最小的可重现示例,但这些行实际上是发生错误所必需的。具体来说,这些行:
char * object_path;
dbus_message_iter_get_basic(&entry_iter, object_path);
std::cout << *object_path << "\n";
这里有一个问题。
char * object_path;
dbus_message_iter_get_basic(&entry_iter, object_path);
std::cout << *object_path << "\n";
您将未初始化的指针传递给函数。这是未定义的行为。您的情况似乎得到了损坏的堆栈数据。
std::cout << *object_path << "\n";
我不知道你对那里有什么期望。至于第三行,您可能期望单个字符数据。代码应该是
char object_path;
dbus_message_iter_get_basic(&entry_iter, &object_path);
std::cout << object_path << "\n";
&object_path
传递字符存储的地址。