【问题标题】:MySqlX (C connector) with Dart's ffi brings error "NoSuchMethodError: The method 'FfiTrampoline' was called on null."带有 Dart ffi 的 MySqlX(C 连接器)带来错误“NoSuchMethodError: The method 'FfiTrampoline' was called on null.”
【发布时间】:2021-08-28 04:06:56
【问题描述】:

我正在尝试使用 Dart 的 ffi 包和 MYSQL 的本机 C-Connector 直接连接到 MYSQL。

我的 Dart 代码是:

import 'dart:ffi';
import 'package:ffi/ffi.dart';

typedef CFun = Pointer Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Pointer<Pointer>?);
typedef DartFun = Pointer Function(Pointer<Utf8>, int, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Pointer<Pointer>?);

void main() {
  final lib = DynamicLibrary.open('/usr/lib64/libmysqlcppconn8.so.2');
  final fc = lib.lookup<NativeFunction<CFun>>('mysqlx_get_session');
  print('We have C: $fc');
  final DartFun fd = fc.asFunction();
  print('We have Dart: $fd');
  Pointer<Pointer>? error;
  Pointer? session;
  session = fd('localhost'.toNativeUtf8(), 33060, '*user*'.toNativeUtf8(), '*password*'.toNativeUtf8(), ''.toNativeUtf8(), error);
}

运行时我得到这个输出:

We have Pointer<NativeFunction<(Pointer<Utf8>, Int32, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Pointer<Pointer<NativeType>>?) => Pointer<NativeType>>>: address=0x7f5b9c78f770
We have Closure: (Pointer<Utf8>, int, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Pointer<Pointer<NativeType>>?) => Pointer<NativeType>
Unhandled exception:
NoSuchMethodError: The method 'FfiTrampoline' was called on null.
Receiver: null
Tried calling: FfiTrampoline()
#0      FfiTrampoline (dart:ffi)
#1      main (package:greudb/src/connection.dart:37:14)
#2      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:283:19)
#3      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

在 C 中尝试同样的方法效果很好:

#include <mysql-cppconn/mysqlx/xapi.h>
#include <stdio.h>

int main()
{
    mysqlx_session_t *session = 0;
    mysqlx_error_t *error = 0;
    session = mysqlx_get_session("localhost", 33060, "*user*", "*password*", "", &error);
    if(error != 0) {
        printf("%s/n", mysqlx_error_message(error));
    }
    return 0;
}

我猜我的 Dart 函数签名不正确,但无论我尝试什么都无济于事。 MySQL 的 C 文档在这里:https://dev.mysql.com/doc/dev/connector-cpp/8.0/group__xapi__sess.html

我正在使用 Linux Fedora 33、MySQL 社区服务器 8、MySQL 连接器 C/C++ 8、Dart 202.8488。

【问题讨论】:

  • 我的感觉是问题在于正确地翻译最后一个论点 (mysqlx_error_t **error)。如果你改为使用nullptr 运行它,它看起来就完成了。但是,我不确定如何处理。

标签: mysql dart ffi connector


【解决方案1】:

环顾四周,it appears 您可以使用以下方法为error 创建适当的类型:

final error = calloc<Pointer>();

在后台调用calloc.allocate&lt;Pointer&gt;(...) 并带有适当的byteCount。由于这是非托管内存,您需要记住通过调用来释放它:

calloc.free(error);

一旦你完成它。

幸运的是,mysqlx API 似乎将所有结构都视为不透明的,因此您可以通过执行以下操作来确定实际错误是什么:

final message = errorMessage(error.value);
print(message.toDartString());

关于释放内存的话题,调用toNativeUtf8()创建的Pointer&lt;Utf8&gt;值也需要释放。

将所有这些放在一起:

import 'dart:ffi';
import 'package:ffi/ffi.dart';

typedef _GetSession = Pointer Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Pointer<Pointer>);
typedef GetSession = Pointer Function(Pointer<Utf8>, int, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Pointer<Pointer>);

typedef _CloseSession = Void Function(Pointer);
typedef CloseSession = void Function(Pointer);

typedef _ErrorMessage = Pointer<Utf8> Function(Pointer);
typedef ErrorMessage = Pointer<Utf8> Function(Pointer);

void main() {
  final lib = DynamicLibrary.open('/usr/lib64/libmysqlcppconn8.so.2');
  final GetSession getSession = lib.lookup<NativeFunction<_GetSession>>('mysqlx_get_session').asFunction();
  final CloseSession closeSession = lib.lookup<NativeFunction<_CloseSession>>('mysqlx_session_close').asFunction();
  final ErrorMessage errorMessage = lib.lookup<NativeFunction<_ErrorMessage>>('mysqlx_error_message').asFunction();

  final host = 'localhost'.toNativeUtf8();
  final user = 'user'.toNativeUtf8();
  final password = 'password'.toNativeUtf8();
  final database = ''.toNativeUtf8();
  final error = calloc<Pointer>();

  final session = getSession(host, 33060, user, password, database, error);

  calloc.free(host);
  calloc.free(user);
  calloc.free(password);
  calloc.free(database);

  if (error.value != nullptr) {
    final message = errorMessage(error.value);
    print(message.toDartString());

    calloc.free(error);
    return;
  }

  // Do something with session

  closeSession(session);
}

【讨论】:

  • 非常感谢,这很有帮助。当我使用正确的登录凭据时,它可以完美运行,如果我传递了错误的东西(比如错误的用户名),我会遇到分段错误。我必须更深入地研究这一点,但这是最有帮助的。
  • 每次库抛出 C++ 异常类时,我都会遇到分段错误。我不确定这是否是 ffi 的问题,因为使用连接器库有一些限制(比如从源代码构建它而不是使用二进制包。但是,如果我构建一个小型 C/C++ 动态库作为包装器和链接将静态连接器库放入其中,抛出异常按预期工作。所以至少我可以将其作为一种解决方法。
  • 啊,是的,C++ 异常。我认为您将不得不包装它,正如您所描述的,并捕获从库本身引发的任何异常。绝对是使用 FFI 的烦恼之一,如果它比 C 更复杂,就会变得棘手。
猜你喜欢
  • 1970-01-01
  • 2021-12-31
  • 2019-12-10
  • 2019-12-05
  • 1970-01-01
  • 1970-01-01
  • 2018-11-08
  • 1970-01-01
  • 2021-08-03
相关资源
最近更新 更多