For a web application, in the msi installer, I used a custom dll and trying the following C++ code to enable 32-bit application on win64 for an application pool, the hr result shows success but still the Enable32BitAppOnWin64 is false for the application pool. pSiteElem is IAppHostElement type and already validated by getting the name of the application pool successfully.
VARIANT vtEnable32Bit;
vtEnable32Bit.vt = VT_BOOL;
vtEnable32Bit.boolVal = true;
hr = pSiteElem->SetMetadata(L"Enable32BitAppOnWin64", vtEnable32Bit);
if(FAILED(hr))
{
MessageBox(NULL, L"Enable32BitAppOnWin64: ",L"FAILED", MB_OK);
}
else
{
MessageBox(NULL, L"Enable32BitAppOnWin64: ",L"success", MB_OK);
}
Related
I'm writing a c++ Win32 Desktop app which shows a Toast notification containing a progress bar. I'm trying to bind this progress bar with my application in order to be able to update it.
The Toast interface containing the progress bar is defined as follow:
<toast>
<visual>
<binding template='ToastGeneric'>
<text>Backup in progress</text>
<progress title='Working folder' value='0' status='Starting backup...' valueStringOverride='0/1 files' />
</binding>
</visual>
</toast>
I'm using the below code to show the Toast:
HRESULT CreateNotificationData(ABI::Windows::UI::Notifications::INotificationData** pData)
{
if (!pData)
return E_INVALIDARG;
*pData = nullptr;
IInspectable* pInstance;
HRESULT hr = ::RoActivateInstance(Microsoft::WRL::Wrappers::HStringReference(RuntimeClass_Windows_UI_Notifications_NotificationData).Get(),
&pInstance);
if (FAILED(hr))
return hr;
hr = pInstance->QueryInterface(pData);
pInstance->Release();
return hr;
}
bool ShowProgress(const std::wstring toastContent)
{
if (!toastContent.length())
return false;
// build the XML from the Toast content
::CComPtr<ABI::Windows::Data::Xml::Dom::IXmlDocument> pDoc;
HRESULT hr = DesktopNotificationManagerCompat::CreateXmlDocumentFromString(toastContent.c_str(), &pDoc);
if (FAILED(hr))
return false;
// create the notifier. Classic Win32 apps MUST use the Compat method to create the notifier
::CComPtr<ABI::Windows::UI::Notifications::IToastNotifier> pNotifier;
hr = DesktopNotificationManagerCompat::CreateToastNotifier(&pNotifier);
if (FAILED(hr))
return false;
// create the Toast notification (using helper method from Compat library)
::CComPtr<ABI::Windows::UI::Notifications::IToastNotification> pToast;
hr = DesktopNotificationManagerCompat::CreateToastNotification(pDoc, &pToast);
if (FAILED(hr))
return false;
// get the IToastNotification4 interface, which contains the required data binding functions
::CComPtr<ABI::Windows::UI::Notifications::IToastNotification4> pToastData;
hr = pToast->QueryInterface(ABI::Windows::UI::Notifications::IID_IToastNotification4, (void**)&pToastData);
if (FAILED(hr))
return false;
// create a notification data instance
::CComPtr<ABI::Windows::UI::Notifications::INotificationData> pNotificationData;
hr = CreateNotificationData(&pNotificationData);
if (FAILED(hr))
return false;
// get the values map from the Toast
::CComPtr<__FIMap_2_HSTRING_HSTRING> pValues;
hr = pNotificationData->get_Values(&pValues);
if (FAILED(hr))
return false;
// create the values to bind
HSTRING pValue;
::WindowsCreateString(L"value", 5, &pValue);
HSTRING pValueVal;
::WindowsCreateString(L"0.1", 3, &pValueVal);
HSTRING pStatus;
::WindowsCreateString(L"status", 6, &pStatus);
HSTRING pStatusVal;
::WindowsCreateString(L"Test test test", 14, &pStatusVal);
boolean replaced;
// add the values in the Toast values map as a key/value pair value
pValues->Insert(pValue, pValueVal, &replaced);
pValues->Insert(pStatus, pStatusVal, &replaced);
::WindowsDeleteString(pValue);
::WindowsDeleteString(pValueVal);
::WindowsDeleteString(pStatus);
::WindowsDeleteString(pStatusVal);
// set the sequence number to 1 in order to refresh the interface (need to be increased
// every time the Toast should be refreshed)
hr = pNotificationData->put_SequenceNumber(1);
if (FAILED(hr))
return false;
// set the data to bind in the Toast
hr = pToastData->put_Data(pNotificationData);
if (FAILED(hr))
return false;
// show the Toast
hr = pNotifier->Show(pToast);
if (FAILED(hr))
return false;
return true;
}
This code builds and executes without problems, unfortunately my progress bar is not updated.
As you can see, this code is very similar to (indeed based on) the following examples:
https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/toast-progress-bar?tabs=builder-syntax
https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/notifications/app-notifications/app-notifications-quickstart?tabs=cs#send-and-update-a-progress-bar-notification
There is a difference, however. These examples insert the notification data into the progress component, whereas I insert the notification data into the root Toast component, as you can see on the below screenshot.
Unfortunately, if I know how to access to the Toast root component, I have absolutely no idea about how to get the child progress component contained in my Toast, and how to bind my values in it.
So the question is: how may I access the child progress component in my Toast and bind my values with it, considering that my application is a c++ Win32 Desktop app?
I'm trying to rewrite some code that calls a local COM Server from C# to C++. The C# code works without issue. The key part is:
Guid lr_FactoryGuid = Guid.Parse("AE7CFA4B-985A-4F76-8CC6-2011649FC8A9");
Guid lr_FactoryClass = Guid.Parse("1CA0D073-4ABB-4D06-B318-BFFDE38E4903");
IntPtr lk_FactoryPtr = new IntPtr();
CoGetClassObject(
ref lr_FactoryClass,
4,
new IntPtr(),
ref lr_FactoryGuid,
out lk_FactoryPtr);
if (lk_FactoryPtr == IntPtr.Zero)
{
MessageBox.Show("lk_FactoryPtr == IntPtr.Zero");
return false;
}
I've tried to rewrite this into C++ and I can't get any further than here, the error is give as "No such interface supported":
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CLSID clsid;
HRESULT hr = CLSIDFromString(L"{1CA0D073-4ABB-4D06-B318-BFFDE38E4903}", &clsid);
CLSID iid;
hr = CLSIDFromString(L"{AE7CFA4B-985A-4F76-8CC6-2011649FC8A9}", &iid);
void* pIFace;
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, iid, &pIFace);
if (!SUCCEEDED(hr))
{
_com_error err(hr);
LPCTSTR errMsg = err.ErrorMessage();
MessageBox(NULL, errMsg, L"SiteKiosk demo", MB_ICONEXCLAMATION | MB_OK);
}
There is a .tlb file that I used to generate the interop DLL for C# and to import into the C++, however it's currently commented out of the C++ in an attempt to keep the code smaller and I still get this error from CoCreateInstance.
The COM application I'm calling is a 32 bit app, so both my C# and C++ clients applications are also 32 bit. Both of the clients are Windows Console applications.
Is there anything else I need to set/do to get the C++ working?
The suggestion by Hans solved the problem, I used CoGetClassObject and the rest of the code then clicked into place.
I am using Media Foundation to create a webcam viewer.
Critical to this application is that the webcam stream is horizontally mirrored.
I am using the Video Processor MFT
to achieve this.
Here's the relevant code to add the MFT:
void tryMirror(IMFPMediaPlayer* pPlayer) {
IMFTransform* pMFT = NULL;
IMFVideoProcessorControl* pVPC = NULL;
HRESULT hr = CoCreateInstance(CLSID_VideoProcessorMFT, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pMFT));
if (FAILED(hr)) {
ShowHResultErrorMessage(L"CoCreateInstance(CLSID_VideoProcessorMFT, ...) failed", hr);
goto done;
}
hr = pMFT->QueryInterface(&pVPC);
if (FAILED(hr)) {
ShowHResultErrorMessage(L"pMFT->QueryInterface(&pVPC) failed", hr);
goto done;
}
hr = pVPC->SetMirror(MIRROR_HORIZONTAL);
if (FAILED(hr)) {
ShowHResultErrorMessage(L"pVPC->SetMirror(MIRROR_HORIZONTAL) failed", hr);
goto done;
}
hr = pPlayer->InsertEffect(pMFT, FALSE); // Not optional - critical functionality
if (FAILED(hr)) {
ShowHResultErrorMessage(L"m_pPlayer->InsertEffect(CLSID_VideoProcessorMFT) failed", hr);
goto done;
}
done:
SafeRelease(&pMFT);
SafeRelease(&pVPC);
}
// class CPreview implements IMFPMediaPlayerCallback as follows
void STDMETHODCALLTYPE CPreview::OnMediaPlayerEvent(MFP_EVENT_HEADER* pEventHeader) {
switch (pEventHeader->eEventType)
{
//...
case MFP_EVENT_TYPE_MEDIAITEM_SET:
OnMediaItemSet(MFP_GET_MEDIAITEM_SET_EVENT(pEventHeader));
break;
case MFP_EVENT_TYPE_PLAY:
OnMfpPlay(MFP_GET_PLAY_EVENT(pEventHeader));
break;
}
}
// Called after I set the webcam media source
void CPreview::OnMediaItemSet(MFP_MEDIAITEM_SET_EVENT* /*pEvent*/)
{
HRESULT hr = m_pPlayer->Play();
if (FAILED(hr)) {
ShowHResultErrorMessage(L"m_pPlayer->Play() failed", hr);
}
}
void CPreview::OnMfpPlay(MFP_PLAY_EVENT* pEvent) {
if (FAILED(pEvent->header.hrEvent)) {
ShowHResultErrorMessage(L"OnMfpPlay failed", pEvent->header.hrEvent);
WCHAR msg[1000];
HRESULT hr = StringCbPrintf(msg, sizeof(msg), L"Event type: 0x%X", pEvent->header.eEventType);
ShowErrorMessage(msg);
return;
}
}
void ShowHResultErrorMessage(PCWSTR errContext, HRESULT hrErr) {
_com_error err(hrErr);
LPCTSTR hrErrMsg = err.ErrorMessage();
WCHAR msg[1000];
HRESULT hr = StringCbPrintf(msg, sizeof(msg), L"%s (HRESULT=0x%X, %s)", errContext, hrErr, hrErrMsg);
if (SUCCEEDED(hr)) {
MessageBox(NULL, msg, L"Error", MB_ICONERROR);
}
}
On my development machine, this program runs without error, exactly as desired.
However, on a different user machine,
it fails with this error:
OnMfpPlay failed (HRESULT=0xC00D36B2, The request is invalid in the current state.)
That is,
this error comes through on the OnMediaPlayerEvent callback
of the IMFPMediaPlayerCallback object.
I do know a few things about the machine that this fails on:
The user has also run a modified version,
with the MFT set to optional, like so:
pPlayer->InsertEffect(pMFT, TRUE).
In this case, the program runs,
but the mirroring MFT has no effect.
The error is definitely caused by this MFT.
This user is running Windows 10, version 1909.
The Video Processor MFT is clearly available.
Its API claims to work - all HRESULTs are successful.
This error, "The request is invalid in the current state", could mean anything, and
I can't find any way to get more observability.
What does 'The request is invalid in the current state' mean?
Why is it generated by adding a Video Processor MFT, only on some machines?
How can I debug this with a more specific error?
I have an installer that tries to (re)start my application in the current user context after the installation is done.
The installer runs in the SYSTEM context and before launching the application it attempts (and theoretically succeeds) to impersonate the current user. However, when I look in the task manager, I see that my application is running in the SYSTEM context.
This is (a snippet from) my code:
TCHAR szUsername[128] = _T("");
DWORD dwUsernameSize = 128;
GetUserName(szUsername, &dwUsernameSize);
// Lets the calling process impersonate the security context of a logged-on user.
if (!ImpersonateLoggedOnUser(hToken))
{
throw Win32Exception(GetLastError(), _T("Failed to impersonate current user"));
}
TCHAR szUsername2[128] = _T("");
DWORD dwUsernameSize2 = 128;
GetUserName(szUsername2, &dwUsernameSize2);
MLOGD(_T("ProcessUtils::StartProcessInCurrentUserContext: Successfully impersonated %s"), szUsername2);
ProcessUtils::StartProcess(sExeName, lstParams, sWorkingDir, bWaitToFinish, errCode);
ProcessUtils::StartProcess is a wrapper around CreateProcess.
szUsername contains SYSTEM and szUsername2 contains the current user. So ImpersonateLoggedOnUser is successful.
However, as mentioned above, the process is started in the SYSTEM context, not the current user one.
I'm not sure how helpful this might be, but my installer is written in NSIS and it's calling the function that contains the code from above via a plugin written in C/C++.
Does anyone know why my application doesn't start in the current user context?
Win32 CreateProcess creates a process in the same security context as the caller which is SYSTEM (even though you are impersonating).
Think you need to be calling CreateProcessAsUser.
I had a very similar problem a couple of years ago when I was also
working on an installer application. After A LOT of frustration, caused
by failed attempts to start an application in the context of the current
user using CreateProcessAsUser, I've finally given up. After a thorough
search on the web, I've found a briliant implementation that uses
IShellDispatch2 interface. Here is an example:
#include <Windows.h>
#include <exdisp.h>
#include <Shobjidl.h>
#include <Shlwapi.h>
#include <comutil.h>
#include <SHLGUID.h>
#include <cstdlib>
#include <iostream>
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "comsuppw.lib")
bool ShellExecuteAsCurrentUser(const TCHAR *pcOperation, const TCHAR *pcFileName, const TCHAR *pcParameters,
const TCHAR *pcsDirectory, const DWORD dwShow)
{
bool bSuccess = false;
IShellWindows *psw = NULL;
HRESULT hr = CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw));
if(SUCCEEDED(hr))
{
HWND hwnd = 0;
IDispatch* pdisp = NULL;
_variant_t vEmpty;
if(S_OK == psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, reinterpret_cast<long*>(&hwnd), SWFO_NEEDDISPATCH, &pdisp))
{
if((hwnd != NULL) && (hwnd != INVALID_HANDLE_VALUE))
{
IShellBrowser *psb;
hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb));
if(SUCCEEDED(hr))
{
IShellView *psv = NULL;
hr = psb->QueryActiveShellView(&psv);
if(SUCCEEDED(hr))
{
IDispatch *pdispBackground = NULL;
HRESULT hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground));
if(SUCCEEDED(hr))
{
IShellFolderViewDual *psfvd = NULL;
hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd));
if(SUCCEEDED(hr))
{
IDispatch *pdisp = NULL;
hr = psfvd->get_Application(&pdisp);
if(SUCCEEDED(hr))
{
IShellDispatch2 *psd;
hr = pdisp->QueryInterface(IID_PPV_ARGS(&psd));
if(SUCCEEDED(hr))
{
_variant_t verb(pcOperation);
_variant_t file(pcFileName);
_variant_t para(pcParameters);
_variant_t dir(pcsDirectory);
_variant_t show(dwShow);
if(SUCCEEDED(psd->ShellExecute(file.bstrVal, para, vEmpty, verb, show)))
bSuccess = true;
psd->Release();
psd = NULL;
}
pdisp->Release();
pdisp = NULL;
}
}
pdispBackground->Release();
pdispBackground = NULL;
}
psv->Release();
psv = NULL;
}
psb->Release();
psb = NULL;
}
}
pdisp->Release();
pdisp = NULL;
}
psw->Release();
psw = NULL;
}
return bSuccess;
}
int main(int argc, char *argv[])
{
CoInitialize(NULL);
if(ShellExecuteAsCurrentUser(L"open", L"notepad", nullptr, nullptr, SW_SHOWNORMAL))
std::cout << "SUCCESS" << std::endl;
CoUninitialize();
return 0;
}
This is just a quick demo, the implementation of ShellExecuteAsCurrentUser can be
improved by using smart pointers for COM interfaces and some refactoring. This method
worked for me on versions WinXP SP3 - Win 8.1, not sure if it works on Windows 10. For
more details, check the authors github page:
https://github.com/lordmulder/stdutils/tree/master/Contrib/StdUtils
If you had read the documentation for CreateProcess, you would have found the answer to your question in the first three sentences:
Creates a new process and its primary thread. The new process runs in the security context of the calling process.
If the calling process is impersonating another user, the new process uses the token for the calling process, not the impersonation token.
There really isn't much else to say; the behaviour you describe is as documented. If you want to create a process as another user, you must use CreateProcessAsUser or one of the related functions.
I've created a C++ DLL (it must be in C++) which dinamically links a .Net DLL to host a Web Service Server. The .Net DLL passes the web service calls to the C++ DLL, which as evaluated and responded also through the .Net DLL.
Something like that:
HSPWebService.DLL
-----------------------------------
HSPProxy.DLL | HSPWebServiceLib.DLL
-----------------------------------
HSPSendData.DLL
HSPWebService.DLL - the proper C++ DLL.
HSPProxy.DLL - proxy generated by the MIDL compiler over the HSP.DLL MIDL interface.
HSPWebServiceLib.DLL - typelib generated by the tlmbimp over the HSP.DLL.
HSPSendData.DLL - .Net DLL which hosts the web service server
Everything works like a charm. The problem is when the DLL files are in a network share (//myPc/share). My application log shows the error 0x80131515:
CreateAssemblyInstance ERROR: Could not create an assembly instance. (hr=80131515)
On my research I've found that the 0x80131515 error occurs because the .Net framework won't load assemblies from external sources by default. For .Net projects the option can be set into the project settings. But I have a C++ project on Visual Studio 2010 and I have no clue about using this configuration on my project (or my code). Any ideas?
The CreateAssemblyInstance function:
HRESULT CHSPWebServiceObjectHost::CreateAssemblyInstance(_AppDomain* pDefAppDomain, CComPtr<IDispatch>& spDisp, LPCTSTR pszAsseblyName, LPCTSTR pszClassNameWithNamespace) const
{
spDisp = NULL;
REQUIRE_IN_POINTER(pDefAppDomain);
try
{
_bstr_t _bstrAssemblyName(pszAsseblyName);
_bstr_t _bstrszClassNameWithNamespace(pszClassNameWithNamespace);
//Creates an Assembly instance
CComPtr<_ObjectHandle> spObjectHandle;
HRESULT hr = pDefAppDomain->CreateInstanceFrom(_bstrAssemblyName, _bstrszClassNameWithNamespace, &spObjectHandle);
if (FAILED(hr))
{
Log(logDriver, _T("CHSPWebServiceObjectHost::CreateAssemblyInstance ERROR: Could not create an assembly instance. (hr=%08X)"), hr);
return hr;
}
CComVariant VntUnwrapped;
hr = spObjectHandle->Unwrap(&VntUnwrapped);
if (FAILED(hr))
{
Log(logDriver, _T("CHSPWebServiceObjectHost::CreateAssemblyInstance ERROR: Could not unwrap assembly object. (hr=%08X)"), hr);
return hr;
}
spDisp = VntUnwrapped.pdispVal;
}
catch (_com_error& e)
{
return e.Error();
}
return S_OK;
}
It is called by the StartCLR function:
HRESULT CHSPWebServiceObjectHost::StartCLR(CComPtr<ICorRuntimeHost>& spRuntimeHost, CComPtr<IDispatch>& spDispHost) const
{
spRuntimeHost = NULL;
spDispHost = NULL;
//Retrieve a pointer to the ICorRuntimeHost interface
HRESULT hr = CorBindToRuntimeEx(L"v4.0.30319",
L"wks",
STARTUP_LOADER_SAFEMODE | STARTUP_CONCURRENT_GC,
CLSID_CorRuntimeHost,
IID_ICorRuntimeHost,
(void**)&spRuntimeHost);
if (FAILED(hr))
{
Log(logDriver, _T("CHSPWebServiceObjectHost::StartCLR ERROR: Could not load CLR into unmanaged host process. ( hr=%08X)"), hr);
return hr;
}
//Start the CLR
hr = spRuntimeHost->Start();
if (FAILED(hr))
{
Log(logDriver, _T("CHSPWebServiceObjectHost::StartCLR ERROR: Could not start CLR. (hr=%08X)"), hr);
return hr;
}
//Retrieve the IUnknown default AppDomain
CComPtr<IUnknown> spUnknown;
hr = spRuntimeHost->GetDefaultDomain(&spUnknown);
if (FAILED(hr))
{
Log(logDriver, _T("CHSPWebServiceObjectHost::StartCLR ERROR: Could not retrieve pointer to domain interface. ( hr=%08X)"), hr);
return hr;
}
CComQIPtr<_AppDomain> spDefAppDomain(spUnknown);
if (spDefAppDomain == NULL)
return E_NOINTERFACE;
CString strAssemblyFullPath = _T(".\\HSPSendData.dll");
return CreateAssemblyInstance(spDefAppDomain, spDispHost, strAssemblyFullPath, _T("Elipse.HSPWebService. HSPWebServiceHost"));
}
Given that your assemblies are located on a network share, you need to add a <loadFromRemoteSources> element to your app.config file. For example:
<configuration>
<runtime>
<loadFromRemoteSources enabled="true"/>
</runtime>
</configuration>
You don't mention your host process, but assuming it is MyService.exe this would go in the file MyService.exe.config.
Aparently the .NET updates have resolved this issue. I've tried to backtrack which update had it done but I wasn't able to find it.