How to get page count of spl file - c++

I'm working on a project that aims at monitoring print jobs. I'm copying spl and shd files from spool folder to a temporary folder and trying to parse the to get data I want such as number of printed pages, owner, date and time...
I'm using the following code to parse spooler spl files (get the emf file structure)
#include "stdafx.h"
#include <windows.h>
#include <winspool.h>
#include <stdio.h>
#include <locale.h>
#include <tchar.h>
#include <iostream>
using namespace std;
BOOL AnalyseFile(const char* pszFileName);
void UPFM(const wchar_t pszInfo[])
{
wprintf(L"%s\n",pszInfo);
}
static char* ID_Func[] =
{
"EMF_HEADER", "EMF_POLYBEZIER", /*....*/
};
int main()
{
setlocale(LC_ALL,"");
const char* pszFileName = "~MyTempFolder\\00031.SPL";
if(!AnalyseFile(pszFileName))
printf("Analyse File Failed!");
else
printf("Analyse File Successed Completed!");
return 0;
}
BOOL AnalyseFile(const char* pszFileName)
{
BOOL bRet = FALSE;
DWORD dwStartPos = 0;
FILE * f ;
f = fopen("log.txt", "w");
FILE* pFile = fopen(pszFileName,"rb");
if(!pFile)
{
fprintf(f,"Open File Failed!");
return bRet;
}
/* =======================Headers================================ */
DWORD dwTmp = 0;
fseek(pFile,0,0);
fread(&dwTmp,sizeof(DWORD),1,pFile);
fread(&dwTmp,sizeof(DWORD),1,pFile);
dwStartPos = dwTmp;
fread(&dwTmp,sizeof(DWORD),1,pFile);
long pos = ftell(pFile);
fseek(pFile,dwTmp,SEEK_SET);
wchar_t pszInfo[256] = {0};
pszInfo[0] = L'(';
WORD wTmp;
int i;
for( i = 1;;i++)
{
fread(&wTmp,sizeof(wTmp),1,pFile);
if(!wTmp)
break;
memcpy((char*)&pszInfo[i],&wTmp,sizeof(wTmp));
}
pszInfo[i] = L')';
UPFM(pszInfo);
fseek(pFile,pos,SEEK_SET);
fread(&dwTmp,sizeof(DWORD),1,pFile);
fseek(pFile,dwTmp,SEEK_SET);
memset(pszInfo,0,sizeof(wchar_t)*256);
pszInfo[0] = L'(';
for(i = 1;;i++)
{
fread(&wTmp,sizeof(wTmp),1,pFile);
if(!wTmp)
break;
memcpy((char*)&pszInfo[i],&wTmp,sizeof(wTmp));
}
pszInfo[i] = L')';
UPFM(pszInfo);
/* ======================== Unknown datas ================================= */
fseek(pFile,dwStartPos,SEEK_SET);
fread(&dwTmp,sizeof(DWORD),1,pFile);
fread(&dwTmp,sizeof(DWORD),1,pFile);
/* ======================== Record datas ================================= */
DWORD dwTmp2 = 0;
for(int i=0;;i++)
{
pos = ftell(pFile);
fread(&dwTmp,sizeof(DWORD),1,pFile);
fread(&dwTmp2,sizeof(DWORD),1,pFile);
FILE *f;
f = fopen("log.txt", "a");
fprintf(f,"index: (%04d) type: 0x%04X size: %04d 0x%08X (%s)\n",i,dwTmp,dwTmp2,pos,ID_Func[dwTmp-1]);
fclose (f);
if(dwTmp == 0x0E)
{
break;
}
fseek(pFile,pos+dwTmp2,SEEK_SET);
}
fclose (f);
if(pFile) fclose(pFile);
bRet = TRUE;
return bRet;
}
Is there a way to calculate page count of the spl file with this method?

