Wix Bootstrapper - download bundle packages from a secured http repository - wix

I'm working on a burn bootstrapper that needs to download prerequisites from a http repository that require authentication.
So, how I should handle this request?
Thanks!

Got it! This can be achieved on OnResolveSource() event:
// variable used for authentication
static const LPCWSTR WIXSTDBA_VARIABLE_HTTP_DOWNLOAD_USER = L"HTTPDownloadUserName";
static const LPCWSTR WIXSTDBA_VARIABLE_HTTP_DOWNLOAD_PASS = L"HTTPDownloadPassword";
virtual STDMETHODIMP_(int) OnResolveSource(
__in_z LPCWSTR wzPackageOrContainerId,
__in_z_opt LPCWSTR wzPayloadId,
__in_z LPCWSTR wzLocalSource,
__in_z_opt LPCWSTR wzDownloadSource
)
{
int nResult = IDERROR; // assume we won't resolve source and that is unexpected.
LPWSTR sczHTTPDwnUserName = NULL;
LPWSTR sczHTTPDwnPassword = NULL;
BOOL bUseHTTPAuth = FALSE;
if (BalStringVariableExists(WIXSTDBA_VARIABLE_HTTP_DOWNLOAD_USER))
{
HRESULT hrUsr = BalGetStringVariable(WIXSTDBA_VARIABLE_HTTP_DOWNLOAD_USER, &sczHTTPDwnUserName);
HRESULT hrPwd = BalGetStringVariable(WIXSTDBA_VARIABLE_HTTP_DOWNLOAD_PASS, &sczHTTPDwnPassword);
if (SUCCEEDED(hrUsr) && SUCCEEDED(hrPwd)) bUseHTTPAuth = TRUE;
}
if (BOOTSTRAPPER_DISPLAY_FULL == m_command.display)
{
if (wzDownloadSource)
{
if (bUseHTTPAuth)
{
HRESULT hr = m_pEngine->SetDownloadSource(wzPackageOrContainerId, wzPayloadId, wzDownloadSource, sczHTTPDwnUserName, sczHTTPDwnPassword);
nResult = SUCCEEDED(hr) ? IDDOWNLOAD : IDERROR;
}
else
nResult = IDDOWNLOAD;
}
else // prompt to change the source location.
{
// related stuff
}
}
else if (wzDownloadSource)
{
// If doing a non-interactive install and download source is available, let's try downloading the package silently
if (bUseHTTPAuth)
{
HRESULT hr = m_pEngine->SetDownloadSource(wzPackageOrContainerId, wzPayloadId, wzDownloadSource, sczHTTPDwnUserName, sczHTTPDwnPassword);
nResult = SUCCEEDED(hr) ? IDRETRY : IDERROR;
}
else
nResult = IDDOWNLOAD;
}
// else there's nothing more we can do in non-interactive mode
return CheckCanceled() ? IDCANCEL : nResult;
}

Related

in my shell namespace extension context menu differs from the default one("new" and "properties" items are missed)

