Skip to content

Commit 23e3df9

Browse files
committed
Updated CHANGES file.
Moved `ActionExecute::StartProcess()` private. Fixed a bad copy paste for `ActionExecute::SetVerb()` and `ActionExecute::GetVerb()`. Updated documentation for verb attribute of <exec> element. Fixed missing closing quotes in logs of ActionExecute::Execute(). Created TestObjectFactory.testParseActionExecute() unit tests to validate parsing. For issue #56.
1 parent d0de816 commit 23e3df9

File tree

8 files changed

+158
-16
lines changed

8 files changed

+158
-16
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Changes for 0.5.0
88
* Fixed issue #51: Action "Open command prompt here" from default.xml configuration does not work.
99
* Fixed issue #52: Define a specific property for use as separator for handling multiple file selection.
1010
* Fixed issue #55: Menu name maximum length limit and escape string.
11+
* Fixed issue #56: Not implemented administrator mode.
1112
* Fixed issue #58: More useful features: class and pattern attributes for validity / visibility.
1213
* Fixed issue #61: Support for WIX installer.
1314
* Fixed issue #66: Github don't identify the repository LICENSE as MIT.

UserManual.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
![ShellAnything logo](docs/ShellAnything-splashscreen.jpg?raw=true)
1+
![ShellAnything logo](docs/ShellAnything-splashscreen.jpg?raw=true)
22

33

44
# Overview #
@@ -550,6 +550,28 @@ For example, the following launche `notepad.exe` and open the `License.txt` docu
550550

551551

552552

