CoInitializeSecurity error - windows

I have an .exe that loads 1.dll and 1.dll loads 1_1.dll.
In the .exe I create multiple threads, from one of them I call a function that calls upon a 1.dll function that between other things .. calls a function from 1_1.dll that fails in doing this:
// Initialize COM.
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if( FAILED(hr) )
{
//m_iStatus = ERROR_COINITIALIZE_EX;
return;
}
// Set general COM security levels.
hr = CoInitializeSecurity(
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
0,
NULL);
if( FAILED(hr) )
{
CoUninitialize();
//m_iStatus = ERROR_COINITIALIZE_SEC;
return;
}
It falis on the call to CoInitializeSecurity with this message:
Security must be initialized before any interfaces are marshalled or unmarshalled. It cannot be changed once initialized.
Can anyone expalin me what is happening here, what am I doing wrong and how should I fix this?

CoInitializeSecurity function...
... is called exactly once per process, either explicitly or implicitly
The only case DLL might need to call CoInitializeSecurity is when it is loaded into process, which is known to not initialize COM on its own. An the process is at all basically a thin host for the DLL. That is, almost never.
It is .EXE's task to do CoInitializeSecurity.

Related

How to show a QDialog on a custom Windows Desktop (HDESK)

Solution
After several tests and good leads from the community, I can now confirm that it is mandatory to apply SetThreadDesktop before creating the QApplication. This is the only way to allow Qt to display QWidgets in the new desktop.
However, SwitchDesktop can be called before or after the QApplication, it doesn't matter as long as SetThreadDesktop has been called before the creation of the QApplication.
Thanks to all of you! I hope this topic will be useful for other people.
Problem
I'm working on a Qt project on Windows where i need to create a custom desktop (HDESK) using API such as CreateDesktop, OpenDesktop and SwitchDesktop. On this new Desktop, i need to show a QDialog (using exec or show depending on my needs) but the QDialog is not displayed on the new desktop but on the "default" one.
My code looks something like this :
/* Starting the QApplication from the main function */
QApplication qApp;
qApp.exec();
/* [...] An HEVENT is fired or a Win32 COM call is made telling me than i need to display the QDialog */
/* Creating the new desktop and switching to it */
HDESK hNewDesktop=NULL;
hNewDesktop=CreateDesktop(L"_MyNewDesktopName", NULL, NULL, DF_ALLOWOTHERACCOUNTHOOK, GENERIC_ALL, NULL);
SwitchDesktop(hNewDesktop);
/* Creating the QDialog and showing it */
QDialog *pqdlgMyDialog=NULL;
pqdlgMyDialog=new QDialog(NULL);
pqdlgMyDialog.show(); // or .exec() depending on the my needs
Doing this, create and display a new desktop with a black background but the QDialog is displayed on the default desktop (the one we see when we start Windows).
I have to tried to set a parent to my QDialog doing something like this but it does not work either :
QWindow *pqwParentWindow=NULL;
QWidget *pwqParentWidget=NULL;
HWND hwndParentWindow=NULL;
hwndParentWindow=GetTopWindow(NULL); // I have also tried GetDesktopWindow, FindWindowEx etc.
pqwParentWindow=QWindow::fromWinId((WId)hwndParentWindow);
pwqParentWidget=QWidget::createWindowContainer(pqwParentWindow);
[...]
QDialog *pqdlgMyDialog=NULL;
pqdlgMyDialog=new QDialog(pwqParentWidget);
pqdlgMyDialog.show(); // or .exec() depending on the my needs
If someone has an idea, i'm willing to try everything !
I have done a lot of reading of Qt Documentation and MSDN, looking for stuff such as "Is a QApplication linked to a desktop" but without success...
HWNDs are tied to threads and threads with HWNDs are tied to desktops.
The SetThreadDesktop function will fail if the calling thread has any windows or hooks on its current desktop (unless the hDesktop parameter is a handle to the current desktop).
I don't know if QApplication creates a hidden window or if you have another HWND there but it is easy to demonstrate that SetThreadDesktop will fail if there is already a window on the thread:
HDESK g_hNew;
DWORD CALLBACK TrySwitch(LPVOID Param)
{
if (Param)
{
CreateWindow(L"EDIT", L"Existing window", WS_VISIBLE|WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, 0, 0, 0, 0);
}
if (SetThreadDesktop(g_hNew))
{
SwitchDesktop(g_hNew);
CreateWindow(L"EDIT", L"New window", WS_VISIBLE|WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, 0, 0, 0, 0);
}
Sleep(3333);
return 0;
}
void Example()
{
HDESK hIn = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, false, DESKTOP_SWITCHDESKTOP);
g_hNew = CreateDesktop(L"_MyNewDesktopName", NULL, NULL, DF_ALLOWOTHERACCOUNTHOOK, GENERIC_ALL, NULL);
WaitForSingleObject(CreateThread(NULL, 0, TrySwitch, (void*) TRUE, 0, NULL), 3333); // Leaks thread handle and I don't care
WaitForSingleObject(CreateThread(NULL, 0, TrySwitch, (void*) FALSE, 0, NULL), 3333); // Leaks thread handle and I don't care
SwitchDesktop(hIn);
CloseDesktop(hIn);
CloseDesktop(g_hNew);
}

