Schannel messages larger than cbmaximummessage - c++

I have problem sending SChannel TLS message larger than the negotiated maximum length.
When "EncryptSend" is called with a buffer larger than SecPkgContext_StreamSizes.cbMaximumMessage, the part greater than SecPkgContext_StreamSizes.cbMaximumMessage is not understood by the server (nor by Wireshark).

You should be able to break your data into chunks that are less than or equal to the cbMaximumMessage size. For example, if you are sending VOID* pvData of ULONG cbData bytes, then...
while(0 < cbData)
{
ULONG cbChunk = (cbData > m_Sizes.cbMaximumMessage) ? m_Sizes.cbMaximumMessage : cbData;
Message.ulVersion = SECBUFFER_VERSION;
Message.cBuffers = ARRAYSIZE(Buffers);
Message.pBuffers = Buffers;
Buffers[0].pvBuffer = m_pSendBuffer;
Buffers[0].cbBuffer = m_Sizes.cbHeader;
Buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
Buffers[1].pvBuffer = m_pSendBuffer + m_Sizes.cbHeader;
Buffers[1].cbBuffer = cbChunk;
Buffers[1].BufferType = SECBUFFER_DATA;
CopyMemory(Buffers[1].pvBuffer, pvData, cbChunk);
Buffers[2].pvBuffer = m_pSendBuffer + m_Sizes.cbHeader + cbChunk;
Buffers[2].cbBuffer = m_Sizes.cbTrailer;
Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
Buffers[3].BufferType = SECBUFFER_EMPTY;
hr = EncryptMessage(&m_hContext, &Message, 0, 0);
if(FAILED(hr))
break;
hr = pSocket->Send(m_pSendBuffer, Buffers[0].cbBuffer + cbChunk + Buffers[2].cbBuffer);
if(FAILED(hr))
break;
pvData = reinterpret_cast<PBYTE>(pvData) + cbChunk;
cbData -= cbChunk;
}
In each iteration of the loop, a chunk that is less than or equal to the maximum size is encrypted and sent. For this to work, the mechanism used to send data to the socket will likely need to employ a buffering strategy in case the socket's internal buffer is filled to capacity.

Related

C++ WINAPI Create Multiple Partitions on Mounted VHD