553+
#### verb attribute: ####
554+
555+
The `verb` attribute defines special directives on how to execute a file or launching the application. For example, the verb `open` or `edit` allows the user to open a document using the associated application. The attribute is optional.
556+
557+
Verbs are specific to a file type but some are supported by multiple types. Commonly available verbs include:
558+
| Verb | Description |
559+
|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
560+
| edit | Launches an editor and opens the document for editing. |
561+
| find | Initiates a search starting from the executed directory. |
562+
| open | Launches an application. If this file is not an executable file, its associated application is launched. |
563+
| print | Prints the document file. |
564+
| properties | Displays the object's properties. |
565+
| runas | Launches an application as Administrator. User Account Control (UAC) will prompt the user for consent to run the application elevated or enter the credentials of an administrator account used to run the application. |
566+
567+
For example, the following launches `notepad.exe` and open the text file `C:\Windows\System32\drivers\etc\hosts` which can only be modified with elevated privileges (as an Administrator) :
568+
```xml
569+
<exec path="C:\Windows\notepad.exe" arguments="C:\Windows\System32\drivers\etc\hosts" verb="runas" />
570+
```
571+
To get extended information about verbs, see the following Microsoft documentation article: [ShellExecute and ShellExecuteEx, Object Verbs](https://docs.microsoft.com/en-us/windows/win32/shell/launch#object-verbs).
572+
573+
574+
553575
### &lt;open&gt; action ###
554576

555577
The &lt;open&gt; element is used to open a document by the default associated application. The &lt;open&gt; element must be added under the &lt;actions&gt; element.

include/shellanything/ActionExecute.h

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,11 @@ namespace shellanything
4040
virtual ~ActionExecute();
4141

4242
/// <summary>
43-
/// Execute an application.
44-
/// </summary>
45-
/// <param name="iContext">The current context of execution.</param>
46-
/// <returns>Returns true if the execution is successful. Returns false otherwise.</returns>
47-
virtual bool Execute(const Context& iContext) const;
48-
49-
/// <summary>
50-
/// Execute an application with RapidAssist method.
43+
/// Execute an application.
5144
/// </summary>
5245
/// <param name="iContext">The current context of execution.</param>
5346
/// <returns>Returns true if the execution is successful. Returns false otherwise.</returns>
54-
virtual bool StartProcess(const Context & iContext) const;
47+
virtual bool Execute(const Context& iContext) const;
5548

5649
/// <summary>
5750
/// Getter for the 'path' parameter.
@@ -91,7 +84,15 @@ namespace shellanything
9184
/// <summary>
9285
/// Setter for the 'verb' parameter.
9386
/// </summary>
94-
void SetVerb(const std::string& iArguments);
87+
void SetVerb(const std::string& iVerb);
88+
89+
private:
90+
/// <summary>
91+
/// Execute an application with RapidAssist method.
92+
/// </summary>
93+
/// <param name="iContext">The current context of execution.</param>
94+
/// <returns>Returns true if the execution is successful. Returns false otherwise.</returns>
95+
virtual bool StartProcess(const Context & iContext) const;
9596

9697
private:
9798
std::string mPath;

src/ActionExecute.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,24 +71,24 @@ namespace shellanything
7171
info.nShow = SW_SHOWDEFAULT;
7272
info.lpFile = pathW.c_str();
7373

74-
LOG(INFO) << "Exec: '" << path;
74+
LOG(INFO) << "Exec: '" << path << "'.";
7575

7676
if (!verb.empty())
7777
{
7878
info.lpVerb = verbW.c_str(); // Verb
79-
LOG(INFO) << "Verb: '" << verb;
79+
LOG(INFO) << "Verb: '" << verb << "'.";
8080
}
8181

8282
if (!arguments.empty())
8383
{
8484
info.lpParameters = argumentsW.c_str(); // Arguments
85-
LOG(INFO) << "Arguments: '" << arguments;
85+
LOG(INFO) << "Arguments: '" << arguments << "'.";
8686
}
8787

8888
if (!basedir.empty())
8989
{
9090
info.lpDirectory = basedirW.c_str(); // Default directory
91-
LOG(INFO) << "Basedir: '" << basedir;
91+
LOG(INFO) << "Basedir: '" << basedir << "'.";
9292
}
9393

9494
BOOL success = ShellExecuteExW(&info);
@@ -198,7 +198,7 @@ namespace shellanything
198198

199199
const std::string& ActionExecute::GetVerb() const
200200
{
201-
return mArguments;
201+
return mVerb;
202202
}
203203

204204
void ActionExecute::SetVerb(const std::string& iVerb)

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ set(CONFIGURATION_TEST_FILES ""
1212
${CMAKE_CURRENT_SOURCE_DIR}/test_files/TestConfigManager.testFileModifications.xml
1313
${CMAKE_CURRENT_SOURCE_DIR}/test_files/TestConfigManager.testParentWithoutChildren.xml
1414
${CMAKE_CURRENT_SOURCE_DIR}/test_files/TestConfiguration.testLoadProperties.xml
15+
${CMAKE_CURRENT_SOURCE_DIR}/test_files/TestObjectFactory.testParseActionExecute.xml
1516
${CMAKE_CURRENT_SOURCE_DIR}/test_files/TestObjectFactory.testParseActionFile.xml
1617
${CMAKE_CURRENT_SOURCE_DIR}/test_files/TestObjectFactory.testParseActionMessage.xml
1718
${CMAKE_CURRENT_SOURCE_DIR}/test_files/TestObjectFactory.testParseActionPrompt.xml

test/TestConfiguration.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ namespace shellanything { namespace test
112112
"test_files\\TestConfigManager.testFileModifications.xml",
113113
"test_files\\TestConfigManager.testParentWithoutChildren.xml",
114114
"test_files\\TestConfiguration.testLoadProperties.xml",
115+
"test_files\\TestObjectFactory.testParseActionExecute.xml",
115116
"test_files\\TestObjectFactory.testParseActionFile.xml",
116117
"test_files\\TestObjectFactory.testParseActionPrompt.xml",
117118
"test_files\\TestObjectFactory.testParseDefaults.xml",

test/TestObjectFactory.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "TestObjectFactory.h"
2626
#include "shellanything/ConfigManager.h"
2727
#include "shellanything/Context.h"
28+
#include "shellanything/ActionExecute.h"
2829
#include "shellanything/ActionFile.h"
2930
#include "shellanything/ActionPrompt.h"
3031
#include "shellanything/ActionMessage.h"
@@ -74,6 +75,23 @@ namespace shellanything { namespace test
7475
return NULL;
7576
}
7677
//--------------------------------------------------------------------------------------------------
78+
ActionExecute * GetFirstActionExecute(Menu * m)
79+
{
80+
if (!m)
81+
return NULL;
82+
83+
Action::ActionPtrList actions = m->GetActions();
84+
for(size_t i=0; i<actions.size(); i++)
85+
{
86+
Action * action = actions[i];
87+
ActionExecute * action_execute = dynamic_cast<ActionExecute *>(action);
88+
if (action_execute)
89+
return action_execute;
90+
}
91+
92+
return NULL;
93+
}
94+
//--------------------------------------------------------------------------------------------------
7795
ActionFile * GetFirstActionFile(Menu * m)
7896
{
7997
if (!m)
@@ -283,6 +301,74 @@ namespace shellanything { namespace test
283301
ASSERT_TRUE( ra::filesystem::DeleteFile(template_target_path.c_str()) ) << "Failed deleting file '" << template_target_path << "'.";
284302
}
285303
//--------------------------------------------------------------------------------------------------
304+
TEST_F(TestObjectFactory, testParseActionExecute)
305+
{
306+
ConfigManager & cmgr = ConfigManager::GetInstance();
307+
308+
static const std::string path_separator = ra::filesystem::GetPathSeparatorStr();
309+
310+
//copy test template file to a temporary subdirectory to allow editing the file during the test
311+
std::string test_name = ra::testing::GetTestQualifiedName();
312+
std::string template_source_path = std::string("test_files") + path_separator + test_name + ".xml";
313+
std::string template_target_path = std::string("test_files") + path_separator + test_name + path_separator + "tmp.xml";
314+
315+
//make sure the target directory exists
316+
std::string template_target_dir = ra::filesystem::GetParentPath(template_target_path);
317+
ASSERT_TRUE( ra::filesystem::CreateDirectory(template_target_dir.c_str()) ) << "Failed creating directory '" << template_target_dir << "'.";
318+
319+
//copy the file
320+
ASSERT_TRUE( ra::filesystem::CopyFile(template_source_path, template_target_path) ) << "Failed copying file '" << template_source_path << "' to file '" << template_target_path << "'.";
321+
322+
//wait to make sure that the next files not dated the same date as this copy
323+
ra::timing::Millisleep(1500);
324+
325+
//setup ConfigManager to read files from template_target_dir
326+
cmgr.ClearSearchPath();
327+
cmgr.AddSearchPath(template_target_dir);
328+
cmgr.Refresh();
329+
330+
//ASSERT the file is loaded
331+
Configuration::ConfigurationPtrList configs = cmgr.GetConfigurations();
332+
ASSERT_EQ( 1, configs.size() );
333+
334+
//ASSERT all menus are available
335+
Menu::MenuPtrList menus = cmgr.GetConfigurations()[0]->GetMenus();
336+
ASSERT_EQ( 4, menus.size() );
337+
338+
//assert all menus have a file element as the first action
339+
ActionExecute * exec00 = GetFirstActionExecute(menus[00]);
340+
ActionExecute * exec01 = GetFirstActionExecute(menus[01]);
341+
ActionExecute * exec02 = GetFirstActionExecute(menus[02]);
342+
ActionExecute * exec03 = GetFirstActionExecute(menus[03]);
343+
344+
ASSERT_TRUE( exec00 != NULL );
345+
ASSERT_TRUE( exec01 != NULL );
346+
ASSERT_TRUE( exec02 != NULL );
347+
ASSERT_TRUE( exec03 != NULL );
348+
349+
//assert menu00 attributes
350+
ASSERT_EQ("C:\\Windows\\System32\\calc.exe", exec00->GetPath());
351+
352+
//assert menu01 attributes
353+
//<exec path="C:\Windows\notepad.exe" basedir="C:\Program Files\7-Zip" arguments="License.txt" />
354+
ASSERT_EQ("C:\\Windows\\notepad.exe", exec01->GetPath());
355+
ASSERT_EQ("C:\\Program Files\\7-Zip", exec01->GetBaseDir());
356+
ASSERT_EQ("License.txt", exec01->GetArguments());
357+
358+
//assert menu02 attributes
359+
//<exec path="C:\Windows\notepad.exe" arguments="C:\Windows\System32\drivers\etc\hosts" verb="runas" />
360+
ASSERT_EQ("C:\\Windows\\notepad.exe", exec02->GetPath());
361+
ASSERT_EQ("C:\\Windows\\System32\\drivers\\etc\\hosts", exec02->GetArguments());
362+
ASSERT_EQ("runas", exec02->GetVerb());
363+
364+
//assert menu03 attributes
365+
//<!-- missing path attribute --> <exec arguments="C:\Windows\System32\drivers\etc\hosts" verb="runas" />
366+
ASSERT_EQ("", exec03->GetPath());
367+
368+
//cleanup
369+
ASSERT_TRUE( ra::filesystem::DeleteFile(template_target_path.c_str()) ) << "Failed deleting file '" << template_target_path << "'.";
370+
}
371+
//--------------------------------------------------------------------------------------------------
286372
TEST_F(TestObjectFactory, testParseActionFile)
287373
{
288374
ConfigManager & cmgr = ConfigManager::GetInstance();
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<root>
3+
<shell>
4+
<menu name="menu00">
5+
<actions>
6+
<exec path="C:\Windows\System32\calc.exe" />
7+
</actions>
8+
</menu>
9+
10+
<menu name="menu01">
11+
<actions>
12+
<exec path="C:\Windows\notepad.exe" basedir="C:\Program Files\7-Zip" arguments="License.txt" />
13+
</actions>
14+
</menu>
15+
16+
<menu name="menu02">
17+
<actions>
18+
<exec path="C:\Windows\notepad.exe" arguments="C:\Windows\System32\drivers\etc\hosts" verb="runas" />
19+
</actions>
20+
</menu>
21+
22+
<menu name="menu03">
23+
<actions>
24+
<!-- missing path attribute -->
25+
<exec arguments="C:\Windows\System32\drivers\etc\hosts" verb="runas" />
26+
</actions>
27+
</menu>
28+
29+
</shell>
30+
</root>

0 commit comments

Comments
 (0)