I am attempting to set the Display Mode of my Monitor using WinAPI C++ functions.
My Problem: Calling ChangeDisplaySettingsEx() always returns DISP_CHANGE_BADPARAM. What am I doing wrong?
I think it may be my devMode.dmDriverExtra value thats causing the error. I've read MSDN and the explaination of devMode.dmDriverExtra is confusing. What is it and how do I find out the Monitors' dmDriverExtra?
Whats causing my code below to always return DISP_CHANGE_BADPARAM?
DEVMODE devMode;
POINTL p = {0,0};
_tcscpy(devMode.dmDeviceName, _T("\\Device\\00000072"));
devMode.dmSpecVersion = DM_SPECVERSION;
devMode.dmDriverVersion = 1; // How do I determine the driver version?
devMode.dmSize = sizeof(DEVMODE);
devMode.dmDriverExtra = 0x5c0000; //
devMode.dmFields = DM_POSITION;
devMode.dmPosition = p;
LONG res = ChangeDisplaySettingsEx(_T("\\Device\\00000072"), &devMode, mainHwnd, 0, NULL);
_tprintf(_T("%s: %d\n\n\n"), _T("\\Device\\00000072"), res);
// The above printf always prints out "\Device\00000072: -5" (DISP_CHANGE_BADPARAM=-5)
The hwnd parameter is documented to be reserved and must be NULL. Additionally dmDriverExtra is a 16 bit value, so 0x5c0000 doesn't fit.
Related
I already asked a Question but I think thats very special and will not get a concrete answer.
Im trying to give a simpler Explanation of what i need help with.
The Issue is that d3d12::pCommandList->CopyTextureRegion; doesnt work because CopyTextureRegion is a function that can only be called, but i need the address of it.
For example this will give me the Address of the ID3D12GraphicsCommandList: d3d12::pCommandList;
This is a small part of my code:
namespace d3d12
{
IDXGISwapChain3* pSwapChain;
ID3D12Device* pDevice;
ID3D12CommandQueue* pCommandQueue;
ID3D12Fence* pFence;
ID3D12DescriptorHeap* d3d12DescriptorHeapBackBuffers = nullptr;
ID3D12DescriptorHeap* d3d12DescriptorHeapImGuiRender = nullptr;
ID3D12DescriptorHeap* pSrvDescHeap = nullptr;;
ID3D12DescriptorHeap* pRtvDescHeap = nullptr;;
ID3D12GraphicsCommandList* pCommandList;
FrameContext* FrameContextArray;
ID3D12Resource** pID3D12ResourceArray;
D3D12_CPU_DESCRIPTOR_HANDLE* RenderTargetDescriptorArray;
HANDLE hSwapChainWaitableObject;
HANDLE hFenceEvent;
UINT NUM_FRAMES_IN_FLIGHT;
UINT NUM_BACK_BUFFERS;
UINT frame_index = 0;
UINT64 fenceLastSignaledValue = 0;
}
d3d12::pDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, d3d12::FrameContextArray[0].CommandAllocator, NULL, IID_PPV_ARGS(&d3d12::pCommandList));
// issue, CopyTextureRegion can only be called. I need the address of it
auto RegionHookAddress = d3d12::pCommandList->CopyTextureRegion;
Taking the address of the member function:
auto RegionHookAddress = &d3d12::ID3D12GraphicsCommandList::CopyTextureRegion;
Calling the member function:
(d3d12::pCommandList->*RegionHookAddress)(...);
I have a problem a window handle (window class = WC_LISTVIEW) after calling
SendMessage(hListView_, LVM_INSERTITEM , 0, (LPARAM)&lvItem);
where
hListView_
is a handle to a list view window and
lvItem
is an LVITEM structure. The following code
std::cout << "Last error: " << GetLastError() << std::endl;
SendMessage(hListView_, LVM_INSERTITEM , 0, (LPARAM)&lvItem);
std::cout << "Last error: " << GetLastError() << std::endl;
prints
Last error: 0
Last error: 6
According to Win32 System Error Codes code 6 means ERROR_INVALID_HANDLE.
I create the LVITEM structure as follows:
// define a char-buffer
char szBuffer[256];
szBuffer[0] = '\0';
// create new list view item
LVITEM lvItem;
lvItem.cchTextMax = 256;
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 0;
lvItem.stateMask = 0;
lvItem.state = 0;
lvItem.iSubItem = 0;
snprintf(szBuffer, 256, "%s", myString.c_str());
lvItem.pszText = szBuffer;
This code is called from the same thread which created the window (list view).
Also note that I have
lvItem.iSubItem = 0;
which is required according to LVM_INSERTITEM. The list view is empty prior to this call. Moreover, I can actually see the value being inserted in the list view (i.e. I can see the item in the list view in the GUI).
However, when I try to use the window handle after this the application crashes (no exception, just crashes).
Greatful for any hints on what might cause this.
Thank you.
Thank you David Heffernan for your help. I did at last find the problem, which was (as you suggested) in a different place in the code.
I had missed the following line (in a different function which I call prior to the code I posted above)
lvItem.pszText = szBuffer;
where
char szBuffer[256]; // char-buffer
when doing the follwing call
SendMessage(hListView_, LVM_GETITEMTEXT, (WPARAM) i, (LPARAM) &lvItem);
Thanks a lot for your help!
Edit: If I had done
lvItem.pszText = myString.c_str();
rather than the char-buffer this would probably not have happened, so thanks for that hint!
There's no reason for you to call GetLastError. The documentation for LVM_INSERTITEM doesn't say that you should do so. All it says is that SendMessage returns the index of the new item on success, and -1 on failure. So, check for errors by inspecting the value returned by SendMessage.
The other problem is that you are not initialising all the fields of LVITEM. That's always a mistake. You can use an initialising declaration like this:
LVITEM lvItem = { 0 };
There's no real need for a separate buffer for the text. You can do it all like this:
LVITEM lvItem = { 0 };
lvItem.mask = LVIF_TEXT;
lvItem.cchTextMax = myString.length() + 1;
lvItem.pszText = myString.c_str();
int indexOfNewItem = SendMessage(hListView_, LVM_INSERTITEM, 0, (LPARAM)&lvItem);
if (indexOfNewItem == -1)
// deal with failure
It's quite possible that your error lies elsewhere in fact. I don't see any particular reason for that SendMessage call to lead to an application crash. At least now you know how to check for errors when sending LVM_INSERTITEM. If that does not result in an error then the evidence would be that the crash is caused by some other code and you have mistakenly identified this code because you erroneously called GetLastError when its value was meaningless.
I have this code, which executes several times during runtime:
// Restore the bitmap from the copy
IWICBitmapLock *pLock = NULL;
UINT cbStride = 0;
hr = m_pWICBitmapCopy->GetSize(&uiWidth,&uiHeight);
WICRect rcLock = { 0, 0, uiWidth, uiHeight };
hr = m_pWICBitmapCopy->Lock(&rcLock, WICBitmapLockRead, &pLock);
hr = pLock->GetDataPointer(&cbBufferSize, &pv);
hr = pLock->GetStride(&cbStride);
m_pWICBitmap->Release();
hr = m_pWICImagingFactory->CreateBitmapFromMemory(uiWidth,uiHeight,GUID_WICPixelFormat32bppPBGRA,cbStride,cbBufferSize,pv,&m_pWICBitmap);
hr = pLock->Release();
uiWidth and uiHeight always different, the initial value is 3264 and 2448. Second time it could be e.g. 778 and 246.
But method pLock->GetStride(&cbStride) always returns the same stride value (in my case 13056). It causes that CreateBitmapFromMemory() method creates a corrupted bitmap. Other variables are changing correctly depending on bitmap dimensions. It looks like pLock cached stride value somewhere.
In MSDN written that, the stride value is specific to the IWICBitmapLock, not the bitmap. However, I did release pLock every time. So what could be the cause of such behavior?
The second way I could use to do the same is like this, but I want to understand what’s wrong with the code above.
m_pWICBitmap->Release();
m_pWICImagingFactory->CreateBitmapFromSource(m_pWICBitmapCopy, WICBitmapCacheOnDemand, &m_pWICBitmap);
I am trying to implement an MFT which is able to rotate a video. The rotation itself would be done inside a transform function. For that i need to change the output frame size but i don´t know how to do that.
As a starting point, i used the MFT_Grayscale example given by Microsoft. I included this MFT in a partial topology as a transform node
HRESULT Player::AddBranchToPartialTopology(
IMFTopology *pTopology,
IMFPresentationDescriptor *pSourcePD,
DWORD iStream
)
{
...
IMFTopologyNode pTransformNode = NULL;
...
hr = CreateTransformNode(CLSID_GrayscaleMFT, &pTransformNode);
...
hr = pSourceNode->ConnectOutput(0, pTransformNode, 0);
hr = pTransformNode->ConnectOutput(0, pOutputNode, 0);
...
}
This code is working so far. The grayscale mft is applied and working as expected. Anyway i want to change this mft to handle video rotation. So lets assume i want to rotate a video by 90 degrees. For that the width and height of my input frame have to be switched. I tried different things but none of them workes as expected.
Based on the first comment in this thread How to change Media Foundation Transform output frame(video) size? i started changing the implementation of SetOutputType. i called GetAttributeSize inside GetOutputType to receive the actual frame_size. It fails when i try to set a new frame_size (when starting playback i receive hresult 0xc00d36b4 (Data specified is invalid, inconsistent, or not supported by this object)
HRESULT CGrayscale::SetOutputType(
DWORD dwOutputStreamID,
IMFMediaType *pType, // Can be NULL to clear the output type.
DWORD dwFlags
)
{ ....
//Receive the actual frame_size of pType (works as expected)
hr = MFGetAttributeSize(
pType,
MF_MT_FRAME_SIZE,
&width,
&height
));
...
//change the framesize
hr = MFSetAttributeSize(
pType,
MF_MT_FRAME_SIZE,
height,
width
));
}
I am sure i miss something here, so any hint will be greatly appreciated.
Thanks in advance
There is a transform available in W8+ that is supposed to do rotation. I haven't had much luck with it myself, but presumably it can be made to work. I'm going to assume that's not a viable solution for you.
The more interesting case is creating an MFT to do the transform.
It turns out there are a number of steps to turn 'Grayscale' into a rotator.
1) As you surmised, you need to affect the frame size on the output type. However, changing the type being passed to SetOutputType is just wrong. The pType being sent to SetOutputType is the type that the client is asking you to support. Changing that media type to something other than what they requested, then returning S_OK to say you support it makes no sense.
Instead what you need to change is the value sent back from GetOutputAvailableType.
2) When calculating the type to send back from GetOutputAvailableType, you need to base it on the IMFMediaType the client sent to SetInputType, with a few changes. And yes, you want to adjust MF_MT_FRAME_SIZE, but you probably also need to adjust MF_MT_DEFAULT_STRIDE, MF_MT_GEOMETRIC_APERTURE, and (possibly) MF_MT_MINIMUM_DISPLAY_APERTURE. Conceivably you might need to adjust MF_MT_SAMPLE_SIZE too.
3) You didn't say whether you intended the rotation amount to be fixed at start of stream, or something that varies during play. When I wrote this, I used the IMFAttributes returned from IMFTransform::GetAttributes to specify the rotation. Before each frame is processed, the current value is read. To make this work right, you need to be able to send MF_E_TRANSFORM_STREAM_CHANGE back from OnProcessOutput.
4) Being lazy, I didn't want to figure out how to rotate NV12 or YUY2 or some such. But there are functions readily available to do this for RGB32. So when my GetInputAvailableType is called, I ask for RGB32.
I experimented with supporting other input types, like RGB24, RGB565, etc, but ran into a problem. When your output type is RGB24, MF adds another MFT downstream to convert the RGB24 back into something it can more easily use (possibly RGB32). And that MFT doesn't support changing media types mid-stream. I was able to get this to work by accepting the variety of subtypes for input, but always outputting RGB32, rotated as specified.
This sounds complicated, but mostly it isn't. If you read the code you'd probably go "Oh, I get it." I'd offer you my source code, but I'm not sure how useful it would be for you. It's in c#, and you were asking about c++.
On the other hand, I'm making a template to make writing MFTs easier. ~A dozen lines of c# code to create the simplest possible MFT. The c# rotation MFT is ~131 lines as counted by VS's Analyze/Calculate code metrics (excluding the template). I'm experimenting with a c++ version, but it's still a bit rough.
Did I forget something? Probably a bunch of things. Like don't forget to generate a new Guid for your MFT instead of using Grayscale's. But I think I've hit the high points.
Edit: Now that my c++ version of the template is starting to work, I feel comfortable posting some actual code. This may make some of the points above clearer. For instance in #2, I talk about basing the output type on the input type. You can see that happening in CreateOutputFromInput. And the actual rotation code is in WriteIt().
I've simplified the code a bit for size, but hopefully this will get you to "Oh, I get it."
void OnProcessSample(IMFSample *pSample, bool Discontinuity, int InputMessageNumber)
{
HRESULT hr = S_OK;
int i = MFGetAttributeUINT32(GetAttributes(), AttribRotate, 0);
i &= 7;
// Will the output use different dimensions than the input?
bool IsOdd = (i & 1) == 1;
// Does the current AttribRotate rotation give a different
// orientation than the old one?
if (IsOdd != m_WasOdd)
{
// Yes, change the output type.
OutputSample(NULL, InputMessageNumber);
m_WasOdd = IsOdd;
}
// Process it.
DoWork(pSample, (RotateFlipType)i);
// Send the modified input sample to the output sample queue.
OutputSample(pSample, InputMessageNumber);
}
void OnSetInputType()
{
HRESULT hr = S_OK;
m_imageWidthInPixels = 0;
m_imageHeightInPixels = 0;
m_cbImageSize = 0;
m_lInputStride = 0;
IMFMediaType *pmt = GetInputType();
// type can be null to clear
if (pmt != NULL)
{
hr = MFGetAttributeSize(pmt, MF_MT_FRAME_SIZE, &m_imageWidthInPixels, &m_imageHeightInPixels);
ThrowExceptionForHR(hr);
hr = pmt->GetUINT32(MF_MT_DEFAULT_STRIDE, &m_lInputStride);
ThrowExceptionForHR(hr);
// Calculate the image size (not including padding)
m_cbImageSize = m_imageHeightInPixels * m_lInputStride;
}
else
{
// Since the input must be set before the output, nulling the
// input must also clear the output. Note that nulling the
// input is only valid if we are not actively streaming.
SetOutputType(NULL);
}
}
IMFMediaType *CreateOutputFromInput(IMFMediaType *inType)
{
// For some MFTs, the output type is the same as the input type.
// However, since we are rotating, several attributes in the
// media type (like frame size) must be different on our output.
// This routine generates the appropriate output type for the
// current input type, given the current state of m_WasOdd.
IMFMediaType *pOutputType = CloneMediaType(inType);
if (m_WasOdd)
{
HRESULT hr;
UINT32 h, w;
// Intentionally backward
hr = MFGetAttributeSize(inType, MF_MT_FRAME_SIZE, &h, &w);
ThrowExceptionForHR(hr);
hr = MFSetAttributeSize(pOutputType, MF_MT_FRAME_SIZE, w, h);
ThrowExceptionForHR(hr);
MFVideoArea *a = GetArea(inType, MF_MT_GEOMETRIC_APERTURE);
if (a != NULL)
{
a->Area.cy = h;
a->Area.cx = w;
SetArea(pOutputType, MF_MT_GEOMETRIC_APERTURE, a);
}
a = GetArea(inType, MF_MT_MINIMUM_DISPLAY_APERTURE);
if (a != NULL)
{
a->Area.cy = h;
a->Area.cx = w;
SetArea(pOutputType, MF_MT_MINIMUM_DISPLAY_APERTURE, a);
}
hr = pOutputType->SetUINT32(MF_MT_DEFAULT_STRIDE, w * 4);
ThrowExceptionForHR(hr);
}
return pOutputType;
}
void WriteIt(BYTE *pBuffer, RotateFlipType fm)
{
Bitmap *v = new Bitmap((int)m_imageWidthInPixels, (int)m_imageHeightInPixels, (int)m_lInputStride, PixelFormat32bppRGB, pBuffer);
if (v == NULL)
throw (HRESULT)E_OUTOFMEMORY;
try
{
Status s;
s = v->RotateFlip(fm);
if (s != Ok)
throw (HRESULT)E_UNEXPECTED;
Rect r;
if (!m_WasOdd)
{
r.Width = (int)m_imageWidthInPixels;
r.Height = (int)m_imageHeightInPixels;
}
else
{
r.Height = (int)m_imageWidthInPixels;
r.Width = (int)m_imageHeightInPixels;
}
BitmapData bmd;
bmd.Width = r.Width,
bmd.Height = r.Height,
bmd.Stride = 4*bmd.Width;
bmd.PixelFormat = PixelFormat32bppARGB;
bmd.Scan0 = (VOID*)pBuffer;
bmd.Reserved = NULL;
s = v->LockBits(&r, ImageLockModeRead + ImageLockModeUserInputBuf, PixelFormat32bppRGB, &bmd);
if (s != Ok)
throw (HRESULT)E_UNEXPECTED;
s = v->UnlockBits(&bmd);
if (s != Ok)
throw (HRESULT)E_UNEXPECTED;
}
catch(...)
{
delete v;
throw;
}
delete v;
}
I have been working with winapi just a little bit, making a project with owner draw on menus. When I called GetMenuItemInfo, it sets the text of the menu item, but not the fType UINT variable flags.
Currently I have declared:
MenuItem->fMask = MIIM_TYPE
And MSDN says:
MIIM_TYPE Retrieves or sets the fType and dwTypeData members.
I don't know If I got confused with the MIIM_TYPE flag.
Here is my code:
void SetOwnerDrawMenu(HMENU * menu)
{
MENUIF * menu_item_information;
HMENU sub_menu_ocational;
UINT uId_menuitem;
int nMenuCountItems = GetMenuItemCount(*menu);
MENUITEMINFO * MenuItem = (MENUITEMINFO*)malloc(sizeof(MENUITEMINFO));
for(int i=0;i<nMenuCountItems;i++)
{
menu_item_information = (MENUIF*)malloc(sizeof(MENUIF));
menu_item_information->isSeparator=false;
menu_item_information->max_width=0;
sub_menu_ocational = 0;
uId_menuitem = GetMenuItemID(*menu,i);
memset(&MenuItem,0,sizeof(MenuItem));
MenuItem = (MENUITEMINFO*)malloc(sizeof(MENUITEMINFO));
MenuItem->cbSize = sizeof(MenuItem);
MenuItem->fMask = MIIM_TYPE;
MenuItem->cch = MAX_ODM_CCH;
MenuItem->dwTypeData = menu_item_information->szItemText;
GetMenuItemInfo(*menu,uId_menuitem,FALSE,MenuItem);
UINT final_flags = MF_BYPOSITION | MF_OWNERDRAW;
if( ( MFT_SEPARATOR & MenuItem->fType ) == MFT_SEPARATOR )
{
final_flags |= MF_SEPARATOR;
menu_item_information->isSeparator = true;
}
else
{
// Not important stuff
}
sub_menu_ocational = GetSubMenu(*menu,i);
if(sub_menu_ocational!=NULL)
{
ModifyMenu(*menu,i,final_flags,0,(LPCTSTR)menu_item_information);
// We got a submenu, repeat this operation
SetOwnerDrawMenu(&sub_menu_ocational);
}
else
{
ModifyMenu(*menu,i,final_flags,0,(LPCTSTR)menu_item_information);
}
}
}
I am inserting the menus with the InsertMenu function:
InsertMenu(tid_cmenu,0,MF_BYPOSITION | MF_SEPARATOR,0,NULL);
InsertMenu(tid_cmenu,0, MF_BYPOSITION | MF_STRING, TID_EXIT, "Exit");
Exactly, why the GetMenuItemInfo is not retriving the fType?
If you were checking the return code from GetMenuItemInfo you would see that it is failing. Your error is in this line:
MenuItem->cbSize = sizeof(MenuItem);
The MENUITEMINFO::cbSize member is supposed to be set to the size of a MENUITEMINFO structure, but you are setting it to the size of a MENUITEMINFO* pointer (i.e. 4 or 8 bytes, depending on the platform).
Change your code to:
MenuItem->cbSize = sizeof(MENUITEMINFO);
Also, your code is allocating MenuItem outside the loop, as well as once per-iteration inside the loop, so you are leaking memory.
Ok. The problem is not syntax or memory size errors.
It is more like 'logic' error and a silly mistake.
The ModifyMenu was changing all the menu items and
setting the fType of each one to NULL or setting the MF_SEPARATOR to all of the items.
That happened because the fourth argument of the ModifyMenu should be the ID of the menu item, I was declaring it as 0.
I changed that argument to the real ID of the menu Item using the GetMenuItemID return value inside the uId_menuitem variable and passing it to the fourth argument of ModifyMenu. That fixed the problem.
Thanks!