I managed to create a VHD and attached it. Afterwards, I created a disk(IOCTL CREATE_DISK) and set its layout using IOCTL_DISK_SET_DRIVE_LAYOUT_EX. Now, when I examine disk through Disk Management. I have a 14MB with a 7 MB partition, expectedly.
int sign = 80001;
CREATE_DISK disk;
disk.Mbr.Signature = sign;
disk.PartitionStyle = PARTITION_STYLE_MBR;
auto res = DeviceIoControl(device_handle, IOCTL_DISK_CREATE_DISK, &disk, sizeof(disk), NULL, 0, NULL, NULL);
res = DeviceIoControl(device_handle, IOCTL_DISK_UPDATE_PROPERTIES, 0, 0, NULL, 0, NULL, NULL);
LARGE_INTEGER partition_size;
partition_size.QuadPart = 0xF00;
DWORD driver_layout_ex_len = sizeof(DRIVE_LAYOUT_INFORMATION_EX);
DRIVE_LAYOUT_INFORMATION_EX driver_layout_info;
memset(&driver_layout_info, 0, sizeof(DRIVE_LAYOUT_INFORMATION_EX));
driver_layout_info.Mbr.Signature = sign;
driver_layout_info.PartitionCount = 1;
driver_layout_info.PartitionStyle = PARTITION_STYLE_MBR;
PARTITION_INFORMATION_EX part_info;
PARTITION_INFORMATION_MBR mbr_info;
part_info.StartingOffset.QuadPart = 32256;
part_info.RewritePartition = TRUE;
part_info.PartitionLength.QuadPart = partition_size.QuadPart/2 * 4096;
part_info.PartitionNumber = 1;
part_info.PartitionStyle = PARTITION_STYLE_MBR;
mbr_info.BootIndicator = TRUE;
mbr_info.HiddenSectors = 32256 / 512;
mbr_info.PartitionType = PARTITION_FAT32;
mbr_info.RecognizedPartition = 1;
part_info.Mbr = mbr_info;
driver_layout_info.PartitionEntry[0] = part_info;
auto res_layout = DeviceIoControl(device_handle, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, &driver_layout_info, sizeof(driver_layout_info), NULL, 0, NULL, NULL);
Now, how do I partitionize this disk into two partitions? I want to create another partition out of the unpartitioned part of the disk(the other half basically). It says in the documentation is that PartitionEntry is an array of variable size(No, it is not it is an array of size 1.) Do I call set layout IOCTL for every partition I want to create? If so, how do you go about that? Is multi-partitioning possible through WINAPI interface?
P.S: I am aware that people usually invoke diskpart for this line of work.
Edit:
Adding second partition two layout was messing my stack up so I took another route (heap).
DWORD driver_layout_ex_len = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + sizeof(PARTITION_INFORMATION_EX); // one layout+partition + partition
PDRIVE_LAYOUT_INFORMATION_EX driver_layout_info = (PDRIVE_LAYOUT_INFORMATION_EX) std::calloc(1, driver_layout_ex_len);
driver_layout_info->Mbr.Signature = sign;
driver_layout_info->PartitionCount = 2;
driver_layout_info->PartitionStyle = PARTITION_STYLE_MBR;
// omitted here..
PARTITION_INFORMATION_EX part_info2;
part_info2.StartingOffset.QuadPart = 32256 + part_info.PartitionLength.QuadPart;
part_info2.RewritePartition = TRUE;
part_info2.PartitionLength.QuadPart = partition_size.QuadPart / 2 * 4096;
part_info2.PartitionNumber = 2;
part_info2.PartitionStyle = PARTITION_STYLE_MBR;
part_info2.Mbr = mbr_info;
driver_layout_info->PartitionEntry[0] = part_info;
driver_layout_info->PartitionEntry[1] = part_info2;
auto res_layout = DeviceIoControl(device_handle, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, driver_layout_info, driver_layout_ex_len, NULL, 0, NULL, NULL);
auto res_err = GetLastError();
Since it was overriding my device_handle I could not IOCTL at all. This improvement eliminated that. Do not forget to pass driver_layout_info instead of &driver_layout_info after this change.
It says in the documentation is that PartitionEntry is an array of
variable size(No, it is not it is an array of size 1.)
"Some Windows structures are variable-sized,
beginning with a fixed header, followed by
a variable-sized array. When these structures
are declared,
they often declare an array of size 1 where the
variable-sized array should be." Refer to #Raymond blog.
Here DRIVE_LAYOUT_INFORMATION_EX structure is an example:
typedef struct _DRIVE_LAYOUT_INFORMATION_EX {
DWORD PartitionStyle;
DWORD PartitionCount;
union {
DRIVE_LAYOUT_INFORMATION_MBR Mbr;
DRIVE_LAYOUT_INFORMATION_GPT Gpt;
} DUMMYUNIONNAME;
PARTITION_INFORMATION_EX PartitionEntry[1];
} DRIVE_LAYOUT_INFORMATION_EX, *PDRIVE_LAYOUT_INFORMATION_EX;
With this declaration, you would allocate memory for one such
variable-sized DRIVE_LAYOUT_INFORMATION_EX structure like this:
PDRIVE_LAYOUT_INFORMATION_EX driver_layout_info = (PDRIVE_LAYOUT_INFORMATION_EX)malloc(FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[NumberOfPartitions]));
You would initialize the structure like this (Use 2 partitions as example):
DWORD NumberOfPartitions = 2;
LARGE_INTEGER partition_size;
partition_size.QuadPart = 0xF00;
PARTITION_INFORMATION_MBR mbr_info;
mbr_info.BootIndicator = TRUE;
mbr_info.HiddenSectors = 32256 / 512;
mbr_info.PartitionType = PARTITION_FAT32;
mbr_info.RecognizedPartition = TRUE;
PDRIVE_LAYOUT_INFORMATION_EX driver_layout_info = (PDRIVE_LAYOUT_INFORMATION_EX)malloc(FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[NumberOfPartitions]));
for (DWORD Index = 0; Index < NumberOfPartitions ; Index++) {
driver_layout_info->PartitionEntry[Index].PartitionStyle = PARTITION_STYLE_MBR;
driver_layout_info->PartitionEntry[Index].PartitionNumber = Index + 1;
driver_layout_info->PartitionEntry[Index].RewritePartition = TRUE;
driver_layout_info->PartitionEntry[Index].PartitionLength.QuadPart = partition_size.QuadPart / 2 * 4096;
driver_layout_info->PartitionEntry[Index].Mbr = mbr_info;
}
driver_layout_info->Mbr.Signature = sign;
driver_layout_info->PartitionCount = 1;
driver_layout_info->PartitionStyle = PARTITION_STYLE_MBR;
driver_layout_info->PartitionEntry[0].StartingOffset.QuadPart = 32256;
driver_layout_info->PartitionEntry[1].StartingOffset.QuadPart = 32256 + driver_layout_info->PartitionEntry->StartingOffset.QuadPart;
DWORD driver_layout_ex_len = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + sizeof(PARTITION_INFORMATION_EX);
Call free(driver_layout_info); after you use completely.

