Programming Call a php webpage from a IFTTT applet using Google Assistant

I wanted to be able to wake up PC on my Lan using voice commands. To achieve that, I did implement a solution using Google Assistant, IFTTT and a php web page hosted on my NAS.

Click to Read More

Regarding IFTTT, you must have an account. Then,

  • Create a new applet,
  • Choose the service “Google Assistant”
    • Choose a trigger, ex.: “Say a phrase with a text ingredient”
    • Type a phrase in “What do you want to say?” such as “Wake up my PC $” ($ is the symbol to be used for the “ingredient”)
    • Type a phrase in “What do you want the Assistant to say in response?” such as “Ok, I will wake up $”
  • Choose next an action service “Webhooks” (to “make a web request”)
    • Type the url of your web page: http://<public Ip of your NAS>/<some path>/action.php?do=wakeup&param={{TextField}} ({{TextField}} will take the value of the ingredient)
    • Chose the “method”: GET
    • Chose the “content type”: text/plain

Regarding the php web page (action.php), I had to do some fine tuning… So, I started with a page logging all information received from IFTTT:

</pre>
<?php
// https://gist.github.com/magnetikonline/650e30e485c0f91f2f40
class DumpHTTPRequestToFile
{
public function execute($targetFile)
{
$boby = json_decode(file_get_contents('php://input') , true);

$data = sprintf("<h2><u>IFTTT call at %s on %s</u></h2><h3>Method:</h3>", date('h:i:s') , date('d/m/Y'));
$data .= sprintf("<blockquote>%s (%s): %s </blockquote><h3>HTTP headers:</h3><ul>", $_SERVER['REQUEST_METHOD'], $_SERVER['SERVER_PROTOCOL'], htmlentities($_SERVER['REQUEST_URI']));
foreach ($this->getHeaderList() as $name => $value)
{
$data .= "<li><b>" . $name . '</b>: ' . $value . "</li>";
}

$data .= "</ul><h3>Post:</h3><ul>";
foreach ($_POST as $key => $value) $data .= "<li>" . $key . '=' . $value . '</li>';

$bodyhtml = $this->jsonToTable($boby);
$data .= "</ul><h3>Request body:</h3><blockquote>$bodyhtml</blockquote>";

$action = trim(strtolower($_GET['action']));
$action = preg_replace('/^ *de +/', '', $action);
$action = preg_replace('/^ *d *\' +/', '', $action);

$param = trim(strtolower($_GET['param']));
$param = preg_replace('/^ *de +/', '', $param);
$param = preg_replace('/^ *d *\' +/', '', $param);

$data .= "<h3>Requested Action:</h3><blockquote>$action: $param</blockquote>";

similar_text(strtolower($param) , "zeus", $percent);
$data .= "Zeus: $percent %<br>";

similar_text(strtolower($param) , "chaos", $percent);
$data .= "Chaos: $percent %<br>";

file_put_contents($targetFile, $data . file_get_contents($targetFile) . "\n");

echo (nl2br($data));
}

private function getHeaderList()
{
$headerList = [];
foreach ($_SERVER as $name => $value)
{
if (preg_match('/^HTTP_/', $name))
{
// convert HTTP_HEADER_NAME to Header-Name
$name = strtr(substr($name, 5) , '_', ' ');
$name = ucwords(strtolower($name));
$name = strtr($name, ' ', '-');
// add to list
$headerList[$name] = $value;
}
}
return $headerList;
}

private function jsonToTable($data)
{
$table = "<table class='json-table' width='100%'>";
foreach ($data as $key => $value)
{
$table .= "<tr valign='top'>";
if (!is_numeric($key))
{
$table .= "
<td>
<strong>" . $key . ":</strong>
</td>
<td>
";
}
else
{
$table .= "
<td colspan='2'>
";
}
if (is_object($value) || is_array($value))
{
$table .= jsonToTable($value);
}
else
{
$table .= $value;
}
$table .= "
</td>
</tr>
";
}
$table .= "</table>";
return $table;
}
}

