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

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.

Related

Call dll function from vb.net

I have CAD dll it's called plclient_kompas.dll. This is a library for standard machinebuilding parts.
In plclient_kompas.dll contains function PLInsert3D, that puts standard part from library to 3D assembly.
In manual to this library said that:
HRESULT **PLInsert3D**(
[in] IPARTLibProvider* aPLClient;
[in] IBOResponseDisp* aMethodResponse;
[in] ksPlacement* aPlacement;
[out, retval] IDispatch** result);
So I want to call this function from vs2017 vb.net code like this:
<DllImport("C:\Program Files\ASCON\KOMPAS-3D v17\Libs\PARTLib\Clients\plclient_kompas.dll", CallingConvention:=CallingConvention.StdCall)>
Public Shared Function **PLInsert3D**(aPLClient As IPARTLibProvider, aMethodResponse As BOResponse, aPlacement As ksPlacement) As ksPart
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
iMainPart = **PLInsert3D**(aPLClient, aMethodResponse, aPlacement)
End Sub
But in this line:
iMainPart = **PLInsert3D**(aPLClient, aMethodResponse, aPlacement)
I have error access violation exception.
What I do wrong? Any help will be appreciated. Thanks!
Addition what I wrote.
This is my code in vb.net:
Imports Kompas6API5
Imports KompasAPI7
Imports PARTLibClient
Imports BOSimple
Imports Kompas6Constants3D
Imports System.Runtime.InteropServices
Imports stdole
Public Class Form1
<DllImport("C:\Program Files\ASCON\KOMPAS-3D v17\Libs\PARTLib\Clients\plclient_kompas.dll", CallingConvention:=CallingConvention.StdCall)>
Public Shared Function PLInsert3D(ByRef aPLClient As IPARTLibProvider, ByRef aMethodResponse As BOResponse, ByRef aPlacement As ksPlacement) As <MarshalAs(UnmanagedType.Interface)> ksPart
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim kompas As Object
Dim ksdoc As Object
kompas = New Kompas6API5.Application
kompas.Visible = True
ksdoc = kompas.Document3D
ksdoc.Create(False, False)
ksdoc.Filename = "C:\Users\Baha1990\Documents\Visual Studio 2017\Projects\Kompas3D\3d files\1.a3d"
ksdoc.UpdateDocumentParam
ksdoc.Save
Dim aPLClient As BOSimpleProvider
aPLClient = New PARTLibClient.BOSimpleProvider
Dim Location As String
Dim Response As Object
Location = ""
Response = aPLClient.SelectEx(0, 0, Location, 0)
Dim aMethodResponse As BOResponse
aMethodResponse = aPLClient.GetMethod(Location, "КОМПАС 3D")
Dim iDoc3D As ksDocument3D
Dim iMainPart As ksPart
Dim aPlacement As ksPlacement
iDoc3D = kompas.ActiveDocument3D
iMainPart = iDoc3D.GetPart(-1)
aPlacement = iMainPart.GetPlacement
aPlacement.SetOrigin(0, 0, 0)
iMainPart = PLInsert3D(aPLClient, aMethodResponse, aPlacement)
End Sub
This is a sample from API manual in C#:
// Тестовая библиотека вставки объекта в КОМПАС из СИ
/*
* Необходимо у подключаемых библиотек BOSimple и PARTLibClient выставить значение параметра
* Embed Interop Types = False
*
* */
using System;
using Microsoft.Win32;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using KAPITypes;
using Kompas6Constants;
using Kompas6API5;
using Kompas6API7;
using PARTLibClient;
using BOSimple;
using Kompas6Constants3D;
using stdole;
using System.Runtime.Versioning;
namespace PartLibAPI
{
[ClassInterface(ClassInterfaceType.AutoDual)]
public class InsertKompas
{
const string _LIBRARYNAME = "Вставка изделия в Компас из СИ";
const string _KEYNAME = #"SOFTWARE\Classes\CLSID\{";
const string _KOMPASLIBRARY = "Kompas_Library";
const string _DLLPATH = "plclient_kompas.dll";
const string _KERNEL32 = "kernel32.dll";
#region Методы для работы библиотеки в Компас
private KompasObject _kompas;
// Название библиотеки
[return: MarshalAs(UnmanagedType.BStr)]
public string GetLibraryName()
{
return _LIBRARYNAME;
}
// Головная функция библиотеки
public void ExternalRunCommand([In] short command, [In] short mode, [In, MarshalAs(UnmanagedType.IDispatch)] object kompas_)
{
_kompas = (KompasObject)kompas_;
switch (command)
{
case 1:
InsertInKompas(_kompas);
break;
}
}
// Функция получения меню
// Описание функции можно найти в SDK API Компас
[return: MarshalAs(UnmanagedType.BStr)]
public string ExternalMenuItem(short number, ref short itemType, ref short command)
{
var result = string.Empty;
itemType = 1; // "MENUITEM"
switch (number)
{
case 1:
result = "Вставить";
command = 1;
break;
//
// Для корректной работы библиотеки необходимо добавить конец меню (или подменю)
//
case 2:
command = -1;
itemType = 3; // "ENDMENU"
break;
}
return result;
}
#endregion
// Делегаты для работы с методами из plclient_kompas.dll
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int _PLInsert2D(IPARTLibProvider aPLClient, IBOResponse aMethodResponse, double aX, double aY, double aAngle);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate void _PLInsert3D([Out] out IDispatch retVal, IPARTLibProvider aPLClient, IBOResponse aMethodResponse, ksPlacement aPlacement);
private void InsertInKompas(KompasObject kompas)
{
// Подключаемся к plclient_kompas.dll (получаем ссылку на запущенный экземпляр в процессе Компаса)
IntPtr pDll = IntPtr.Zero;
try
{
pDll = LoadLibrary(_DLLPATH);
if (pDll == IntPtr.Zero)
throw new Exception("Не могу найти plclient_kompas.dll");
// Получаем адреса экспортируемых функций из ранее загруженной plclient_kompas.dll
var pProc2D = GetProcAddress(pDll, "PLInsert2D");
var pProc3D = GetProcAddress(pDll, "PLInsert3D");
// Получаем экземпляры делегатов, приведенных к соответствующему типу
var PLInsert2D = (_PLInsert2D)Marshal.GetDelegateForFunctionPointer(pProc2D, typeof(_PLInsert2D));
var PLInsert3D = (_PLInsert3D)Marshal.GetDelegateForFunctionPointer(pProc3D, typeof(_PLInsert3D));
IApplication kompasApp = kompas.ksGetApplication7();
if (kompasApp == null)
throw new Exception("Не удалось получить указатель на интерфейс приложения!");
var doc2D = (Document2D)kompas.ActiveDocument2D();
var doc3D = (Document3D)kompas.ActiveDocument3D();
if ((doc2D == null) && (doc3D == null))
throw new Exception("Откройте документ или создайте новый!");
// Подключаемся к API справочника
IPARTLibProvider PLClient = null;
try
{
PLClient = new BOSimpleProvider();
var errorMsg = String.Empty;
// Подключаемся к серверу приложений
if (PLClient.Connect(ref errorMsg) == 0)
{
try
{
// Получаем доступ к изделиям и папкам в справочнике
var commonData = PLClient.CreateCommonDataObj();
commonData.InitUserData();
string plm2D = "2D"; // Документ 2D
string plm3D = "Solid3D"; // Документ 3D
// Типы документов Компаса
string[] methodNames = { "", "КОМПАС 2D", "КОМПАС 2D", "КОМПАС 3D", "КОМПАС 3D", "КОМПАС 3D", "" };
string[] docName = { "?", plm2D, plm2D, plm3D, plm3D, plm3D, "?" };
// Получаем тип документа
var docType = GetDocType(kompasApp);
ksPart mainPart = null;
ksPart pPart = null;
ksPlacement partPlace = null;
if (docType == DocumentTypeEnum.ksDocumentAssembly)
{
if (doc3D != null)
{
mainPart = doc3D.GetPart((short)Part_Type.pTop_Part);
if (mainPart != null)
{
pPart = doc3D.GetPart(0);
partPlace = pPart != null ? pPart.GetPlacement() : mainPart.GetPlacement();
}
}
}
// Задаем режим работы справочника в зависимости от типа активного документа в КОМПАС
commonData.SetFindOptions(docName[(int)docType]);
// Путь до корневого каталога с ISO стандартами по уникальному ID
var ISOFolder = commonData.FolderByID("A259_151417DFF6474BF6");
// Просматриваем каталог в глубину
while (ISOFolder.FolderCollection().Count() > 0)
{
ISOFolder = ISOFolder.FolderCollection().Folder(0);
// В случае если в каталоге есть хотя бы один класс
if (ISOFolder.ClassCollection().Count() > 0)
{
// Получаем указатель на интерфейс первой типоразмерной таблицы его изделий
var ISOClass = ISOFolder.ClassCollection().plClass(0);
var instance = ISOClass.Instances("", docName[(int)docType]);
var paramGrid = instance.ParamGrid(0);
// Просматриваем типоразмерную таблицу сверху-вниз
for (int i = 0; i < paramGrid.PGRowCount(); i++)
{
// Выбираем строку
paramGrid.SelectRow(i);
// Получаем полный location получившегося изделия
var location = instance.plPart().GetLocation(0);
// Получаем метод для вставки изделия в зависимости от типа активного документа
var methodResponce = PLClient.GetMethod(location, methodNames[(int)docType]);
// Вставляем изделие в КОМПАС
var X = 5 * i * Math.Sin(15 * Math.PI * i / 180);
var Y = 5 * i * Math.Cos(15 * Math.PI * i / 180);
var Angle = 180 - i * 15;
IDispatch kPart;
switch (docType)
{
case DocumentTypeEnum.ksDocumentAssembly:
// Задаем место в сборке
partPlace.SetOrigin(X, Y, 0);
PLInsert3D(out kPart, PLClient, methodResponce, partPlace);
break;
case DocumentTypeEnum.ksDocumentDrawing:
PLInsert2D(PLClient, methodResponce, X, Y, Angle);
break;
case DocumentTypeEnum.ksDocumentFragment:
PLInsert2D(PLClient, methodResponce, X, Y, Angle);
break;
case DocumentTypeEnum.ksDocumentSpecification:
PLInsert3D(out kPart, PLClient, methodResponce, null);
break;
}
}
}
}
}
catch (Exception ex)
{
kompas.ksMessage(ex.Message);
}
finally
{
// Отключаемся от сервера приложений
PLClient.Disconnect();
}
}
else
kompas.ksMessage("Ошибка подключения к серверу приложений: " + errorMsg);
}
catch (Exception ex)
{
kompas.ksMessage("Ошибка! Не удалось создать COM-объект." + ex.Message);
}
}
catch (Exception ex)
{
kompas.ksMessage(ex.Message);
}
finally
{
// Освобождаем загруженную plclient_kompas.dll
FreeLibrary(pDll);
}
}
/// <summary>
/// Метод определения типа документа
/// </summary>
/// <param name="kompasApp"></param>
/// <returns></returns>
private DocumentTypeEnum GetDocType(IApplication kompasApp)
{
return kompasApp.ActiveDocument == null ? DocumentTypeEnum.ksDocumentUnknown : kompasApp.ActiveDocument.DocumentType;
}
// Импортируем в проект методы из kernel32.dll для подключения к plclient_kompas.dll
[DllImport(_KERNEL32, CharSet = CharSet.Unicode, SetLastError = true)]
[ResourceExposure(ResourceScope.Process)]
public static extern IntPtr LoadLibrary(string libFilename);
[DllImport(_KERNEL32, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
static internal extern IntPtr GetProcAddress(IntPtr HModule, [MarshalAs(UnmanagedType.LPStr), In] string funcName/*lpcstr*/);
[DllImport(_KERNEL32, CharSet = CharSet.Unicode, SetLastError = true)]
[ResourceExposure(ResourceScope.Process)]
public static extern bool FreeLibrary(IntPtr hModule);
#region COM Registration
// Эта функция выполняется при регистрации класса для COM
// Она добавляет в ветку реестра компонента раздел Kompas_Library,
// который сигнализирует о том, что класс является приложением Компас,
// а также заменяет имя InprocServer32 на полное, с указанием пути.
// Все это делается для того, чтобы иметь возможность подключить
// библиотеку на вкладке ActiveX.
[ComRegisterFunction]
public static void RegisterKompasLib(Type t)
{
RegistryKey regKey = Registry.LocalMachine;
try
{
var keyName = string.Format("{0}{1}{2}", _KEYNAME, t.GUID.ToString(), "}");
regKey = regKey.OpenSubKey(keyName, true);
regKey.CreateSubKey(_KOMPASLIBRARY);
regKey = regKey.OpenSubKey("InprocServer32", true);
regKey.SetValue(null, System.Environment.GetFolderPath(Environment.SpecialFolder.System) + #"\mscoree.dll");
}
catch (Exception ex)
{
MessageBox.Show(string.Format("При регистрации класса для COM-Interop произошла ошибка:\n{0}", ex));
}
finally
{
regKey.Close();
}
}
// Эта функция удаляет раздел Kompas_Library из реестра
[ComUnregisterFunction]
public static void UnregisterKompasLib(Type t)
{
RegistryKey regKey = Registry.LocalMachine;
var keyName = string.Format("{0}{1}{2}", _KEYNAME, t.GUID.ToString(), "}");
RegistryKey subKey = regKey.OpenSubKey(keyName, true);
try
{
subKey.DeleteSubKey(_KOMPASLIBRARY);
}
finally { subKey.Close(); }
}
#endregion
}
}

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();
}
}

Wix Bootstrapper - download bundle packages from a secured http repository

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;
}