NTE_BAD_DATA in CryptSetKeyParam while setting KP_P in wincrypt

I am having the below code. I am setting a prime for diffie-hellman algorithm using char *.
I am getting bad data after i set the prime. Where am i doing wrong?
I followed the same example in this link.
https://msdn.microsoft.com/en-us/library/aa381969(VS.85).aspx#exchanging_diffie-hellman_keys
What is the correct way to set prime in diffie-hellman using wincrypt?
#define DHKEYSIZE 1024
int fld_sz = 256;
BYTE* g_rgbPrime = new BYTE[DHKEYSIZE/8];
char * prime = "A1BD60EBD2D43C53FA78D938C1EF8C9AD231F9862FC402739302DEF1B6BEB01E5BE59848A04C48B0069A8FB56143688678F7CC1097B921EA3E13E1EF9B9EB5381BEFDE7BBF614C13827493A1CA31DA76B4083B62C5073451D6B1F06A2F1049C291464AC68CBB2F69474470BBAD374073392696B6447C82BF55F20B2D015EB97B";
string s_prime(prime, fld_sz);
vector<std::string> res;
// split the string two charactes for converting into hex format
for (size_t i = 0; i < fld_sz; i += 2)
res.push_back(s_prime.substr(i, 2));
for(int i = 0; i < res.size(); i++) {
BYTE b = static_cast<BYTE>(std::stoi(res[i], 0, 16));
g_rgbPrime[i] = b;
}
BYTE g_rgbGenerator[128] =
{
0x02
};
BOOL fReturn;
HCRYPTPROV hProvParty1 = NULL;
HCRYPTPROV hProvParty2 = NULL;
CRYPT_DATA_BLOB P;
CRYPT_DATA_BLOB G;
HCRYPTKEY hPrivateKey1 = NULL;
HCRYPTKEY hPrivateKey2 = NULL;
PBYTE pbKeyBlob1 = NULL;
PBYTE pbKeyBlob2 = NULL;
HCRYPTKEY hSessionKey1 = NULL;
HCRYPTKEY hSessionKey2 = NULL;
PBYTE pbData = NULL;
/************************
Construct data BLOBs for the prime and generator. The P and G
values, represented by the g_rgbPrime and g_rgbGenerator arrays
respectively, are shared values that have been agreed to by both
parties.
************************/
P.cbData = DHKEYSIZE / 8;
P.pbData = (BYTE*)(g_rgbPrime);
G.cbData = DHKEYSIZE / 8;
G.pbData = (BYTE*)(g_rgbGenerator);
/************************
Create the private Diffie-Hellman key for party 1.
************************/
// Acquire a provider handle for party 1.
fReturn = CryptAcquireContext(
&hProvParty1,
NULL,
MS_ENH_DSS_DH_PROV,
PROV_DSS_DH,
CRYPT_VERIFYCONTEXT);
if(!fReturn)
{
goto ErrorExit;
}
// Create an ephemeral private key for party 1.
fReturn = CryptGenKey(
hProvParty1,
CALG_DH_EPHEM,
DHKEYSIZE << 16 | CRYPT_EXPORTABLE | CRYPT_PREGEN,
&hPrivateKey1);
if(!fReturn)
{
goto ErrorExit;
}
// Set the prime for party 1's private key.
fReturn = CryptSetKeyParam(
hPrivateKey1,
KP_P,
(PBYTE)&P,
0);
if(!fReturn)
{
std::cout << GetLastError() << endl;
goto ErrorExit;
}
// Set the generator for party 1's private key.
fReturn = CryptSetKeyParam(
hPrivateKey1,
KP_G,
(PBYTE)&G,
0);
if(!fReturn)
{
std::cout << GetLastError() << endl;
goto ErrorExit;
}
Thanks in advance.
Update 1:
Thanks to #RbMm I was able to set the prime. The problem was with DHKEYSize. However i am getting an error in while setting KP_X. updated the code above to reflect the new code.
Here i converted the string to hex bytes array.
size of prime KP_P (and KP_G) and DH key size hard connected. must be cbKey == 8*cbP. look for example Diffie-Hellman Client Code for Creating the Master Key:
as key size if used cbP * 8 where cbP size of prime P. in your link also P.cbData = DHKEYSIZE/8;
also in code instead hard-code size of P (and G) you can get it in runtime:
ULONG dwDataLen;
CryptGetKeyParam(hPrivateKey1, KP_P, 0, &(dwDataLen = 0), 0);
CryptGetKeyParam(hPrivateKey1, KP_G, 0, &(dwDataLen = 0), 0);
and you can sure that dwDataLen == DHKEYSIZE / 8 where DHKEYSIZE is key size.
because you use 512 as key size, the length of data for P and G must be 512/8=64. but you use 256 (for P) and 1 (for G). as result and error.