I have my custom shell namespace extension.
Just want to have a virtual folder mapped to some folder on disk C:/ with the same functionality.
using namespace ATL;
class ATL_NO_VTABLE CMyShellFolder :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CMyShellFolder, &CLSID_CMyShellFolder>,
public IPersistFolder2,
public IShellFolder2,
public IExplorerPaneVisibility
{
CComHeapPtr<ITEMIDLIST_ABSOLUTE> m_pidl;
CComPtr<IShellFolder2> m_folder;
CComPtr<IThumbnailHandlerFactory> m_thFactory;
CComPtr<IUnknown> m_site;
public:
CMyShellFolder()
{
}
static HRESULT WINAPI UpdateRegistry(BOOL reg) throw();
BEGIN_COM_MAP(CMyShellFolder)
COM_INTERFACE_ENTRY(IShellFolder)
COM_INTERFACE_ENTRY(IShellFolder2)
COM_INTERFACE_ENTRY2(IPersist, IPersistFolder)
COM_INTERFACE_ENTRY(IPersistFolder)
COM_INTERFACE_ENTRY(IPersistFolder2)
COM_INTERFACE_ENTRY(IExplorerPaneVisibility)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
---------------------------------
HRESULT CMyShellFolder::BindToObject(PCUIDLIST_RELATIVE pidl, IBindCtx* pbc, REFIID riid, LPVOID* ppv)
{
if (riid == __uuidof(IShellFolder3))
return E_NOINTERFACE;
HR;
CComObject<CMyShellFolder>* folder = nullptr;
CHECKARG(pidl);
hr = E_NOINTERFACE;
if (riid == IID_IShellFolder ||
riid == IID_IShellFolder2)
{
// check it's a folder
SFGAOF atts = SFGAO_FOLDER;
auto hr2 = GetAttributesOf(1, (PCUITEMID_CHILD_ARRAY)&pidl, &atts);
if (FAILED(hr2) || !(atts & SFGAO_FOLDER))
goto cleanup; // nope, get out
CHECKHR(CreateInstanceAddRef(&folder));
CHECKHR(folder->_Initialize(this, pidl));
CHECKHR(folder->QueryInterface(riid, ppv));
}
RELEASE(folder);
HRONFAIL(L"CMyShellFolder::BindToObject");
}
HRESULT CMyShellFolder::CreateViewObject(HWND hwnd, REFIID riid, LPVOID* ppv)
{
HR;
SFV_CREATE sfvc = { 0 };
DEFCONTEXTMENU dcm = { 0 };
CHECKITEM;
CHECKARG(ppv);
hr = E_NOINTERFACE;
if (riid == IID_IShellView)
{
sfvc.cbSize = sizeof(SFV_CREATE);
CHECKHR(QueryInterface(IID_PPV_ARGS(&sfvc.pshf)));
QueryInterface(IID_PPV_ARGS(&sfvc.psfvcb));
CHECKHR(SHCreateShellFolderView(&sfvc, (IShellView**)ppv));
goto cleanup;
}
if (riid == IID_IContextMenu)
{
dcm.hwnd = hwnd;
//dcm.pidlFolder = (PCIDLIST_ABSOLUTE)m_pidl.m_pData;
QueryInterface(IID_PPV_ARGS(&dcm.pcmcb));
CHECKHR(QueryInterface(IID_PPV_ARGS(&dcm.psf)));
CHECKHR(SHCreateDefaultContextMenu(&dcm, riid, ppv));
goto cleanup;
}
if (riid == IID_ITransferSource || // for delete & file operations
riid == IID_IDropTarget) // for copy paste & dd
{
CHECKHR(m_folder->CreateViewObject(hwnd, riid, ppv));
goto cleanup;
}
CHECKHR(m_folder->CreateViewObject(hwnd, riid, ppv));
cleanup:
RELEASE(dcm.pcmcb);
RELEASE(dcm.psf);
RELEASE(sfvc.psfvcb);
RELEASE(sfvc.pshf);
HRONFAIL(L"CMyShellFolder::CreateViewObject");
}
From the first site everything works fine, but then I noticed a few unclear moments:
Context menu is not the same. "New" and "Properties" are missed.
Pure context menu
Can't rename folder. New name is not applied.
I tried to create CM in different way:
{
HKEY result;
LSTATUS st = RegOpenKeyEx(HKEY_CLASSES_ROOT, L"Directory\\Background", NULL, KEY_QUERY_VALUE, &result); // just check if it exists
if (st == ERROR_SUCCESS)
{
aKeys[cKeys] = result;
cKeys++;
}
}
DEFCONTEXTMENU dcm =
{
hwndOwner,
NULL,
NULL, // _spidl,
static_cast<IShellFolder2 *>(this),
cidl,
rgpidl,
NULL,
cKeys,
aKeys
};
hr = SHCreateDefaultContextMenu(&dcm, riid, ppv);
And "New" item appeared(still no "Properties") but didn't work and leaded explorer to crash.

Read output from Process in custiom action dont return symbols selected language Wix

I use the same code in custom action wix and console app. In custom action it dont return polish symbol "ł" and "ą" in custom action. Replaces them with other symbols or spaces. In console app it works well.
Already in the "message" variable there isnt polish letters.
private static void RunTest(Session session)
{
try
{
Process p = CreateProcess();
p.StartInfo.FileName = "....exe"; ;
p.StartInfo.Arguments = "-t";
string message = "";
int errorCount = 0;
p.OutputDataReceived += (sender, args) =>
{
if (args.Data == null)
return;
message += args.Data;
message += "\n";
};
p.ErrorDataReceived += (sender, args) =>
{
if (args.Data == null)
return;
errorCount++;
message += args.Data;
message += "\n";
};
p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
SaveNewRtf(session, message);
}
catch (Exception)
{
}
}
private static Process CreateProcess()
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
return p;
}
Edit:
This is happen because Massages from Process are in Unicode. But unfortunately I don't know how repair that. I changed encoding to utf-8 for messages in Program I run by Process and still the message are download in Unicode.
I solved it by
p.StartInfo.StandardOutputEncoding = OemEncoding.GetDefaultOemCodePageEncoding();
and
public static class OemEncoding
{
private const Int32 MAX_DEFAULTCHAR = 2;
private const Int32 MAX_LEADBYTES = 12;
private const Int32 MAX_PATH = 260;
private const UInt32 CP_OEMCP = 1;
public static Encoding GetDefaultOemCodePageEncoding()
{
CPINFOEX cpInfoEx;
if (GetCPInfoEx(CP_OEMCP, 0, out cpInfoEx) == 0)
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
"GetCPInfoEx() failed with error code {0}",
Marshal.GetLastWin32Error()));
return Encoding.GetEncoding((int)cpInfoEx.CodePage);
}
[DllImport("Kernel32.dll", EntryPoint = "GetCPInfoExW", SetLastError = true)]
private static extern Int32 GetCPInfoEx(UInt32 CodePage, UInt32 dwFlags, out CPINFOEX lpCPInfoEx);
[StructLayout(LayoutKind.Sequential)]
private unsafe struct CPINFOEX
{
internal UInt32 MaxCharSize;
internal fixed Byte DefaultChar[MAX_DEFAULTCHAR];
internal fixed Byte LeadByte[MAX_LEADBYTES];
internal Char UnicodeDefaultChar;
internal UInt32 CodePage;
internal fixed Char CodePageName[MAX_PATH];
}
}

