Can file mapping object and file object be used interchangeably? - c++

Say I want to a generated a wrapper function to CreateFile function
This new function will not generate a real file on the disk but create file mapping object and return a handle to the new object.
I've looked at this example, Creating Named Shared Memory, and tried to implement my function:
#define BUF_SIZE 256
TCHAR szName[] = TEXT("Global\\MyFileMappingObject");
HANDLE MyCreateFile()
{
HANDLE hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file
NULL, // default security
PAGE_READWRITE, // read/write access
0, // maximum object size (high-order DWORD)
BUF_SIZE, // maximum object size (low-order DWORD)
szName); // name of mapping object
return hMapFile;
}
Problem
This looked OK to me, however, when tried using the returned HANDLE in ReadFile function I got error code 6 The handle is invalid.
Question
Can file mapping object and file object be used interchangeably? If so, then what is the issue with my code? If not, any idea how can such function be implemented?

The handle that CreateFileMapping returns is a file mapping object and not that of regular files. CreateFileMapping is part of a family of functions which allows access to files as if they are memory or array of bytes.
One way, would be to also call MapViewOfFile(with appropriate parameters) inside your MyCreateFile() function and let MyCreateFile() function return the pointer which is returned by MapViewOfFile. Now you can write your MyReadFile() and MyWriteFile() using this pointer.
It would be more nice if you can create a class and include all these functions inside it.
class CustomFile
{
private:
LPVOID *m_pData;
public:
//m_pData is initialized here via CreateFileMapping and
//MapViewOfFile.
CreateFile(...);
//m_pData is used here.
ReadFile(...);
WriteFile(...);
};

Related

How to allocate memory in a DriverKit system extension and map it to another process?

I have allocated memory in my application and passed its pointer and size to IOConnectCallStructMethod. Using IOMemoryDescriptor::CreateMapping I have then mapped this memory to the DriverKit system extension process, and it is possible to write to this mapped memory location and read the data from my application.
I would now like to do something similar for memory that is allocated in the system extension, and then map it to the application that is using the system extension. I would like to create a set of memory buffers in the system extension, and then write to it from the application and then signal to the system extension with IOConnectCallScalarMethod that a given buffer should be sent to the USB device, using IOUSBHostPipe::AsyncIO. When the CompleteAsyncIO callback then comes as a result of the sending completing, I would notify back to the application that it is now possible to copy data to the first buffer that was sent. The mechanism for this could probably be done using IOConnectCallAsyncStructMethod, and the OSAction object that is created in the system extension. What I don't understand is how to map memory allocated in the system extension to the application.
This is what IOUserClient::CopyClientMemoryForType in DriverKit is for, which gets invoked when your user process calls IOConnectMapMemory64 from IOKit.framework. The kext equivalent, incidentally, is IOUserClient::clientMemoryForType and essentially works exactly the same.
To make it work, you need to override the CopyClientMemoryForType virtual function in your user client subclass.
In the class definition in .iig:
virtual kern_return_t CopyClientMemoryForType(
uint64_t type, uint64_t *options, IOMemoryDescriptor **memory) override;
In the implementation .cpp, something along these lines:
kern_return_t IMPL(MyUserClient, CopyClientMemoryForType) //(uint64_t type, uint64_t *options, IOMemoryDescriptor **memory)
{
kern_return_t res;
if (type == 0)
{
IOBufferMemoryDescriptor* buffer = nullptr;
res = IOBufferMemoryDescriptor::Create(kIOMemoryDirectionInOut, 128 /* capacity */, 8 /* alignment */, &buffer);
if (res != kIOReturnSuccess)
{
os_log(OS_LOG_DEFAULT, "MyUserClient::CopyClientMemoryForType(): IOBufferMemoryDescriptor::Create failed: 0x%x", res);
}
else
{
*memory = buffer; // returned with refcount 1
}
}
else
{
res = this->CopyClientMemoryForType(type, options, memory, SUPERDISPATCH);
}
return res;
}
In user space, you would call:
mach_vm_address_t address = 0;
mach_vm_size_t size = 0;
IOReturn res = IOConnectMapMemory64(connection, 0 /*memoryType*/, mach_task_self(), &address, &size, kIOMapAnywhere);
Some notes on this:
The value in the type parameter comes from the memoryType parameter to the IOConnectMapMemory64 call that caused this function to be called. Your driver therefore can have some kind of numbering convention; in the simplest case you can treat it similarly to the selector in external methods.
memory is effectively an output parameter and this is where you're expected to return the memory descriptor you want to map into user space when your function returns kIOReturnSuccess. The function has copy semantics, i.e. the caller expects to take ownership of the memory descriptor, i.e. it will eventually drop the reference count by 1 when it is no longer needed. The returned memory descriptor need not be an IOBufferMemoryDescriptor as I've used in the example, it can also be a PCI BAR or whatever.
The kIOMapAnywhere option in the IOConnectMapMemory64 call is important and normally what you want: if you don't specify this, the atAddress parameter becomes an in-out parameter, and the caller is expected to select a location in the address space where the driver memory should be mapped. Normally you don't care where this is, and indeed specifying an explicit location can be dangerous if there's already something mapped there.
If user space must not write to the mapped memory, set the options parameter to CopyClientMemoryForType accordingly: *options = kIOUserClientMemoryReadOnly;
To destroy the mapping, the user space process must call IOConnectUnmapMemory64().

