I am using this module hierarchy :
Node: {udpApp[0]<->udp<->networkLayer->wlan[0]} and wlan[0]: {CNPCBeacon<->mac<->radio}
I have given some initial parameter in the ini file for udpApp as :
**.host*.numUdpApps = 2
**.host*.udpApp[0].typename = "UDPBasicApp"
**.host*.udpApp[0].chooseDestAddrMode = "perBurst"
**.host*.udpApp[0].destAddresses = "gw1"
**.host*.udpApp[0].startTime = 1.32s
**.host*.udpApp[0].stopTime = 1.48s
But at run time I want to change the startTime and stopTime for udpAPP[0] through CNPCBeacon module. Hence I changed CNPCBeacon.cc as:-
cModule* parentmod = getParentModule();
cModule* grantParentmod = parentmod->getParentModule();
cModule* udpmod;
for (cSubModIterator iter(*grantParentmod); !iter.end(); iter++)
{
//EV<<"get the modulde "<< iter()->getFullName()<<endl;
if (strcmp(iter()->getFullName(), "udpApp[0]") == 0)
{
udpmod = iter();
break;
}
}
cPar& startTime = udpmod->par("startTime");
cPar& stopTime = udpmod->par("stopTime");
And I am successfully able to receive the values of startTime and stopTime. However I want to change these value in current module, which is resulting in an error by following code:
udpmod->par("startTime").setDoubleValue(4.2);
Can anybody please suggest me a way to change it at run time.
Declaring your parameter as volatile should solve your problem. But for future reference I'll provide further explanation below
Volatile vs. non-volatile:
Here it depends how you want to use this parameter. Mainly via the .ini file you have two types of parameters: volatile and non-volatile.
volatile parameters are read every time during your run. That woule be helpful if you want this parameter to be generated by a built-in function, for example, uniform(0,10) each time this volatile parameter will get a different value.
On the other hand non-volatile parameters are read just one, as they don't change from run to run.
Using the volatile type parameter does not give you full flexibility, in the sense that your parameter value will always fall with in a range predefined in the .ini
Dynamic Variable (parameter) Reassignment:
Instead what you could do is use a more robust approach, and re-define the variable which stores the value from that module parameter each time you have to do so.
For example in your case you could do the following:
varHoldingStartTime = par("startTime").doubleValue();
varHoldingStartTime = 4.2;
This way the actual value will change internally without reflecting to your run.
Parameter Studies:
Alternatively if you want this change of the parameter to be applied to multiple runs you could use the advanced built-in approach provided by OMNeT++ which allows you to perform Parameter Studies.
I have explained here how Parameter Studies work: https://stackoverflow.com/a/30572095/4786271 and also here how it can be achieved with constraints etc: https://stackoverflow.com/a/29622426/4786271
If none of the approaches suggested by me fit your case, answers to this question altogether might solve your problem: How to change configuration of network during simulation in OMNeT++?
EDIT: extending the answer to roughly explain handleParameterChange()
I have not used handleParameterChange() before as well, but from what can I see this function provides a watchdog functionality to the module which utilizes it.
To activate this functionality first the void handleParameterChange(const char *parameterName); has to be re-defined.
In essence what it seems to do is the following:
Assume we have two modules moduleA and moduleB and moduleB has parameter parB. moduleA changes the parB and when that happens, moduleB reacts to this change based on the behaviour defined in:
moduleB::handleParameterChange(parB);
The behaviour could be re-reading the original value for parB from the .ini etc.
Related
Let me elaborate on the title:
I want to implement a system that would allow me to enable/disable/modify the general behavior of my program. Here are some examples:
I could switch off and on logging
I could change if my graphing program should use floating or pixel coordinates
I could change if my calculations should be based upon some method or some other method
I could enable/disable certain aspects like maybe a extension api
I could enable/disable some basic integrated profiler (if I had one)
These are some made-up examples.
Now I want to know what the most common solution for this sort of thing is.
I could imagine this working with some sort of singelton class that gets instanced globally or in some other globally available object. Another thing that would be possible would be just constexpr or other variables floating around in a namespace, again globally.
However doing something like that, globally, feels like bad practise.
second part of the question
This might sound like I cant decide what I want, but I want a way to modify all these switches/flags or whatever they are actually called in a single location, without tying any of my classes to it. I don't know if this is possible however.
Why don't I want to do that? Well I like to make my classes somewhat reusable and I don't like tying classes together, unless its required by the DRY principle and or inheritance. I basically couldn't get rid of the flags without modifying the possible hundreds of classes that used them.
What I have tried in the past
Having it all as compiler defines. This worked reasonably well, however I didnt like that I couldnt make it so if the flag file was gone there were some sort of default settings that would make the classes themselves still operational and changeable (through these default values)
Having it as a class and instancing it globally (system class). Worked ok, however I didnt like instancing anything globally. Also same problem as above
Instancing the system class locally and passing it to the classes on construction. This was kinda cool, since I could make multiple instruction sets. However at the same time that kinda ruined the point since it would lead to things that needed to have one flag set the same to have them set differently and therefore failing to properly work together. Also passing it on every construction was a pain.
A static class. This one worked ok for the longest time, however there is still the problem when there are missing dependencies.
Summary
Basically I am looking for a way to have a single "place" where I can mess with some values (bools, floats etc.) and that will change the behaviour of all classes using them for whatever, where said values either overwrite default values or get replaced by default values if said "place" isnt defined.
If a Singleton class does not work for you , maybe using a DI container may fit in your third approach? It may help with the construction and make the code more testable.
There are some DI frameworks for c++, like https://github.com/google/fruit/wiki or https://github.com/boost-experimental/di which you can use.
If you decide to use switch/flags, pay attention for "cyclometric complexity".
If you do not change the skeleton of your algorithm but only his behaviour according to the objets in parameter, have a look at "template design pattern". This method allow you to define a generic algorithm and specify particular step for a particular situation.
Here's an approach I found useful; I don't know if it's what you're looking for, but maybe it will give you some ideas.
First, I created a BehaviorFlags.h file that declares the following function:
// Returns true iff the given feature/behavior flag was specified for us to use
bool IsBehaviorFlagEnabled(const char * flagName);
The idea being that any code in any of your classes could call this function to find out if a particular behavior should be enabled or not. For example, you might put this code at the top of your ExtensionsAPI.cpp file:
#include "BehaviorFlags.h"
static const enableExtensionAPI = IsBehaviorFlagEnabled("enable_extensions_api");
[...]
void DoTheExtensionsAPIStuff()
{
if (enableExtensionsAPI == false) return;
[... otherwise do the extensions API stuff ...]
}
Note that the IsBehaviorFlagEnabled() call is only executed once at program startup, for best run-time efficiency; but you also have the option of calling IsBehaviorFlagEnabled() on every call to DoTheExtensionsAPIStuff(), if run-time efficiency is less important that being able to change your program's behavior without having to restart your program.
As far as how the IsBehaviorFlagEnabled() function itself is implemented, it looks something like this (simplified version for demonstration purposes):
bool IsBehaviorFlagEnabled(const char * fileName)
{
// Note: a real implementation would find the user's home directory
// using the proper API and not just rely on ~ to expand to the home-dir path
std::string filePath = "~/MyProgram_Settings/";
filePath += fileName;
FILE * fpIn = fopen(filePath.c_str(), "r"); // i.e. does the file exist?
bool ret = (fpIn != NULL);
fclose(fpIn);
return ret;
}
The idea being that if you want to change your program's behavior, you can do so by creating a file (or folder) in the ~/MyProgram_Settings directory with the appropriate name. E.g. if you want to enable your Extensions API, you could just do a
touch ~/MyProgram_Settings/enable_extensions_api
... and then re-start your program, and now IsBehaviorFlagEnabled("enable_extensions_api") returns true and so your Extensions API is enabled.
The benefits I see of doing it this way (as opposed to parsing a .ini file at startup or something like that) are:
There's no need to modify any "central header file" or "registry file" every time you add a new behavior-flag.
You don't have to put a ParseINIFile() function at the top of main() in order for your flags-functionality to work correctly.
You don't have to use a text editor or memorize a .ini syntax to change the program's behavior
In a pinch (e.g. no shell access) you can create/remove settings simply using the "New Folder" and "Delete" functionality of the desktop's window manager.
The settings are persistent across runs of the program (i.e. no need to specify the same command line arguments every time)
The settings are persistent across reboots of the computer
The flags can be easily modified by a script (via e.g. touch ~/MyProgram_Settings/blah or rm -f ~/MyProgram_Settings/blah) -- much easier than getting a shell script to correctly modify a .ini file
If you have code in multiple different .cpp files that needs to be controlled by the same flag-file, you can just call IsBehaviorFlagEnabled("that_file") from each of them; no need to have every call site refer to the same global boolean variable if you don't want them to.
Extra credit: If you're using a bug-tracker and therefore have bug/feature ticket numbers assigned to various issues, you can creep the elegance a little bit further by also adding a class like this one:
/** This class encapsulates a feature that can be selectively disabled/enabled by putting an
* "enable_behavior_xxxx" or "disable_behavior_xxxx" file into the ~/MyProgram_Settings folder.
*/
class ConditionalBehavior
{
public:
/** Constructor.
* #param bugNumber Bug-Tracker ID number associated with this bug/feature.
* #param defaultState If true, this beheavior will be enabled by default (i.e. if no corresponding
* file exists in ~/MyProgram_Settings). If false, it will be disabled by default.
* #param switchAtVersion If specified, this feature's default-enabled state will be inverted if
* GetMyProgramVersion() returns any version number greater than this.
*/
ConditionalBehavior(int bugNumber, bool defaultState, int switchAtVersion = -1)
{
if ((switchAtVersion >= 0)&&(GetMyProgramVersion() >= switchAtVersion)) _enabled = !_enabled;
std::string fn = defaultState ? "disable" : "enable";
fn += "_behavior_";
fn += to_string(bugNumber);
if ((IsBehaviorFlagEnabled(fn))
||(IsBehaviorFlagEnabled("enable_everything")))
{
_enabled = !_enabled;
printf("Note: %s Behavior #%i\n", _enabled?"Enabling":"Disabling", bugNumber);
}
}
/** Returns true iff this feature should be enabled. */
bool IsEnabled() const {return _enabled;}
private:
bool _enabled;
};
Then, in your ExtensionsAPI.cpp file, you might have something like this:
// Extensions API feature is tracker #4321; disabled by default for now
// but you can try it out via "touch ~/MyProgram_Settings/enable_feature_4321"
static const ConditionalBehavior _feature4321(4321, false);
// Also tracker #4222 is now enabled-by-default, but you can disable
// it manually via "touch ~/MyProgram_Settings/disable_feature_4222"
static const ConditionalBehavior _feature4222(4222, true);
[...]
void DoTheExtensionsAPIStuff()
{
if (_feature4321.IsEnabled() == false) return;
[... otherwise do the extensions API stuff ...]
}
... or if you know that you are planning to make your Extensions API enabled-by-default starting with version 4500 of your program, you can set it so that Extensions API will be enabled-by-default only if GetMyProgramVersion() returns 4500 or greater:
static ConditionalBehavior _feature4321(4321, false, 4500);
[...]
... also, if you wanted to get more elaborate, the API could be extended so that IsBehaviorFlagEnabled() can optionally return a string to the caller containing the contents of the file it found (if any), so that you could do shell commands like:
echo "opengl" > ~/MyProgram_Settings/graphics_renderer
... to tell your program to use OpenGL for its 3D graphics, or etc:
// In Renderer.cpp
std::string rendererType;
if (IsDebugFlagEnabled("graphics_renderer", &rendererType))
{
printf("The user wants me to use [%s] for rendering 3D graphics!\n", rendererType.c_str());
}
else printf("The user didn't specify what renderer to use.\n");
I'm confused by the difference between V8 ObjectTemplate's Set and SetAccessor methods. My question has a simple context and 4 concrete sub-questions.
Context
Let's say I have code snippet that wants to provide a global object global to the targeting JS context. global has a property x, whose value takes int value. global also has a property log, which is a function. All the snippets are taken from V8's source, process.cc and Embedder's Guide to be precise.
HandleScope handle_scope(GetIsolate());
// Create a template for the global object where we set the
// built-in global functions.
Local<ObjectTemplate> global = ObjectTemplate::New(GetIsolate());
global->Set(String::NewFromUtf8(GetIsolate(), "log",
NewStringType::kNormal).ToLocalChecked(),
FunctionTemplate::New(GetIsolate(), LogCallback));
So this code snippet provides function log to the global. Then from the Embedder's Guide to accessors, it says
An accessor is a C++ callback that calculates and returns a value when an object property is accessed by a JavaScript script. Accessors are configured through an object template, using the SetAccessor method.
The code snippet follows:
void XGetter(Local<String> property,
const PropertyCallbackInfo<Value>& info) {
info.GetReturnValue().Set(x);
}
void XSetter(Local<String> property, Local<Value> value,
const PropertyCallbackInfo<Value>& info) {
x = value->Int32Value();
}
// YGetter/YSetter are so similar they are omitted for brevity
Local<ObjectTemplate> global_templ = ObjectTemplate::New(isolate);
global_templ->SetAccessor(String::NewFromUtf8(isolate, "x"), XGetter, XSetter);
global_templ->SetAccessor(String::NewFromUtf8(isolate, "y"), YGetter, YSetter);
Persistent<Context> context = Context::New(isolate, NULL, global_templ);
As I understand this code snippet, it's providing some integer value x to the global as the description goes.
Now,from the source of V8, I see ObjectTemplate doesn't have a Set method, instead, it's inherited from parent class Template. From Template's source code, it says:
/**
* Adds a property to each instance created by this template.
*
* The property must be defined either as a primitive value, or a template.
*/
void Set(Local<Name> name, Local<Data> value,
propertyAttribute attributes = None);
Questions
Template's Set method says it can set a primitive value to the instance of the template, then can I use Set to set x in the second code snippet instead of using SetAccessor?
If the answer to question 1 is true, then what's the difference for setting x between using SetMethod and Set? Is the difference being that any modification in JS to the property set by Set will not be reflected in C++?
If the answer to question 1 is false, then why can't I use Set on X?
From the description of accessors, it says it computes and return value. So does it mean we don't use SetAccessor to return functions? I'm confused because I mainly write JS and Haskell. Both languages spoils me to take functions as values.
Now I know it should be easy to verify all my assumptions by actually building the samples, but I have difficulties compiling the V8 source, hence I'm asking for any help.
Thank you in advanced for any effort!
1. Yes.
2. Set is the C++ equivalent (modulo property attributes) of:
Object.defineProperty(global, "x", {value: 3})
SetAccessor is the C++ equivalent of:
Object.defineProperty(global, "x", {get: function XGetter() { return ...; },
set: function XSetter(val) { ... }});
As you suggest, a consequence is that in case of Set, the C++ side has no way of knowing whether the value was changed from the JavaScript side.
3. n/a
4. The getter can return any value you want; in particular the value can be a function.
I'm using the program Maya to make a rather large project in python. I have numerous options that will be determined by a GUI and input by the user.
One example of an option is what dimensions to render at. However I did not make a GUI yet and am still in the testing faze.
What I ultimately want is a way to have variables be able to be looked up and used by various classes/methods within multiple modules. And also that there be a way that I can test all the code without having an actual GUI.
Should I directly pass all data to each method? My issue with this is if method foo relies on variable A, but method bar needs to call foo, it could get real annoying passing these variables to Foo from everywhere its called.
Another way I saw was passing all variables through to each class instance itself and using instance variables to access. But what if an option changes, then i'd have to put reload imports every time it runs.
For testing what I use now is a module that gets variables from a config file with the variables, and i import that module and use the instance variables throughout the script.
def __init__(self):
# Get and assign all instance variables.
options = config_section_map('Attrs', '%s\\ui_options.ini' %(data_path))
for k, v in options.items():
if v.lower() == 'none':
options[k] = None
self.check_all = int(options['check_all'])
self.control_group = options['control_group']
Does anyone have advice or can point me in the right direction dealing with getting/using ui variables?
If the options list is not overly long and won't change, you can simply set member variables in the class initializer, which makes the initialization easy for readers to understand:
class OptionData(object):
def __init___(self):
#set the options on startup
self.initial_path = "//network"
self.initial_name = "filename"
self.use_hdr = True
# ... etc
If you expect the initializations to change often you can split out the initial values into the constructor for the class:
class OptionData(object):
def __init___(self, path = "//network", name = "filename", hdr=True)
self.initial_path = path
self.initial_name = name
self.use_hdr = hdr
If you need to persist the data, you can fill out the class reading the cfg file as you're doing, or store it in some other way. Persisting makes things harder because you can't guarantee that the user won't open two Maya's at the same time, potentially changing the saved data in unpredictable ways. You can store per-file copies of the data using Maya's fileInfo.
In both of these cases I'd make the actual GUI take the data object (the OptionData or whatever you call yours) as an initializer. That way you can read and write the data from the GUI. Then have the actual functional code read the OptionData:
def perform_render(optiondata):
#.... etc
That way you can run a batch process without the gui at all and the functional code will be none the wiser. The GUI's only job is to be a custom editor for the data object and then to pass it on to the final function in a valid state.
I am trying to create a node at run time in my module in Omnet. I am able to create it with this code and its working fine.
cModule* parentmod = getParentModule();
cModule* grantParentMod = parentmod->getParentModule();
cModule* grantParentMod1 = grantParentMod->getParentModule();
// To check if the module is already created
for (cSubModIterator iter(*grantParentMod1); !iter.end(); iter++)
{
EV << iter()->getFullName()<<endl;
if (iter()->getFullName() == "host_send4")
return;
}
cModuleType *meshnode1 = cModuleType::get("inet.networklayer.manetrouting.PASER.meshnode");
cModule *mod = meshnode1->create("host_send4", grantParentMod1);
cDisplayString& dispstr = getDisplayString();
dispstr.parse("p=1000,535;r=200,green");
mod->finalizeParameters();
mod->buildInside();
mod->scheduleStart(simTime()+2*beaconInterval);
However this module is not generated at desired place in simulation output (the coordinates and the display). I believe the display string created here is not attached to the module and hence I tried to do it by this :-
cDisplayString& dispstr = getDisplayString();
dispstr.parse("p=1000,535;r=200,green");
mod->getDisplayString().set(dispstr);
But with this I encounter following error at run time :- Cannot access display string yet: Parameters not yet set up . I know the problem is in mod->getDisplayString().set(dispstr);
So is there any other way to assign the parameter or am I doing some minor error.
Thanks for this help.
Make sure you are following the module creation procedure as given in the OMNeT++ manual.
If you navigate to the The Detailed Procedure sub-section you will notice a comprehensive list which tells what step should be performed where:
Find the factory object;
Create the module;
Set up its parameters and gate sizes as needed;
Tell the (possibly compound) module to recursively create its internal submodules and connections;
Schedule activation message(s) for the new simple module(s).
Step 3 I believe is the one you are looking for. Little below is given a detailed explanation of what should be done for step 3:
If you want to set up parameter values or gate vector sizes (Step
3.), the code goes between the create() and buildInside() calls:
// create
cModuleType *moduleType = cModuleType::get("foo.nodes.WirelessNode");
cModule *module = moduleType->create("node", this);
// set up parameters and gate sizes before we set up its submodules
module->par("address") = ++lastAddress;
module->finalizeParameters();
module->setGateSize("in", 3);
module->setGateSize("out", 3);
// create internals, and schedule it
module->buildInside();
module->scheduleStart(simTime());
Be aware of the usage of the module->par("<parameter_name>") function.
PS: I was writing my answer, and in meanwhile you answered your own question. This answer can be left there for future reference, if useful.
Well I modified the code as :-
cModuleType *meshnode1 = cModuleType::get("inet.networklayer.manetrouting.PASER.meshnode");
cModule *mod = meshnode1->create("host_send4", grantParentMod1);
mod->finalizeParameters();
std::string displayString = "p=1000,535;r=200,green;i=device/smallrouter";
mod->getDisplayString().parse(displayString.c_str());
mod->buildInside();
mod->scheduleStart(simTime()+2*beaconInterval);
and then its working perfect. According to my understanding, I should add mod->finalizeParameters(); before changing the display setting and display string should be a simple string but not the cDisplayString object.
Is it possible to execute C++ code from within Progress ABL?
Specifically, I am looking to use the function SHGetKnownFolderPath (documentation here) to determine the location of the "Documents" folder on a Windows 7 machine that has the documents folder redirected to another location.
Or, is there an alternative way to determine this information without resorting to checking a registry key?
You can call external shared libraries and DLLs.
http://documentation.progress.com/output/OpenEdge113/pdfs/dvpin/dvpin.pdf
Section 3 "External Interfaces" is what you are looking for.
This http://dbappraise.com/ppt/shlib.pptx might also be helpful.
C++ is often problematic due to the way it names things. You might be better off building a "shim" using plain old C to bridge between OpenEdge and C++
Callling Windows system functions is usually easy though. Something like:
procedure SHGetKnownFolderPath external "pathToLibrary":
define parameter a as someType.
define parameter b as someType.
define return parameter x as someType.
end.
Check the "Programming Interfaces" document, "External Program Interfaces" section.
Also, some versions of ABL also support direct .NET calls as an option.
I was able to get this working in 10.2B after consulting some sources:
The C# solution, for a working example: https://stackoverflow.com/a/21953690/763102
Win32 OpenEdge samples, for translation examples: http://www.oehive.org/book/export/html/385.html
The difficult part for SHGetKnownFolderPath is the rfid parameter which needs to be passed by reference. C# has the annotation [System.Runtime.InteropServices.MarshalAs(UnmanagedType.LPStruct)] or the ref keyword. I couldn't figure out how to pass a reference of System.Guid due to Progress OpenEdge's limitations of external procedure parameter datatypes, so I performed a bytewise copy of a .NET Guid and passed that via MEMPTR. I apologize for leaning so heavily on .NET here.
Here is a working example that gets the provided known folder GUID, plus usage to get the Documents folder:
PROCEDURE SHGetKnownFolderPath EXTERNAL "shell32.dll":
DEFINE INPUT PARAMETER rfid AS MEMPTR.
DEFINE INPUT PARAMETER dwFlags AS UNSIGNED-LONG.
DEFINE INPUT PARAMETER hToken AS LONG.
DEFINE OUTPUT PARAMETER ppszPath AS LONG.
DEFINE RETURN PARAMETER result AS LONG.
END PROCEDURE.
FUNCTION prepareGuidPointer RETURNS MEMPTR(
pGuid AS System.Guid):
DEFINE VARIABLE lGuidBytes AS INTEGER EXTENT.
ASSIGN lGuidBytes = pGuid:ToByteArray().
DEFINE VARIABLE lGuidPointer AS MEMPTR NO-UNDO.
SET-SIZE(lGuidPointer) = EXTENT(lGuidBytes).
DEFINE VARIABLE ii AS INTEGER NO-UNDO.
DO ii = 1 TO EXTENT(lGuidBytes):
PUT-BYTE(lGuidPointer, ii) = lGuidBytes[ii].
END.
RETURN lGuidPointer.
END FUNCTION.
FUNCTION deallocatePointer RETURNS INT64(
pPointer AS MEMPTR):
SET-SIZE(pPointer) = 0.
RETURN GET-SIZE(pPointer).
END FUNCTION.
FUNCTION GetKnownFolderPath RETURNS CHARACTER(
pGuidString AS CHARACTER):
DEFINE VARIABLE lDontVerifyFolderFlag AS INT64 NO-UNDO
INITIAL 16384. /* 0x4000 */
DEFINE VARIABLE lUseDefaultUser AS INTEGER NO-UNDO
INITIAL 0.
DEFINE VARIABLE lGuidPointer AS MEMPTR NO-UNDO.
ASSIGN lGuidPointer = prepareGuidPointer( NEW System.Guid(pGuidString) ).
DEFINE VARIABLE lResult AS INTEGER NO-UNDO.
DEFINE VARIABLE lPathResponse AS INTEGER NO-UNDO.
RUN SHGetKnownFolderPath(
INPUT lGuidPointer,
INPUT lDontVerifyFolderFlag,
INPUT lUseDefaultUser,
OUTPUT lPathResponse,
OUTPUT lResult).
deallocatePointer(lGuidPointer).
IF lResult GE 0 THEN
DO:
DEFINE VARIABLE lStringPath AS CHARACTER NO-UNDO.
DEFINE VARIABLE lPathPointer AS System.IntPtr NO-UNDO.
ASSIGN lPathPointer = NEW System.IntPtr(lPathResponse).
ASSIGN lStringPath =
System.Runtime.InteropServices.Marshal:PtrToStringUni(lPathPointer).
System.Runtime.InteropServices.Marshal:FreeCoTaskMem(lPathPointer).
RETURN lStringPath.
END.
ELSE
UNDO, THROW NEW System.Runtime.InteropServices.ExternalException(
"Unable to retrieve the known folder path. It may not be available on this system.",
lResult).
END FUNCTION.
DEFINE VARIABLE lDocumentsGuidString AS CHARACTER NO-UNDO
INITIAL "~{FDD39AD0-238F-46AF-ADB4-6C85480369C7}".
MESSAGE GetKnownFolderPath(lDocumentsGuidString)
VIEW-AS ALERT-BOX.