我正试图从存储在SQLite中的blob中获取一个 foreach (DataRow row in table.Rows)
{
Byte[] bytes = (Byte[])row["password_value"];
}
,但byte []的长度始终为1
public class SQLiteBase
{
// imports system functions for work with pointers
[DllImport("kernel32")]
private extern static IntPtr HeapAlloc(IntPtr heap, UInt32 flags, UInt32 bytes);
[DllImport("kernel32")]
private extern static IntPtr GetProcessHeap();
[DllImport("kernel32")]
private extern static int lstrlen(IntPtr str);
// imports SQLite functions
[DllImport("sqlite3")]
private static extern int sqlite3_open(IntPtr fileName, out IntPtr database);
[DllImport("sqlite3")]
private static extern int sqlite3_close(IntPtr database);
[DllImport("sqlite3")]
private static extern int sqlite3_exec(IntPtr database, IntPtr query, IntPtr callback, IntPtr arguments,
out IntPtr error);
[DllImport("sqlite3")]
private static extern IntPtr sqlite3_errmsg(IntPtr database);
[DllImport("sqlite3")]
private static extern int sqlite3_prepare_v2(IntPtr database, IntPtr query, int length, out IntPtr statement,
out IntPtr tail);
[DllImport("sqlite3")]
private static extern int sqlite3_step(IntPtr statement);
[DllImport("sqlite3")]
private static extern int sqlite3_column_count(IntPtr statement);
[DllImport("sqlite3")]
private static extern IntPtr sqlite3_column_name(IntPtr statement, int columnNumber);
[DllImport("sqlite3")]
private static extern int sqlite3_column_type(IntPtr statement, int columnNumber);
[DllImport("sqlite3")]
private static extern int sqlite3_column_int(IntPtr statement, int columnNumber);
[DllImport("sqlite3")]
private static extern double sqlite3_column_double(IntPtr statement, int columnNumber);
[DllImport("sqlite3")]
private static extern IntPtr sqlite3_column_text(IntPtr statement, int columnNumber);
[DllImport("sqlite3")]
private static extern IntPtr sqlite3_column_blob(IntPtr statement, int columnNumber);
[DllImport("sqlite3")]
private static extern IntPtr sqlite3_column_table_name(IntPtr statement, int columnNumber);
[DllImport("sqlite3")]
private static extern int sqlite3_finalize(IntPtr handle);
// SQLite constants
private const int SQL_OK = 0;
private const int SQL_ROW = 100;
private const int SQL_DONE = 101;
/// <summary>
/// SQLite data types.
/// </summary>
public enum SQLiteDataTypes
{
/// <summary>
/// Integer numbers.
/// </summary>
INT = 1,
/// <summary>
/// Decimal numbers.
/// </summary>
FLOAT,
/// <summary>
/// All kinds of texts.
/// </summary>
TEXT,
/// <summary>
/// Blob objects - binary large objects.
/// </summary>
BLOB,
/// <summary>
/// Nothing.
/// </summary>
NULL
};
// pointer to database
private IntPtr database;
/// <summary>
/// Creates new instance of SQLiteBase class with no database attached.
/// </summary>
public SQLiteBase()
{
database = IntPtr.Zero;
}
/// <summary>
/// Creates new instance of SQLiteBase class and opens database with given name.
/// </summary>
/// <param name="baseName">Name (and path) to SQLite database file</param>
public SQLiteBase(String baseName)
{
OpenDatabase(baseName);
}
/// <summary>
/// Opens database.
/// </summary>
/// <param name="baseName">Name of database file</param>
public void OpenDatabase(String baseName)
{
// opens database
if (sqlite3_open(StringToPointer(baseName), out database) != SQL_OK)
{
// if there is some error, database pointer is set to 0 and exception is throws
database = IntPtr.Zero;
throw new Exception("Error with opening database " + baseName + "!");
}
}
/// <summary>
/// Closes opened database.
/// </summary>
public void CloseDatabase()
{
// closes the database if there is one opened
if (database != IntPtr.Zero)
{
sqlite3_close(database);
}
}
/// <summary>
/// Returns the list of tables in opened database.
/// </summary>
/// <returns></returns>
public ArrayList GetTables()
{
// executes query that select names of all tables and views in master table of every database
String query = "SELECT name FROM sqlite_master " +
"WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'" +
"UNION ALL " +
"SELECT name FROM sqlite_temp_master " +
"WHERE type IN ('table','view') " +
"ORDER BY 1";
DataTable table = ExecuteQuery(query);
// when table is generater, it writes all table names in list that is returned
ArrayList list = new ArrayList();
foreach (DataRow row in table.Rows)
{
list.Add(row.ItemArray[0].ToString());
}
return list;
}
/// <summary>
/// Executes query that does not return anything (e.g. UPDATE, INSERT, DELETE).
/// </summary>
/// <param name="query"></param>
public void ExecuteNonQuery(String query)
{
// calles SQLite function that executes non-query
IntPtr error;
sqlite3_exec(database, StringToPointer(query), IntPtr.Zero, IntPtr.Zero, out error);
// if there is error, excetion is thrown
if (error != IntPtr.Zero)
throw new Exception("Error with executing non-query: \"" + query + "\"!\n" +
PointerToString(sqlite3_errmsg(error)));
}
/// <summary>
/// Executes query that does return something (e.g. SELECT).
/// </summary>
/// <param name="query"></param>
/// <returns></returns>
public DataTable ExecuteQuery(String query)
{
// processed query
IntPtr statement;
// excess data, it has no use
IntPtr excessData;
// process query and make statement
sqlite3_prepare_v2(database, StringToPointer(query), GetPointerLenght(StringToPointer(query)),
out statement, out excessData);
// table for result of function
DataTable table = new DataTable();
// reads first row - it is different from next rows because it also creates table columns
// result - returns SLQ_ROW while there is next row
int result = ReadFirstRow(statement, ref table);
// reads rows
while (result == SQL_ROW)
{
result = ReadNextRow(statement, ref table);
}
// finalize executing this query
sqlite3_finalize(statement);
// returns table
return table;
}
// private function for reading firs row and creating DataTable
private int ReadFirstRow(IntPtr statement, ref DataTable table)
{
// create new instance of DataTable with name "resultTable"
table = new DataTable("resultTable");
// evaluates statement
int resultType = sqlite3_step(statement);
// if result of statement is SQL_ROW, create new table and write row in it
if (resultType == SQL_ROW)
{
// returns number of columns returned by statement
int columnCount = sqlite3_column_count(statement);
// declartaion of variables for reading first row
String columnName = "";
int columnType = 0;
object[] columnValues = new object[columnCount];
// reads columns one by one
for (int i = 0; i < columnCount; i++)
{
// returns the name of current column
columnName = PointerToString(sqlite3_column_name(statement, i));
// returns the type of current column
columnType = sqlite3_column_type(statement, i);
// checks type of columns - neccessary because different functions are required for different types
switch (columnType)
{
// in case of integer column
case (int) SQLiteDataTypes.INT:
{
// adds new integer column to table
table.Columns.Add(columnName, Type.GetType("System.Int32"));
// writes column value in object array
columnValues[i] = sqlite3_column_int(statement, i);
break;
}
// same as for integer, this one is for float
case (int) SQLiteDataTypes.FLOAT:
{
table.Columns.Add(columnName, Type.GetType("System.Single"));
columnValues[i] = sqlite3_column_double(statement, i);
break;
}
// ... for text
case (int) SQLiteDataTypes.TEXT:
{
table.Columns.Add(columnName, Type.GetType("System.String"));
columnValues[i] = PointerToString(sqlite3_column_text(statement, i));
break;
}
// ... for blob - blob are written in table as strings!!
case (int) SQLiteDataTypes.BLOB:
{
table.Columns.Add(columnName, Type.GetType("System.Byte[]"));
columnValues[i] = PointerToByte(sqlite3_column_blob(statement, i));
break;
}
// in case of something other, value is read as string
default:
{
table.Columns.Add(columnName, Type.GetType("System.String"));
columnValues[i] = "";
break;
}
}
}
// writes column values to table
table.Rows.Add(columnValues);
}
// evalute statemnet for next results
return sqlite3_step(statement);
}
// private function for reading rows other than first
// it' same like first row, only without creating table and columns
private int ReadNextRow(IntPtr statement, ref DataTable table)
{
int columnCount = sqlite3_column_count(statement);
int columnType = 0;
object[] columnValues = new object[columnCount];
for (int i = 0; i < columnCount; i++)
{
columnType = sqlite3_column_type(statement, i);
switch (columnType)
{
case (int) SQLiteDataTypes.INT:
{
columnValues[i] = sqlite3_column_int(statement, i);
break;
}
case (int) SQLiteDataTypes.FLOAT:
{
columnValues[i] = sqlite3_column_double(statement, i);
break;
}
case (int) SQLiteDataTypes.TEXT:
{
columnValues[i] = PointerToString(sqlite3_column_text(statement, i));
break;
}
case (int) SQLiteDataTypes.BLOB:
{
columnValues[i] = PointerToByte(sqlite3_column_blob(statement, i));
break;
}
default:
{
columnValues[i] = "";
break;
}
}
}
table.Rows.Add(columnValues);
return sqlite3_step(statement);
}
// converts string to pointer
private IntPtr StringToPointer(String str)
{
// if string is null, pointer is 0
if (str == null)
{
return IntPtr.Zero;
}
else
{
// else, convert it to pointer
Encoding encoding = Encoding.UTF8;
Byte[] bytes = encoding.GetBytes(str);
int length = bytes.Length + 1;
IntPtr pointer = HeapAlloc(GetProcessHeap(), 0, (UInt32) length);
Marshal.Copy(bytes, 0, pointer, bytes.Length);
Marshal.WriteByte(pointer, bytes.Length, 0);
return pointer;
}
}
// convert pointer to string
private String PointerToString(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return null;
Encoding encoding = Encoding.UTF8;
int length = GetPointerLenght(ptr);
Byte[] bytes = new Byte[length];
Marshal.Copy(ptr, bytes, 0, length);
return encoding.GetString(bytes, 0, length);
}
private Byte[] PointerToByte(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return null;
int length = GetPointerLenght(ptr);
Byte[] bytes = new Byte[length];
Marshal.Copy(ptr, bytes, 0, length);
return bytes;
}
// returns length of pointer
private int GetPointerLenght(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return 0;
return lstrlen(ptr);
}
}
这是我使用的Wrapper类,我想包装器中的问题是我使用普通的SQLite包装器测试了代码,它可以正常工作,但是我无法弄清楚包装器是什么问题
private Byte[] PointerToByte(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return null;
int length = GetPointerLenght(ptr);
Byte[] bytes = new Byte[length];
Marshal.Copy(ptr, bytes, 0, length);
return bytes;
}
,我认为问题出在本节中
private int GetPointerLenght(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return 0;
return lstrlen(ptr); // here the problem this gets the length of string and im passing an array of bytes
}
UPDATE
{{1}}
那么现在的问题是如何从指针获取字节数组的长度?
答案 0 :(得分:1)
感谢@FCin指出
sqlite3_column_bytes()或sqlite3_column_bytes16()
这里的解决方案从非托管代码中获取size或byte []数组
[DllImport("sqlite3")]
private static extern int sqlite3_column_bytes(IntPtr statement, int columnNumber);
private int getSizeOfBytes(IntPtr statement,int i)
{
return sqlite3_column_bytes(statement, i);
}
你这样传递吗
columnValues[i] = PointerToByte(sqlite3_column_blob(statement, i), getSizeOfBytes(statement,i));