Мне нужно поставить и получить большие двоичные данные из таблицы MS SQL.
Чтобы поставить данные я использую ifstream и SQLPutData. Вот мой код для размещения данных (без проверки ошибок и выделения некоторых дескрипторов):
char file[MAX_PATH] = "";
strcpy(file, argv[2]);
ifstream f(file, ios::binary | ios::ate);
size_t sz = f.tellg();
f.seekg(0, ios::beg);
char *buf = new char[sz];
char *ptr = buf;
f.read(ptr, sz);
f.close();
SQLLEN lbytes, cbBinarySize;
PTR pParamID;
lbytes = (SDWORD) sz;
cbBinarySize = SQL_LEN_DATA_AT_EXEC(lbytes);
char SQL[512] = "INSERT INTO bindata (filename, binData) VALUES ('";
strcat(SQL, file);
strcat(SQL, "', ?)");
ret = SQLPrepare(stmt, (SQLCHAR *) SQL, SQL_NTS);
SQLSMALLINT dataType, decDigits, nullable;
SQLUINTEGER paramsize;
ret = SQLDescribeParam(stmt, 1, &dataType, ¶msize, &decDigits, &nullable);
ret = SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, dataType, paramsize, 0, (VOID *)1, 0, &cbBinarySize);
ret = SQLExecDirect(stmt, (SQLCHAR *) SQL, SQL_NTS);
if (ret != SQL_NEED_DATA && ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) return ret;
ret = SQLParamData(stmt, &pParamID);
if (ret == SQL_NEED_DATA) {
while (lbytes > 5000 ) {
ret = SQLPutData(stmt, (SQLCHAR *)ptr, 5000);
lbytes -= 5000;
ptr += 5000;
}
ret = SQLPutData(stmt, ptr, lbytes);ret = SQLParamData(stmt, &pParamID);
if (ret != SQL_SUCCESS) return ret;
}
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
delete[] ptr;
delete[] buf;
Это прекрасно работает для меня, у меня есть двоичные данные в моем поле таблицы VARBINARY (MAX). Теперь я хочу получить эти данные оттуда. Вот что я пытался сделать (я проверил статью MSDN https://msdn.microsoft.com/en-us/library/ms811006.aspx):
unsigned char* data = new unsigned char[2500000];
SQLINTEGER cbFdata = SQL_NTS;
SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
ret = SQLExecDirect(stmt, (SQLCHAR *) "SELECT binData FROM bindata WHERE filename='AgentCPP.exe'", SQL_NTS);
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) return ret;
ret = SQLFetch(stmt);
do {
ret = SQLGetData(stmt, 1, SQL_C_BINARY, data, 5000, &cbFdata);
} while (ret != SQL_NO_DATA_FOUND);SQLFreeHandle(SQL_HANDLE_STMT, stmt);
Но вместо массива, содержащего мои данные, я получаю мусор, который может быть только частью моих данных. Что я делаю неправильно?
Если бы я использовал .Net, я мог бы использовать какой-нибудь массив byte [] или что-то еще, но здесь я не могу. Не могли бы вы мне помочь?
UPD. Просто попытался сделать это с помощью C #, и мне потребовалось около 3 минут и дюжины строк кода. Просто чтобы показать, какой результат я пытаюсь получить — этот код создает полностью работающий exe-файл из двоичных данных, который был помещен в базу данных моим первым блоком кода из этого вопроса:
OdbcCommand com = new OdbcCommand(@"SELECT binData FROM bindata WHERE filename='AgentCPP.exe'", con);
com.Connection = con;
OdbcDataReader reader = com.ExecuteReader();
if (reader.Read())
binData = (byte[]) reader.GetValue(0);
reader.Dispose();
com.Dispose();
con.Close();
FileStream fs = new FileStream(@"agent.exe", FileMode.Create, FileAccess.Write);
fs.Write(binData, 0, binData.Length);
fs.Close();
Как я могу добиться того же, используя C Winapi?
Хорошо, похоже, я разобрался, что не так в моем коде.
Во-первых, мне нужно было получить реальную длину данных, а не просто использовать приблизительный размер массива 2500000, поэтому сначала я использовал поддельный указатель для получения размера данных, а затем использовал std :: vector.
Во-вторых, я поместил 5000 в SQLGetData как «часть» моих данных, что неправильно, мне нужно использовать фактический размер данных. Вот код, который отлично работает для меня:
SQLINTEGER cbSize = SQL_NTS;
ret = SQLExecDirect(stmt, (SQLCHAR *) "SELECT binData FROM bindata WHERE filename='AgentCPP.exe'", SQL_NTS);
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) return ret;
ret = SQLFetch(stmt);
char *ptr = new char[1];
if ((ret = SQLGetData(stmt, 1, SQL_C_BINARY, ptr, 0, &cbSize) != SQL_NO_DATA))
{
vector<char> vec(cbSize);
SQLGetData(stmt, 1, SQL_C_BINARY, &vec[0], cbSize, &cbSize);
ofstream fout;
fout.open("test.exe", ios::binary);
fout.write(&vec[0], cbSize);
fout.close();
vec.clear();
}
delete[] ptr;
Других решений пока нет …