C++ - EncryptMessage not encrypting the correct data

I've been following this tutorial for SSL/TLS online (well its more of reading the guys source code and following along) but I've hit a bumpy road with this EncryptMessage part because it pushes the data out of the way and encrypts the wrong info.
The pbloBuffer that I send it is:
GET / HTTP/1.1\r\n
HOST: www.google.com\r\n\r\n
But when I do pbMessage = pbloBuffer + Sizes.cbHeader; I end up with (even the microsoft websites says to do this)
1\r\n
HOST: www.google.com\r\n\r\n
Now pbMessage is the code above, and that's inserted under SECBUFFER_DATA so it's not even getting the full data. From what I understand SECBUFFER_DATA is the "user" data that the Webserver will decode and process.
Can you find out how to fix this and properly send the encrypted data?
Full source: (This code is experimental as I am trying to understand it before I makes changes)
int Adaptify::EncryptSend(PBYTE pbloBuffer, int Size) {
SECURITY_STATUS scRet{ 0 };
SecBufferDesc Message{ 0 };
SecBuffer Buffers[4]{ 0 };
DWORD cbMessage = 0, cbData = 0;
PBYTE pbMessage = nullptr;
SecPkgContext_StreamSizes Sizes = { 0 };
QueryContextAttributesW(&hContext, SECPKG_ATTR_STREAM_SIZES, &Sizes);
pbMessage = pbloBuffer + Sizes.cbHeader;
cbMessage = (DWORD)strlen((const char*)pbMessage);
Buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
Buffers[0].cbBuffer = Sizes.cbHeader;
Buffers[0].pvBuffer = pbloBuffer;
Buffers[1].BufferType = SECBUFFER_DATA;
Buffers[1].pvBuffer = pbMessage;
Buffers[1].cbBuffer = cbMessage;
Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
Buffers[2].cbBuffer = Sizes.cbTrailer;
Buffers[2].pvBuffer = pbMessage + cbMessage;
Buffers[3].BufferType = SECBUFFER_EMPTY;
Buffers[3].cbBuffer = SECBUFFER_EMPTY;
Buffers[3].pvBuffer = SECBUFFER_EMPTY;
Message.cBuffers = 4;
Message.pBuffers = Buffers;
Message.ulVersion = SECBUFFER_VERSION;
scRet = EncryptMessage(&hContext, 0, &Message, 0);
if (send(hSocket, (const char*)pbloBuffer, Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer, 0) < 0) {
MessageBox(0, L"Send error", 0, 0);
}
return 0;
}
first - you need call QueryContextAttributesW only once after InitializeSecurityContextW return SEC_E_OK - no sense call it every time, when you need send data. and save result. say inherit your class from SecPkgContext_StreamSizes - class Adaptify : SecPkgContext_StreamSizes; and call on end handshake (once) QueryContextAttributesW(&hContext, SECPKG_ATTR_STREAM_SIZES, this);
about send send data - in your case Buffers[1].pvBuffer of course must point to your actual data pbloBuffer but not to pbloBuffer + Sizes.cbHeader code can be like this:
int Adaptify::EncryptSend(PBYTE pbloBuffer, int Size) {
SECURITY_STATUS ss = SEC_E_INSUFFICIENT_MEMORY;
if (PBYTE Buffer = new BYTE[cbHeader + Size + cbTrailer]) {
memcpy(Buffer + cbHeader, pbloBuffer, Size);
SecBuffer sb[4] = {
{ cbHeader, SECBUFFER_STREAM_HEADER, Buffer},
{ Size, SECBUFFER_DATA, Buffer + cbHeader},
{ cbTrailer, SECBUFFER_STREAM_TRAILER, Buffer + cbHeader + Size},
};
SecBufferDesc sbd = {
SECBUFFER_VERSION, 4, sb
};
if (SEC_E_OK == (ss = ::EncryptMessage(this, 0, &sbd, 0)))) {
if (SOCKET_ERROR == send(hSocket, (const char*)Buffer, sb[0].cbBuffer+sb[1].cbBuffer+sb[2].cbBuffer+sb[3].cbBuffer, 0))
ss = WSAGetLastError();
}
delete [] Buffer;
}
return ss;
}
so you need allocate new buffer with cbHeader + Size + cbTrailer size (wher Size is your actual message Size and copy your message at Buffer + cbHeader

How can I get sample from AudioBufferList

I get the AudioBufferList from a wav file(Which Sample Rate is 44100HZ and the long time is 2second).
But I can't get 44100*2=88200 samples. In actually I got an AudiobufferList which contain 512 nNumberBuffers.
How can I get the sample from the AudioBufferList?
here is my method to get the samples from a wav file
-(float *) GetSoundFile:(NSString *)fileName
{
NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:[fileName stringByDeletingPathExtension]
ofType:[fileName pathExtension]];
NSURL *audioURL = [NSURL fileURLWithPath:path];
if (!audioURL)
{
NSLog(#"file: %# not found.", fileName);
}
OSStatus err = 0;
theFileLengthInFrames = 0; //this is global
AudioStreamBasicDescription theFileFormat;
UInt32 thePropertySize = sizeof(theFileFormat);
ExtAudioFileRef extRef = NULL;
void* theData = NULL;
AudioStreamBasicDescription theOutputFormat;
// Open a file with ExtAudioFileOpen()
err = ExtAudioFileOpenURL((__bridge CFURLRef)(audioURL), &extRef);
// Get the audio data format
err = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileDataFormat, &thePropertySize, &theFileFormat);
theOutputFormat.mSampleRate = samplingRate = theFileFormat.mSampleRate;
theOutputFormat.mChannelsPerFrame = numChannels = 1;
theOutputFormat.mFormatID = kAudioFormatLinearPCM;
theOutputFormat.mBytesPerFrame = sizeof(Float32) * theOutputFormat.mChannelsPerFrame ;
theOutputFormat.mFramesPerPacket = theFileFormat.mFramesPerPacket;
theOutputFormat.mBytesPerPacket = theOutputFormat.mFramesPerPacket * theOutputFormat.mBytesPerFrame;
theOutputFormat.mBitsPerChannel = sizeof(Float32) * 8 ;
theOutputFormat.mFormatFlags = 9 | 12;
//set the output property
err = ExtAudioFileSetProperty(extRef, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &theOutputFormat);
// Here I Get the total frame count and write it into gloabal variable
thePropertySize = sizeof(theFileLengthInFrames);
err = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileLengthFrames, &thePropertySize, &theFileLengthInFrames);
UInt32 dataSize = (UInt32)(theFileLengthInFrames * theOutputFormat.mBytesPerFrame);
theData = malloc(dataSize);
AudioBufferList theDataBuffer;
if (theData)
{
theDataBuffer.mNumberBuffers = 1;
theDataBuffer.mBuffers[0].mDataByteSize = dataSize;
theDataBuffer.mBuffers[0].mNumberChannels = theOutputFormat.mChannelsPerFrame;
theDataBuffer.mBuffers[0].mData = theData;
// Read the data into an AudioBufferList
err = ExtAudioFileRead(extRef, (UInt32*)&theFileLengthInFrames, &theDataBuffer);
}
return (float*)theDataBuffer.mBuffers[0].mData;
//here is the data that has thefilelengthInframes amount frames
}
AudioBufferList should not be set to a fixed size because is a temporary buffer, hardware dependent.
The size is unpredictable and you can only set a preferable size, moreover is not granted to be the same size between 2 reading calls.
To get 88200 samples (or what you like) you must incrementally fill a new buffer using,
time by time, the samples in AudioBufferList.
I suggest to use this circular buffer https://github.com/michaeltyson/TPCircularBuffer
made for this purpose.
Hope this help
As I know, AudioBufferList must be configured by you to receive data to it and then it must be filled by some reading function (e.g. ExtAudioFileRead()). So you should prepare it by allocating buffers you need (usually 1 or 2), set nNumberBuffers to that number and read audio data to them. AudioBufferList just stores that buffers and they are will contain frames values.