Quick Access and Namespace Extensions: Unpin object

When I right click on an lnk file - link on a virtual folder in a namespace extension -, then the functions CreateViewObject and/or GetUiObjectOf of my IShellFolder/IShellFolder2 implementations are called. These functions are called with the IID_IContextMenu as parameter (riid). Also when QueryContextMenu is called the flag CMF_VERBSONLY (0x00000002) is set. It means that 1) I know that a menu should be shown and 2) that because of the flag CMF_VERBSONLY that this menu was queried by a .lnk file and that (source Microsoft):
0x00000002. The shortcut menu is that of a shortcut file (normally, a
.lnk file). Shortcut menu handlers should ignore this value.
Most of the time I don't add menu items when this flag is present. When right-clicking on an .lnk file then windows will return a standard menu for .lnk files and the opportunity will be offered to delete this file. I had the same opportunity with the favorites folder on Windows 7. Since Windows 10 and "introduction" of quick access it is not possible anymore. No "Unpin" menu item will be shown by default.
Since it's very difficult from the namespace extension, assuming IID_IContextMenu and CMF_VERBSONLY to know if the object is pinned in quick access and also how to unpin it - I probably would have to open the automatic jump lists folder and then check all the jump list files against my object displayname - , I was wondering if there is an easier way to handle this (at the end jump list are a concatenation of lnk files).
Thanks for your help
Thanks to Simon Mourier's hint, here a possible way to check if a folder (of any type) is pinned in quick access or not...
extern bool __cdecl IsInQuickAccess(LPWSTR folderParsingName)
{
IShellFolder* desktopFolder;
HRESULT hr = SHGetDesktopFolder(&desktopFolder);
bool isInQuickAccess = false;
if (SUCCEEDED(hr))
{
LPITEMIDLIST quickAccessIdList;
hr = desktopFolder->ParseDisplayName(NULL, NULL, _T("shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}"), NULL, &quickAccessIdList, NULL);
if (SUCCEEDED(hr))
{
IShellFolder* quickAccessFolder;
hr = desktopFolder->BindToObject(quickAccessIdList, NULL, IID_PPV_ARGS(&quickAccessFolder));
if (SUCCEEDED(hr))
{
IEnumIDList* currentChildren = NULL;
hr = quickAccessFolder->EnumObjects(NULL, SHCONTF_FOLDERS, &currentChildren);
if (SUCCEEDED(hr))
{
CString wPathToFolder = CharLower(folderParsingName);
LPITEMIDLIST childPidl = NULL;
while (!isInQuickAccess && currentChildren->Next(1, &childPidl, NULL) == S_OK)
{
STRRET childDisplayName;
hr = quickAccessFolder->GetDisplayNameOf(childPidl, SHGDN_FORPARSING, &childDisplayName);
if (SUCCEEDED(hr))
{
LPWSTR childDisplayNameString;
hr = StrRetToStr(&childDisplayName, NULL, &childDisplayNameString);
if (SUCCEEDED(hr))
{
LPWSTR childDisplayNameStringToLower = CharLower(childDisplayNameString);
if (wPathToFolder.Compare(childDisplayNameStringToLower) == 0)
isInQuickAccess = true;
CoTaskMemFree(childDisplayNameString);
}
}
}
CoTaskMemFree(childPidl);
currentChildren->Release();
}
quickAccessFolder->Release();
}
CoTaskMemFree(quickAccessIdList);
}
desktopFolder->Release();
}
return isInQuickAccess;
}
and here unpin from home (with check if folder with given display name is in quick access).
extern void __cdecl UnpinFromHome(LPWSTR folderParsingName)
{
IShellFolder* desktopFolder;
HRESULT hr = SHGetDesktopFolder(&desktopFolder);
if (SUCCEEDED(hr))
{
LPITEMIDLIST quickAccessIdList;
hr = desktopFolder->ParseDisplayName(NULL, NULL, _T("shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}"), NULL, &quickAccessIdList, NULL);
if (SUCCEEDED(hr))
{
IShellFolder* quickAccessFolder;
hr = desktopFolder->BindToObject(quickAccessIdList, NULL, IID_PPV_ARGS(&quickAccessFolder));
if (SUCCEEDED(hr))
{
IEnumIDList* currentChildren = NULL;
hr = quickAccessFolder->EnumObjects(NULL, SHCONTF_FOLDERS, &currentChildren);
if (SUCCEEDED(hr))
{
CString wPathToFolder = CharLower(folderParsingName);
LPITEMIDLIST childPidl = NULL;
bool isInQuickAccess = false;
while (!isInQuickAccess && currentChildren->Next(1, &childPidl, NULL) == S_OK)
{
STRRET childDisplayName;
hr = quickAccessFolder->GetDisplayNameOf(childPidl, SHGDN_FORPARSING, &childDisplayName);
if (SUCCEEDED(hr))
{
LPWSTR childDisplayNameString;
hr = StrRetToStr(&childDisplayName, NULL, &childDisplayNameString);
if (SUCCEEDED(hr))
{
LPWSTR childDisplayNameStringToLower = CharLower(childDisplayNameString);
if (wPathToFolder.Compare(childDisplayNameStringToLower) == 0)
{
IContextMenu* childContextMenu;
LPCITEMIDLIST childCPidl = childPidl;
hr = quickAccessFolder->GetUIObjectOf(NULL, 1, &childCPidl, IID_IContextMenu, NULL, (void**)&childContextMenu);
if (SUCCEEDED(hr))
{
HMENU hmenu = CreatePopupMenu();
if (hmenu)
{
hr = childContextMenu->QueryContextMenu(hmenu, 0, 1, 0x7FFF, CMF_NORMAL);
if (SUCCEEDED(hr))
{
CMINVOKECOMMANDINFO info = { 0 };
info.cbSize = sizeof(info);
info.hwnd = NULL;
info.lpVerb = "unpinfromhome";
info.nShow = 1;
info.fMask = CMIC_MASK_ASYNCOK;
childContextMenu->InvokeCommand(&info);
}
DestroyMenu(hmenu);
}
}
isInQuickAccess = true;
}
CoTaskMemFree(childDisplayNameString);
}
}
}
CoTaskMemFree(childPidl);
currentChildren->Release();
}
quickAccessFolder->Release();
}
CoTaskMemFree(quickAccessIdList);
}
desktopFolder->Release();
}
}

