[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 847
  • Last Modified:

Read binary file as char * / LPVOID

Hi,

I want to use HttpSendRequest function that takes as 4th parameter a LPVOID.
My need is to read a binary file and pass its content as LPVOID.
I found a function that reads binary file as std::string, but I have no solution to pass this std::string as LPVOID. Here is that function :
string CSslConnection::GetFileContents(const string &filename)
{
      
      HANDLE hFile = CreateFile(filename.c_str(), GENERIC_READ , 0, NULL, OPEN_EXISTING, 0, NULL);
      assert(hFile != INVALID_HANDLE_VALUE);

      DWORD dwFileSize = GetFileSize(hFile, NULL);
      char *p = (char *)malloc(dwFileSize);

      DWORD dwBytesRead;
      ReadFile(hFile, p, dwFileSize, &dwBytesRead, NULL);

     string fileContents;
      fileContents.assign(p, dwFileSize);
       free(p);
      return fileContents;
      
}

I tried to convert the std::string returned by GetFileContents to a char * using c_str function, but the char * obtained is corrupted.

Any help would be much appreciated.
0
hopskeps
Asked:
hopskeps
  • 7
  • 5
  • 2
  • +1
1 Solution
 
grg99Commented:
You can skip the bizness of making a string, just return "p" and declare the function as returning a char *.

0
 
Kent OlsenData Warehouse Architect / DBACommented:
Hi hopskeps,

A std::string object is probably not the right container for a binary file.  Consider other types of objects, including a raw buffer.


Good Luck,
Kent
0
 
hopskepsAuthor Commented:
grg99 ,

"cout << p << endl"  prints partial data of the binary content, while "cout << fileContents << endl" prints all the data
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
hopskepsAuthor Commented:
Kdo,

I am new to C++. Can you please give more details

thanks
0
 
Kent OlsenData Warehouse Architect / DBACommented:
Sure.

You're dealing with two significant issues.  Both involving terminators/separators.

  In the strictest sense, a C string is some number of non-zero characters followed by a zero byte.  Binary data is full of zero bytes.
  If the file really contains lines of text, you've got a possible problem with the carriage return / line feed used by Windows and the newline used by unix.  The C libraries resolve this when the file is read in text mode by stripping the carriage return.  This way the exact same data is returned on a successful read on both operating systems.

What are you trying to do with the data?

0
 
hopskepsAuthor Commented:
Kdo:

The data read from the file must be appended to other text content in order to build a MIME formatted string like this :

-----------------------------7d22bb3b890472
Content-Disposition: form-data; name="Archive"; filename="file.zip"
Content-Type: application/x-zip-compressed

PK   Zy- %   '      e-Parapheur.confK,J,Ku,uq
qI-H,J,H-qH PK   J{-^         e-Parapheur-Bor.xmlU        
-----------------------------7d22bb3b890472
Content-Disposition: form-data; name="Author"

ATEXO
-----------------------------7d22bb3b890472
Content-Disposition: form-data; name="Recipient"

CG95
-----------------------------7d22bb3b890472--

Finally that string must be passed to HttpSendRequest function (WinInet function) as a LPVOID.

Thank you very much
0
 
Kent OlsenData Warehouse Architect / DBACommented:
Ok.  Then what you're really doing is reading a text file.  MIME is a mechanism for converting data (often binary data) into a format that is suitable for sending via SMTP.  SMTP sends text so the MIME output must be text.

So back to the original problem, your reading of the file looks pretty reasonable.  Have you checked to see how much data was read by the call to ReadFile?

Also, p is defined as (char *).  That can be recast to an LPVOID without having to assign the data to String.  

One more thing -- in C++ it's usually preferred that buffers be assigned with the new[] operator instead of a call to malloc().


0
 
grg99Commented:
If it is a binary file you should not be doing a : "cout << p << endl"

That will stop writing from p when it encoutners a zero byte.  That is the way char * strings are terminated.

You need something like  fwrite( p, Len, stdout );  Where "Len" is the amount of data.
0
 
hopskepsAuthor Commented:
grg99:

I think there are two ways to get a solution to my problem :

1/ how to copy a binary content (stored in a file or in a std::string) into a char* that will not stop when it encoutners a zero byte
2/ how to use a std::string with binary data as a LPVOID
0
 
Kent OlsenData Warehouse Architect / DBACommented:
Hi hopskeps,

>>1/  how to copy a binary content (stored in a file or in a std::string) into a char* that will not stop when it encoutners a zero byte

The ReadFile() function that you're using should read a binary file just fine.  Judging by the headers that you posted, you're really reading text data, which should work just fine.

>>2/ how to use a std::string with binary data as a LPVOID

You can't.  That's been the message all along.  You can certainly read all of the data into a buffer, but the string functions will stop processing the data as soon as they encounter a zero byte.

You read the data into a buffer pointed to by p.  Modify the function to return a char* or LPVOID and return p.  (You'll probably want to add a bit of logic to declare as a class variable so that you can manage it without memory leaks.)


Kent

0
 
hopskepsAuthor Commented:
Hi Kdo:

The GetFileContents really works fine. but the conversion to LPVOID is the problem...
0
 
Kent OlsenData Warehouse Architect / DBACommented:
It looks to be no more than a problem with recasting.  Add two lines to declare and initialize a pointer in the class.


In CSslConnection.h (in the private section of the class definition), declare:

  char  *FileBuffer;


In CSslConnection::CSslConnection:

  FileBuffer = NULL;


Then change the method to this:


LPVOID CSslConnection::GetFileContents(const string &filename)
{

      HANDLE hFile = CreateFile(filename.c_str(), GENERIC_READ , 0, NULL, OPEN_EXISTING, 0, NULL);
      assert(hFile != INVALID_HANDLE_VALUE);

      DWORD dwFileSize = GetFileSize(hFile, NULL);

      if (FileBuffer)
        Delete FileBuffer;

      FileBuffer = new char[dwFileSize];

      DWORD dwBytesRead;
      ReadFile(hFile, p, dwFileSize, &dwBytesRead, NULL);

      return (LPVOID)fileContents;
     
}


Good Luck,
Kent
0
 
itsmeandnobodyelseCommented:
>>2/ how to use a std::string with binary data as a LPVOID
>>>> You can't.  That's been the message all along.  
Actually, that isn't true. A std::string may not be the best candidate for storing binary data but actually it is only a few functions, e. g. the constructor that takes a const char*, which were dependend on the buffer not containing zero characters before end of data. A std::string (or any string class object) has the advantage that it stores length and buffer, so that there is no need to always pass a pair. Furthermore it handles allocation and deallocation.

So the following would create a valid std::string from the file contents:

       string fileContents((const char*)p, dwBytesRead);

 
regardless whether it has binary or text data.

Note, I used 'dwBytesRead' rather than 'dwFileSize'  though these should be identical if the file was read in binary mode.

You also may use the std::string::assign as you did it in your initially code, but you can't output the string by using the c_str() function cause that wouldn't use the stored length information. However, the pointer the c_str function would return can be used wherever a void* (or LPVOID what is the same) is required, but you always need to have to pass the length of the data additionally:

   // get data from connection
   string data = sslConnection.GetFileContents("data.dat");
   
   // pass it as void* to somewhere(LPVOID, DWORD dlen);

   somewhere(data.c_str(), data.length());

Note, if the 'somewhere' function is supposed to run asynchronously you can't pass the c_str() of a local string object cause the pointer would become invalid after leaving the current scope. Instead you should use a class member string or a static string in the function so that the pointer keeps valid.

>>>> HANDLE hFile = CreateFile(...
Hmmm. You might consider of using ifstream instead of CreateFile/ReadFile. I would turn the GetFileConnections to

#include <sys/stat.h>
...

bool CSslConnection::GetFileContents(const string &filename, string& buf)
{
     struct stat fs = { 0 };
     if (stat(filename.c_str()) != 0)  // file doesn't exist
          return false;
     ifstream ifs(filename.c_str(), ios::in | ios::binary);
     char* p = new char[fs.st_size];
     if (!ifs.read(p, fs.st_size)) // error while read
     {
          ifs.close();
          return false;  
     }
     ifs.close();
     buf.assign(p, fs.st_size);
     delete [] p;
     return true;
}

Note, passing the (return) buffer by reference has the advantage that the data must not be copied at return.

>>>> PK   Zy- %   '      e-Parapheur.confK,J,Ku,uq
>>>> Then what you're really doing is reading a text file.

Normally 'text-files' look differently. The   is a strong evident that the file is a binary file. If that file was to get passed by MIME the conversion to ASCII or UTF-7 still needs to be done.

Regards, Alex


0
 
itsmeandnobodyelseCommented:
>>>>  if (stat(filename.c_str()) != 0)  // file doesn't exist
correction:
      if (stat(filename.c_str(), &fs) != 0)  // file doesn't exist
0
 
hopskepsAuthor Commented:
my probleme is when i convert the file content in char i lost the content,and i must have this content to be char or LPVOID for to be able send it in HttpSendRequest function (WinInet function)
0
 
hopskepsAuthor Commented:
this is the fonction where i convert a string to char* and exactly that's where i lost th content of file:::
bool CSslConnection::SendHttpsRequest()
{
      CHAR boundary[] = "---------------------------7d22bb3b890472";
      
      CHAR bstrHeaders[] = "Content-Type: multipart/form-data; boundary=---------------------------7d22bb3b890472" ;
      HttpAddRequestHeaders(m_hRequest, bstrHeaders, -1, HTTP_ADDREQ_FLAG_ADD);
       string str = GenerateRequestBody("atexo", "MITALI","c:\\test.tar.gz",boundary);
      size_t taille = str.size() + 1;
      char *wchData = new char[taille];
        strncpy(wchData,str.c_str(), taille );
   
    try {      
               for (int tries = 0; tries < 1; ++tries) {
          int result =  HttpSendRequest(m_hRequest, bstrHeaders,strlen(bstrHeaders),wchData,strlen(wchData));
                        if (result) return true;
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

  • 7
  • 5
  • 2
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now