Is it possible to cache mapped regions returned from MapViewOfFile?

Good afternoon, It is a well known fact that when dealing with large files
that cannot be mapped to one view in Win32, create code that carefully maps
and unmaps file regions as they are needed. The pastebin url is:
I created and tested a cMemoryMappedFile class that deals with large files
that cannot be mapped to one view in Win32. I tested the class and found
that while it functions OK, it takes a long time(i.e 3 seconds) for
random access. This is because the class has to unmap and map a file
region for every random access. I was wondering if it was possible to
cache the mapped regions returned from MapViewFile to speed up random access.
Yesterday, I noticed that UnMapViewOfFile invalidates a previously
mapped region returned from MapViewOfFile. Does anyone have ideas
about how to speed up random access through caching or other methods?
Currently the viewport is 128KB. I believe that if I enlarge the
viewport it will reduce the number of calls to UnMapViewOfFile
and MapViewOfFile. However, I was wondering if could use other
methods. Please look at the method,
char* cMemoryMappedFile::GetPointer(int , bool) to see how the
viewport is shifted with the file mapping. Thank you.
The pastebin url for the class is
> .
I am adding the source code here in case no one can access the url.
// cMemoryMappedFile.Cpp
#include "cException.h"
#include "cMemoryMappedFile.h"
#define BUFFER_SIZE 10
#define MEM_BLOCK_SIZE 65536 * 2
/**
\class cMemoryMappedFile
\brief Encapsulation of the Windows Memory Management API.
The cMemoryMapped class makes some memory mapping operations easier.
*/
/**
\brief Constructor for cMemoryMappedFile object.
\param FileSize Size of file.
\param OpenMode File open mode
\param AccessModes File access mode
\param ShareMode File sharing mode
\param Flags File attributes and flags
\param ShareMode File sharing mode
\param Flags File attributes and flags
\param Security Security Attributes
\param Template Extended attributes tp apply to a newly created file
*/
cMemoryMappedFile::cMemoryMappedFile(long FileSize_, OpenModes OpenMode_,AccessModes AccessMode_,
ShareModes ShareMode_,long Flags_,void *Security_,FILEHANDLE Template_) {
FileSize = FileSize_;
char buffer[BUFFER_SIZE];
DWORD dwRetVal = 0;
UINT uRetVal = 0;
DWORD dwPtr = 0;
BOOL isSetEndOfFile = FALSE;
LARGE_INTEGER Distance_;
DWORD ErrorCode = 0;
char lpTempPathBuffer[MAX_PATH];
PreviousNCopy = 0;
PreviousN = 0;
// Gets the temp path env string (no guarantee it's a valid path).
dwRetVal = GetTempPath(MAX_PATH, // length of the buffer
lpTempPathBuffer); // buffer for path
if (dwRetVal > MAX_PATH || (dwRetVal == 0))
{
throw cException(ERR_MEMORYMAPPING,"");
}
// Generates a temporary file name.
uRetVal = GetTempFileName(lpTempPathBuffer, // directory for tmp files
TEXT("DEMO"), // temp file name prefix
0, // create unique name
TempFileName); // buffer for name
if (uRetVal == 0)
{
throw cException(ERR_MEMORYMAPPING,lpTempPathBuffer);
}
// Creates the new file
hFile = CreateFile((LPTSTR) TempFileName, // file name
AccessMode_, // open for write
0, // do not share
(SECURITY_ATTRIBUTES *) Security_, // default security
OpenMode_, // CREATE_ALWAYS,
Flags_,// normal file
Template_); // no template
if (hFile == INVALID_HANDLE_VALUE)
{
throw cException(ERR_MEMORYMAPPING,TempFileName);
}
Distance_.LowPart = (ULONG)FileSize_;
Distance_.HighPart = 0; // (ULONG)(FileSize_ >> 32);
dwPtr = ::SetFilePointer(hFile,Distance_.LowPart,
&(Distance_.HighPart), FileBegin);
if (dwPtr == INVALID_SET_FILE_POINTER){
throw cException(ERR_MEMORYMAPPING,TempFileName);
}
isSetEndOfFile = SetEndOfFile(hFile);
if (!isSetEndOfFile){
ErrorCode = GetLastError();
throw cException(ERR_MEMORYMAPPING,TempFileName);
}
hMapping=::CreateFileMapping(hFile,(SECURITY_ATTRIBUTES *)Security_,PAGE_READWRITE,0,0,0);
if (hMapping==NULL)
throw cException(ERR_MEMORYMAPPING,TempFileName);
MapPtr = 0;
adjustedptr = 0;
prevadjustedptr = adjustedptr;
FilePath=new char[strlen(TempFileName)+1];
strcpy(FilePath,TempFileName);
}
char * cMemoryMappedFile::GetPointer(int n, bool Caching){
unsigned int baseoff;
if( n < MEM_BLOCK_SIZE / 2)
{
baseoff = 0;
}
else
{
baseoff = ((n + MEM_BLOCK_SIZE / 4) &
(~(MEM_BLOCK_SIZE / 2 - 1))) - MEM_BLOCK_SIZE / 2;
}
// the correct memory mapped view is already mapped in
if (adjustedptr != 0 && mappedoffset == baseoff && Caching)
return adjustedptr;
else if (Caching)
{
/*
retrieve adjustedptr from cache
*/
}
// get a new memory mapped viewport
else{
if (MapPtr){
UnmapViewOfFile(MapPtr);
PreviousNCopy = PreviousN;
prevadjustedptr = adjustedptr;
}
PreviousN = n;
mappedlength = min(FileSize - baseoff, MEM_BLOCK_SIZE);
// MapViewOfFile should be aligned to 64K boundary
MapPtr = (char*)::MapViewOfFile( hMapping,
FILE_MAP_WRITE | FILE_MAP_READ, 0,
baseoff, mappedlength);
mappedoffset = baseoff;
adjustedptr = MapPtr - mappedoffset;
printf("Value: %u n: %u\n",adjustedptr[n],n);
/*
cache PreviousNCopy,PreviousN, prevadjustedptr[PreviousNCopy]
*/
}
return adjustedptr;
}
You could have a "free list" style cache --- when the user of your class asks to unmap a region you don't really, you just add it to the list. When they ask to map a new region then you reuse an existing mapping if possible, otherwise you create a new mapping, deleting the least-recently-used one from the cache if you've got too many mappings open, or where the mapped size of the cached mappings is too large.