+/* FIXME: is this already implemented somewhere else? */
+static HKEY ShellExecute_GetClassKey( LPSHELLEXECUTEINFOW sei )
+{
+ LPCWSTR ext = NULL, lpClass = NULL;
+ LPWSTR cls = NULL;
+ DWORD type = 0, sz = 0;
+ HKEY hkey = 0;
+ LONG r;
+
+ if (sei->fMask & SEE_MASK_CLASSALL)
+ return sei->hkeyClass;
+
+ if (sei->fMask & SEE_MASK_CLASSNAME)
+ lpClass = sei->lpClass;
+ else
+ {
+ ext = PathFindExtensionW( sei->lpFile );
+ TRACE("ext = %s\n", debugstr_w( ext ) );
+ if (!ext)
+ return hkey;
+
+ r = RegOpenKeyW( HKEY_CLASSES_ROOT, ext, &hkey );
+ if (r != ERROR_SUCCESS )
+ return hkey;
+
+ r = RegQueryValueExW( hkey, NULL, 0, &type, NULL, &sz );
+ if ( r == ERROR_SUCCESS && type == REG_SZ )
+ {
+ sz += sizeof (WCHAR);
+ cls = HeapAlloc( GetProcessHeap(), 0, sz );
+ cls[0] = 0;
+ RegQueryValueExW( hkey, NULL, 0, &type, (LPBYTE) cls, &sz );
+ }
+
+ RegCloseKey( hkey );
+ lpClass = cls;
+ }
+
+ TRACE("class = %s\n", debugstr_w(lpClass) );
+
+ hkey = 0;
+ if ( lpClass )
+ RegOpenKeyW( HKEY_CLASSES_ROOT, lpClass, &hkey );
+
+ HeapFree( GetProcessHeap(), 0, cls );
+
+ return hkey;
+}
+
+static IDataObject *shellex_get_dataobj( LPSHELLEXECUTEINFOW sei )
+{
+ LPCITEMIDLIST pidllast = NULL;
+ IDataObject *dataobj = NULL;
+ IShellFolder *shf = NULL;
+ LPITEMIDLIST pidl = NULL;
+ HRESULT r;
+
+ if (sei->fMask & SEE_MASK_CLASSALL)
+ pidl = sei->lpIDList;
+ else
+ {
+ WCHAR fullpath[MAX_PATH];
+
+ fullpath[0] = 0;
+ r = GetFullPathNameW( sei->lpFile, MAX_PATH, fullpath, NULL );
+ if (!r)
+ goto end;
+
+ pidl = ILCreateFromPathW( fullpath );
+ }
+
+ r = SHBindToParent( pidl, &IID_IShellFolder, (LPVOID*)&shf, &pidllast );
+ if ( FAILED( r ) )
+ goto end;
+
+ IShellFolder_GetUIObjectOf( shf, NULL, 1, &pidllast,
+ &IID_IDataObject, NULL, (LPVOID*) &dataobj );
+
+end:
+ if ( pidl != sei->lpIDList )
+ ILFree( pidl );
+ if ( shf )
+ IShellFolder_Release( shf );
+ return dataobj;
+}
+
+static HRESULT shellex_run_context_menu_default( IShellExtInit *obj,
+ LPSHELLEXECUTEINFOW sei )
+{
+ IContextMenu *cm = NULL;
+ CMINVOKECOMMANDINFOEX ici;
+ MENUITEMINFOW info;
+ WCHAR string[0x80];
+ INT i, n, def = -1;
+ HMENU hmenu = 0;
+ HRESULT r;
+
+ TRACE("%p %p\n", obj, sei );
+
+ r = IShellExtInit_QueryInterface( obj, &IID_IContextMenu, (LPVOID*) &cm );
+ if ( FAILED( r ) )
+ return r;
+
+ hmenu = CreateMenu();
+ if ( !hmenu )
+ goto end;
+
+ /* the number of the last menu added is returned in r */
+ r = IContextMenu_QueryContextMenu( cm, hmenu, 0, 0x20, 0x7fff, CMF_DEFAULTONLY );
+ if ( FAILED( r ) )
+ goto end;
+
+ n = GetMenuItemCount( hmenu );
+ for ( i = 0; i < n; i++ )
+ {
+ memset( &info, 0, sizeof info );
+ info.cbSize = sizeof info;
+ info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_DATA | MIIM_ID;
+ info.dwTypeData = string;
+ info.cch = sizeof string;
+ string[0] = 0;
+ GetMenuItemInfoW( hmenu, i, TRUE, &info );
+
+ TRACE("menu %d %s %08x %08lx %08x %08x\n", i, debugstr_w(string),
+ info.fState, info.dwItemData, info.fType, info.wID );
+ if ( ( !sei->lpVerb && (info.fState & MFS_DEFAULT) ) ||
+ ( sei->lpVerb && !lstrcmpiW( sei->lpVerb, string ) ) )
+ {
+ def = i;
+ break;
+ }
+ }
+
+ r = E_FAIL;
+ if ( def == -1 )
+ goto end;
+
+ memset( &ici, 0, sizeof ici );
+ ici.cbSize = sizeof ici;
+ ici.fMask = CMIC_MASK_UNICODE;
+ ici.nShow = sei->nShow;
+ ici.lpVerb = MAKEINTRESOURCEA( def );
+ ici.hwnd = sei->hwnd;
+ ici.lpParametersW = sei->lpParameters;
+
+ r = IContextMenu_InvokeCommand( cm, (LPCMINVOKECOMMANDINFO) &ici );
+
+ TRACE("invoke command returned %08lx\n", r );
+
+end:
+ if ( hmenu )
+ DestroyMenu( hmenu );
+ if ( cm )
+ IContextMenu_Release( cm );
+ return r;
+}
+
+static HRESULT shellex_load_object_and_run( HKEY hkey, LPCGUID guid, LPSHELLEXECUTEINFOW sei )
+{
+ IDataObject *dataobj = NULL;
+ IObjectWithSite *ows = NULL;
+ IShellExtInit *obj = NULL;
+ HRESULT r;
+
+ TRACE("%p %s %p\n", hkey, debugstr_guid( guid ), sei );
+
+ r = CoInitialize( NULL );
+ if ( FAILED( r ) )
+ goto end;
+
+ r = CoCreateInstance( guid, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IShellExtInit, (LPVOID*)&obj );
+ if ( FAILED( r ) )
+ {
+ ERR("failed %08lx\n", r );
+ goto end;
+ }
+
+ dataobj = shellex_get_dataobj( sei );
+ if ( !dataobj )
+ {
+ ERR("failed to get data object\n");
+ goto end;
+ }
+
+ r = IShellExtInit_Initialize( obj, NULL, dataobj, hkey );
+ if ( FAILED( r ) )
+ goto end;
+
+ r = IShellExtInit_QueryInterface( obj, &IID_IObjectWithSite, (LPVOID*) &ows );
+ if ( FAILED( r ) )
+ goto end;
+
+ IObjectWithSite_SetSite( ows, NULL );
+
+ r = shellex_run_context_menu_default( obj, sei );
+
+end:
+ if ( ows )
+ IObjectWithSite_Release( ows );
+ if ( dataobj )
+ IDataObject_Release( dataobj );
+ if ( obj )
+ IShellExtInit_Release( obj );
+ CoUninitialize();
+ return r;
+}
+
+
+/*************************************************************************
+ * ShellExecute_FromContextMenu [Internal]
+ */
+static LONG ShellExecute_FromContextMenu( LPSHELLEXECUTEINFOW sei )
+{
+ static const WCHAR szcm[] = { 's','h','e','l','l','e','x','\\',
+ 'C','o','n','t','e','x','t','M','e','n','u','H','a','n','d','l','e','r','s',0 };
+ HKEY hkey, hkeycm = 0;
+ WCHAR szguid[39];
+ HRESULT hr;
+ GUID guid;
+ DWORD i;
+ LONG r;
+
+ TRACE("%s\n", debugstr_w(sei->lpFile) );
+
+ hkey = ShellExecute_GetClassKey( sei );
+ if ( !hkey )
+ return ERROR_FUNCTION_FAILED;
+
+ r = RegOpenKeyW( hkey, szcm, &hkeycm );
+ if ( r == ERROR_SUCCESS )
+ {
+ i = 0;
+ while ( 1 )
+ {
+ r = RegEnumKeyW( hkeycm, i++, szguid, 39 );
+ if ( r != ERROR_SUCCESS )
+ break;
+ r = ERROR_FUNCTION_FAILED;
+ hr = CLSIDFromString( szguid, &guid );
+ if ( FAILED( hr ) )
+ break;
+ r = ERROR_SUCCESS;
+ /* stop at the first one that succeeds in running */
+ hr = shellex_load_object_and_run( hkey, &guid, sei );
+ if ( SUCCEEDED( hr ) )
+ break;
+ }
+ RegCloseKey( hkeycm );
+ }
+
+ if ( hkey != sei->hkeyClass )
+ RegCloseKey( hkey );
+ return r;
+}
+