Getting a IRandomAccessStream to a file in Windows Store App

I need to get IRandomAccessStream to a file without a picker.
However, I'm stuck with IStorageFile or IRandomAccessStreamReference and can't find a way to get IRandomAccessStream from either of those.
I write the code with C++ (no /ZW), but I don't think it matters in this case.
Please help,
Moshe
I managed to do what I wanted using OpenReadAsync of StorageFile. But this involves 2 async calls and is cumbersome. I wonder if there is a shorter and a more elegant way. The relevant sample code below:
IFACEMETHODIMP MyClass::BeginCreateObject(
//... rest of parameters
_In_ IMFAsyncCallback *pCallback,
_In_ IUnknown *punkState)
{
OutputDebugStringW(__FUNCTIONW__);
HRESULT hr;
HString hstrStorageFile;
hstrStorageFile.Set(RuntimeClass_Windows_Storage_StorageFile);
// Get the Activation Factory
ComPtr<IActivationFactory> pStorageFileActivationFactory;
hr = GetActivationFactory(hstrStorageFile.Get(), &pStorageFileActivationFactory);
if (FAILED(hr))
{
::Microsoft::WRL::Details::RaiseException(hr);
}
// QI for StorageFileStatics
ComPtr<IStorageFileStatics> pStorageFileStatics;
hr = pStorageFileActivationFactory.As(&pStorageFileStatics);
if (FAILED(hr))
{
::Microsoft::WRL::Details::RaiseException(hr);
}
HString hstrFileName;
hstrFileName.Set(L"My_Cool_Movie.ts");
// Call CreateFileAsync
__FIAsyncOperation_1_Windows__CStorage__CStorageFile * operation;
hr = pStorageFileStatics->GetFileFromPathAsync(hstrFileName.Get(),
&operation
);
if (FAILED(hr))
{
::Microsoft::WRL::Details::RaiseException(hr);
}
typedef IAsyncOperationCompletedHandler<StorageFile*> HandlerDoneType;
ComPtr<HandlerDoneType> handler(Callback<HandlerDoneType>(
this, &MyClass::FileOpenCompletionHandler));
hr = operation->put_Completed(handler.Get());
ComPtr<IMFAsyncResult> spResult;
//maybe I should pass some data in the first parameter?
hr = MFCreateAsyncResult(nullptr, pCallback, punkState, &_spOpenResult);
//the further processing will be done in the completion handler
return hr;
}
IFACEMETHODIMP MyClass::EndCreateObject(
_In_ IMFAsyncResult *pResult,
_Out_ MF_OBJECT_TYPE *pObjectType,
_Out_ IUnknown **ppObject)
{
OutputDebugStringW(__FUNCTIONW__);
if (pResult == nullptr || pObjectType == nullptr || ppObject == nullptr)
{
return E_INVALIDARG;
}
HRESULT hr = S_OK;
//get a random access stream
if(!_spFile)
{
return E_FAIL;
}
ComPtr<IRandomAccessStream> streamHandle;
hr = _spStream.As(&streamHandle);
if (FAILED(hr))
{
::Microsoft::WRL::Details::RaiseException(hr);
}
//Do what you need with IRandomAccessStream!
//...
return S_OK;
}
IFACEMETHODIMP MyClass::CancelObjectCreation(
_In_ IUnknown *pIUnknownCancelCookie)
{
return E_NOTIMPL;
}
HRESULT MyClass::FileOpenCompletionHandler( IAsyncOperation<StorageFile*>* async, AsyncStatus status)
{
//file is open, call the invoke to carry one
if (status == Completed) {
HRESULT hr = async->GetResults(_spFile.GetAddressOf());
if (_spFile) {
ComPtr<IRandomAccessStreamReference> streamReference;
hr = _spFile.As(&streamReference);
if (FAILED(hr))
{
::Microsoft::WRL::Details::RaiseException(hr);
}
__FIAsyncOperation_1_Windows__CStorage__CStreams__CIRandomAccessStreamWithContentType * operation;
hr = streamReference->OpenReadAsync(&operation);
if (FAILED(hr))
{
::Microsoft::WRL::Details::RaiseException(hr);
}
typedef IAsyncOperationCompletedHandler<IRandomAccessStreamWithContentType*> OpenReadAsyncHandlerDoneType;
ComPtr<OpenReadAsyncHandlerDoneType> handler(Callback<OpenReadAsyncHandlerDoneType>(
this, &MyClass::FileOpenReadCompletionHandler));
hr = operation->put_Completed(handler.Get());
}
return hr;
}
else {
OutputDebugStringW(L"Unexpected async status\n");
return E_FAIL;
}
return S_OK;
}
HRESULT MyClass::FileOpenReadCompletionHandler( IAsyncOperation<IRandomAccessStreamWithContentType*>* async, AsyncStatus status)
{
if (status == Completed) {
HRESULT hr = async->GetResults(_spStream.GetAddressOf());
if (_spStream) {
_spOpenResult->SetStatus(S_OK);
hr = MFInvokeCallback(_spOpenResult.Get());
_spOpenResult.ReleaseAndGetAddressOf();
}
return hr;
}
else {
OutputDebugStringW(L"Unexpected async status\n");
return E_FAIL;
}
return S_OK;
}