LsaLookupAuthenticationPackage returns 0xc00000fe

I'm implementing a credential provider, and inside the credential's method there is this function to look for the correct authentication package to use
HRESULT RetrieveNegotiateAuthPackage(ULONG * pulAuthPackage)
{
HRESULT hr;
HANDLE hLsa;
NTSTATUS status = LsaConnectUntrusted(&hLsa);
if (SUCCEEDED(HRESULT_FROM_NT(status)))
{
ULONG ulAuthPackage;
LSA_STRING lsaszKerberosName;
LsaInitString(&lsaszKerberosName, NEGOSSP_NAME);
status = LsaLookupAuthenticationPackage(hLsa, &lsaszKerberosName, &ulAuthPackage);
if (SUCCEEDED(HRESULT_FROM_NT(status)))
{
*pulAuthPackage = ulAuthPackage;
hr = S_OK;
}
else
{
hr = HRESULT_FROM_NT(status);
}
LsaDeregisterLogonProcess(hLsa);
}
else
{
hr= HRESULT_FROM_NT(status);
}
return hr;
}
When I call the API LsaLookupAuthenticationPackage, it returns 0xc00000fe (an error). Here is the values of the parameters I got from debugging:
hLsa (it can vary each time) 0x00391c60
lsaszKerberosName L"Negotiate"
Does anyone know what possibly causes this? And what to do to fix it? Thanks :)
according to ms api doc, the package name parameter is defined by the following macros:
MSV1_0_PACKAGE_NAME
MICROSOFT_KERBEROS_NAME_A
NEGOSSP_NAME_A
the macro defines the type of the authentication package that you want to use
and furthermore, you must use the ascii type intead of unicode
https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsalookupauthenticationpackage

MFC CWnd::Create triggers assertion wincore.cpp

I working on an MFC class which derives from CWnd and creates a hidden window in its constructor. The object itself is constructed inside a derived CWinApp::InitInstance function.
if (
this->CWnd::Create(
nullptr,
nullptr,
WS_DISABLED, // Even disabled it will receive broadcast messages.
{0, 0, 0, 0},
CWnd::GetDesktopWindow(),
fakeWindowId
) == FALSE
)
throw runtime_error{"failed to create window"};
When I run this code in a debug build it triggers the following assertion:
Debug Assertion Failed!
Program: C:\WINDOWS\SYSTEM32\mfc140ud.dll File:
f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp Line: 571
For information on how your program can cause an assertion failure,
see the Visual C++ documentation on asserts.
(Press Retry to debug the application)
If I ignore the assertion, the code works fine and no ill effect is observable. How do I address this assertion?
I'm registering the window as follows as well:
BOOL HiddenWindow::PreCreateWindow(CREATESTRUCTW& cs)
{
if ( ! CWnd::PreCreateWindow(cs))
return FALSE;
cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
WNDCLASSEXW wc;
ZeroMemory(&wc, sizeof(WNDCLASSEXW));
wc.cbSize = sizeof(WNDCLASSEXW);
const auto instance{AfxGetInstanceHandle()};
if (GetClassInfoExW(instance, this->className_.c_str(), &wc) == FALSE)
{
wc.lpszClassName = this->className_.c_str();
if ( ! RegisterClassExW(&wc))
{
Logger::Fatal(
"Registering the window for copy data message failed! Messages will not be "
"copied, error code {}.",
GetLastError()
);
return FALSE;
}
}
else
Logger::Debug(
"There is already a window registered under the class name '{}'.",
toString(this->className_)
);
cs.lpszClass = _wcsdup(this->className_.c_str());
return TRUE;
}
So, I was unable to ever figure out what caused the MFC assertion. The solution was to remove the MFC window entirely and replace it with a Win32 window underneath the class, i.e. CreateWindowExW, GetClassinfoExW, and RegisterClassExW.