To calculate pages you have to count PAGE headers.
Here is a part of code which does it
SpoolFilename = Path.ChangeExtension(SpoolFilename, ".SPL")
'\\ Open a binary reader for the spool file
Dim SpoolFileStream As New System.IO.FileStream(SpoolFilename, FileMode.Open, FileAccess.Read)
Dim SpoolBinaryReader As New BinaryReader(SpoolFileStream, System.Text.Encoding.UTF8)
'Read the spooler records and count the total pages
Dim recNext As EMFMetaRecordHeader = NextHeader(SpoolBinaryReader)
While recNext.iType <> SpoolerRecordTypes.SRT_EOF
If recNext.iType = SpoolerRecordTypes.SRT_PAGE Then
_Pages += 1
End If
'SpoolfileReaderPerformaceCounter.Increment()
Call SkipAHeader(recNext, SpoolBinaryReader)
recNext = NextHeader(SpoolBinaryReader)
End While
Code gets filename, opens binary stream for it, and read header after header. If header code is SRT_PAGE, increment pages counter.
more details and source here

Here is how you do it in C++
struct EMFMetaRecordHeader {
long Seek;
SpoolerRecordTypes iType;
int Size;
};
if (splfile)
{
EMFMetaRecordHeader recNext = NextHeader(splfile);
while (recNext.iType != SpoolerRecordTypes::SRT_EOF)
{
if (recNext.iType == SpoolerRecordTypes::SRT_PAGE)
{
nPages++;
}
recNext = NextHeader(splfile);
}
nEmf_record_code = 0;
}
EMFMetaRecordHeader EMFSpoolfileReader::NextHeader(FILE* splfile){
EMFMetaRecordHeader recRet;
recRet.Seek = ftell(splfile);
fread(&recRet.iType, sizeof(int), 1, splfile);
if (feof(splfile))
{
recRet.iType = SpoolerRecordTypes::SRT_EOF;
return recRet;
}
fread(&recRet.Size, sizeof(int), 1, splfile);
return recRet;
}
splfile is a spool file pointer
more details and source here

Related

How to get the last line of a binary file in C?