BluetoothGATTSetCharacteristicValue returns E_INVALIDARG or ERROR_INVALID_FUNCTION

I have build a set C++ containing classes on top of the BluetoothAPIs apis.
I can enumerate open handles to services, characteristics and descriptors. I can read characteristic values. The issue that I have is that I cannot write to a characteristic value.
Below is the code use to write the characteristic value
void BleGattCharacteristic::setValue(UCHAR * data, ULONG size){
if (pGattCharacteristic->IsSignedWritable || pGattCharacteristic->IsWritable || pGattCharacteristic->IsWritableWithoutResponse)
{
size_t required_size = sizeof(BTH_LE_GATT_CHARACTERISTIC_VALUE) + size;
PBTH_LE_GATT_CHARACTERISTIC_VALUE gatt_value = (PBTH_LE_GATT_CHARACTERISTIC_VALUE)malloc(required_size);
ZeroMemory(gatt_value, required_size);
gatt_value->DataSize = (ULONG)size;
memcpy(gatt_value->Data, data, size);
HRESULT hr = BluetoothGATTSetCharacteristicValue(bleDeviceContext.getBleServiceHandle(), pGattCharacteristic, gatt_value, NULL, BLUETOOTH_GATT_FLAG_NONE);
free(gatt_value);
if (HRESULT_FROM_WIN32(S_OK) != hr)
{
stringstream msg;
msg << "Unable to write the characeristic value. Reason: ["
<< Util.getLastError(hr) << "]";
throw BleException(msg.str());
}
}
else
{
throw BleException("characteristic is not writable");
}}
The call to bleDeviceContext.getBleServiceHandle() returns the open handle to the device info service.
pGattCharacteristics is the pointer to the characteristic to write too. It was opened with a call to BluetoothGATTGetCharacteristics.
I have tried different combinations of the flags with no difference in the return code.
I have also tried using the handle to the device not to the service. In that case I get an ERROR_INVALID_FUNCTION return error code.
I would appreciate any pointers as to what I am doing wrong or what other possible options I could try.
1- You have to use the Service Handle, right.
2- I don't know how you designed your class, and then how you allocate some memory for the Characteristic's Value itself.
What I do (to be sure to have enough and proper memory for Value's data):
a) at init of the Value object, call ::BluetoothGATTGetCharacteristicValue twice, to get the needed size and then actually allocate some internal memory for it.
b) when using it, set the inner memory to what it may , then call ::BluetoothGATTSetCharacteristicValue
hr=::BluetoothGATTSetCharacteristicValue(
handle,
(PBTH_LE_GATT_CHARACTERISTIC)Characteristic,
value,//actually a (PBTH_LE_GATT_CHARACTERISTIC_VALUE) to allocated memory
0,//BTH_LE_GATT_RELIABLE_WRITE_CONTEXT ReliableWriteContext,
BLUETOOTH_GATT_FLAG_NONE)
So a few things:
typedef struct _BTH_LE_GATT_CHARACTERISTIC_VALUE {
ULONG DataSize;
UCHAR Data[];
} BTH_LE_GATT_CHARACTERISTIC_VALUE, *PBTH_LE_GATT_CHARACTERISTIC_VALUE;
is how the data structure used in the parameter CharacteristicValue is defined. Please note that Data is NOT an allocated array, but rather a pointer. So accessing Data[0] is undefined behavior and could be accessing anywhere in memory. Rather you need to do gatt_value.Data = &data; setting the pointer to the address of the input parameter.
Secondly the documentation is quite clear as to why you might get ERROR_INVALID_FUNCTION; if another reliable write is already pending then this write will fail. You should consider retry logic in that case.
As for E_INVALIDARG I'd assume it's related to the undefined behavior but I'd check after fixing the other issues previously mentioned.

Strange CBitmapRenderTarget::GetBitmap API - Direct2D MFC

