【问题标题】:Debugging DLLImport in C#在 C# 中调试 DLLImport
【发布时间】:2011-10-19 14:17:01
【问题描述】:

在过去 12 小时左右的时间里,我一直试图让 MySQL Embedded Library 在我的 C# 应用程序中工作,并且已经卡住了很长一段时间。当我打电话给mysql_server_init()时出现以下错误...

未处理的异常:System.AcessViolationException:试图读取 或写受保护的内存。

C++ 方法接受一个int 和两个char** 作为参数,但有人告诉我一个空终止的string[] 数组在C# 中就足够了。由于这是您应该调用的第一个方法,因此我对这个问题有些茫然。

这是我的代码...

public class MySQLServer
{
    [DllImport("libmysqld.dll")]
    static extern int mysql_server_init(int argc, string[] argv, string[] groups);

    [DllImport("libmysqld.dll")]
    static extern void mysql_server_end();

    public static bool Start()
    {
        string[] argv = new string[3];
        argv[0] = "mysql_test";
        argv[1] = "--datadir=C:/MYSQLTEST";
        argv[2] = null;

        string[] groups = new string[3];
        groups[0] = "libmysqd_server";
        groups[1] = "libmysqd_client";
        groups[2] = null;

        int res;

        if ((res = mysql_server_init(3, argv, groups)) == 1)
        {
            Console.WriteLine("MySQL Library Init Failed with error code: %d", res);
            return false;
        }

        Console.WriteLine("MySQL Library Started Successfully!");
        return true;
    }
}

【问题讨论】:

  • 嗯,在您在@Mitch 的帖子中的评论中,您声明它会制作本地副本。它没有。它保留对它的引用。但是 MySql 对 C# 垃圾收集一无所知,所以 方法返回后,您的程序会崩溃。请参阅:bytes.com/topic/c-sharp/answers/…(相当深入的线程)
  • 我不得不问你不能使用以下的原因:dev.mysql.com/doc/refman/5.1/en/connector-net.html
  • 您可以随时自己修改和编译库。
  • 我的理解是不适用于 MySQL 嵌入式库。

标签: c# .net mysql pinvoke dllimport


【解决方案1】:

如果正在读取字符串,您应该能够作为字符串数组传递。

如果正在写入它们,那么您必须将参数声明为 IntPtrs,为它们分配内存,然后自己编组数据(另外,您还需要取消分配内存)。

如果你这样调用会发生什么 (argc := 2):

    if ((res = mysql_server_init(2, argv, groups)) == 1) 
    { 
        Console.WriteLine("MySQL Library Init Failed with error code: %d", res); 
        return false; 
    } 

【讨论】:

  • 根据文档,它只是从字符串数组中读取数据并保存在本地,以便它们可以被销毁。由于它只是阅读它们,我不知道为什么会收到此错误。
【解决方案2】:

在我看来,您有一个小错误,导致mysql_server_init 读取超出数组边界。将 3 替换为 2 并删除第三个数组条目。默认封送拆收器将完成其余的工作。

public static bool Start()
{
    string[] argv = new string[3];
    argv[0] = "mysql_test";
    argv[1] = "--datadir=C:/MYSQLTEST";

    string[] groups = new string[3];
    groups[0] = "libmysqd_server";
    groups[1] = "libmysqd_client";

    int res;

    if ((res = mysql_server_init(2, argv, groups)) == 1)
    {
        Console.WriteLine("MySQL Library Init Failed with error code: %d", res);
        return false;
    }
}

更新

对于this thread,您根本不应该使用mysql_server_init,而应该使用mysql_library_init

  1. 'argv' 和 'groups' 都由库存储并在以后使用。这是一个严重的问题,因为如果您使用 P/Invoke 编组, .NET 传递给函数的指针将立即失效 当函数返回时。当库稍后尝试引用 argv 或组,它只会在那里找到垃圾,导致意外 行为或崩溃。

如果您使用的是 MySQL 5.0.3 或更高版本,请尝试使用 mysql_library_init 相反 - 它制作了 argv 和组的内部副本,因此您可以 继续使用 P/Invoke 编组没有问题(只要添加一个 组末尾的空条目)。否则,您将不得不分配 自己为数组和每个字符串存储内存,以确保 只要您的程序正在运行,指针就保持有效。

所以,如果您可以切换到使用mysql_library_init,我建议您这样做。手工编组不是一件容易的事(但也不是太难)。

【讨论】:

  • 你需要固定字符串数组的大小,它当前为 3,所以数组中的最后一个元素默认为空(这是他目前拥有的)。
  • 我不使用 mysql_library_init 的原因是函数没有导出。我没有意识到内部副本只是由 mysql_library_init 制作的,我认为两者都是在 5.0.3 及更高版本中完成的。
  • @BrettPowell,所以你不能使用 mysql_library_init?然后我们将进行一次不错的手动编组......
  • 实际上,我刚刚添加了一堆 Console.ReadLine() 行,我可以看到它现在正在向控制台打印一些东西,但是它关闭得太快了。在我调用 mysql_server_init() 之后,ReadLine() 似乎没有保持打开状态,还有其他方法可以让我阅读它所说的内容吗?
  • @Brett 如果您在通话后放置断点,是否有帮助?通常,您可以通过转到选项对话框 -> 调试 -> 选中“将所有输出窗口文本重定向到即时窗口”来捕获所有控制台输出。
【解决方案3】:

C++ 方法接受一个 int 和两个 char** 作为参数,但我是 告诉一个空终止的字符串 [] 数组在 C# 中就足够了。因为这是 你应该调用的第一种方法,我有点 关于这个问题的损失。

这究竟是谁告诉你的?

我相信您可以只使用对 char[]

的引用

[DllImport("libmysqld.dll")] 静态外部 int mysql_server_init(int argc, ref char[] argv, ref char[] 组);

确保调用约定匹配,否则会遇到问题。

Mitch Wheat 的回答可以通过这个线程来增强:Marshalling a char** in C#

一些附加信息:C# Marshalling char** and unsigned char**

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-19
    • 1970-01-01
    • 2012-05-29
    • 1970-01-01
    相关资源
    最近更新 更多