This commit is contained in:
tongjunhui 2016-07-05 18:03:30 +08:00
Родитель dd2502c17c
Коммит d0f3914ac1
23 изменённых файлов: 1402 добавлений и 1 удалений

63
.gitattributes поставляемый Normal file
Просмотреть файл

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

38
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,38 @@
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
out/
# Visual Studio temp files
*.vcxproj.user
*.sdf
*.suo
*.opensdf
*.opendb
*.VC.db

22
LICENSE Normal file
Просмотреть файл

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) Microsoft Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Просмотреть файл

@ -1,2 +1,46 @@
# compoundfilereader
A simple header file to read Microsoft compound file(Structured Storage File) with minimal efforts.
simple standalone c++ header file to read compound file (Structured Storage File) content.
# Source code structure
- **src/include/compoundfilereader.h**
The only header file needed for parsing compound file.
- **src/include/utf.h**
The helper header file used for converting between utf16, utf8, and unicode. It's used by samples.
- **test/data**
Real world compound files for tests.
- **samples/cfb**
command line tool to list and dump compound files.
- **samples/IEOpenedTabParser**
command line tool to show IE opened tab information.
- **vsproject**
project and solution files for Microsoft Visual Studio.
# Usage
- copy compoundfilereader.h to your source tree
or: install "compoundfilereader" by git sub-module
- #include <compoundfilereader.h> in your source code
- construct a **CompoundFileReader** object by giving the buffer (see compoundfilereader.h for details)
# Build the samples
## Linux
run `make'
(requires gcc and g++)
## Windows
option1: double click 'vsproject\cfbreader\cfbreader.sln' then build in Visual Studio
option2: run 'build.bat' in Visual Studio Command Prompt
(requires visual studio)
## Run the samples
try the following:
``` batchfile
out/ieot "test/data/{BC59C035-E8AC-11E4-825B-10604B7CB9F0}.dat"
out/cfb list "test/data/a test email message.msg"
out/cfb dump "test/data/a test email message.msg" __properties_version1.0
```
# TODO
- unit tests
- make the reader able to connect to abstract interfaces such as istream
#Microsoft Open Source Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

4
build.bat Normal file
Просмотреть файл

@ -0,0 +1,4 @@
:: this script can only be run under visual studio tools command line
:: (search "Command Prompt" on start menu to pick one)
devenv.com vsproject\cfbreader\cfbreader.sln /build Debug

21
makefile Normal file
Просмотреть файл

@ -0,0 +1,21 @@
IDIR=src/include
CFBHDR=$(wildcard $(IDIR)/*.h)
CC=g++
CFLAGS=-I$(IDIR) -std=c++11 -Wall
ODIR=out
all: out/ieot out/cfb
out/ieot: samples/IEOpenedTabParser/openedtab.cpp samples/IEOpenedTabParser/IEOpenedTabParser.h $(CFBHDR)
mkdir -p out
$(CC) -o $@ $< $(CFLAGS)
out/cfb: samples/cfb/cfb.cpp $(CFBHDR)
mkdir -p out
$(CC) -o $@ $< $(CFLAGS)
.PHONY: clean
clean:
rm -rf out

Просмотреть файл

@ -0,0 +1,59 @@
/**
IE opened tab dat file parser
opened tab files are in %localappdata%\Microsoft\Internet Explorer\TabRoaming\{GUID}\NNNN, (N is digit 0-9).
They are compound file.
*/
#include <compoundfilereader.h>
#include <utf.h>
#include <string>
#include <vector>
struct OPENED_TAB_INFO
{
std::wstring url;
std::wstring title;
std::wstring faviconUrl;
};
struct OpenedTabFileParser
{
static const CFB::COMPOUND_FILE_ENTRY* GetOpenedTabPropertyStream(CFB::CompoundFileReader& reader)
{
const CFB::COMPOUND_FILE_ENTRY* entry = nullptr;
reader.EnumFiles(reader.GetRootEntry(), -1,
[&](const CFB::COMPOUND_FILE_ENTRY* e, const CFB::utf16string&, int)->void
{
if (reader.IsPropertyStream(e))
{
entry = e;
}
});
return entry;
}
static bool GetOpenedTabInfo(const void* buffer, size_t bufferLen, OPENED_TAB_INFO* info)
{
CFB::CompoundFileReader reader(buffer, bufferLen);
const CFB::COMPOUND_FILE_ENTRY* propertyFile = GetOpenedTabPropertyStream(reader);
bool ret = false;
if (propertyFile != nullptr && propertyFile->size <= std::numeric_limits<size_t>::max())
{
size_t size = static_cast<size_t>(propertyFile->size);
char* data = new char[size];
reader.ReadFile(propertyFile, 0, data, size);
CFB::PropertySetStream propertySetStream(data, size);
CFB::PropertySet propertySet = propertySetStream.GetPropertySet(0);
info->url = UTF16ToWstring(propertySet.GetStringProperty(3));
info->title = UTF16ToWstring(propertySet.GetStringProperty(5));
info->faviconUrl = UTF16ToWstring(propertySet.GetStringProperty(1002));
ret = true;
delete [] data;
}
return ret;
}
};

Просмотреть файл

@ -0,0 +1,52 @@
#include "IEOpenedTabParser.h"
#include <utf.h>
#include <cstdio>
#include <cstdlib>
#include <clocale>
#include <cstring>
#include <algorithm>
#include <stdint.h>
#include <wchar.h>
#include <string>
#include <memory>
using namespace std;
int main(int argc, char* argv[])
{
if (argc != 2)
{
printf("please give a path\n");
return 1;
}
FILE* fp = fopen(argv[1], "rb");
if (fp == NULL)
{
perror("read file error\n");
return 1;
}
fseek(fp, 0, SEEK_END);
size_t len = ftell(fp);
unsigned char* buffer = new unsigned char[len];
fseek(fp, 0, SEEK_SET);
len = fread(buffer, 1, len, fp);
printf("file length: %lu\n", len);
OPENED_TAB_INFO info;
if (!OpenedTabFileParser::GetOpenedTabInfo(buffer, len, &info))
{
printf("cannot find the property set\n");
return 1;
}
printf("url: %s\ntitle: %s\nfavicon url: %s\n",
WstringToUTF8(info.url.c_str()).c_str(),
WstringToUTF8(info.title.c_str()).c_str(),
WstringToUTF8(info.faviconUrl.c_str()).c_str());
return 0;
}

217
samples/cfb/cfb.cpp Normal file
Просмотреть файл

@ -0,0 +1,217 @@
#include <compoundfilereader.h>
#include <utf.h>
#include <string.h>
#include <stdio.h>
#include <memory>
#include <iostream>
#include <iomanip>
using namespace std;
void ShowUsage()
{
cout <<
"usage:\n"
"cfb list FILENAME\n"
"cfb dump FILENAME STREAM_PATH\n"
"cfb info FILENAME\n"
"cfb info FILENAME STREAM_PATH\n"
<< endl;
}
void DumpBuffer(const void* buffer, size_t len)
{
const unsigned char* str = static_cast<const unsigned char*>(buffer);
for (size_t i = 0; i < len; i++)
{
if (i > 0 && i % 16 == 0)
cout << endl;
cout << setw(2) << setfill('0') << hex << static_cast<int>(str[i]) << ' ';
}
cout << endl;
}
void DumpText(const char* buffer, size_t len)
{
cout << std::string(buffer, len) << endl;
}
void OutputFileInfo(const CFB::CompoundFileReader& reader)
{
const CFB::COMPOUND_FILE_HDR* hdr = reader.GetFileInfo();
cout
<< "file version: " << hdr->majorVersion << "." << hdr->minorVersion << endl
<< "difat sector: " << hdr->numDIFATSector << endl
<< "directory sector: " << hdr->numDirectorySector << endl
<< "fat sector: " << hdr->numFATSector << endl
<< "mini fat sector: " << hdr->numMiniFATSector << endl;
}
void OutputEntryInfo(const CFB::CompoundFileReader& reader, const CFB::COMPOUND_FILE_ENTRY* entry)
{
cout
<< "entry type: " << (reader.IsPropertyStream(entry) ? "property" : (reader.IsStream(entry) ? "stream" : "directory")) << endl
<< "color flag: " << entry->colorFlag << endl
<< "creation time: " << entry->creationTime << endl
<< "modified time: " << entry->modifiedTime << endl
<< "child ID: " << entry->childID << endl
<< "left sibling ID: " << entry->leftSiblingID << endl
<< "right sibling ID: " << entry->startSectorLocation << entry->rightSiblingID << endl
<< "start sector: " << entry->startSectorLocation << endl
<< "size: " << entry->size << endl;
}
const void ListDirectory(const CFB::CompoundFileReader& reader)
{
reader.EnumFiles(reader.GetRootEntry(), -1,
[&](const CFB::COMPOUND_FILE_ENTRY* entry, const CFB::utf16string& dir, int level)->void
{
bool isDirectory = !reader.IsStream(entry);
std::string name = UTF16ToUTF8(entry->name);
std::string indentstr(level * 4 - 4, ' ');
cout << indentstr.c_str() << (isDirectory ? "[" : "") << name.c_str() << (isDirectory ? "]" : "") << endl;
});
}
const CFB::COMPOUND_FILE_ENTRY* FindStream(const CFB::CompoundFileReader& reader, const char* streamName)
{
const CFB::COMPOUND_FILE_ENTRY* ret = nullptr;
reader.EnumFiles(reader.GetRootEntry(), -1,
[&](const CFB::COMPOUND_FILE_ENTRY* entry, const CFB::utf16string& u16dir, int level)->void
{
if (reader.IsStream(entry))
{
std::string name = UTF16ToUTF8(entry->name);
if (u16dir.length() > 0)
{
std::string dir = UTF16ToUTF8(u16dir.c_str());
if (strncmp(streamName, dir.c_str(), dir.length()) == 0 &&
streamName[dir.length()] == '\\' &&
strcmp(streamName + dir.length() + 1, name.c_str()) == 0)
{
ret = entry;
}
}
else
{
if (strcmp(streamName, name.c_str()) == 0)
{
ret = entry;
}
}
}
});
return ret;
}
int main_internal(int argc, char* argv[])
{
const char* cmd = nullptr;
const char* file = nullptr;
const char* streamName = nullptr;
bool dumpraw = false;
for (int i = 1; i < argc; i++)
{
if (i == 1)
{
cmd = argv[i];
}
else if (strcmp(argv[i], "-r") == 0)
{
dumpraw = true;
}
else
{
if (file == nullptr)
file = argv[i];
else
streamName = argv[i];
}
}
if (cmd == nullptr || file == nullptr)
{
ShowUsage();
return 1;
}
FILE* fp = fopen(file, "rb");
if (fp == NULL)
{
cerr << "read file error" << endl;
return 1;
}
fseek(fp, 0, SEEK_END);
size_t len = ftell(fp);
std::unique_ptr<unsigned char> buffer(new unsigned char[len]);
fseek(fp, 0, SEEK_SET);
len = fread(buffer.get(), 1, len, fp);
CFB::CompoundFileReader reader(buffer.get(), len);
if (strcmp(cmd, "list") == 0)
{
ListDirectory(reader);
}
else if (strcmp(cmd, "dump") == 0 && streamName != nullptr)
{
const CFB::COMPOUND_FILE_ENTRY* entry = FindStream(reader, streamName);
if (entry == nullptr)
{
cerr << "error: stream doesn't exist" << endl;
return 2;
}
cout << "size: " << entry->size << endl;
if (entry->size > std::numeric_limits<size_t>::max())
{
cerr << "error: stream too large" << endl;
return 2;
}
size_t size = static_cast<size_t>(entry->size);
std::unique_ptr<char> content(new char[size]);
reader.ReadFile(entry, 0, content.get(), size);
if (dumpraw)
DumpText(content.get(), size);
else
DumpBuffer(content.get(), size);
}
else if (strcmp(cmd, "info") == 0)
{
if (streamName == nullptr)
{
OutputFileInfo(reader);
}
else
{
const CFB::COMPOUND_FILE_ENTRY* entry = FindStream(reader, streamName);
if (entry == NULL)
{
cerr << "error: stream doesn't exist" << endl;
return 2;
}
OutputEntryInfo(reader, entry);
}
}
else
{
ShowUsage();
return 1;
}
return 0;
}
int main(int argc, char* argv[])
{
try
{
return main_internal(argc, argv);
}
catch (CFB::CFBException& e)
{
cerr << "error: " << e.what() << endl;
return 2;
}
}

Просмотреть файл

@ -0,0 +1,480 @@
/**
Microsoft Compound File (and Property Set) Reader
http://en.wikipedia.org/wiki/Compound_File_Binary_Format
Format specification:
MS-CFB: https://msdn.microsoft.com/en-us/library/dd942138.aspx
MS-OLEPS: https://msdn.microsoft.com/en-us/library/dd942421.aspx
Note:
1. For simplification, the code assumes that the target system is little-endian.
2. The reader operates the passed buffer in-place.
You must keep the input buffer valid when you are using the reader.
3. Single thread usage.
Example 1: print all streams in a compound file
\code
CFB::CompoundFileReader reader(buffer, len);
reader.EnumFiles(reader.GetRootEntry(), -1,
[&](const CFB::COMPOUND_FILE_ENTRY* entry, const CFB::utf16string& dir, int level)->void
{
bool isDirectory = !reader.IsStream(entry);
std::string name = UTF16ToUTF8(entry->name);
std::string indentstr(level * 4, ' ');
printf("%s%s%s%s\n", indentstr.c_str(), isDirectory ? "[" : "", name.c_str(), isDirectory ? "]" : "");
});
\endcode
*/
#include <algorithm>
#include <stdint.h>
#include <string.h>
#include <exception>
#include <stdexcept>
#include <functional>
namespace CFB
{
struct CFBException : public std::runtime_error
{
CFBException(const char* desc) : std::runtime_error(desc) {}
};
struct WrongFormat : public CFBException
{
WrongFormat() : CFBException("Wrong file format") {}
};
struct FileCorrupted : public CFBException
{
FileCorrupted() : CFBException("File corrupted") {}
};
#pragma pack(push)
#pragma pack(1)
struct COMPOUND_FILE_HDR
{
unsigned char signature[8];
unsigned char unused_clsid[16];
uint16_t minorVersion;
uint16_t majorVersion;
uint16_t byteOrder;
uint16_t sectorShift;
uint16_t miniSectorShift;
unsigned char reserved[6];
uint32_t numDirectorySector;
uint32_t numFATSector;
uint32_t firstDirectorySectorLocation;
uint32_t transactionSignatureNumber;
uint32_t miniStreamCutoffSize;
uint32_t firstMiniFATSectorLocation;
uint32_t numMiniFATSector;
uint32_t firstDIFATSectorLocation;
uint32_t numDIFATSector;
uint32_t headerDIFAT[109];
};
struct COMPOUND_FILE_ENTRY
{
uint16_t name[32];
uint16_t nameLen;
uint8_t type;
uint8_t colorFlag;
uint32_t leftSiblingID; // Note that it's actually the left/right child in the RB-tree.
uint32_t rightSiblingID; // So entry.leftSibling.rightSibling does NOT go back to entry.
uint32_t childID;
unsigned char clsid[16];
uint32_t stateBits;
uint64_t creationTime;
uint64_t modifiedTime;
uint32_t startSectorLocation;
uint64_t size;
};
struct PROPERTY_SET_STREAM_HDR
{
unsigned char byteOrder[2];
uint16_t version;
uint32_t systemIdentifier;
unsigned char clsid[16];
uint32_t numPropertySets;
struct
{
char fmtid[16];
uint32_t offset;
} propertySetInfo[1];
};
struct PROPERTY_SET_HDR
{
uint32_t size;
uint32_t NumProperties;
struct
{
uint32_t id;
uint32_t offset;
} propertyIdentifierAndOffset[1];
};
#pragma pack(pop)
const size_t MAXREGSECT = 0xFFFFFFFA;
struct helper
{
static uint32_t ParseUint32(const void* buffer)
{
return *static_cast<const uint32_t*>(buffer);
}
};
typedef std::basic_string<uint16_t> utf16string;
typedef std::function<void(const COMPOUND_FILE_ENTRY*, const utf16string& dir, int level)>
EnumFilesCallback;
class CompoundFileReader
{
public:
CompoundFileReader(const void* buffer, size_t len)
: m_buffer(static_cast<const unsigned char*>(buffer))
, m_bufferLen(len)
, m_hdr(static_cast<const COMPOUND_FILE_HDR*>(buffer))
, m_sectorSize(512)
, m_minisectorSize(64)
, m_miniStreamStartSector(0)
{
if (buffer == NULL || len == 0) throw std::invalid_argument("");
if (m_bufferLen < sizeof(*m_hdr) ||
memcmp(m_hdr->signature, "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1", 8) != 0)
{
throw WrongFormat();
}
m_sectorSize = m_hdr->majorVersion == 3 ? 512 : 4096;
// The file must contains at least 3 sectors
if (m_bufferLen < m_sectorSize * 3) throw FileCorrupted();
const COMPOUND_FILE_ENTRY* root = GetEntry(0);
if (root == NULL) throw FileCorrupted();
m_miniStreamStartSector = root->startSectorLocation;
}
/// Get entry (directory or file) by its ID.
/// Pass "0" to get the root directory entry. -- This is the start point to navigate the compound file.
/// Use the returned object to access child entries.
const COMPOUND_FILE_ENTRY* GetEntry(size_t entryID) const
{
if (entryID == 0xFFFFFFFF)
{
return NULL;
}
if (m_bufferLen / sizeof(COMPOUND_FILE_ENTRY) <= entryID)
{
throw std::invalid_argument("");
}
size_t sector = 0;
size_t offset = 0;
LocateFinalSector(m_hdr->firstDirectorySectorLocation, entryID * sizeof(COMPOUND_FILE_ENTRY), &sector, &offset);
return reinterpret_cast<const COMPOUND_FILE_ENTRY*>(SectorOffsetToAddress(sector, offset));
}
const COMPOUND_FILE_ENTRY* GetRootEntry() const
{
return GetEntry(0);
}
const COMPOUND_FILE_HDR* GetFileInfo() const
{
return m_hdr;
}
/// Get file(stream) data start with "offset".
/// The buffer must have enough space to store "len" bytes. Typically "len" is derived by the steam length.
void ReadFile(const COMPOUND_FILE_ENTRY* entry, size_t offset, char* buffer, size_t len) const
{
if (entry->size < offset || entry->size - offset < len) throw std::invalid_argument("");
if (entry->size < m_hdr->miniStreamCutoffSize)
{
ReadMiniStream(entry->startSectorLocation, offset, buffer, len);
}
else
{
ReadStream(entry->startSectorLocation, offset, buffer, len);
}
}
bool IsPropertyStream(const COMPOUND_FILE_ENTRY* entry) const
{
// defined in [MS-OLEPS] 2.23 "Property Set Stream and Storage Names"
return entry->name[0] == 5;
}
bool IsStream(const COMPOUND_FILE_ENTRY* entry) const
{
return entry->type == 2;
}
void EnumFiles(const COMPOUND_FILE_ENTRY* entry, int maxLevel, EnumFilesCallback callback) const
{
utf16string dir;
EnumNodes(GetEntry(entry->childID), 0, maxLevel, dir, callback);
}
private:
// Enum entries with same level, including 'entry' itself
void EnumNodes(const COMPOUND_FILE_ENTRY* entry, int currentLevel, int maxLevel,
const utf16string& dir, EnumFilesCallback callback) const
{
if (maxLevel > 0 && currentLevel >= maxLevel)
return;
if (entry == nullptr)
return;
callback(entry, dir, currentLevel + 1);
const COMPOUND_FILE_ENTRY* child = GetEntry(entry->childID);
if (child != nullptr)
{
utf16string newDir = dir;
if (dir.length() != 0)
newDir.append(1, '\n');
newDir.append(entry->name, entry->nameLen / 2);
EnumNodes(GetEntry(entry->childID), currentLevel + 1, maxLevel, newDir, callback);
}
EnumNodes(GetEntry(entry->leftSiblingID), currentLevel, maxLevel, dir, callback);
EnumNodes(GetEntry(entry->rightSiblingID), currentLevel, maxLevel, dir, callback);
}
void ReadStream(size_t sector, size_t offset, char* buffer, size_t len) const
{
LocateFinalSector(sector, offset, &sector, &offset);
// copy as many as possible in each step
// copylen typically iterate as: m_sectorSize - offset --> m_sectorSize --> m_sectorSize --> ... --> remaining
while (len > 0)
{
const unsigned char* src = SectorOffsetToAddress(sector, offset);
size_t copylen = std::min(len, m_sectorSize - offset);
if (m_buffer + m_bufferLen < src + copylen) throw FileCorrupted();
memcpy(buffer, src, copylen);
buffer += copylen;
len -= copylen;
sector = GetNextSector(sector);
offset = 0;
}
}
// Same logic as "ReadStream" except that use MiniStream functions instead
void ReadMiniStream(size_t sector, size_t offset, char* buffer, size_t len) const
{
LocateFinalMiniSector(sector, offset, &sector, &offset);
// copy as many as possible in each step
// copylen typically iterate as: m_sectorSize - offset --> m_sectorSize --> m_sectorSize --> ... --> remaining
while (len > 0)
{
const unsigned char* src = MiniSectorOffsetToAddress(sector, offset);
size_t copylen = std::min(len, m_minisectorSize - offset);
if (m_buffer + m_bufferLen < src + copylen) throw FileCorrupted();
memcpy(buffer, src, copylen);
buffer += copylen;
len -= copylen;
sector = GetNextMiniSector(sector);
offset = 0;
}
}
size_t GetNextSector(size_t sector) const
{
// lookup FAT
size_t entriesPerSector = m_sectorSize / 4;
size_t fatSectorNumber = sector / entriesPerSector;
size_t fatSectorLocation = GetFATSectorLocation(fatSectorNumber);
return helper::ParseUint32(SectorOffsetToAddress(fatSectorLocation, sector % entriesPerSector * 4));
}
size_t GetNextMiniSector(size_t miniSector) const
{
size_t sector, offset;
LocateFinalSector(m_hdr->firstMiniFATSectorLocation, miniSector * 4, &sector, &offset);
return helper::ParseUint32(SectorOffsetToAddress(sector, offset));
}
// Get absolute address from sector and offset.
const unsigned char* SectorOffsetToAddress(size_t sector, size_t offset) const
{
if (sector >= MAXREGSECT ||
offset >= m_sectorSize ||
m_bufferLen <= static_cast<uint64_t>(m_sectorSize) * sector + m_sectorSize + offset)
{
throw FileCorrupted();
}
return m_buffer + m_sectorSize + m_sectorSize * sector + offset;
}
const unsigned char* MiniSectorOffsetToAddress(size_t sector, size_t offset) const
{
if (sector >= MAXREGSECT ||
offset >= m_minisectorSize ||
m_bufferLen <= static_cast<uint64_t>(m_minisectorSize) * sector + offset)
{
throw FileCorrupted();
}
LocateFinalSector(m_miniStreamStartSector, sector * m_minisectorSize + offset, &sector, &offset);
return SectorOffsetToAddress(sector, offset);
}
// Locate the final sector/offset when original offset expands multiple sectors
void LocateFinalSector(size_t sector, size_t offset, size_t* finalSector, size_t* finalOffset) const
{
while (offset >= m_sectorSize)
{
offset -= m_sectorSize;
sector = GetNextSector(sector);
}
*finalSector = sector;
*finalOffset = offset;
}
void LocateFinalMiniSector(size_t sector, size_t offset, size_t* finalSector, size_t* finalOffset) const
{
while (offset >= m_minisectorSize)
{
offset -= m_minisectorSize;
sector = GetNextMiniSector(sector);
}
*finalSector = sector;
*finalOffset = offset;
}
size_t GetFATSectorLocation(size_t fatSectorNumber) const
{
if (fatSectorNumber < 109)
{
return m_hdr->headerDIFAT[fatSectorNumber];
}
else
{
fatSectorNumber -= 109;
size_t entriesPerSector = m_sectorSize / 4 - 1;
size_t difatSectorLocation = m_hdr->firstDIFATSectorLocation;
while (fatSectorNumber >= entriesPerSector)
{
fatSectorNumber -= entriesPerSector;
const unsigned char* addr = SectorOffsetToAddress(difatSectorLocation, m_sectorSize - 4);
difatSectorLocation = helper::ParseUint32(addr);
}
return helper::ParseUint32(SectorOffsetToAddress(difatSectorLocation, fatSectorNumber * 4));
}
}
private:
const unsigned char * m_buffer;
size_t m_bufferLen;
const COMPOUND_FILE_HDR* m_hdr;
size_t m_sectorSize;
size_t m_minisectorSize;
size_t m_miniStreamStartSector;
};
class PropertySet
{
public:
PropertySet(const void* buffer, size_t len, const char* fmtid)
: m_buffer(static_cast<const unsigned char*>(buffer))
, m_bufferLen(len)
, m_hdr(reinterpret_cast<const PROPERTY_SET_HDR*>(buffer))
, m_fmtid(fmtid)
{
if (m_bufferLen < sizeof(*m_hdr) ||
m_bufferLen < sizeof(*m_hdr) + (m_hdr->NumProperties - 1) * sizeof(m_hdr->propertyIdentifierAndOffset[0]))
{
throw FileCorrupted();
}
}
/// return the string property in UTF-16 format
const uint16_t* GetStringProperty(uint32_t propertyID)
{
for (uint32_t i = 0; i < m_hdr->NumProperties; i++)
{
if (m_hdr->propertyIdentifierAndOffset[i].id == propertyID)
{
uint32_t offset = m_hdr->propertyIdentifierAndOffset[i].offset;
if (m_bufferLen < offset + 8) throw FileCorrupted();
uint32_t stringLengthInChar = helper::ParseUint32(m_buffer + offset + 4);
if (m_bufferLen < offset + 8 + stringLengthInChar*2) throw FileCorrupted();
return reinterpret_cast<const uint16_t*>(m_buffer + offset + 8);
}
}
return NULL;
}
/// Note: Getting property of types other than "string" is not implemented yet.
/// However most other types are simpler than string so can be easily added. see [MS-OLEPS]
const char* GetFmtID()
{
return m_fmtid;
}
private:
const unsigned char* m_buffer;
size_t m_bufferLen;
const PROPERTY_SET_HDR* m_hdr;
const char* m_fmtid; // 16 bytes
};
class PropertySetStream
{
public:
PropertySetStream(const void* buffer, size_t len)
: m_buffer(static_cast<const unsigned char*>(buffer))
, m_bufferLen(len)
, m_hdr(reinterpret_cast<const PROPERTY_SET_STREAM_HDR*>(buffer))
{
if (m_bufferLen < sizeof(*m_hdr) ||
m_bufferLen < sizeof(*m_hdr) + (m_hdr->numPropertySets - 1) * sizeof(m_hdr->propertySetInfo[0]))
{
throw FileCorrupted();
}
}
size_t GetPropertySetCount()
{
return m_hdr->numPropertySets;
}
PropertySet GetPropertySet(size_t index)
{
if (index >= GetPropertySetCount()) throw FileCorrupted();
uint32_t offset = m_hdr->propertySetInfo[index].offset;
if (m_bufferLen < offset + 4) throw FileCorrupted();
uint32_t size = helper::ParseUint32(m_buffer + offset);
if (m_bufferLen < offset + size) throw FileCorrupted();
return PropertySet(m_buffer + offset, size, m_hdr->propertySetInfo[index].fmtid);
}
private:
const unsigned char * m_buffer;
size_t m_bufferLen;
const PROPERTY_SET_STREAM_HDR* m_hdr;
};
}

137
src/include/utf.h Normal file
Просмотреть файл

@ -0,0 +1,137 @@
#pragma once
#include <stdint.h>
#include <string>
template <typename T>
static bool GetNextCodePointFromUTF16z(const T* u16, size_t* pos, uint32_t* cp)
{
*cp = static_cast<uint32_t>(u16[*pos]);
if (*cp == 0)
return false;
(*pos)++;
if ((*cp & 0xFC00) == 0xD800)
{
uint16_t cp2 = static_cast<uint16_t>(u16[*pos]);
if ((cp2 & 0xFC00) == 0xDC00)
{
(*pos)++;
*cp = (*cp << 10) + cp2 - 0x35FDC00;
}
}
return true;
}
template <typename T>
static bool GetNextCodePointFromUTF16(const T* u16, size_t len, size_t* pos, uint32_t* cp)
{
if (len == 0)
return GetNextCodePointFromUTF16z(u16, pos, cp);
if (*pos >= len)
return false;
*cp = static_cast<uint32_t>(u16[*pos]);
(*pos)++;
if ((*cp & 0xFC00) == 0xD800)
{
if (*pos < len)
{
uint16_t cp2 = static_cast<uint16_t>(u16[*pos]);
if ((cp2 & 0xFC00) == 0xDC00)
{
(*pos)++;
*cp = (*cp << 10) + cp2 - 0x35FDC00;
}
}
}
return true;
}
static int CodePointToUTF8(uint32_t cp, uint32_t* c1, uint32_t* c2, uint32_t* c3, uint32_t* c4)
{
if (cp < 0x80)
{
*c1 = cp;
return 1;
}
else if (cp <= 0x7FF)
{
*c1 = (cp >> 6) + 0xC0;
*c2 = (cp & 0x3F) + 0x80;
return 2;
}
else if (cp <= 0xFFFF)
{
*c1 = (cp >> 12) + 0xE0;
*c2 = ((cp >> 6) & 0x3F) + 0x80;
*c3 = (cp & 0x3F) + 0x80;
return 3;
}
else if (cp <= 0x10FFFF)
{
*c1 = (cp >> 18) + 0xF0;
*c2 = ((cp >> 12) & 0x3F) + 0x80;
*c3 = ((cp >> 6) & 0x3F) + 0x80;
*c4 = (cp & 0x3F) + 0x80;
return 4;
}
return 0;
}
template <typename T>
std::string UTF16ToUTF8(const T* u16, size_t len = 0)
{
std::string u8;
uint32_t cp;
size_t pos = 0;
while (GetNextCodePointFromUTF16(u16, len, &pos, &cp))
{
uint32_t c[4];
int count = CodePointToUTF8(cp, c, c+1, c+2, c+3);
for (int i = 0; i < count; i++)
{
u8 += static_cast<char>(c[i]);
}
}
return u8;
}
template <typename T>
std::wstring UTF16ToWstring(const T* u16, size_t len = 0)
{
std::wstring ret;
#ifdef _MSC_VER
while (*u16) ret += *u16++;
#else
uint32_t cp;
size_t pos = 0;
while (GetNextCodePointFromUTF16(u16, len, &pos, &cp))
{
ret += cp;
}
#endif
return ret;
}
template <typename T>
std::string WstringToUTF8(const T* wstr)
{
#ifdef _MSC_VER
return UTF16ToUTF8(wstr);
#else
std::string u8;
uint32_t cp;
while ((cp = *wstr++) != 0)
{
uint32_t c[4];
int count = CodePointToUTF8(cp, c, c+1, c+2, c+3);
for (int i = 0; i < count; i++)
{
u8 += static_cast<char>(c[i]);
}
}
return u8;
#endif
}

Двоичные данные
test/data/1.dat Normal file

Двоичный файл не отображается.

Двоичные данные
test/data/2.dat Normal file

Двоичный файл не отображается.

Двоичные данные
test/data/a test email message.msg Normal file

Двоичный файл не отображается.

Двоичные данные
test/data/unicode.dat Normal file

Двоичный файл не отображается.

Двоичные данные
test/data/{B85C5677-E8BC-11E4-825B-10604B7CB9F0}.dat Normal file

Двоичный файл не отображается.

Двоичные данные
test/data/{BC59C035-E8AC-11E4-825B-10604B7CB9F0}.dat Normal file

Двоичный файл не отображается.

Двоичные данные
test/data/{FE554E21-EA21-11E4-825B-10604B7CB9F0}.dat Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{7CD5AD07-5FE9-465D-A290-CAB53C97D545}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>IEOpenedTabParser</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)..\..\out\</OutDir>
<IntDir>$(SolutionDir)..\..\out\intermediate\$(ProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)..\..\out\</OutDir>
<IntDir>$(SolutionDir)..\..\out\intermediate\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(SolutionDir)/../../src/include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(SolutionDir)/../../src/include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\..\..\samples\IEOpenedTabParser\IEOpenedTabParser.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\samples\IEOpenedTabParser\openedtab.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

Просмотреть файл

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\samples\IEOpenedTabParser\IEOpenedTabParser.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\samples\IEOpenedTabParser\openedtab.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{CED76631-0BBD-4775-9284-E3CF0E5A7A92}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>cfb</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)..\..\out\</OutDir>
<IntDir>$(SolutionDir)..\..\out\intermediate\$(ProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)..\..\out\</OutDir>
<IntDir>$(SolutionDir)..\..\out\intermediate\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(SolutionDir)/../../src/include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(SolutionDir)/../../src/include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\..\samples\cfb\cfb.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\samples\cfb\cfb.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.21005.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IEOpenedTabParser", "IEOpenedTabParser\IEOpenedTabParser.vcxproj", "{7CD5AD07-5FE9-465D-A290-CAB53C97D545}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cfb", "cfb\cfb.vcxproj", "{CED76631-0BBD-4775-9284-E3CF0E5A7A92}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7CD5AD07-5FE9-465D-A290-CAB53C97D545}.Debug|Win32.ActiveCfg = Debug|Win32
{7CD5AD07-5FE9-465D-A290-CAB53C97D545}.Debug|Win32.Build.0 = Debug|Win32
{7CD5AD07-5FE9-465D-A290-CAB53C97D545}.Release|Win32.ActiveCfg = Release|Win32
{7CD5AD07-5FE9-465D-A290-CAB53C97D545}.Release|Win32.Build.0 = Release|Win32
{CED76631-0BBD-4775-9284-E3CF0E5A7A92}.Debug|Win32.ActiveCfg = Debug|Win32
{CED76631-0BBD-4775-9284-E3CF0E5A7A92}.Debug|Win32.Build.0 = Debug|Win32
{CED76631-0BBD-4775-9284-E3CF0E5A7A92}.Release|Win32.ActiveCfg = Release|Win32
{CED76631-0BBD-4775-9284-E3CF0E5A7A92}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal