Search code examples
c#.netmysqlpinvokedllimport

Debugging DLLImport in C#


I have been attempting to get the MySQL Embedded Library working in my C# application for the past 12 hours or so, and have been stuck for quite some time. I am getting the following error when I call mysql_server_init()...

Unhandled Exception: System.AcessViolationException: Attempted to read or write protected memory.

The C++ method takes an int and two char** as parameters, but I was told a null terminated string[] array would suffice in C#. As this is the first method that you're supposed to call, I am somewhat at a loss as to this issue.

Here's my code...

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;
    }
}

Solution

  • It seems to me that you have a tiny error, causing mysql_server_init to read beyond the array boundaries. Replace 3 with 2 and remove the third array entry. The default marshaler will do the rest.

    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;
        }
    }
    

    Update

    From this thread, you should not use mysql_server_init at all, but mysql_library_init instead:

    1. Both 'argv' and 'groups' are stored by the library and used later. This is a serious problem, because if you use P/Invoke marshaling, the pointers that .NET passes to the function will become invalid as soon as the function returns. When the library later tries to refer to argv or groups, it'll find nothing but garbage there, causing unexpected behavior or a crash.

    If you're using MySQL 5.0.3 or later, try using mysql_library_init instead - it makes an internal copy of argv and groups, so you can keep using P/Invoke marshaling with no trouble (as long as you add a null entry to the end of groups). Otherwise, you'll have to allocate the memory yourself for the arrays and each string, to make sure the pointers stay valid as long as your program is running.

    So, if you can switch to using mysql_library_init, I'd suggest doing so. By-hand marshaling is not a trivial undertaking (but not too hard either).