Getting ICLRRuntimeInfo instance is null C++

I'm getting ICLRRuntimeInfo instance is null C++
DWORD pid = 2076;
HRESULT hr;
HANDLE hProcess;
ICLRMetaHost *pMetaHost = NULL;
IEnumUnknown *ppEnumerator = NULL;
ICLRRuntimeInfo *CLRRuntimeInfo = NULL;
ULONG pFetched = 0;
DWORD versionLength;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (hProcess == NULL) {
printf("process unable to open");
return 0;
}
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost,*)&pMetaHost);
pMetaHost->EnumerateLoadedRuntimes(hProcess, &ppEnumerator);
if (ppEnumerator == 0) {
return 0;
}
hr = ppEnumerator->Next(1, (IUnknown **)&CLRRuntimeInfo, &pFetched);
if (hr == S_FALSE) {
printError(TEXT("CLRRuntimeInfo"));
return 0;
}
When I tried with EnumerateInstalledRuntimes, got all installed Runtimes
pMetaHost->EnumerateInstalledRuntimes( &ppEnumerator)
Windows error i got :failed with error 18 (There are no more files)
In Project properties -> VC++ Directories -> Library Dirctories-> $(NETFXKitsDir)Lib\um\x86
But I'm building solution with x64. Now changed to x86. working fine.

how to use ICLRStrongName in .NET 4?

Related to my previous posts I'm moving to .NET 4. I've found that using the previous StrongName.h to get my assembly signing key in unmanaged code is now deprecated, and I need to use MetaHost.h and ICLRStrongName::StrongNameTokenFromAssembly.
The previous StrongNameTokenFromAssembly(..) was very straight forward, now this new one has no documentation on how to use. Does anyone have experience with this interface?
Wow... that required a lot of hacking around. Here we go!
ICLRMetaHost *pMetaHost = NULL;
HRESULT hr = CLRCreateInstance(CLSID_CLRMetaHost,
IID_ICLRMetaHost, (LPVOID*)&pMetaHost);
if(hr == S_OK)
{
WCHAR version[100];
DWORD size;
hr == pMetaHost->GetVersionFromFile(MyGetApplicationExecutablePath().c_str(), (LPWSTR) &version, &size);
if(hr == S_OK)
{
LPWSTR assemblyVer = version;
ICLRRuntimeInfo *pRuntimInfo = NULL;
hr = pMetaHost->GetRuntime(assemblyVer, IID_ICLRRuntimeInfo, (LPVOID*)&pRuntimInfo);
if (hr == S_OK)
{
ICLRStrongName *pStrongName = NULL;
hr = pRuntimInfo->GetInterface(CLSID_CLRStrongName, IID_ICLRStrongName, (LPVOID*)&pStrongName);
if(hr == S_OK)
{
pStrongName->StrongNameTokenFromAssembly(MyGetApplicationExecutablePath().c_str(), &token, &len);
DWORD verified = 0;
BOOLEAN sigVerified = pStrongName->StrongNameSignatureVerification(MyGetApplicationExecutablePath().c_str(), SN_INFLAG_FORCE_VER , &verified);
if (!verified)
{
//Do something nasty here if the Signature verification failed
}
pStrongName->StrongNameFreeBuffer(token);
}
}
}
}