I want to get the last record in a binary file, extract its ID and add one to it to mimic an auto-incrementing ID feature. So for example, if the previous user had an ID of 1, then the next user should have an ID of 2, and so on. The issue is I can't get the last record of the binary file using the code below. How can I go about doing this? Here's my code so far:
#include <stdio.h>
struct Applicant
{
int id;
};
void increment()
{
char fileName = "data.dat";
FILE *file;
// Instantiate the applicant struct
struct Applicant applicant;
// Will contain details of the last applicant in the file
struct Applicant previousApplicant;
// Open the file in 'append binary' mode
file = fopen(fileName, "ab");
// If file does not exist, print error message and exit
if (!file)
{
printf("\nSorry, that file does not exist");
exit(1);
}
// Read the last applicant to get their id
while(1)
{
fread(&previousApplicant, sizeof(previousApplicant), 1, file);
if (feof(file)) { break; }
}
// If there's no previous applicant, set the ID to 100
if (previousApplicant.id == 0)
{
applicant.id = 100;
}
// Otherwise just increment the previous applicant's id and assign it to the new applicant
else
{
applicant.id = previousApplicant.id + 1;
}
fwrite(&applicant, sizeof(applicant), 1, file);
fclose(file);
}
To achieve this, I'm using structs
The way I'd suggest doing it is to call fseek() to seek directly to the last record in the file, read it in to RAM, update it, and write it out again. That way you don't have to read in every record in the file just to update the last one, which would be really inefficient if the file is large and/or you have to perform this operation often.
Something like this:
#include <stdio.h>
#include <stdlib.h>
struct Applicant
{
int id;
};
void increment()
{
const char fileName[] = "data.dat";
// Open the file in 'append binary' mode
FILE *file = fopen(fileName, "a+b");
if (file == NULL)
{
printf("Couldn't open file [%s]\n", fileName);
return;
}
// Seek to the end of the file
if (fseek(file, 0, SEEK_END) != 0)
{
printf("Couldn't seek to the end of the file!\n");
fclose(file);
return;
}
// Will contain details of the last applicant in the file
struct Applicant previousApplicant;
// Check to make sure the file is big enough to hold at least one record
const long fileLengthBytes = ftell(file);
if (fileLengthBytes < sizeof(previousApplicant))
{
printf("File is to short to contain a full record!?\n");
fclose(file);
return;
}
// Check to make sure the file length is an even multiple of the record-length
if ((fileLengthBytes % sizeof(previousApplicant)) != 0)
{
printf("File length (%li) is not a multiple of record size (%zu)!?\n", fileLengthBytes, sizeof(previousApplicant));
fclose(file);
return;
}
// Seek backwards to the start of the last record in the file
if (fseek(file, -sizeof(previousApplicant), SEEK_CUR) != 0)
{
printf("Couldn't seek to the start of the last record in the file!\n");
fclose(file);
return;
}
// Read in the last record in the file
if (fread(&previousApplicant, sizeof(previousApplicant), 1, file) != 1)
{
printf("Couldn't read the last record in the file!\n");
fclose(file);
return;
}
// Increment the record
printf("Incrementing ID of last record in the file from %i to %i\n", previousApplicant.id, previousApplicant.id+1);
previousApplicant.id++;
// Finally, write out the new record in the file with incremented ID
if (fwrite(&previousApplicant, sizeof(previousApplicant), 1, file) != 1)
{
printf("Couldn't write the last record in the file!\n");
fclose(file);
return;
}
// success!
fclose(file);
}
int main(int, char **)
{
increment();
return 0;
}
There were a few problems with your code. You were reading the applicant ID into the applicant struct variable rather than the previousApplicant variable and when reading and writing files it is best to separate out the tasks: first open the file for reading, then when you're ready to write, close it and open it for writing.
Here's the modified working code which will do what you want:
#include <stdio.h>
#include <stdlib.h>
struct Applicant
{
int id;
};
void increment()
{
char* fileName = "data.dat";
FILE *file;
// Instantiate the applicant struct
struct Applicant applicant;
// Will contain details of the last applicant in the file
struct Applicant previousApplicant;
// Open the file in 'append binary' mode
file = fopen(fileName, "rb");
// If file does not exist, print error message and exit
if (!file)
{
printf("\nSorry, that file does not exist");
exit(1);
}
// Read the last applicant to get their id
while(1)
{
fread(&previousApplicant, sizeof(applicant), 1, file);
if( feof(file) ) break;
}
// If there's no previous applicant, set the ID to 100
if (previousApplicant.id == 0)
{
applicant.id = 100;
}
// Otherwise just increment the previous applicant's id and assign it to the new applicant
else
{
applicant.id = previousApplicant.id + 1;
}
fclose(file);
FILE* out = fopen(fileName, "ab");
fwrite(&applicant, sizeof(applicant), 1, out);
printf("%d\n", applicant.id);
fclose(out);
}
void main() {
increment();
}
I am not sure I got the logic of what you are trying to do. As pointed above, you can use just fseek() to move the current pointer for the file.
from the [documentation][1]
int fseek(
FILE *stream,
long offset,
int origin
);
int _fseeki64(
FILE *stream,
__int64 offset,
int origin
);
As for the offset: you can use this pre-defined constants: SEEK_CUR meaning the current position in file, SEEK_SET meaning the beginning and SEEK_END pointing, well, to the END of the file. The current offset from the beginning of file you can get by calling fseek()'s sister, ftell(), that returns a long with the position. First byte is 0. You can for sure seek for any position.
An example:
At first consider this code:
int create(const char* f_name, unsigned int n_rec)
{
Applicant one = {100};
FILE* f = fopen(f_name, "wb");
if (f == NULL) return -1;
for (unsigned i = 0; i < n_rec; i += 1, one.id+= 1)
if (fwrite(&one, sizeof(one), 1, f) != 1)
return 1 + i;
fclose(f);
return n_rec;
}; // create()
This function creates the file and writes down the supplied number of IDs. e.g.
create( "x.dat", 300 )
will go as expected and create x.dat and write 300 IDs starting at 100 --- inclusive --- in the file.
Then the following function in the example below reopens the file, get the last ID used and returns it, and you can use it as a prototype:
int get_last_id(const char* f_name)
{
Applicant one = {0};
int res = 0;
FILE* f = fopen(f_name, "rb+");
if (f == NULL) return -1;
res = fseek(f, SEEK_END, SEEK_SET);
if (res != 0) return -2;
res = fseek(f, -(long) (sizeof(one)), SEEK_END);
if (res != 0)
{
printf("res = %d\n", res);
perror("Error in seek()");
return -3;
}
printf("(file position: byte #%ld)\n", ftell(f));
if (fread(&one, sizeof(one), 1, f) != 1) return -4;
fclose(f);
return one.id;
}
The lines you want are these:
Applicant one = {0};
fseek(f, SEEK_END, SEEK_SET);
fseek(f, -(long) (sizeof(one)), SEEK_END);
fread(&one, sizeof(one), 1, f);
You go to the end if file, then goes back 1 record and read it.
sample output
5 Ids written to "sample.dat"
now open file and get the last ID used
(file position: byte #16)
Last ID on "sample.dat" is 104
For this
C code
#include <stdio.h>
typedef struct
{ int id;
} Applicant;
int create(const char*,unsigned);
int get_last_id(const char*);
int main(void)
{
const char* f_name = "sample.dat";
const unsigned initial_size = 5;
int res = 0;
// a few ids are written to file
if ( (res = create(f_name, initial_size)) < 0) return -1;
printf("\
%d Ids written to \"%s\"\n\
now open file and get the last ID used\n",
res, f_name);
// get the last id on file
res = get_last_id(f_name);
if (res < 0)
{
printf("Could not get id from file (%d)\n", res);
return -1;
}
else
printf("Last ID on \"%s\" is % d\n", f_name, res);
return 0;
}
int get_last_id(const char* f_name)
{
Applicant one = {0};
int res = 0;
FILE* f = fopen(f_name, "rb+");
if (f == NULL) return -1;
res = fseek(f, SEEK_END, SEEK_SET);
if (res != 0) return -2;
res = fseek(f, -(long) (sizeof(one)), SEEK_END);
if (res != 0)
{
printf("res = %d\n", res);
perror("Error in seek()");
return -3;
}
printf("(file position: byte #%ld)\n", ftell(f));
if (fread(&one, sizeof(one), 1, f) != 1) return -4;
fclose(f);
return one.id;
}
int create(const char* f_name, unsigned int n_rec)
{
Applicant one = {100};
FILE* f = fopen(f_name, "wb");
if (f == NULL) return -1;
for (unsigned i = 0; i < n_rec; i += 1, one.id+= 1)
if (fwrite(&one, sizeof(one), 1, f) != 1)
return 1 + i;
fclose(f);
return n_rec;
}; // create()
```
[1]: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fseek-fseeki64?view=msvc-160

Can't read USN journal non-stop

My goal is to read write operations from a chosen drive (usually C), using USN journal.
In the next code I've written, I made a small class that processes USN records by using DeviceIoControl
with the FSCTL_QUERY_USN_JOURNAL and FSCTL_ENUM_USN_DATA codes.
#include "stdafx.h"
#include <stdio.h>
#include <assert.h>
#include <vector>
#include <system_error>
#include <Windows.h>
[[noreturn]] void throw_system_error(int error_code) {
throw std::system_error(error_code, std::system_category());
}
class usn_journal {
private:
HANDLE m_drive_handle;
std::vector<uint8_t> m_buffer;
USN_JOURNAL_DATA* m_usn_journal_data;
USN m_next_usn_record_id;
public:
usn_journal(const wchar_t* driver_name) {
m_next_usn_record_id = 0;
m_drive_handle = ::CreateFileW(
driver_name,
GENERIC_READ,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_ALWAYS,
FILE_FLAG_NO_BUFFERING,
nullptr);
if (m_drive_handle == INVALID_HANDLE_VALUE) {
throw_system_error(::GetLastError());
}
m_buffer.resize(1024 * 1024);
}
~usn_journal() {
::CloseHandle(m_drive_handle);
}
void refresh_jounral() {
assert(m_buffer.size() == 1024 * 1024);
DWORD buffer_count = 0;
if (!DeviceIoControl(
m_drive_handle,
FSCTL_QUERY_USN_JOURNAL,
nullptr,
0,
m_buffer.data(),
m_buffer.size(),
&buffer_count,
nullptr)) {
throw_system_error(::GetLastError());
}
m_usn_journal_data =
reinterpret_cast<decltype(m_usn_journal_data)>(m_buffer.data());
}
void process_entries() {
DWORD bytes_read = 0;
MFT_ENUM_DATA_V0 mft_enum_data = {};
mft_enum_data.StartFileReferenceNumber = m_next_usn_record_id;
mft_enum_data.LowUsn = 0;
mft_enum_data.HighUsn = m_usn_journal_data->MaxUsn;
assert(m_buffer.size() == 1024 * 1024);
for (;;){
auto buffer = m_buffer.data();
if (!DeviceIoControl(
m_drive_handle,
FSCTL_ENUM_USN_DATA,
&mft_enum_data,
sizeof(mft_enum_data),
buffer,
m_buffer.size(),
&bytes_read,
nullptr)){
auto error_code = ::GetLastError();
if (error_code == ERROR_HANDLE_EOF) {
return;
}
else {
throw_system_error(::GetLastError());
}
}
m_next_usn_record_id = *reinterpret_cast<USN*>(buffer);
auto buffer_real_begin = buffer + sizeof(USN);
auto usn_cursor = reinterpret_cast<USN_RECORD*>(buffer_real_begin);
int64_t total_usn_buffer_number = bytes_read - sizeof(USN);
while (total_usn_buffer_number >= 0){
total_usn_buffer_number -= usn_cursor->RecordLength;
buffer = reinterpret_cast<uint8_t*>(usn_cursor) + usn_cursor->RecordLength;
usn_cursor = reinterpret_cast<USN_RECORD*>(usn_cursor);
if (usn_cursor->Reason != 0) {
printf("%d\n", (int)usn_cursor->Reason);
}
}
mft_enum_data.StartFileReferenceNumber = m_next_usn_record_id;
}
}
};
int main(int argc, char ** argv){
usn_journal my_journal(L"\\\\?\\c:");
while (true) {
my_journal.refresh_jounral();
my_journal.process_entries();
}
return 0;
}
Here is my problem, after a while, the records are exhausted, and calling DeviceIoControl and FSCTL_ENUM_USN_DATA
DeviceIoControl fails and the error code I get is ERROR_HANDLE_EOF, even if I refresh the journal, I get the same error.
I want to be able to stream any new USN record, and handle write events. I know for sure it's possible as there are
third party tools which present USN records non-stop.
how can reproduce this state of non-stop streaming?

Reading Multiple Files Parallel into a buffer

Am working in a project where i have to read a set of files and put it in a buffer.The List comprises of small as well as large files.I have to read these files and for more efficiency i tried implementing it in multiple threads.Each thread will take a file from vector of file names and start reading it put it into a buffer and these buffer have to be put in a queue.I happened to have some error in program and i i don't know where exactly in my program the error occurs also don't know why ? Please help me whether there is any mistake in my logic or in my code and how to correct it. Thanks in advance
using namespace std;
#define MAX_THREADS 2
#define BUFFER_SIZE 8388608
vector<string>files;
deque<string>bufferq;
CRITICAL_SECTION Readlock;
int count = 0;
DWORD WINAPI ReadThread(LPVOID s);
int main(int argc,char *argv[])
{
HANDLE ReadT[MAX_THREADS];
char *filelist[5];
DWORD threadid;
filelist[0] = "1.txt";
filelist[1] = "cloudy.jpg";
filelist[2] = "connectify.exe";
filelist[3] = "VMware.exe";
filelist[4] = "Sherlock.mp4";
for(int i=0;i<5;i++)
files.push_back(filelist[i]);
InitializeCriticalSection(&Readlock);
long t1 = GetTickCount();
for(int k = 0; k< MAX_THREADS; k++)
ReadT[k] = CreateThread(NULL,0,ReadThread,NULL,NULL,&threadid);
WaitForMultipleObjects(MAX_THREADS,ReadT,TRUE,INFINITE);
cout << " Time Taken "<< GetTickCount()-t1 << "ms" ;
system("pause");
return 0;
}
DWORD WINAPI ReadThread(LPVOID s)
{
long pending = 0;
//int freespace = BUFFER_SIZE;
char *filename = new char[50];
char fsize[10];
string file;
char *buf;
buf = new char[BUFFER_SIZE];
long filesize = 0;
int numfiles = files.size();
int filled = 0;
int i = 0;
FILE *fp;
char* ptr;
ptr = buf;
while(true)
{
EnterCriticalSection(&Readlock);
if(files.empty())
{
LeaveCriticalSection(&Readlock);
break;
}
else
{
file = files.front();
files.erase(files.begin());
LeaveCriticalSection(&Readlock);
}
bool buff_full = false;
buf = ptr;
int freespace = BUFFER_SIZE;
memset(buf,0,BUFFER_SIZE);
if(!buff_full)
{
if(pending == 0)
{
fp = fopen(file.c_str(),"rb");
if(!fp)
{
cout<<"\nNo such file";
cout<<files[i];
system("pause");
return 0;
}
int r1 =fseek(fp, 0L, SEEK_END);
filesize = ftell(fp);
int r2 =fseek(fp, 0L, SEEK_SET);
sprintf(fsize, "%ld", filesize);
if(freespace >= (strlen(fsize) + strlen(file.c_str()) + 2))
{
count++;
memcpy(buf, file.c_str(), strlen(file.c_str())+1);
freespace = freespace - strlen(file.c_str()) - 1;
buf += strlen(file.c_str()) + 1;
memcpy(buf,fsize,strlen(fsize)+1);
buf += strlen(fsize) + 1;
freespace = freespace - strlen(fsize) - 1;
cout<<"Files read is "<<count<<"\n";
if(freespace == 0)
{
buff_full = true;
pending = filesize;
break;
}
}
else
{
filled = BUFFER_SIZE - freespace;
fclose(fp);
break;
}
if(freespace >= filesize)
{
fread(buf, 1, filesize, fp);
buf += filesize;
freespace = freespace - filesize;
bufferq.push_back(buf);
//cout << "pop"<<bufferq.size();
//i++;
if(files.empty())
{
filled = BUFFER_SIZE - freespace;
fclose(fp);
break;
}
fclose(fp);
}
else
{
fread(buf, 1, freespace, fp);
bufferq.push_back(buf);
//cout <<"pop "<<bufferq.size();
buff_full = true;
}
}
else
{
if(freespace >= pending)
{
fread(buf, 1, pending, fp);
bufferq.push_back(buf);
freespace = freespace - pending;
pending = 0;
//i++;
if(files.empty())
{
filled = BUFFER_SIZE - freespace;
fclose(fp);
break;
}
if(freespace > 0)
buf += pending;
else
buff_full = true;
fclose(fp);
}
else
{
fread(buf, 1, freespace, fp);
bufferq.push_back(buf);
cout << bufferq.size();
pending = pending - freespace;
buff_full = true;
}
}
}
if(buff_full)
{
buf = ptr;
cout << "popping buffer " << bufferq.size();
//bufferq.pop_back();
}
}
return 0;
}
In the context that bug occurs on big files, I suppose that this line can cause problems
sprintf(fsize, "%ld", filesize);
fsize is char[10], and if filesize is >= 1,000,000,000 you'll overwrite fsize array with trailing 0. This will cause "Run-Time Check Failure #2 - Stack around the variable 'fsize' was corrupted.", as you wrote. Please check the sizes of your test files.
Among others, you are filling files in loop on i, and then you wrote:
files.erase(files.begin());
// ...
cout<<"\nNo such file";
cout<<files[i];
files[i] already points to another element as you erased them, and if files are empty on the last iteration it will cause crash.
And what for are you copying file and fsize to buf if you do not copy it to the bufferq?
As bufferq is writable and is shared between threads the access to it should be protected by lock, critical section as you chose.
That's my little code review.

GetLogicalDriveStrings() and char - Where am I doing wrongly

I want to search a file which may be present in any drives such as C:\, D:\ etc. Using GetLogicalDriveStrings I can able to get the list of drives but when I add anything extra for the output, I am getting a null in the output prompt. Here is my code:
#include "StdAfx.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>
// Buffer length
DWORD mydrives = 100;
// Buffer for drive string storage
char lpBuffer[100];
const char *extFile = "text.ext";
// You may want to try the wmain() version
int main(void)
{
DWORD test;
int i;
test = GetLogicalDriveStrings(mydrives, (LPWSTR)lpBuffer);
if(test != 0)
{
printf("GetLogicalDriveStrings() return value: %d, Error (if any): %d \n", test, GetLastError());
printf("The logical drives of this machine are:\n");
// Check up to 100 drives...
for(i = 0; i<100; i++)
printf("%c%s", lpBuffer[i],extFile);
printf("\n");
}
else
printf("GetLogicalDriveStrings() is failed lor!!! Error code: %d\n", GetLastError());
_getch();
return 0;
}
I want above output as C:\text.ext D:\text.ext ... rather I am getting text.ext only. I am using Microsoft Visual C++ 2010 Express
GetLogicalDriveStrings() returns a double-null terminated list of null-terminated strings. E.g., say you had drives A, B and C in your machine. The returned string would look like this:
A:\<nul>B:\<nul>C:\<nul><nul>
You can use the following code to iterate through the strings in the returned buffer and print each one in turn:
DWORD dwSize = MAX_PATH;
char szLogicalDrives[MAX_PATH] = {0};
DWORD dwResult = GetLogicalDriveStrings(dwSize,szLogicalDrives);
if (dwResult > 0 && dwResult <= MAX_PATH)
{
char* szSingleDrive = szLogicalDrives;
while(*szSingleDrive)
{
printf("Drive: %s\n", szSingleDrive);
// get the next drive
szSingleDrive += strlen(szSingleDrive) + 1;
}
}
Note that the details of how the function works, including the example code that I shamelessly copied and pasted, can be found by reading the docs.
Did you mean to put the printf in the loop?
Currently, you set extFile 100 times (just to be sure?!)
for(i = 0; i<100; i++)
extFile = "text.ext";
You meant to show all the drive letters in a loop:
for(i = 0; i<100; i++)
{
extFile = "text.ext";
printf("%c%s", lpBuffer[i], extFile); //I guess you mean extFile here?
}
DWORD dwSize = MAX_PATH;
WCHAR szLogicalDrives[MAX_PATH] = { 0 };
DWORD dwResult = GetLogicalDriveStrings(dwSize, szLogicalDrives);
CStringArray m_Drives;
m_Drives.RemoveAll();
if (dwResult > 0 && dwResult <= MAX_PATH)
{
WCHAR* szSingleDrive = szLogicalDrives;
while (*szSingleDrive)
{
UINT nDriveType = GetDriveType(szSingleDrive);
m_Drives.Add(CString(szSingleDrive, 2));
// get the next drive
szSingleDrive += wcslen(szSingleDrive) + 1;
}
}
return m_Drives;
class DriveList {
protected:
LPTSTR m_driveList;
DWORD m_driveCount;
DWORD m_bufSize = 32 * sizeof(TCHAR);
public:
virtual ~DriveList() {
free(m_driveList);
}
DriveList() {
m_driveList = (LPTSTR)malloc(m_bufSize);
}
int getDriveCount() const {
return m_driveCount;
}
TCHAR operator[] (const int index) const {
return m_driveList[index];
}
void loadDriveList() {
DWORD mask;
if((mask = GetLogicalDrives()) == 0) {
throw;
}
m_driveCount = 0;
for(int x = 0; x <= 25; x++ ) {
if(mask & 1) {
m_driveList[m_driveCount] = TCHAR(65 + x);
m_driveCount += 1;
}
mask >>= 1;
}
}
};

Read multiple .dat files in C++

I used the code below to read one .dat file and find the execution time, it worked very well. I tried to build a loop to read multiple files as I have more than 20 files with different names (I need to keep their names), but it did not work. How can I develop this code to read all files located in a certain folder no matter how many they are? (based on following code)
#include <Windows.h>
#include <ctime>
#include <stdint.h>
#include <iostream>
using std::cout;
using std::endl;
#include <fstream>
using std::ifstream;
#include <cstring>
/* Returns the amount of milliseconds elapsed since the UNIX epoch. Works on both
* windows and linux. */
uint64_t GetTimeMs64()
{
FILETIME ft;
LARGE_INTEGER li;
/* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
* to a LARGE_INTEGER structure. */
GetSystemTimeAsFileTime(&ft);
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
uint64_t ret;
ret = li.QuadPart;
ret -= 116444736000000000LL; /* Convert from file time to UNIX epoch time. */
ret /= 10000; /* From 100 nano seconds (10^-7) to 1 millisecond (10^-3) intervals */
return ret;
}
const int MAX_CHARS_PER_LINE = 512;
const int MAX_TOKENS_PER_LINE = 20;
const char* const DELIMITER = "|";
int main()
{
// create a file-reading object
ifstream fin;
fin.open("promotion.txt"); // open a file
if (!fin.good())
return 1; // exit if file not found
// read each line of the file
while (!fin.eof())
{
// read an entire line into memory
char buf[MAX_CHARS_PER_LINE];
fin.getline(buf, MAX_CHARS_PER_LINE);
// parse the line into blank-delimited tokens
int n = 0; // a for-loop index
// array to store memory addresses of the tokens in buf
const char* token[MAX_TOKENS_PER_LINE] = {}; // initialize to 0
// parse the line
token[0] = strtok(buf, DELIMITER); // first token
if (token[0]) // zero if line is blank
{
for (n = 1; n < MAX_TOKENS_PER_LINE; n++)
{
token[n] = strtok(0, DELIMITER); // subsequent tokens
if (!token[n]) break; // no more tokens
}
}
// process (print) the tokens
for (int i = 0; i < n; i++) // n = #of tokens
cout << "Token[" << i << "] = " << token[i] << endl;
cout << endl;
}
uint64_t z = GetTimeMs64();
cout << z << endl;
system("pause");
}
For listing files in a directory on Windows, refer to this link:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365200(v=vs.85).aspx
Notes about your code:
don't use fin.eof() to test the end of input, see why: eof of istream in C++
to read multiple files, remember fin.clear() before fin.close if you use the same fin to read multiple files.
UPDATE:
The following code prints out the files name in a directory D:\\Test. If you need absolute path for every file or files in subfolders, change GetFiles to do that. This is pretty straightforward according to the link I provided. The code is test on VS2012 Win7 Pro.
#include <windows.h>
#include <Shlwapi.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
#pragma comment(lib, "Shlwapi.lib")
int GetFiles(const string &path, vector<string> &files, const string &wildcard = "\\*")
{
wstring basepath(path.begin(), path.end());
wstring wpath = basepath + wstring(wildcard.begin(), wildcard.end());
WIN32_FIND_DATA ffd;
HANDLE hFind = INVALID_HANDLE_VALUE;
DWORD dwError = 0;
hFind = FindFirstFile(wpath.c_str(), &ffd);
if (INVALID_HANDLE_VALUE == hFind) {
// display error messages
return dwError;
}
TCHAR buf[MAX_PATH];
do {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
// directory
} else {
PathCombine(buf, basepath.c_str(), ffd.cFileName);
wstring tmp(buf);
files.push_back(string(tmp.begin(), tmp.end()));
}
} while (FindNextFile(hFind, &ffd));
dwError = GetLastError();
if (ERROR_NO_MORE_FILES != dwError) {
// some errors
}
FindClose(hFind);
return dwError;
}
int main()
{
string path("D:\\Documents\\Visual Studio 2012\\Projects\\SigSpatial2013");
vector<string> files;
GetFiles(path, files);
string line;
ifstream fin;
for (int i = 0; i < files.size(); ++i) {
cout << files[i] << endl;
fin.open(files[i].c_str());
if (!fin.is_open()) {
// error occurs!!
// break or exit according to your needs
}
while (getline(fin, line)) {
// now process every line
}
fin.clear();
fin.close();
}
}
I think it's easier:
1- if you factor out the code that reads a file and process its content into its own function: void process_file( char* filename );
2- add a new function to list a directory's content: char** list_dir( char* dir );
3- combine the 2 functions in your main()
this makes for cleaner and more testable code
I agree with the suggestions to encapsulate this.
On Windows the code looks like this
HANDLE h;
WIN32_FIND_DATA find_data;
h = FindFirstFile( "*.dat", & find_data );
if( h == INVALID_HANDLE_VALUE ) {
// Error
return;
}
do {
char * s = find_data.cFileName;
// Your code here
} while( FindNextFile( h, & find_data ) );
FindClose( h );