简体   繁体   中英

How to retrieve large binary data from MS SQL table using WinApi?

I need to put and retrieve large binary data from ms sql table.

To put data I use ifstream and SQLPutData. Here is my code to put data (without error checking and allocating some handles):

 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, &paramsize, &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;

This works fine for me, i have binary data in my VARBINARY(MAX) table field. Now i want to get this data from there. Here is what i tried to do (i checked out MSDN article 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);

But instead of an array containing my data, i get some garbage, which may be only a part of my data. What am i doing wrong? If i was using .Net, I maybe used some kind of byte[] array, or something, but here i can't. Can you please help me?

UPD. Just tried to do it using C# and it took me about 3 minutes and a dozen lines of code. Just to show what result i'm trying to get - this code creates a fully-working exe file from binary data, which was put into database by my first block of code from this question:

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();

How can i achieve the same using c winapi?

Ok, it seems I fugured out what was wrong in my code.

Firstly, i had to retreive actual length of data, not just using 2500000 approximate size of array, so at first i used fake pointer to get a size of data, and then used std::vector. Secondly, i placed 5000 into SQLGetData as a "part" of my data, which is wrong, i need to use actual size of data. So here is the code, which is working fine for me:

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM