之前写了在C#中调用libsqlite3中函数的一些代码:
[DllImport("sqlite3.dll")]
private extern staticstring sqlite3_column_text(IntPtr pStmt, int iCol);
[DllImport("sqlite3.dll")]
privatee xtern static string sqlite3_errmsg(IntPtr pDb);
public static string Errmsg(IntPtr pDb){
return sqlite3_errmsg(pDb);
}
public static string ColumnText(IntPtr pStmt,int iCol){
return sqlite3_column_text(pStmt, iCol);
}
在实际使用Errmsg或ColumnText,发现会产生如下错误:
pointer being freed was not allocated
释放了没分配的内存,怀疑是Marshal对返回的char *进行free搞的鬼。 经google,果然如此:
SQLite manages its own memory, so the char* returned can not be freed by the interop marshaler. The marshaler calls CoTaskMemFree() on every char* that is returned.
So instead of "string", "IntPtr" needs to be used for all returned string pointers.
SQLite returns the string encoded in UTF-8. A direct conversion from a UTF-8 IntPtr to a .NET string is not supported in the Marshal class, but here is an article on how to do the conversion:
http://blog.gebhardtcomputing.com/2007/11/marshal-utf8-strings-in-net.html
And whenever possible, the UTF-16 SQLite functions (suffixed with "16") should be used. To convert the UTF-16 strings from an IntPtr, a simple call to Marshal.PtrToStringUni can be used.
sqlite3_column_text和sqlite3_column_text的共同特点是返回值char *,并且返回的char *不能由我们自己释放,返回的char *是用libsqlite3管理的。
因此 正确的调用方法是:
[DllImport("sqlite3.dll")]
private extern static IntPtr sqlite3_column_text(IntPtr pStmt, int iCol);
[DllImport("sqlite3.dll")]
private extern static IntPtr sqlite3_errmsg(IntPtr pDb);
public static string Errmsg(IntPtr pDb){
IntPtr strPtr = sqlite3_errmsg(pDb);
return Marshal.PtrToStringUni(strPtr);
}
public static string ColumnText(IntPtr pStmt, int iCol){
IntPtr strPtr = sqlite3_column_text(pStmt, iCol);
return Marshal.PtrToStringUni(strPtr);
}