I am completely confused by MFC wrapper for directd2d intefaces. Take a look at the following for example:-
BOOL CreateCompatibleRenderTarget(
CBitmapRenderTarget& bitmapTarget,
CD2DSizeF sizeDesired = CD2DSizeF(0.,
0.),
CD2DSizeU sizePixelDesired = CD2DSizeU(0,
0),
D2D1_PIXEL_FORMAT* desiredFormat = NULL,
D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS options = D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE
);
bitmapTarget When this method returns, contains the address of a
pointer to a new bitmap render target. This parameter is passed
uninitialized.
I am completely puzzled by what I should pass to the the function. As on contrary to documentation it receives the object and not the pointer to the uninitialized as in Directd2d IDL. And the object must be initialized.
Now one can tell that CBitmapRenderTarget is an object created with default contstructor. However this is not working with the GetBimap member of the CBitmapRenderTarget which also follows the same patter in documentation:-
BOOL GetBitmap(
CD2DBitmap& bitmap
);
bitmap When this method returns, contains the valid bitmap for this
render target. This bitmap can be used for drawing operations.
However the CD2DBitmap DOES NOT HAVE the default ctor, so I cannot create the object in a first place. The question is how do I correctly call to GetBitmap of CBitmapRenderTarget API. How do I create the uninitialized CD2DBitmap object ???
I encountered same issue. Looking at the CD2DBitmap implementation, there's no constructor without argument, and one with the only the parent CRenderTarget* ans an argument, but it's protected so not usable from outside. So apparently the only way is to use one of the 3 public constructors which are only crating Bitmap from existing resources (from handle, resource id or file path).
On my case, as a workaround because my intent is to replace this bitbap by a new one (GetBitmap), I created the Bitamp from a PNG file stored on my resources :
CD2DBitmap bitmap(GetRenderTarget(), (UINT)IDB_LOGO_PETIT, _T("PNG"));
m_pTraceRenderTarget->GetBitmap(bitmap);
But you can use any other CD2DBitmap constructor:
CD2DBitmap(CRenderTarget* pParentTarget, UINT uiResID, LPCTSTR lpszType = NULL, CD2DSizeU sizeDest = CD2DSizeU(0, 0), BOOL bAutoDestroy = TRUE);
CD2DBitmap(CRenderTarget* pParentTarget, LPCTSTR lpszPath, CD2DSizeU sizeDest = CD2DSizeU(0, 0), BOOL bAutoDestroy = TRUE);
CD2DBitmap(CRenderTarget* pParentTarget, HBITMAP hbmpSrc, CD2DSizeU sizeDest = CD2DSizeU(0, 0), BOOL bAutoDestroy = TRUE);

Issues with the size of the shared file mapping object in C++ code

I am trying to write into a (shared) named file mapping object like so:
//ENTER CRITICAL SECTION FIRST
int ncbSzMapping = 0x92B8; //Size of a shared struct
hFileMapping = CreateFileMapping((HANDLE)INVALID_HANDLE_VALUE,
NULL, PAGE_READWRITE,
0, ncbSzMapping,
_T("mapping_name"));
if(hFileMapping)
{
BYTE* pRWData = MapViewOfFile(hFileMapping,
FILE_MAP_ALL_ACCESS, 0, 0, ncbSzMapping);
if(pRWData)
{
//Write data into 'pRWData' of 'ncbSzMapping' bytes
UnmapViewOfFile(pRWData);
}
}
...
//LEAVE CRITICAL SECTION
The code above works without a problem. But when I change the ncbSzMapping to 0x8A8B8 the code above succeeds but later down the code I get an exception c00000fd right before a function call that makes no sense to me.
Any idea why that size increase makes a difference and how to fix it?
0xc00000fd is a stack overflow. Are you declaring a buffer as a local variable with that size? If so, that's your problem. Move the buffer off the stack by making it a global or static, or allocate it dynamically using new/delete.

.wav Player : mmioOpen API

I am trying to make an audio player that plays .wav files. I wrote a function ReadWaveFile(CString szFilename) for reading the wave data of the file into the WAVEHDR structure. In this function
BOOL CWavePlay::ReadWaveFile(CString szFilename)
{
hmmio = mmioOpen((LPTSTR)&szFilename,NULL,MMIO_READ);
ASSERT(hmmio); //error here: hmmio=0x00000000
if(hmmio==0)
return FALSE;
....
}
mmioOpen is always returning 0 whenever I pass a filepath to this function for opening the specified file. And what baffles me is when i pass the filepath explicitly in mmioOpen API the code works; i.e., a valid handle is returned.
can some body explain why is this happening??
What will happen when you say
MessageBox(NULL,(LPTSTR)&szFilename,"Foo",MB_ICONINFORMATION);
When passing strings to system functions you will need to pick up the pointer to the raw string. For example, if you want to use an std::string object to build your path you will need to say
mmioOpen(filename.c_str(),NULL,MMIO_READ);
Your cast assumes from CString* to LPTSTR assumes that a CString is binary compatible with a LPTSRT which is not the case. When you write LPCTSTR on szFilename you will invoke a cast operator defined on CStrings that converts it to apropriate format. Did you tried just
hmmio = mmioOpen((LPCTSTR)szFilename,NULL,MMIO_READ);
The last cast does not do anything real here so it should be enough.