MFC Custom HtmlHelp

I'm doing maintenance on a legacy MFC application. When the user selects Help → Help from the main menu along the top of the app, I need my custom OnHelp() to be called. From my research, I've learned that MFC normally intercepts this command automatically and brings up your help file itself. But you can override this command and intercept this message yourself. I have this in my message map:
BEGIN_MESSAGE_MAP(MyApp, MyBaseApp)
//{{AFX_MSG_MAP(MyApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
ON_COMMAND(ID_HELP, OnHelp)
//}}AFX_MSG_MAP
// Standard file based document commands
END_MESSAGE_MAP()
The "About" OnAppAbout() gets called, but not my OnHelp() (MFC still intercepts it and brings up the help by itself). My implementation is pretty straightforward:
void MyApp::OnHelp()
{
// This never gets called
MessageBox( NULL, "HtmlHelp: MyApp", "Hey", MB_OK );
CString csHelpFile;
csHelpFile.Format( "%s/MyHelp.chm", MyDoc::GetHelpPath() );
::HtmlHelp(
NULL,
csHelpFile,
HELP_WM_HELP,
NULL );
}
I know it's not getting called because my MessageBox never appears (I can't use Visual Studio to debug this; message boxes only). I also tried to wedge it into the CMDIFrameWnd, with the message map and similar implementation, with no success either (a different item from the Help menu item is implemented here and works fine). Any idea what I need to do to hook into my own custom help function?
You need to add ON_WM_HELPINFO() into your CMainFrame's message map. Define afx_msg BOOL OnHelpInfo(HELPINFO* pHelpInfo); in your header file for the Main Frame, and implement it in the cpp file:
BOOL CMainFrame::OnHelpInfo(HELPINFO* pHelpInfo)
{
#ifdef _DEBUG
MessageBox( NULL, "HtmlHelp: MyApp", "Hey", MB_OK );
#endif
CString csHelpFile;
csHelpFile.Format( "%s/MyHelp.chm", MyDoc::GetHelpPath() );
::HtmlHelp(
NULL,
csHelpFile,
HELP_WM_HELP,
NULL );
return CFrameWnd::OnHelpInfo(pHelpInfo);
}

Make IWebBrowser2 Control safe for scripting

I'm using IWebBrowser2 control in my application to display external web pages.
The problem is that the object is not safe for scripting and calls to get_Document fails (with S_FALSE as return value).
I've implemented a new class, IScriptableWebBrowser2 that inherits both from IWebBrowser2 & IObjectSafety, and tried to use it instead of IWebBrowser2 but that didn't do the trick.
How do I make my IWebBrowser2 control safe for scripting ?
class IScriptableWebBrowser2 :
public CComObjectRootEx<CComSingleThreadModel>,
public IWebBrowser2,
public IObjectSafety
{
BEGIN_COM_MAP(IScriptableWebBrowser2)
COM_INTERFACE_ENTRY(IObjectSafety)
END_COM_MAP()
// IObjectSafety implementation
STDMETHODIMP GetInterfaceSafetyOptions(REFIID riid,
DWORD *pdwSupportedOptions,
DWORD *pdwEnabledOptions )
{
*pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER |
INTERFACESAFE_FOR_UNTRUSTED_DATA;
*pdwEnabledOptions = *pdwSupportedOptions;
return S_OK;
}
STDMETHODIMP SetInterfaceSafetyOptions(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
{
return S_OK;
}
};
I guess you created the browser in a thread and passed it on to another thread.
If that's the case, here's what you should do:
Before passing the IWebBrowser2 instance to another thread, in the creating thread, call CoMarshalInterThreadInterfaceInStream, that will marshal (convert) it to a thread-safe IStream object, and only then pass it to the tagert thread.
Later, in the target thread, you should call CoGetInterfaceAndReleaseStream with the previously marshaled IStream instance, which will unmarshal it back to your original object in the target thread (and release the IStream object along the way).
CoMarshalInterThreadInterfaceInStream in MSDN
CoGetInterfaceAndReleaseStream in MSDN
Hope that helps.
Well, I finally had some time to come back to this one..
It turns out that get_Document fails if you call it BEFORE the page completely loaded but the return value (S_FALSE) indicates a completely different error ("not safe for scripting")
btw, Loading local pages will give you the desirable behavior.
Therefore, calling get_Document after the page was loaded (DISPID_NAVIGATECOMPLETE2, DISPID_DOWNLOADCOMPLETE, DISPID_DOCUMENTCOMPLETE) will do the trick.
Hope this helps.

Resources