I am writing a window wrapper for win32 to make gui creation easier. I have an abstractdisplay class, a displayclass class, and a displayclass. In the end, the window does not show up. After some debugging, my windowclass is not being registered properly. After GetLastError, I got the error code of INVALID PARAMETER
displayclass.h:
#pragma once
#include <Windows.h>
#include "abstractdisplay.h"
class DisplayClass : protected WNDCLASSEX
{
public:
// Public variables
private:
// Private variables
protected:
// Protected variables
public:
// Public functions
DisplayClass(HINSTANCE hInst, const TCHAR* className);
DisplayClass();
~DisplayClass();
// Registers the class
// Get the class name
virtual const TCHAR* getClassName() const { return lpszClassName; }
virtual bool Register();
private:
// Private functions
protected:
// Protected functions
};
displayclass.cpp:
#include "displayclass.h"
#include "abstractdisplay.h"
#include <string>
#include <cstring>
DisplayClass::DisplayClass(HINSTANCE hInst, const TCHAR* className)
{
hInstance = hInst;
// All messages for windows that belong to this Window Class will be sent to Message Router
lpfnWndProc = AbstractDisplay::MessageRouter;
lpszClassName = className;
// Set values for the rest of the WNDCLASSEX structure
ZeroMemory(this, sizeof(WNDCLASSEX));
lpszMenuName = 0;
cbSize = sizeof(WNDCLASSEX);
cbClsExtra = 0;
cbWndExtra = 0;
hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
hIconSm = ::LoadIcon(NULL, IDI_APPLICATION);
hCursor = ::LoadCursor(NULL, IDC_ARROW);
style = CS_HREDRAW | CS_VREDRAW;
hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
}
DisplayClass::DisplayClass()
{
}
DisplayClass::~DisplayClass()
{
}
// Returns the last Win32 error, in string format. Returns an empty string if there is no error.
std::string GetLastErrorAsString()
{
// Get the error message, if any.
DWORD errorMessageID = ::GetLastError();
if (errorMessageID == 0)
return std::string(); // No error message has been recorded
LPSTR messageBuffer = nullptr;
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
std::string message(messageBuffer, size);
// Free the buffer.
LocalFree(messageBuffer);
return message;
}
bool DisplayClass::Register()
{
if (::RegisterClassEx(this) != 0)
{
return true;
}
else
{
OutputDebugString("ERROR CODE BE LIKE:");
OutputDebugString(GetLastErrorAsString().c_str());
OutputDebugString("\n");
return false;
}
}
Debug:
ERROR CODE BE LIKE:The parameter is incorrect.
(Somewhat following this source: http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper)
You're calling ZeroMemory after assigning some of the struct members (i.e. hInstance, lpfnWndProc and lpszClassName will all be null).
Related
C++/MFC, Windows 11. Visual Studio 2022 17.4.3.
I'm using CFileDialog to allow user to chose a file. By creating a new class derived from CFileDialog, I am being notified whenever the user changes directories (folders).
I implemented this so I could control the filter applied to the list of files in the directory. However, I have been unsuccessful in this. Even if I don't change m_ofn, I get an error.
Here is some sample code:
// Caller
#include "Browsing_test.h"
P brTest(true, NULL, NULL, 0, fileTypes);
brTest.BrowseTest();
// Browsing_test.h
class P : CFileDialog
{
public:
P(BOOL bOpenFileDialog,
LPCTSTR lpszDefExt = NULL,
LPCTSTR lpszFileName = NULL,
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
LPCTSTR lpszFilter = NULL,
CWnd *pParentWnd = NULL,
DWORD dwSize = 0,
BOOL bVistaStyle = TRUE) : CFileDialog(bOpenFileDialog,
lpszDefExt,
lpszFileName,
dwFlags,
lpszFilter,
pParentWnd,
dwSize,
bVistaStyle) {};
int BrowseTest(void);
#include "stdafx.h"
#include "Browsing_test.h"
int P::BrowseTest(void)
{
int resultDebug = (int)DoModal();
return resultDebug;
}
void P::OnFolderChange()
{
auto s = GetOFN(); // for modifying m_ofn member of the base class,
// but not used in this sample code
// Add modificatons to m_ofn here
ApplyOFNToShellDialog(); // Gets assert on updating flags
}
Running this code gives an error in dlgfile.cpp (Microsoft code) at line:
hr = (static_cast<IFileDialog*>(m_pIFileDialog))->SetOptions(dwFlags);
which returns hr = E_UNEXPECTED Catastrophic failure. The value of dwFlags was hex 40.
I'd like to change the font of a few dialogs. The fonts of these dialogs are not to be changed using the ressource editor they are to be changed at runtime.
The dialogs in question are all based on ATL/WTL and they're declared pretty much like the following example:
class CDiscardErrorDlg :
public CDialogImpl<CDiscardErrorDlg>,
public CDialogResize<CDiscardErrorDlg>
{
}
My question is how to change the font for a whole CDialogImpl derived class.
As far as I know, changing the DLGTEMPLATE is the way to go.
But I have no idea on how to achieve that!?!
Where do I have access to DLGTEMPLATE's?
Is CDialogImpl the correct class to solve my problem?
Do you have an idea or web reference which might help me on that problem?
just wanted to let you know that I have found a solution to the problem:
Here's what to do:
derive a class from CDialogImpl
overwrite DoModal
load the DLGTEMPLATE template in memory and
take an instance of CDialogTemplate to change the template's font
pass the modified template to DialogBoxIndirectParam
template <class T, class TBase = CWindow >
class ATL_NO_VTABLE CDialogImplEx : public CDialogImpl<T, TBase>
{
public:
INT_PTR DoModal(
_In_ HWND hWndParent = ::GetActiveWindow(),
_In_ LPARAM dwInitParam = NULL)
{
T* pT = static_cast<T*>(this);
ATLASSERT(pT->m_hWnd == NULL);
LPDLGTEMPLATE pTemplate = nullptr;
HINSTANCE hInstance = AfxGetResourceHandle();
HRSRC hDlg = AtlFindResource(hInstance, MAKEINTRESOURCE(static_cast<T*>(this)->IDD), RT_DIALOG);
if (hDlg != NULL)
{
HRSRC hDlgInit = AtlFindResource(hInstance, MAKEINTRESOURCE(static_cast<T*>(this)->IDD), _ATL_RT_DLGINIT);
HGLOBAL hData = NULL;
BYTE* pInitData = NULL;
if (hDlgInit)
{
hData = ::LoadResource(hInstance, hDlgInit);
ATLASSUME(hData != nullptr);
pInitData = (BYTE*) ::LockResource(hData);
}
DWORD dwLastError = 0;
HGLOBAL hResource = LoadResource(hInstance, hDlg);
if (hResource != nullptr)
{
DLGTEMPLATE* pTempl = (DLGTEMPLATE*)LockResource(hResource);
CDialogTemplate DialogTempl(pTempl);
DialogTempl.SetFont(_T("Segoe UI"), 20); // Set a huge font
HGLOBAL hDialogTemplate = DialogTempl.Detach();
pTemplate = (DLGTEMPLATE*)::GlobalLock(hDialogTemplate);
::FreeResource(hResource);
hResource = nullptr;
}
else
{
dwLastError = ::GetLastError();
}
}
#if (_ATL_VER >= 0x0800)
// Allocate the thunk structure here, where we can fail gracefully.
BOOL bRet = m_thunk.Init(nullptr, nullptr);
if (bRet == FALSE)
{
::SetLastError(ERROR_OUTOFMEMORY);
return -1;
}
#endif // (_ATL_VER >= 0x0800)
_AtlWinModule.AddCreateWndData(&m_thunk.cd, (ATL::CDialogImplBaseT< TBase >*)pT);
#ifdef _DEBUG
m_bModal = true;
#endif // _DEBUG
INT_PTR nRet = ::DialogBoxIndirectParam(hInstance, pTemplate, hWndParent, (DLGPROC)T::StartDialogProc, dwInitParam);
if (nRet == -1)
{
DWORD dwErr = ::GetLastError();
dwErr = 0;
}
return nRet;
//return CDialogImpl<T>::DoModal(hWndParent, dwInitParam);
}
};
Hope it'll be helpful to someone, too.
Best regards,
I was trying to wrap CFileDialog and IFileOpenDialog in a class, here is the code:
class ITest
{
public:
virtual ~ITest(){};
virtual INT_PTR DoModal() = 0;
virtual IFileOpenDialog* GetDlg() = 0;
};
class test : public ITest
{
public:
test(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs
LPCTSTR lpszDefExt = NULL,
LPCTSTR lpszFileName = NULL,
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
LPCTSTR lpszFilter = NULL,
CWnd* pParentWnd = NULL,
DWORD dwSize = 0,
BOOL bVistaStyle = FALSE)
{
dlg = new CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd, dwSize, bVistaStyle);
};
~test(){};
INT_PTR DoModal(){ return S_OK; };
IFileOpenDialog* GetDlg(){ return dlg->GetIFileOpenDialog(); };
private:
CFileDialog* dlg;
};
Then when I call it like:
ITest* a = new test(TRUE, NULL, 0, OFN_ALLOWMULTISELECT | OFN_NOVALIDATE, LoadResourceString(IDS_MYSTRING), this);
fdlg = a->GetDlg();
fdlg->Show(NULL);//it crashes here since fdlg is NULL
Then I tried to call CFileDialog and IFileOpenDialog directly:
IFileOpenDialog* fdlg = NULL;
CFileDialog* b = new CFileDialog(TRUE, NULL, 0, OFN_ALLOWMULTISELECT | OFN_NOVALIDATE, LoadResourceString(IDS_MYSTRING), this);
fdlg = b->GetIFileOpenDialog();
fdlg->Show(NULL);
This part of code works perfectly.
Anyone knows why the crash happens ?
It confuses me so much since I feel they are so similar.
class test : public ITest
{
public:
test(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs
LPCTSTR lpszDefExt = NULL,
LPCTSTR lpszFileName = NULL,
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
LPCTSTR lpszFilter = NULL,
CWnd* pParentWnd = NULL,
DWORD dwSize = 0,
BOOL bVistaStyle = FALSE)
{
dlg = new CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd, dwSize, bVistaStyle);
};
~test(){};
INT_PTR DoModal(){ return S_OK; };
IFileOpenDialog* GetDlg();
private:
CFileDialog* dlg;
};
IFileOpenDialog* test::GetDlg()
{
return dlg->GetIFileOpenDialog();
};
The above code also doesn't work( it defines IFileOpenDialog* GetDlg(); outside of the class declartion)
From the help:
Use this function only under Windows Vista with an object that has
bVistaStyle set to true. This function returns NULL if the CFileDialog
is not an Open dialog box or if bVistaStyle is set to false.
When you construct your test object:
ITest* a = new test(TRUE, NULL, 0, OFN_ALLOWMULTISELECT | OFN_NOVALIDATE, LoadResourceString(IDS_MYSTRING), this);
you are leaving our the last parameter of your constructor, bVistaStyle, which defaults to FALSE. So just add it:
ITest* a = new test(TRUE, NULL, 0, OFN_ALLOWMULTISELECT | OFN_NOVALIDATE, LoadResourceString(IDS_MYSTRING), this, 0,
TRUE); // bVistaStyle
When error occurs in the program, on the screen we can see something like this:
Is there anyway to determine this situation using c++ winapi? I have aldready tried to use this code to find out if the main thread of the procces is suspend. But it doesn't.
I also tried to send timeot messages(code below) but result is always true, even if error window have appeared.
typedef struct tagENUMINFO
{
// In Parameters
DWORD PId;
// Out Parameters
HWND hWnd;
HWND hEmptyWnd;
HWND hInvisibleWnd;
HWND hEmptyInvisibleWnd;
} ENUMINFO, *PENUMINFO;
BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
{
DWORD pid = 0;
PENUMINFO pInfo = (PENUMINFO)lParam;
TCHAR szTitle[_MAX_PATH+1];
// sanity checks
if (pInfo == NULL)
// stop the enumeration if invalid parameter is given
return(FALSE);
// get the processid for this window
if (!::GetWindowThreadProcessId(hWnd, &pid))
// this should never occur :-)
return(TRUE);
// compare the process ID with the one given as search parameter
if (pInfo->PId == pid)
{
// look for the visibility first
if (::IsWindowVisible(hWnd))
{
// look for the title next
if (::GetWindowText(hWnd, szTitle, _MAX_PATH) != 0)
{
pInfo->hWnd = hWnd;
// we have found the right window
return(FALSE);
}
else
pInfo->hEmptyWnd = hWnd;
}
else
{
// look for the title next
if (::GetWindowText(hWnd, szTitle, _MAX_PATH) != 0)
{
pInfo->hInvisibleWnd = hWnd;
}
else
pInfo->hEmptyInvisibleWnd = hWnd;
}
}
// continue the enumeration
return(TRUE);
}
HWND GetMainWindow(DWORD PId)
{
ENUMINFO EnumInfo;
// set the search parameters
EnumInfo.PId = PId;
// set the return parameters to default values
EnumInfo.hWnd = NULL;
EnumInfo.hEmptyWnd = NULL;
EnumInfo.hInvisibleWnd = NULL;
EnumInfo.hEmptyInvisibleWnd = NULL;
// do the search among the top level windows
::EnumWindows((WNDENUMPROC)EnumWindowsProc, (LPARAM)&EnumInfo);
// return the one found if any
if (EnumInfo.hWnd != NULL)
return(EnumInfo.hWnd);
else if (EnumInfo.hEmptyWnd != NULL)
return(EnumInfo.hEmptyWnd);
else if (EnumInfo.hInvisibleWnd != NULL)
return(EnumInfo.hInvisibleWnd);
else
return(EnumInfo.hEmptyInvisibleWnd);
}
DWORD GetProcessByExeName(char *ExeName)
{
DWORD Pid;
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
return false;
}
if (Process32First(hProcessSnap, &pe32))
{
do
{
if (strcmpi(pe32.szExeFile, ExeName) == 0)
{
CloseHandle(hProcessSnap);
return pe32.th32ProcessID;
}
} while (Process32Next(hProcessSnap, &pe32));
}
CloseHandle(hProcessSnap);
return 0;
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE prev, LPSTR cmdline, int show)
{
HWND Hwnd;
LRESULT res;
DWORD PID;
PID=GetProcessByExeName("procces.exe");
Hwnd=GetMainWindow(PID);
res = SendMessageTimeout(Hwnd, WM_NULL, NULL, NULL, SMTO_ABORTIFHUNG, 3000,NULL);
//res == 1 always
}
Yes there is a way, all crash interceptors work this way, like the firefox crash reporter.
On windows you can use structured exception handling:
reference:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms680657(v=vs.85).aspx
and howto:
http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus
extract:
LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionPtrs)
{
// Do something, for example generate error report
//..
// Execute default exception handler next
return EXCEPTION_EXECUTE_HANDLER;
}
void main()
{
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
// .. some unsafe code here
}
I found another solution. This error message has it's own procces WerFault.exe, we can simply destroy it with TerminateProcess() and the hang procces will be destroyed too. And then it is quite simple to notice that the required process does not exist.
Need to set the port on XPS printer. I found some example on Stackoverflow but it doesnt work.
Here is a code(Lots of trash):
LPTSTR pDeviceName = _T("Microsoft XPS Document Writer");
HANDLE phPrinter(nullptr);
PRINTER_DEFAULTS defaults;
defaults.DesiredAccess = PRINTER_ACCESS_USE;
defaults.pDatatype = 0;
PORT_INFO_3 pInfo3;;
DWORD needed;
DWORD XcvResult;
DWORD err = OpenPrinter(pDeviceName,&phPrinter,NULL);
//const BYTE* portValue = reinterpret_cast<const BYTE*>("TestPort");
PBYTE port = (PBYTE)_T("Test1");
if(err) {
int res = XcvData(phPrinter,_T("AddPort"),port,sizeof(port),NULL,0,&needed,&XcvResult);
}
else {
AfxMessageBox(_T("ERROR."),MB_OK);
}
ClosePrinter(phPrinter);
the funniest thing that this code worked just once(the first starting of XcvData func)!
Another example the same behaviour:
BOOL AddPortX(void)
{
DWORD cbneed,cbstate;
PBYTE pOutputData;
HANDLE hXcv = INVALID_HANDLE_VALUE;
PRINTER_DEFAULTS Defaults = { NULL,NULL,SERVER_ACCESS_ADMINISTER };
WCHAR pszPortName[]=L"UTReportPDFPort:";
pOutputData=(PBYTE)malloc(MAX_PATH);
if(!OpenPrinter(_T("Microsoft XPS Document Writer"),&hXcv,NULL ))
{
LPVOID lpMsgBuf;
GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), NULL,(LPTSTR) &lpMsgBuf, 0, NULL );
::MessageBox(NULL,(LPCTSTR)lpMsgBuf,_T("ERROR"),MB_OK|MB_ICONINFORMATION);
free(pOutputData);
LocalFree( lpMsgBuf );
return FALSE;
}
// False
if(!XcvData(hXcv,L"AddPort",(PBYTE)pszPortName,sizeof(pszPortName),(PBYTE)pOutputData,MAX_PATH,&cbneed,&cbstate))
{
LPVOID lpMsgBuf;
SetLastError(cbstate);
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), NULL,(LPTSTR) &lpMsgBuf, 0, NULL );
::MessageBox(NULL,(LPCTSTR)lpMsgBuf,_T("ERROR"),MB_OK|MB_ICONINFORMATION);
LocalFree( lpMsgBuf );
free(pOutputData);
}
free(pOutputData);
ClosePrinter(hXcv);
return TRUE;
}
So, how to set add printer port right, and automatically select it after adding?
Maybe, somebody knows why it works just once? I mean - the XcvData function. All next times it returns the error code 6.
The .NET solution would be good too.
public static class Winspool
{
[StructLayout(LayoutKind.Sequential)]
private class PRINTER_DEFAULTS
{
public string pDatatype;
public IntPtr pDevMode;
public int DesiredAccess;
}
[DllImport("winspool.drv", EntryPoint = "XcvDataW", SetLastError = true)]
private static extern bool XcvData(
IntPtr hXcv,
[MarshalAs(UnmanagedType.LPWStr)] string pszDataName,
IntPtr pInputData,
uint cbInputData,
IntPtr pOutputData,
uint cbOutputData,
out uint pcbOutputNeeded,
out uint pwdStatus);
[DllImport("winspool.drv", EntryPoint = "OpenPrinterA", SetLastError = true)]
private static extern int OpenPrinter(
string pPrinterName,
ref IntPtr phPrinter,
PRINTER_DEFAULTS pDefault);
[DllImport("winspool.drv", EntryPoint = "ClosePrinter")]
private static extern int ClosePrinter(IntPtr hPrinter);
public static int AddLocalPort(string portName)
{
PRINTER_DEFAULTS def = new PRINTER_DEFAULTS();
def.pDatatype = null;
def.pDevMode = IntPtr.Zero;
def.DesiredAccess = 1; //Server Access Administer
IntPtr hPrinter = IntPtr.Zero;
int n = OpenPrinter(",XcvMonitor Local Port", ref hPrinter, def);
if (n == 0)
return Marshal.GetLastWin32Error();
if (!portName.EndsWith("\0"))
portName += "\0"; // Must be a null terminated string
// Must get the size in bytes. Rememeber .NET strings are formed by 2-byte characters
uint size = (uint)(portName.Length * 2);
// Alloc memory in HGlobal to set the portName
IntPtr portPtr = Marshal.AllocHGlobal((int)size);
Marshal.Copy(portName.ToCharArray(), 0, portPtr, portName.Length);
uint needed; // Not that needed in fact...
uint xcvResult; // Will receive de result here
XcvData(hPrinter, "AddPort", portPtr, size, IntPtr.Zero, 0, out needed, out xcvResult);
ClosePrinter(hPrinter);
Marshal.FreeHGlobal(portPtr);
return (int)xcvResult;
}
}
Resource is from CodeProject
The first parameter in OpenPrinter() have to be - XcvMonitor Local Port.
Than we can use .NET management objects to select port is default.