(new DumpHTTPRequestToFile)->execute('./action.html');

?>
<pre>

The log is stored in a html page “log.html”.

Notes:

  • I notice that Google was not good at recognizing the name of my PC (IMO because I use French sentences). So I am using a php command “similar_text” to evaluate the “ingredient”. But this is really far from effective.
  • I tried to use a “POST” method instead of “Get” and a Content Type “application/json”, it works. But when I add a Body like {“token”:”mseries”,”command”: “<<<{{TextField}}>>>”, ”test”: “data”}, I don’t see it received by my php page :(
  • If Google Assistant does not call correcly IFTTT, look at you voice command history to be sure it understood you order: https://myactivity.google.com/myactivity?restrict=vaa&utm_source=help
  • If Google Assistant understood correctly your command, but IFTTT didn’t call your web page, look at your IFTTT activity: https://ifttt.com/activity

A log for a call using a GET method will look like:

IFTTT call at 11:32:29 on 25/09/2018

Method: GET (HTTP/1.1): /ifttt/action.php?action=wakeup&param=CARROS

HTTP headers:

X-Newrelic-Transaction: PxQGUlECCwFRBVEEVQAAAlQTGlUDChAHHEAJVA1cAgMKA1gEVFNVUVUHFU1EAQACBV1UBlUTag==
X-Newrelic-Id: VwAOU1RRGwAFUFZUAwQE
Content-Type: text/plain
Content-Length: 74
Connection: close
Host: olympe.letroye.be
X-Real-Port: 55748
X-Port: 80
X-Real-Ip: 54.164.110.125
X-Forwarded-By: 192.168.0.200

Post:

Request body:

Requested Action: wakeup: CARROS

Zeus: 20 %
Chaos: 72.727272727273 %

A log for a call using a POST method with application/json content-type will look like:

IFTTT call at 11:41:14 on 25/09/2018

Method: POST (HTTP/1.1): /ifttt/action.php?action=wakeup&param=Kaos

HTTP headers:

X-Newrelic-Id: VwAOU1RRGwAFUFZUAwQE
Content-Type: application/json
Content-Length: 77
Connection: close
Host: olympe.letroye.be
X-Real-Port: 59892
X-Port: 80
X-Real-Ip: 54.211.16.53
X-Forwarded-By: 192.168.0.200

Post:

Request body:

Requested Action: wakeup: kaos

Zeus: 25 %
Chaos: 66.666666666667 %

Programming Visual Studio: Compare and Merge sources with Beyond Compare

As a memo (but this info can be found on Beyond Compare website), here are my settings to compare and merge sources from Visual Studio, with Beyond Compare.

Click to Read More

  • Go to Tools > Options… > Source Control > Visual Studio Team Foundation Server
  • Click “Configure User Tools…”
  • In the “Configure User Tools…” window, click “Add…” to configure the Compare Operation… then Type:
    • Extension: .*
    • Operation: Compare
    • Command: D:\Tools\Beyond Compare 3\BComp.exe
    • Arguments: %1 %2 /title1=%6 /title2=%7
  • Click “Ok” and then again “Add..” to configure the Merge Operation… Type now:
    • Extension: .*
    • Operation: Merge
    • Command: D:\Tools\Beyond Compare 3\BComp.exe
    • .Arguments: %1 %2 %3 %4 /title1=%6 /title2=%7 /title3=%8 /title4=%9

Et voilà.

ProgrammingTFS TFS 2013 plugins cannot load dependencies located in the “Plugins” folder

I have a plugin for TFS 2010 that reads a config file containing custom section handlers. When I deployed my plugin on TFS 2013, it thrown the following error when trying to instantiate those section handlers :

A first chance exception of type ‘System.Configuration.ConfigurationErrorsException’ occurred in System.Configuration.dll

Additional information: An error occurred creating the configuration section handler for xxxxx: Could not load file or assembly ‘xxxxx’ or one of its dependencies. The system cannot find the file specified.

The problem was that there was no adequate probing path defined for .NET 4.5 in the TFS 2013 Services’ web config.

Click to Read More

TFS 2013 Services are loading plugins from C:\Program Files\Microsoft Team Foundation Server 12.0\Application Tier\Web Services\bin\Plugins.

But once those plugins loaded, they can only load their own dependencies from the GAC or from the TFS 2013 Services’ bin folder (C:\Program Files\Microsoft Team Foundation Server 12.0\Application Tier\Web Services\bin). I.e.: the sub-folder ‘Plugins’ of that bin folder is not probed…

In my case, when my plugin read its custom section, it needs to load the assembly containing the related custom section handlers (actually, the plugin and the custom handlers are in the very same assembly…). It works fine if I deploy the assembly in TFS 2013 Services’ bin folder (or in the GAC I presume) but I cannot bring myself  to simply do that. Especially taking into account that it was working fine within TFS 2010.

Having a look at TFS 2013 Services’ web config, in order to specify my own probing path, I noticed that there was already one defined, but ONLY for .Net 2.0. I.e.: here is the <runtime> section of C:\Program Files\Microsoft Team Foundation Server 12.0\Application Tier\Web Services\web.config

<!-- Plugin directory path -->
<runtime>
<assemblyBinding appliesTo="v2.0.50727" xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="bin\Plugins;bin\Plugins\zh-chs;bin\Plugins\zh-cht;bin\Plugins\de;bin\Plugins\es;bin\Plugins\fr;bin\Plugins\it;bin\Plugins\ja;bin\Plugins\ko"/>
<dependentAssembly>
<assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Extensions.Design" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>

As my plugin targets .Net 4.5, I simply defined the probing path for all target runtime.

<!-- Plugin directory path -->
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="bin\Plugins;bin\Plugins\zh-chs;bin\Plugins\zh-cht;bin\Plugins\de;bin\Plugins\es;bin\Plugins\fr;bin\Plugins\it;bin\Plugins\ja;bin\Plugins\ko"/>
</assemblyBinding>

<assemblyBinding appliesTo="v2.0.50727" xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Extensions.Design" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>

And that solved my issue !

FYI (and google indexing): my plugin is a Server Side Event Handler for TFS…

ProgrammingTips Programmatic access to “Print Dialog” of my network printer is really slow

In a .Net application, I am using the standard “Print Dialog” of Windows 7 x64 to print PDF document on a network printer named “\\Server\Printer”. It noticed on most test machines that the dialog window was taking up to 8 seconds only to open (I.e.: before doing the actual print). Configuring the printer “\\Server\Printer” as a “local printer on port \\Server\Printer” instead of configuring it as a “network printer” is solving that issue.

Click to Read More

I will post the .Net piece of code I am using to open the “Print Dialog” asap. It’s really the most simple one.

I experience the issue with a network Printer “Ricoh Aficio MP C5000”. I didn’t try with another model as all printers I have access to at my Office are the sames.

I looked for a solution to solve this issue without upgrading the network drivers or the firmware of the printer (as far as this could solve the issue as sometimes reported for HP printer) as I don’t have the required privileges to do so.  Here is the trick I found:

  1. If removed the current network printer via Control Panel > Device and Printers.
  2. Add a new Printer
  3. Choose  “local printer” on port  “\\Server\Printer”
  4. Choose the drivers in the list of available ones. Notice: I chose Ricoh Aficio MP C6000 PCL 6 as C5000 was not available.

Next, the “Print Dialog” opened immediately on all workstations where I did test the trick.

As soon as I re-install the printer as a “network printer”, the performance issue is back… Notice however that the dialog window always opens immediately when called from, e.g., MS Office (Word, Excel, …) whatever configuration mode I use for the printer.

Programming Create a Shared Folder and Set permissions in C#

Here is my code to share a folder, remove that share and set permissions using the System.Management library in C#.

Click to Read More

using System;
using System.ComponentModel;
using System.IO;
using System.Management;
using System.Reflection;
using System.Security.Principal;
namespace Fortis.Framework.Services.Config
{
 internal static class ManagementHelper
 {

  internal enum MethodStatus : uint
  {
   [Description("Operation succeeded")]
   Success = 0,
   [Description("Access denied")]
   AccessDenied = 2,
   [Description("Unknown failure")]
   UnknownFailure = 8,
   [Description("Invalid name")]
   InvalidName = 9,
   [Description("Invalid level")]
   InvalidLevel = 10,
   [Description("Invalid parameter")]
   InvalidParameter = 21,
   [Description("Duplicate share")]
   DuplicateShare = 22,
   [Description("Redirected path")]
   RedirectedPath = 23,
   [Description("Unknown device or directory")]
   UnknownDevice = 24,
   [Description("Net name not found")]
   NetNameNotFound = 25
  }

  internal enum ShareType : uint
  {
   [Description("Disk Drive")]
   DiskDrive = 0x0,
   [Description("Print Queue")]
   PrintQueue = 0x1,
   [Description("Device")]
   Device = 0x2,
   [Description("IPC")]
   IPC = 0x3,
   [Description("Disk Drive Admin")]
   DiskDriveAdmin = 0x80000000,
   [Description("Print Queue Admin")]
   PrintQueueAdmin = 0x80000001,
   [Description("Device Admin")]
   DeviceAdmin = 0x80000002,
   [Description("IPC Admin")]
   IpcAdmin = 0x80000003
  }

  internal enum ControlFlags : uint
  {
   SE_OWNER_DEFAULTED = 0x1, //Indicates an SD with a default owner security identifier (SID). Use this bit to find all of the objects that have default owner permissions set.
   SE_GROUP_DEFAULTED = 0x2, //Indicates an SD with a default group SID. Use this bit to find all of the objects that have default group permissions set.
   SE_DACL_PRESENT = 0x4, //Indicates an SD that has a DACL. If this flag is not set, or if this flag is set and the DACL is NULL, the SD allows full access to everyone.
   SE_DACL_DEFAULTED = 0x8, //Indicates an SD with a default DACL. For example, if an object creator does not specify a DACL, the object receives the default DACL from the access token of the creator. This flag can affect how the system treats the DACL, with respect to access control entry (ACE) inheritance. The system ignores this flag if the SE_DACL_PRESENT flag is not set.
   SE_SACL_PRESENT = 0x10, //Indicates an SD that has a system access control list (SACL).
   SE_SACL_DEFAULTED = 0x20, //Indicates an SD with a default SACL. For example, if an object creator does not specify an SACL, the object receives the default SACL from the access token of the creator. This flag can affect how the system treats the SACL, with respect to ACE inheritance. The system ignores this flag if the SE_SACL_PRESENT flag is not set.
   SE_DACL_AUTO_INHERIT_REQ = 0x100, //Requests that the provider for the object protected by the SD automatically propagate the DACL to existing child objects. If the provider supports automatic inheritance, the DACL is propagated to any existing child objects, and the SE_DACL_AUTO_INHERITED bit in the SD of the parent and child objects is set.
   SE_SACL_AUTO_INHERIT_REQ = 0x200, //Requests that the provider for the object protected by the SD automatically propagate the SACL to existing child objects. If the provider supports automatic inheritance, the SACL is propagated to any existing child objects, and the SE_SACL_AUTO_INHERITED bit in the SDs of the parent object and child objects is set.
   SE_DACL_AUTO_INHERITED = 0x400, //Indicates an SD in which the DACL is set up to support automatic propagation of inheritable ACEs to existing child objects. The system sets this bit when it performs the automatic inheritance algorithm for the object and its existing child objects. This bit is not set in SDs for Windows NT versions 4.0 and earlier, which do not support automatic propagation of inheritable ACEs.
   SE_SACL_AUTO_INHERITED = 0x800, //Indicates an SD in which the SACL is set up to support automatic propagation of inheritable ACEs to existing child objects. The system sets this bit when it performs the automatic inheritance algorithm for the object and its existing child objects. This bit is not set in SDs for Windows NT versions 4.0 and earlier, which do not support automatic propagation of inheritable ACEs.
   SE_DACL_PROTECTED = 0x1000, //Prevents the DACL of an SD from being modified by inheritable ACEs.
   SE_SACL_PROTECTED = 0x2000, //Prevents the SACL of an SD from being modified by inheritable ACEs.
   SE_SELF_RELATIVE = 0x8000, //Indicates an SD in self-relative format with all the security information in a contiguous block of memory. If this flag is not set, the SD is in absolute format. For more information, see Absolute and Self-Relative Security Descriptors.
  }

  internal enum FileAccessMask : uint
  {
   FILE_READ_DATA = 0x1, //Grants the right to read data from the file.
   FILE_WRITE_DATA = 0x2, //Grants the right to write data to the file.
   FILE_APPEND_DATA = 0x4, //Grants the right to append data to the file.
   FILE_READ_EA = 0x8, //Grants the right to read extended attributes.
   FILE_WRITE_EA = 0x10, //Grants the right to write extended attributes.
   FILE_EXECUTE = 0x20, //Grants the right to execute a file.
   FILE_DELETE_CHILD = 0x40,//Grants the right to delete a directory and all the files it contains (its children), even if the files are read-only.
   FILE_READ_ATTRIBUTES = 0x80, //Grants the right to read file attributes.
   FILE_WRITE_ATTRIBUTES = 0x100, //Grants the right to change file attributes.
   DELETE = 0x10000, //Grants delete access.
   READ_CONTROL = 0x20000, //Grants read access to the security descriptor and owner.
   WRITE_DAC = 0x40000,//Grants write access to the discretionary access control list (ACL).
   WRITE_OWNER = 0x80000,//Assigns the write owner.
   SYNCHRONIZE = 0x100000,//Synchronizes access and allows a process to wait for an object to enter the signaled state.
  }

  internal enum FolderAccessMask : uint
  {
   FILE_LIST_DIRECTORY = 0x1, //Grants the right to list the contents of the directory.
   FILE_ADD_FILE = 0x2, //Grants the right to create a file in the directory.
   FILE_ADD_SUBDIRECTORY = 0x4, //Grants the right to create a subdirectory.
   FILE_READ_EA = 0x8, //Grants the right to read extended attributes.
   FILE_WRITE_EA = 0x10, //Grants the right to write extended attributes.
   FILE_TRAVERSE = 0x20, //Grants the right to traverse the directory.
   FILE_DELETE_CHILD = 0x40,//Grants the right to delete a directory and all the files it contains (its children), even if the files are read-only.
   FILE_READ_ATTRIBUTES = 0x80, //Grants the right to read folder attributes.
   FILE_WRITE_ATTRIBUTES = 0x100, //Grants the right to change folder attributes.
   DELETE = 0x10000, //Grants delete access.
   READ_CONTROL = 0x20000, //Grants read access to the security descriptor and owner.
   WRITE_DAC = 0x40000,//Grants write access to the discretionary access control list (ACL).
   WRITE_OWNER = 0x80000,//Assigns the write owner.
   SYNCHRONIZE = 0x100000,//Synchronizes access and allows a process to wait for an object to enter the signaled state.
  }

  internal const uint FolderAccessFullControl = (uint)
   (FolderAccessMask.FILE_LIST_DIRECTORY | FolderAccessMask.FILE_ADD_FILE |
   FolderAccessMask.FILE_TRAVERSE | FolderAccessMask.FILE_ADD_SUBDIRECTORY |
   FolderAccessMask.FILE_READ_EA | FolderAccessMask.FILE_WRITE_EA |
   FolderAccessMask.FILE_DELETE_CHILD | FolderAccessMask.FILE_READ_ATTRIBUTES |
   FolderAccessMask.FILE_WRITE_ATTRIBUTES | FolderAccessMask.DELETE |
   FolderAccessMask.WRITE_DAC | FolderAccessMask.READ_CONTROL |
   FolderAccessMask.WRITE_OWNER | FolderAccessMask.SYNCHRONIZE);

  internal enum AceFlags : uint
  {
   OBJECT_INHERIT_ACE = 0x1, //Noncontainer child objects inherit the ACE as an effective ACE. For child objects that are containers, the ACE is inherited as an inherit-only ACE unless the NO_PROPAGATE_INHERIT_ACE bit flag is also set.
   CONTAINER_INHERIT_ACE = 0x2, //Child objects that are containers, such as directories, inherit the ACE as an effective ACE. The inherited ACE is inheritable unless the NO_PROPAGATE_INHERIT_ACE bit flag is also set.
   NO_PROPAGATE_INHERIT_ACE = 0x4, //If the ACE is inherited by a child object, the system clears the OBJECT_INHERIT_ACE and CONTAINER_INHERIT_ACE flags in the inherited ACE. This prevents the ACE from being inherited by subsequent generations of objects.
   INHERIT_ONLY_ACE = 0x8, //Indicates an inherit-only ACE which does not control access to the object to which it is attached. If this flag is not set, the ACE is an effective ACE which controls access to the object to which it is attached. Both effective and inherit-only ACEs can be inherited depending on the state of the other inheritance flags.
   INHERITED_ACE = 0x10, //The system sets this bit when it propagates an inherited ACE to a child object.
   SUCCESSFUL_ACCESS_ACE_FLAG = 0x40, //Used with system-audit ACEs in an SACL to generate audit messages for successful access attempts.
   FAILED_ACCESS_ACE_FLAG = 0x80 //Used with system-audit ACEs in an SACL to generate audit messages for failed access attempts.
  }

  internal enum AceType : uint
  {
   Access_Allowed = 0,
   Access_Denied = 1,
   Audit = 2
  }

  internal static string GetDescription(this Enum value)
  {
   FieldInfo fi = value.GetType().GetField(value.ToString());

   DescriptionAttribute[] attributes =
    (DescriptionAttribute[])fi.GetCustomAttributes(
     typeof(DescriptionAttribute), false);

   if (attributes != null && attributes.Length > 0)
     return attributes[0].Description;
   else
     return value.ToString();
  }

  /// <summary>
  /// Add a Share on a folder.
  /// </summary>
  /// <param name="folderPath">Path of the folder to be shared.</param>
  /// <param name="shareName">Name to be given to the share.</param>
  /// <param name="description">Description to be set on the share.</param>
  /// <param name="securityDescriptor">Security to be set on the share, including username and access rights.</param>
  /// <remarks>The folder is created if it does not yet exist.</remarks>
  internal static void AddSharedFolder(string folderPath,
                            string shareName, string description,
                            ManagementObject securityDescriptor)
  {
   try
   {
    // Create the folder if required
    var folder = new DirectoryInfo(folderPath);
    if (!folder.Exists) folder.Create();

    // Create a ManagementClass object
    ManagementClass managementClass = new ManagementClass("Win32_Share");

    // Create ManagementBaseObjects for in and out parameters
    ManagementBaseObject inParams =
                         managementClass.GetMethodParameters("Create");
    ManagementBaseObject outParams;

    // Set the input parameters
    inParams["Description"] = description;
    inParams["Name"] = shareName;
    inParams["Path"] = folderPath;
    inParams["Type"] = ShareType.DiskDrive;
    //inParams["MaximumAllowed"] = 20;
    inParams["Password"] = null;
    inParams["Access"] = securityDescriptor;

    // Invoke the "create" method on the ManagementClass object
    outParams = managementClass.InvokeMethod("Create", inParams, null);

    // Check the result
    var result =
        GetMethodStatus(outParams.Properties["ReturnValue"].Value);
    Console.WriteLine("Create Share: " + result.GetDescription());
   }
   catch (Exception ex)
   {
    Console.WriteLine("Error: " + ex.Message);
   }
  }

  /// <summary>
  /// Get security settings required to grant access rights on Files of Folders to a user account.
  /// </summary>
  /// <param name="domain">Domain of the User account</param>
  /// <param name="username">Name of the User account.</param>
  /// <param name="accessMask">Requested Access Rights.</param>
  /// <returns>A security Descriptor with the requested access right for the provided user account.</returns>
  internal static ManagementObject GetSecurityDescriptor(string domain, string username, object accessMask)
  {
   var everyoneAccount = new NTAccount(domain, username);
   var sid = everyoneAccount.Translate(typeof(SecurityIdentifier)) as SecurityIdentifier;
   var sidArray = new byte[sid.BinaryLength];
   sid.GetBinaryForm(sidArray, 0);

   ManagementObject everyone = new ManagementClass("Win32_Trustee");
   everyone["Domain"] = domain;
   everyone["Name"] = username;
   everyone["SID"] = sidArray;

   ManagementObject dacl = new ManagementClass("Win32_Ace");
   dacl["AccessMask"] = accessMask;
   dacl["AceFlags"] = AceFlags.OBJECT_INHERIT_ACE | AceFlags.CONTAINER_INHERIT_ACE;
   dacl["AceType"] = AceType.Access_Allowed;
   dacl["Trustee"] = everyone;

   ManagementObject securityDescriptor = new ManagementClass("Win32_SecurityDescriptor");
   securityDescriptor["ControlFlags"] = ControlFlags.SE_DACL_PRESENT;
   securityDescriptor["DACL"] = new object[] { dacl };
   return securityDescriptor;
  }

  /// <summary>
  /// Delete a Share.
  /// </summary>
  /// <param name="shareName">The name of the share to be deleted.</param>
  internal static void RemoveSharedFolder(string shareName)
  {
   try
   {
    // Create a ManagementClass object
    ManagementClass managementClass = new ManagementClass("Win32_Share");

    // Get all existing shares and find the one to be deleted
    ManagementObjectCollection shares = managementClass.GetInstances();
    foreach (ManagementObject share in shares)
    {
     if (Convert.ToString(share["Name"]).Equals(shareName, StringComparison.InvariantCultureIgnoreCase))
     {
      // Invoke the "delete" method on the Share object
      var outParams = share.InvokeMethod("Delete", new object[] { });

      // Check the Result
      var result = GetMethodStatus(outParams);
      Console.WriteLine("Delete Share: " + result.GetDescription());

      break;
     }
    }
   }
   catch (Exception ex)
   {
    Console.WriteLine("Error: " + ex.Message);
   }
  }

  private static MethodStatus GetMethodStatus(object outParams)
  {
   MethodStatus result = MethodStatus.UnknownFailure;
   Enum.TryParse<MethodStatus>(Convert.ToString(outParams), out result);
   return result;
  }
 }
}

And here is a code sample I am using:

var path = @"C:\Users\Public\Documents\SomeFolder";

var folder = new DirectoryInfo(path);
 if (!folder.Exists) folder.Create();

ManagementHelper.RemoveSharedFolder("MyShare");

// Get the security Descriptor
 ManagementObject securityDescriptor = ManagementHelper.GetSecurityDescriptor(null, "everyone", ManagementHelper.FolderAccessFullControl);

// Create the Share
 ManagementHelper.AddSharedFolder(path, "MyShare", "My Shared Folder", securityDescriptor);