I'm trying to use gnu's gdbm3.dll from C# (as I could not find a native implementation or decoded examples for these key-value pair databases).
So far, I'm focussing on the following C code from gdbm.h:
/* The file information header. This is good enough for most applications. */
typedef struct {int dummy[10];} *GDBM_FILE;
/* Determine if the C(++) compiler requires complete function prototype */
#ifndef __P
#if defined(__STDC__) || defined(__cplusplus) || defined(c_plusplus)
#define __P(x) x
#else
#define __P(x) ()
#endif
#endif
/* External variable, the gdbm build release string. */
GDBM_DLL_IMPEXP char *gdbm_version;
GDBM_DLL_IMPEXP GDBM_FILE gdbm_open __P((char *, int, int, int, void (*)()));
GDBM_DLL_IMPEXP void gdbm_close __P((GDBM_FILE));
GDBM_DLL_IMPEXP datum gdbm_firstkey __P((GDBM_FILE));
and translated it to:
public delegate void fatal_func();
[StructLayout(LayoutKind.Sequential)]
public struct GDBM_FILE
{
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 10, ArraySubType = UnmanagedType.I4)]
public int[] dummy;
}
[DllImport("gdbm3.dll", EntryPoint = "gdbm_open", CallingConvention = CallingConvention.Cdecl)]
public static extern GDBM_FILE gdbm_open(IntPtr file, int block_size, int flags, int mode, fatal_func callback);
[DllImport("gdbm3.dll", EntryPoint = "gdbm_close", CallingConvention = CallingConvention.Cdecl)]
public static extern void gdbm_close(ref GDBM_FILE dbf);
[DllImport("gdbm3.dll", EntryPoint = "gdbm_firstkey", CallingConvention = CallingConvention.Cdecl)]
public static extern datum gdbm_firstkey(ref GDBM_FILE dbf);
However, the following code snippet crashes on fdbm_open with a 'Method's type signature is not PInvoke compatible.' exception:
IntPtr fn = Marshal.StringToHGlobalAuto("keys.gdbm");
GDBM_FILE dbf = NativeMethods.gdbm_open(fn, 512, (int)NativeMethods.GDBM_READER, 0640, null); // was null
Marshal.FreeHGlobal(fn);
datum key = NativeMethods.gdbm_firstkey(ref dbf);
NativeMethods.gdbm_close(ref dbf);
Swapping GDBM_FILE for an Intptr (by using GDBM_FILE = System.IntPtr;) seems to work but returns a 0 IntPtr. Subsequently, the code crashes on gdbm_firstkey().
Update (some progress):
- Changed GMDB_FILE to an IntPtr and adjusted the signature of gmdb_open() to
public static extern IntPtr gdbm_open(
[In()][MarshalAs(UnmanagedType.LPStr)] string file,
int block_size,
int flags,
int mode,
fatal_func callback); // was fatal_func callback
Now lets me create gmdb files and open these subsequently for writing.
My original input gdbm file does not seem to work somehow (although its gmdb version is equal).
- Changed gmdb_store to:
[DllImport("gdbm3.dll", EntryPoint = "gdbm_store", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern int gdbm_store(IntPtr dbf, datum key, datum content, int flags);
and datum to:
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
public struct datum
{
/// char*
[MarshalAsAttribute(UnmanagedType.LPStr)]
public string dptr;
/// int
public int dsize;
/// <summary>
/// Constructor.
/// </summary>
///
/// <param name="dptr"> The dptr. </param>
public datum(String dptr)
{
this.dptr = dptr;
this.dsize = dptr.Length;
}
}
Which allows me to write keys & values (but overwriting still fails).
I'm currently stuck on how to get the datum return value from:
[DllImport("gdbm3.dll", EntryPoint = "gdbm_firstkey", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.Struct)]
public static extern datum gdbm_firstkey(datum dbf);
Changing the return value to IntPtr and poking around reveals the string member from datum. The struct itself generates an error as can't be marshalled.