<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title><![CDATA[LA/QM forum - Shared C# code]]></title>
		<link>https://www.libreautomate.com/forum/</link>
		<description><![CDATA[LA/QM forum - https://www.libreautomate.com/forum]]></description>
		<pubDate>Wed, 29 Apr 2026 05:49:19 +0000</pubDate>
		<generator>MyBB</generator>
		<item>
			<title><![CDATA[Simple WinForms app with LA]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7880</link>
			<pubDate>Tue, 14 Oct 2025 22:39:10 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=1539">burque505</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7880</guid>
			<description><![CDATA[(Caution: After I reread this, it sounds like it'll be about SharpDocx, but that's just the motivation for the post. I'm trying to use LibreAutomate with SharpDocx. SharpDocx allows use of C# code with a Word file, which is executed when the doc is generated. See <a href="https://github.com/egonl/SharpDocx" target="_blank" rel="noopener" class="mycode_url">the SharpDocx github repo</a> for more if you're interested. )<br />
<br />
Ideally I would use wpfBuilder to interact with SharpDocx. For now, I'm getting namespace and assembly errors from my flailing around, so for now I'm using Winforms. To that end, I created a simple Winform that does in fact run from SharpDocx, though I'm just posting the Winform itself that runs directly from LA. No main() method, just top-level statements. I'm sure there's a better way, and if I find one I'll share it.<br />
<br />
That said, here's a link to a standalone Winforms designer, no documentation in any language I speak of, but it works: <a href="https://github.com/hry2566/SWD4CS" target="_blank" rel="noopener" class="mycode_url">SWD4CS</a>. Caveats for that program is are a) you'll have to build it, and b) once you have it compiled you can make nice Winforms with code to copy, but you'll get a Form1.Designer.cs that you'll have to tweak (read: mangle).<br />
<br />
Hopefully this code can serve as a starting point for creating your own Winforms to use with LibreAutomate.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code><span class='kw'>using</span> <span class='ns'>System</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Forms</span><span class='pn'>;</span><br />
<br />
<span class='tp'>Application</span><span class='op'>.</span>EnableVisualStyles<span class='pn'>();</span><br />
<span class='tp'>Application</span><span class='op'>.</span>SetCompatibleTextRenderingDefault<span class='pn'>(</span><span class='kw'>false</span><span class='pn'>);</span><br />
<br />
<span class='tp'>Form</span> <span class='vr'>Form1</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>Form</span><span class='pn'>();</span><br />
<span class='vr'>Form1</span><span class='op'>.</span><span class='fn'>Text</span> <span class='op'>=</span> <span class='st'>"Form1"</span><span class='pn'>;</span><br />
<span class='vr'>Form1</span><span class='op'>.</span><span class='fn'>Size</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Drawing</span><span class='op'>.</span><span class='tp'>Size</span><span class='pn'>(</span><span class='nr'>480</span><span class='pn'>,</span> <span class='nr'>400</span><span class='pn'>);</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>Button0</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>Button</span> <span class='pn'>{</span><br />
    Text <span class='op'>=</span> <span class='st'>"MyButton"</span><span class='pn'>,</span><br />
    Location <span class='op'>=</span> <span class='kw'>new</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Drawing</span><span class='op'>.</span><span class='tp'>Point</span><span class='pn'>(</span><span class='nr'>48</span><span class='pn'>,</span> <span class='nr'>48</span><span class='pn'>),</span><br />
    Size <span class='op'>=</span> <span class='kw'>new</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Drawing</span><span class='op'>.</span><span class='tp'>Size</span><span class='pn'>(</span><span class='nr'>104</span><span class='pn'>,</span> <span class='nr'>23</span><span class='pn'>)<br />
};</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>TextBox1</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>TextBox</span> <span class='pn'>{</span><br />
    Multiline <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>,</span><br />
    Text <span class='op'>=</span> <span class='st'>"TextBox1"</span><span class='pn'>,</span><br />
    Location <span class='op'>=</span> <span class='kw'>new</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Drawing</span><span class='op'>.</span><span class='tp'>Point</span><span class='pn'>(</span><span class='nr'>60</span><span class='pn'>,</span> <span class='nr'>84</span><span class='pn'>),</span><br />
    Size <span class='op'>=</span> <span class='kw'>new</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Drawing</span><span class='op'>.</span><span class='tp'>Size</span><span class='pn'>(</span><span class='nr'>320</span><span class='pn'>,</span> <span class='nr'>80</span><span class='pn'>),</span><br />
    TabIndex <span class='op'>=</span> <span class='nr'>1</span><br />
<span class='pn'>};</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>ListBox2</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>ListBox</span> <span class='pn'>{</span><br />
    ItemHeight <span class='op'>=</span> <span class='nr'>15</span><span class='pn'>,</span><br />
    Location <span class='op'>=</span> <span class='kw'>new</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Drawing</span><span class='op'>.</span><span class='tp'>Point</span><span class='pn'>(</span><span class='nr'>64</span><span class='pn'>,</span> <span class='nr'>180</span><span class='pn'>),</span><br />
    Size <span class='op'>=</span> <span class='kw'>new</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Drawing</span><span class='op'>.</span><span class='tp'>Size</span><span class='pn'>(</span><span class='nr'>316</span><span class='pn'>,</span> <span class='nr'>154</span><span class='pn'>),</span><br />
    TabIndex <span class='op'>=</span> <span class='nr'>2</span><br />
<span class='pn'>};</span><br />
<br />
<span class='vr'>Form1</span><span class='op'>.</span><span class='fn'>Controls</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>Button0</span><span class='pn'>);</span><br />
<span class='vr'>Form1</span><span class='op'>.</span><span class='fn'>Controls</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>TextBox1</span><span class='pn'>);</span><br />
<span class='vr'>Form1</span><span class='op'>.</span><span class='fn'>Controls</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>ListBox2</span><span class='pn'>);</span><br />
<br />
<span class='cm'>// ? Add a Click event handler</span><br />
<span class='vr'>Button0</span><span class='op'>.</span>Click <span class='op'>+=</span> <span class='pn'>(</span><span class='vr'>sender</span><span class='pn'>,</span> <span class='vr'>e</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
    <span class='cm'>// Example: write to the textbox and listbox</span><br />
    <span class='vr'>TextBox1</span><span class='op'>.</span>AppendText<span class='pn'>(</span><span class='st'>"</span><span class='se'>&#92;r&#92;n</span><span class='st'>Button clicked at "</span> <span class='op'>+</span> <span class='tp'>DateTime</span><span class='op'>.</span><span class='fn'>Now</span><span class='pn'>);</span><br />
    <span class='vr'>ListBox2</span><span class='op'>.</span>Items<span class='op'>.</span>Add<span class='pn'>(</span><span class='st'>"Clicked at "</span> <span class='op'>+</span> <span class='tp'>DateTime</span><span class='op'>.</span><span class='fn'>Now</span><span class='op'>.</span><span class='fn'>ToLongTimeString</span><span class='pn'>());</span><br />
    <span class='cm'>// Or show a popup:<br />
    // MessageBox.Show("Button was clicked!");</span><br />
<span class='pn'>};</span><br />
<br />
<span class='tp'>Application</span><span class='op'>.</span>Run<span class='pn'>(</span><span class='vr'>Form1</span><span class='pn'>);</span></code></div></div><br />
Best regards,<br />
burque505<br />
<br />
P.S. I forgot mention. the attached screenshot is from the Winform being run as part of the generation of a docx file with SharpDocx. Looks exactly the same and runs the same when executed standalone.<br /><!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://www.libreautomate.com/forum/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=1385" target="_blank" title="">2025_10_14 @ 16_37_52 (451x375).PNG</a> (Size: 5.12 KB / Downloads: 103)
<!-- end: postbit_attachments_attachment -->]]></description>
			<content:encoded><![CDATA[(Caution: After I reread this, it sounds like it'll be about SharpDocx, but that's just the motivation for the post. I'm trying to use LibreAutomate with SharpDocx. SharpDocx allows use of C# code with a Word file, which is executed when the doc is generated. See <a href="https://github.com/egonl/SharpDocx" target="_blank" rel="noopener" class="mycode_url">the SharpDocx github repo</a> for more if you're interested. )<br />
<br />
Ideally I would use wpfBuilder to interact with SharpDocx. For now, I'm getting namespace and assembly errors from my flailing around, so for now I'm using Winforms. To that end, I created a simple Winform that does in fact run from SharpDocx, though I'm just posting the Winform itself that runs directly from LA. No main() method, just top-level statements. I'm sure there's a better way, and if I find one I'll share it.<br />
<br />
That said, here's a link to a standalone Winforms designer, no documentation in any language I speak of, but it works: <a href="https://github.com/hry2566/SWD4CS" target="_blank" rel="noopener" class="mycode_url">SWD4CS</a>. Caveats for that program is are a) you'll have to build it, and b) once you have it compiled you can make nice Winforms with code to copy, but you'll get a Form1.Designer.cs that you'll have to tweak (read: mangle).<br />
<br />
Hopefully this code can serve as a starting point for creating your own Winforms to use with LibreAutomate.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code><span class='kw'>using</span> <span class='ns'>System</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Forms</span><span class='pn'>;</span><br />
<br />
<span class='tp'>Application</span><span class='op'>.</span>EnableVisualStyles<span class='pn'>();</span><br />
<span class='tp'>Application</span><span class='op'>.</span>SetCompatibleTextRenderingDefault<span class='pn'>(</span><span class='kw'>false</span><span class='pn'>);</span><br />
<br />
<span class='tp'>Form</span> <span class='vr'>Form1</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>Form</span><span class='pn'>();</span><br />
<span class='vr'>Form1</span><span class='op'>.</span><span class='fn'>Text</span> <span class='op'>=</span> <span class='st'>"Form1"</span><span class='pn'>;</span><br />
<span class='vr'>Form1</span><span class='op'>.</span><span class='fn'>Size</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Drawing</span><span class='op'>.</span><span class='tp'>Size</span><span class='pn'>(</span><span class='nr'>480</span><span class='pn'>,</span> <span class='nr'>400</span><span class='pn'>);</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>Button0</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>Button</span> <span class='pn'>{</span><br />
    Text <span class='op'>=</span> <span class='st'>"MyButton"</span><span class='pn'>,</span><br />
    Location <span class='op'>=</span> <span class='kw'>new</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Drawing</span><span class='op'>.</span><span class='tp'>Point</span><span class='pn'>(</span><span class='nr'>48</span><span class='pn'>,</span> <span class='nr'>48</span><span class='pn'>),</span><br />
    Size <span class='op'>=</span> <span class='kw'>new</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Drawing</span><span class='op'>.</span><span class='tp'>Size</span><span class='pn'>(</span><span class='nr'>104</span><span class='pn'>,</span> <span class='nr'>23</span><span class='pn'>)<br />
};</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>TextBox1</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>TextBox</span> <span class='pn'>{</span><br />
    Multiline <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>,</span><br />
    Text <span class='op'>=</span> <span class='st'>"TextBox1"</span><span class='pn'>,</span><br />
    Location <span class='op'>=</span> <span class='kw'>new</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Drawing</span><span class='op'>.</span><span class='tp'>Point</span><span class='pn'>(</span><span class='nr'>60</span><span class='pn'>,</span> <span class='nr'>84</span><span class='pn'>),</span><br />
    Size <span class='op'>=</span> <span class='kw'>new</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Drawing</span><span class='op'>.</span><span class='tp'>Size</span><span class='pn'>(</span><span class='nr'>320</span><span class='pn'>,</span> <span class='nr'>80</span><span class='pn'>),</span><br />
    TabIndex <span class='op'>=</span> <span class='nr'>1</span><br />
<span class='pn'>};</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>ListBox2</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>ListBox</span> <span class='pn'>{</span><br />
    ItemHeight <span class='op'>=</span> <span class='nr'>15</span><span class='pn'>,</span><br />
    Location <span class='op'>=</span> <span class='kw'>new</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Drawing</span><span class='op'>.</span><span class='tp'>Point</span><span class='pn'>(</span><span class='nr'>64</span><span class='pn'>,</span> <span class='nr'>180</span><span class='pn'>),</span><br />
    Size <span class='op'>=</span> <span class='kw'>new</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Drawing</span><span class='op'>.</span><span class='tp'>Size</span><span class='pn'>(</span><span class='nr'>316</span><span class='pn'>,</span> <span class='nr'>154</span><span class='pn'>),</span><br />
    TabIndex <span class='op'>=</span> <span class='nr'>2</span><br />
<span class='pn'>};</span><br />
<br />
<span class='vr'>Form1</span><span class='op'>.</span><span class='fn'>Controls</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>Button0</span><span class='pn'>);</span><br />
<span class='vr'>Form1</span><span class='op'>.</span><span class='fn'>Controls</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>TextBox1</span><span class='pn'>);</span><br />
<span class='vr'>Form1</span><span class='op'>.</span><span class='fn'>Controls</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>ListBox2</span><span class='pn'>);</span><br />
<br />
<span class='cm'>// ? Add a Click event handler</span><br />
<span class='vr'>Button0</span><span class='op'>.</span>Click <span class='op'>+=</span> <span class='pn'>(</span><span class='vr'>sender</span><span class='pn'>,</span> <span class='vr'>e</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
    <span class='cm'>// Example: write to the textbox and listbox</span><br />
    <span class='vr'>TextBox1</span><span class='op'>.</span>AppendText<span class='pn'>(</span><span class='st'>"</span><span class='se'>&#92;r&#92;n</span><span class='st'>Button clicked at "</span> <span class='op'>+</span> <span class='tp'>DateTime</span><span class='op'>.</span><span class='fn'>Now</span><span class='pn'>);</span><br />
    <span class='vr'>ListBox2</span><span class='op'>.</span>Items<span class='op'>.</span>Add<span class='pn'>(</span><span class='st'>"Clicked at "</span> <span class='op'>+</span> <span class='tp'>DateTime</span><span class='op'>.</span><span class='fn'>Now</span><span class='op'>.</span><span class='fn'>ToLongTimeString</span><span class='pn'>());</span><br />
    <span class='cm'>// Or show a popup:<br />
    // MessageBox.Show("Button was clicked!");</span><br />
<span class='pn'>};</span><br />
<br />
<span class='tp'>Application</span><span class='op'>.</span>Run<span class='pn'>(</span><span class='vr'>Form1</span><span class='pn'>);</span></code></div></div><br />
Best regards,<br />
burque505<br />
<br />
P.S. I forgot mention. the attached screenshot is from the Winform being run as part of the generation of a docx file with SharpDocx. Looks exactly the same and runs the same when executed standalone.<br /><!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://www.libreautomate.com/forum/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=1385" target="_blank" title="">2025_10_14 @ 16_37_52 (451x375).PNG</a> (Size: 5.12 KB / Downloads: 103)
<!-- end: postbit_attachments_attachment -->]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Nilesoft Shell. Modify Explorer context menu, add C# scripts.]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7819</link>
			<pubDate>Sat, 03 May 2025 11:22:51 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=2">Gintaras</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7819</guid>
			<description><![CDATA[<a href="https://nilesoft.org/" target="_blank" rel="noopener" class="mycode_url">https://nilesoft.org/</a><br />
<br />
Very powerful, but not easy to use. No GUI. Need to read documentation and edit a configuration file.<br />
<br />
<span style="font-weight: bold;" class="mycode_b">Setup</span><br />
Download and install Nilesoft Shell (NS).<br />
<br />
You can edit NS configuration files in any text editor. I edit in LibreAutomate. If you'll use VSCode, install the NS formatter extension.<br />
<br />
Create new empty NS configuration file somewhere not in the NS folder. For example file <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">My shell.nss</code> in LibreAutomate. Copy its full path. Open the NS folder, and open file <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">shell.nss</code> in a text editor running as administrator (for example import as link in LA). Before line <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">import 'imports/modify.nss'</code> insert line <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">import 'the path'</code>. Example:<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>...<br />
import 'C:&#92;LA workspace&#92;files&#92;Shell menu&#92;My shell.nss'<br />
import 'imports/modify.nss'<br />
...</code></div></div><br />
Don't add more code in <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">shell.nss</code> (NS setup deletes it). Keep your code in the new nss file.<br />
<br />
<span style="font-weight: bold;" class="mycode_b">Example: add menu items to run C# scripts in LA</span><br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>//Create submenu "LibreAutomate" and add some new items there.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `mode='multiple'` means "add it even if multiple files are selected". Child menu items inherit the mode and can override.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `mode`, `title`, `image` etc are documented in https://nilesoft.org/docs/configuration/properties<br />
menu(mode='multiple' title='LibreAutomate' image='C:&#92;Program Files&#92;LibreAutomate&#92;Au.Editor.exe')<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;//The menu command executes C# script "Shell menu script 1". The selected file path will be in `args[0]`.<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='C# script 1' mode='single' type='file' cmd='Au.Editor.exe' args='"Shell menu script 1" @sel(true)')<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;//The menu command executes C# script "Shell menu script 1". The selected file path will be in `args[1]`, and `"-example"` in `args[0]`.<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='C# script 1' mode='single' type='file'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;find='.txt|.cs|.xml' //we can specify file types with `find` (simple) or `where` (supports expressions)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmd='Au.Editor.exe' args='"Shell menu script 1" -example @sel(true)')<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;separator<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;//The menu command executes C# script "Shell menu script 2". File paths will be in the `args` array. Script example: foreach (var path in args) { print.it(path); }<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='C# script 2'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmd='Au.Editor.exe' args='"Shell menu script 2" @sel(true)'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image=&#92;uE150 //See https://nilesoft.org/gallery/glyphs. Or can be `image='path of ico, exe, png etc file'` etc.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tip="Example tooltip&#92;nLine 2"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Notes:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Mode not specified, therefore this item inherits `mode='multiple'` from menu properties.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Type not specified, therefore this item is shown for everything (files, directories etc) except taskbar.<br />
&nbsp;&nbsp;&nbsp;&nbsp;)<br />
}</code></div></div><br />
<span style="font-weight: bold;" class="mycode_b">More examples</span><br />
I just started using Nilesoft Shell, and this is my <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">nss</code> file now.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>//In shell.nss import this file before `import 'imports/modify.nss'`.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;Example: import 'C:&#92;LA workspace&#92;files&#92;Shell context menu&#92;My shell.nss'<br />
<br />
//Set dark theme.<br />
theme<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;dark=true<br />
}<br />
<br />
//Remove useless/redundant menu items<br />
remove(where=this.id(id.open,id.copy_as_path,id.extract_all,id.rotate_left,id.rotate_right,id.set_as_desktop_background))<br />
//Why I remove them: Open - I double-click instead. Copy as path - see "File manage &gt; Copy path". Extract all - I use 7-Zip.<br />
<br />
//Move rarely used menu items to submenu "More options"<br />
modify(where=this.id(id.add_to_favorites,id.print) menu=title.more_options)<br />
<br />
//Add separator after Properties<br />
modify(where=this.id(id.properties) sep=after)<br />
<br />
//Create submenu "Cut etc" and move some items there. Instead I use hotkeys Ctrl+X etc, but not always.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `mode='multiple'` means "add it even if multiple files are selected". Default mode is `single`.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `mode`, `title`, `image`, `pos`, `where` etc are documented in https://nilesoft.org/docs/configuration/properties<br />
menu(mode='multiple' title='Cut etc' image=icon.cut pos=bottom)<br />
{<br />
}<br />
modify(where=this.id(id.cut,id.copy,id.paste,id.delete,id.rename) menu="Cut etc")<br />
<br />
//Create submenu "LibreAutomate" and add some new items there.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `mode='multiple'` means "add it even if multiple files are selected". Child menu items inherit the mode and can override.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `mode`, `title`, `image` etc are documented in https://nilesoft.org/docs/configuration/properties<br />
menu(mode='multiple' title='LibreAutomate' image='C:&#92;Program Files&#92;LibreAutomate&#92;Au.Editor.exe')<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;//The menu command executes C# script "Shell menu script 1". The selected file path will be in `args[0]`.<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='C# script 1' mode='single' type='file' cmd='Au.Editor.exe' args='"Shell menu script 1" @sel(true)')<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;//The menu command executes C# script "Shell menu script 1". The selected file path will be in `args[1]`, and `"-example"` in `args[0]`.<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='C# script 1' mode='single' type='file'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;find='.txt|.cs|.xml' //we can specify file types with `find` (simple) or `where` (supports expressions)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmd='Au.Editor.exe' args='"Shell menu script 1" -example @sel(true)')<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;separator<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;//The menu command executes C# script "Shell menu script 2". File paths will be in the `args` array. Script example: foreach (var path in args) { print.it(path); }<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='C# script 2'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmd='Au.Editor.exe' args='"Shell menu script 2" @sel(true)'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image=&#92;uE150 //See https://nilesoft.org/gallery/glyphs. Or can be `image='path of ico, exe, png etc file'` etc.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tip="Example tooltip&#92;nLine 2"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Notes:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Mode not specified, therefore this item inherits `mode='multiple'` from menu properties.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Type not specified, therefore this item is shown for everything (files, directories etc) except taskbar.<br />
&nbsp;&nbsp;&nbsp;&nbsp;)<br />
}<br />
<br />
//Create submenu "Edit with" and add some new items there.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `type='file'` means show the menu only for files but not for folders.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `find="..."` specifies file types for which to add the menu item.<br />
menu(type='file' title='Edit with' image=icon.edit pos=top)<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='Dependencies'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type='file' find=".dll|.exe"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmd='C:&#92;Program Files&#92;Dependencies&#92;DependenciesGui.exe' args='@sel(true)')<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='PEView'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type='file' find=".dll|.exe"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmd='C:&#92;Program Files&#92;PEview&#92;PEview.exe' args='@sel(true)')<br />
}<br />
<br />
//Move some items to the "Edit with" submenu<br />
modify(where=this.id(id.edit) menu="Edit with")<br />
modify(find="Edit with *|Open with *" menu="Edit with")<br />
<br />
//Move the "Open with" submenu to the top (after "Edit with")<br />
modify(where=this.id(id.open_with) pos=top)<br />
<br />
//I use app TreeSize to show folder sizes. It adds a menu item. But sometimes need to run it as admin. Therefore I replace that item with an item that runs TreeSize as admin. I could use the easy way (commented out below), but to avoid UAC consent screen I use a C# script instead.<br />
remove(find="Scan with TreeSize*")<br />
//item(title='TreeSize' type='dir|drive|back' image='C:&#92;Program Files&#92;TreeSize Free&#92;TreeSizeFree.exe' cmd='C:&#92;Program Files&#92;TreeSize Free&#92;TreeSizeFree.exe' args='@sel(true)' admin=true)<br />
item(title='TreeSize'<br />
&nbsp;&nbsp;&nbsp;&nbsp;type='dir|drive|back' image='C:&#92;Program Files&#92;TreeSize Free&#92;TreeSizeFree.exe'<br />
&nbsp;&nbsp;&nbsp;&nbsp;cmd='Au.Editor.exe' args='"Shell menu" "treesize" @sel(true)')<br />
<br />
//Add some new items to menu "7-Zip".<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `find="..."` specifies file types for which to add the menu item.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The menu command executes C# script "Shell menu" and passes the selected file path and 2 more arguments to it.<br />
item(title='Unzip'<br />
&nbsp;&nbsp;&nbsp;&nbsp;type='file' find=".zip|.7z|.gz|.tar|.nupkg"<br />
&nbsp;&nbsp;&nbsp;&nbsp;cmd='Au.Editor.exe' args='"Shell menu" "unzip" @sel(true) ""'<br />
&nbsp;&nbsp;&nbsp;&nbsp;image=&#92;ue202<br />
&nbsp;&nbsp;&nbsp;&nbsp;move='7-Zip' pos=0)<br />
item(title='Unzip and delete'<br />
&nbsp;&nbsp;&nbsp;&nbsp;type='file' find=".zip|.7z|.gz|.tar|.nupkg"<br />
&nbsp;&nbsp;&nbsp;&nbsp;cmd='Au.Editor.exe' args='"Shell menu" "unzip" @sel(true) "delete"'<br />
&nbsp;&nbsp;&nbsp;&nbsp;image=&#92;ue202<br />
&nbsp;&nbsp;&nbsp;&nbsp;move='7-Zip' pos=1 sep=after)</code></div></div><br />
<span style="font-weight: bold;" class="mycode_b">More info</span><br />
<br />
To apply changes after editing, Ctrl+right-click in a folder window or desktop, taskbar.<br />
<br />
If there are errors in <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">nss</code> file text:<br />
<ol type="1" class="mycode_list"><li>Shell writes an error line in file <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">shell.log</code>. To quickly access the file, you can import it in LA as link.<br />
</li>
<li>Shell stops working (the right-click menu is unmodified). After fixing the error need to restart Explorer processes. Run <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">shell.exe</code> (the NS program file) and click <span style="font-weight: bold;" class="mycode_b">Restart Explorer</span>. Or run <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">shell.exe -restart</code>.<br />
</li>
</ol>
To show hidden items too, Shift+right-click in a folder window (or desktop, taskbar).<br />
<br />
To temporarily disable the modified menu: Ctrl+Win+rightclick in a folder window. To enable: Ctrl+rightclick.<br />
<br />
C# code "for each selected path" when code in the nss file is like <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">args='"Script name" @sel(true)'</code>:<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code><span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>path</span> <span class='kw'>in args</span><span class='pn'>) {</span><br />
    <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='vr'>path</span><span class='pn'>);<br />
}</span></code></div></div><br />
C# code "for each selected path" when code in the nss file is like <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">args='"Script name" -example @sel(true)'</code>:<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code><span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>path</span> <span class='kw'>in args</span><span class='op'>.</span><span class='fn'>Skip</span><span class='pn'>(</span><span class='nr'>1</span><span class='pn'>)) {</span><br />
    <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='vr'>path</span><span class='pn'>);<br />
}</span></code></div></div>]]></description>
			<content:encoded><![CDATA[<a href="https://nilesoft.org/" target="_blank" rel="noopener" class="mycode_url">https://nilesoft.org/</a><br />
<br />
Very powerful, but not easy to use. No GUI. Need to read documentation and edit a configuration file.<br />
<br />
<span style="font-weight: bold;" class="mycode_b">Setup</span><br />
Download and install Nilesoft Shell (NS).<br />
<br />
You can edit NS configuration files in any text editor. I edit in LibreAutomate. If you'll use VSCode, install the NS formatter extension.<br />
<br />
Create new empty NS configuration file somewhere not in the NS folder. For example file <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">My shell.nss</code> in LibreAutomate. Copy its full path. Open the NS folder, and open file <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">shell.nss</code> in a text editor running as administrator (for example import as link in LA). Before line <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">import 'imports/modify.nss'</code> insert line <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">import 'the path'</code>. Example:<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>...<br />
import 'C:&#92;LA workspace&#92;files&#92;Shell menu&#92;My shell.nss'<br />
import 'imports/modify.nss'<br />
...</code></div></div><br />
Don't add more code in <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">shell.nss</code> (NS setup deletes it). Keep your code in the new nss file.<br />
<br />
<span style="font-weight: bold;" class="mycode_b">Example: add menu items to run C# scripts in LA</span><br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>//Create submenu "LibreAutomate" and add some new items there.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `mode='multiple'` means "add it even if multiple files are selected". Child menu items inherit the mode and can override.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `mode`, `title`, `image` etc are documented in https://nilesoft.org/docs/configuration/properties<br />
menu(mode='multiple' title='LibreAutomate' image='C:&#92;Program Files&#92;LibreAutomate&#92;Au.Editor.exe')<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;//The menu command executes C# script "Shell menu script 1". The selected file path will be in `args[0]`.<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='C# script 1' mode='single' type='file' cmd='Au.Editor.exe' args='"Shell menu script 1" @sel(true)')<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;//The menu command executes C# script "Shell menu script 1". The selected file path will be in `args[1]`, and `"-example"` in `args[0]`.<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='C# script 1' mode='single' type='file'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;find='.txt|.cs|.xml' //we can specify file types with `find` (simple) or `where` (supports expressions)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmd='Au.Editor.exe' args='"Shell menu script 1" -example @sel(true)')<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;separator<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;//The menu command executes C# script "Shell menu script 2". File paths will be in the `args` array. Script example: foreach (var path in args) { print.it(path); }<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='C# script 2'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmd='Au.Editor.exe' args='"Shell menu script 2" @sel(true)'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image=&#92;uE150 //See https://nilesoft.org/gallery/glyphs. Or can be `image='path of ico, exe, png etc file'` etc.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tip="Example tooltip&#92;nLine 2"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Notes:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Mode not specified, therefore this item inherits `mode='multiple'` from menu properties.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Type not specified, therefore this item is shown for everything (files, directories etc) except taskbar.<br />
&nbsp;&nbsp;&nbsp;&nbsp;)<br />
}</code></div></div><br />
<span style="font-weight: bold;" class="mycode_b">More examples</span><br />
I just started using Nilesoft Shell, and this is my <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">nss</code> file now.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>//In shell.nss import this file before `import 'imports/modify.nss'`.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;Example: import 'C:&#92;LA workspace&#92;files&#92;Shell context menu&#92;My shell.nss'<br />
<br />
//Set dark theme.<br />
theme<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;dark=true<br />
}<br />
<br />
//Remove useless/redundant menu items<br />
remove(where=this.id(id.open,id.copy_as_path,id.extract_all,id.rotate_left,id.rotate_right,id.set_as_desktop_background))<br />
//Why I remove them: Open - I double-click instead. Copy as path - see "File manage &gt; Copy path". Extract all - I use 7-Zip.<br />
<br />
//Move rarely used menu items to submenu "More options"<br />
modify(where=this.id(id.add_to_favorites,id.print) menu=title.more_options)<br />
<br />
//Add separator after Properties<br />
modify(where=this.id(id.properties) sep=after)<br />
<br />
//Create submenu "Cut etc" and move some items there. Instead I use hotkeys Ctrl+X etc, but not always.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `mode='multiple'` means "add it even if multiple files are selected". Default mode is `single`.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `mode`, `title`, `image`, `pos`, `where` etc are documented in https://nilesoft.org/docs/configuration/properties<br />
menu(mode='multiple' title='Cut etc' image=icon.cut pos=bottom)<br />
{<br />
}<br />
modify(where=this.id(id.cut,id.copy,id.paste,id.delete,id.rename) menu="Cut etc")<br />
<br />
//Create submenu "LibreAutomate" and add some new items there.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `mode='multiple'` means "add it even if multiple files are selected". Child menu items inherit the mode and can override.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `mode`, `title`, `image` etc are documented in https://nilesoft.org/docs/configuration/properties<br />
menu(mode='multiple' title='LibreAutomate' image='C:&#92;Program Files&#92;LibreAutomate&#92;Au.Editor.exe')<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;//The menu command executes C# script "Shell menu script 1". The selected file path will be in `args[0]`.<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='C# script 1' mode='single' type='file' cmd='Au.Editor.exe' args='"Shell menu script 1" @sel(true)')<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;//The menu command executes C# script "Shell menu script 1". The selected file path will be in `args[1]`, and `"-example"` in `args[0]`.<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='C# script 1' mode='single' type='file'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;find='.txt|.cs|.xml' //we can specify file types with `find` (simple) or `where` (supports expressions)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmd='Au.Editor.exe' args='"Shell menu script 1" -example @sel(true)')<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;separator<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;//The menu command executes C# script "Shell menu script 2". File paths will be in the `args` array. Script example: foreach (var path in args) { print.it(path); }<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='C# script 2'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmd='Au.Editor.exe' args='"Shell menu script 2" @sel(true)'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;image=&#92;uE150 //See https://nilesoft.org/gallery/glyphs. Or can be `image='path of ico, exe, png etc file'` etc.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tip="Example tooltip&#92;nLine 2"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Notes:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Mode not specified, therefore this item inherits `mode='multiple'` from menu properties.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Type not specified, therefore this item is shown for everything (files, directories etc) except taskbar.<br />
&nbsp;&nbsp;&nbsp;&nbsp;)<br />
}<br />
<br />
//Create submenu "Edit with" and add some new items there.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `type='file'` means show the menu only for files but not for folders.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `find="..."` specifies file types for which to add the menu item.<br />
menu(type='file' title='Edit with' image=icon.edit pos=top)<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='Dependencies'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type='file' find=".dll|.exe"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmd='C:&#92;Program Files&#92;Dependencies&#92;DependenciesGui.exe' args='@sel(true)')<br />
&nbsp;&nbsp;&nbsp;&nbsp;item(title='PEView'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;type='file' find=".dll|.exe"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmd='C:&#92;Program Files&#92;PEview&#92;PEview.exe' args='@sel(true)')<br />
}<br />
<br />
//Move some items to the "Edit with" submenu<br />
modify(where=this.id(id.edit) menu="Edit with")<br />
modify(find="Edit with *|Open with *" menu="Edit with")<br />
<br />
//Move the "Open with" submenu to the top (after "Edit with")<br />
modify(where=this.id(id.open_with) pos=top)<br />
<br />
//I use app TreeSize to show folder sizes. It adds a menu item. But sometimes need to run it as admin. Therefore I replace that item with an item that runs TreeSize as admin. I could use the easy way (commented out below), but to avoid UAC consent screen I use a C# script instead.<br />
remove(find="Scan with TreeSize*")<br />
//item(title='TreeSize' type='dir|drive|back' image='C:&#92;Program Files&#92;TreeSize Free&#92;TreeSizeFree.exe' cmd='C:&#92;Program Files&#92;TreeSize Free&#92;TreeSizeFree.exe' args='@sel(true)' admin=true)<br />
item(title='TreeSize'<br />
&nbsp;&nbsp;&nbsp;&nbsp;type='dir|drive|back' image='C:&#92;Program Files&#92;TreeSize Free&#92;TreeSizeFree.exe'<br />
&nbsp;&nbsp;&nbsp;&nbsp;cmd='Au.Editor.exe' args='"Shell menu" "treesize" @sel(true)')<br />
<br />
//Add some new items to menu "7-Zip".<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The `find="..."` specifies file types for which to add the menu item.<br />
//&nbsp;&nbsp;&nbsp;&nbsp;The menu command executes C# script "Shell menu" and passes the selected file path and 2 more arguments to it.<br />
item(title='Unzip'<br />
&nbsp;&nbsp;&nbsp;&nbsp;type='file' find=".zip|.7z|.gz|.tar|.nupkg"<br />
&nbsp;&nbsp;&nbsp;&nbsp;cmd='Au.Editor.exe' args='"Shell menu" "unzip" @sel(true) ""'<br />
&nbsp;&nbsp;&nbsp;&nbsp;image=&#92;ue202<br />
&nbsp;&nbsp;&nbsp;&nbsp;move='7-Zip' pos=0)<br />
item(title='Unzip and delete'<br />
&nbsp;&nbsp;&nbsp;&nbsp;type='file' find=".zip|.7z|.gz|.tar|.nupkg"<br />
&nbsp;&nbsp;&nbsp;&nbsp;cmd='Au.Editor.exe' args='"Shell menu" "unzip" @sel(true) "delete"'<br />
&nbsp;&nbsp;&nbsp;&nbsp;image=&#92;ue202<br />
&nbsp;&nbsp;&nbsp;&nbsp;move='7-Zip' pos=1 sep=after)</code></div></div><br />
<span style="font-weight: bold;" class="mycode_b">More info</span><br />
<br />
To apply changes after editing, Ctrl+right-click in a folder window or desktop, taskbar.<br />
<br />
If there are errors in <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">nss</code> file text:<br />
<ol type="1" class="mycode_list"><li>Shell writes an error line in file <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">shell.log</code>. To quickly access the file, you can import it in LA as link.<br />
</li>
<li>Shell stops working (the right-click menu is unmodified). After fixing the error need to restart Explorer processes. Run <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">shell.exe</code> (the NS program file) and click <span style="font-weight: bold;" class="mycode_b">Restart Explorer</span>. Or run <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">shell.exe -restart</code>.<br />
</li>
</ol>
To show hidden items too, Shift+right-click in a folder window (or desktop, taskbar).<br />
<br />
To temporarily disable the modified menu: Ctrl+Win+rightclick in a folder window. To enable: Ctrl+rightclick.<br />
<br />
C# code "for each selected path" when code in the nss file is like <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">args='"Script name" @sel(true)'</code>:<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code><span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>path</span> <span class='kw'>in args</span><span class='pn'>) {</span><br />
    <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='vr'>path</span><span class='pn'>);<br />
}</span></code></div></div><br />
C# code "for each selected path" when code in the nss file is like <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">args='"Script name" -example @sel(true)'</code>:<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code><span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>path</span> <span class='kw'>in args</span><span class='op'>.</span><span class='fn'>Skip</span><span class='pn'>(</span><span class='nr'>1</span><span class='pn'>)) {</span><br />
    <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='vr'>path</span><span class='pn'>);<br />
}</span></code></div></div>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Automate Gmail with GmailHelper]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7818</link>
			<pubDate>Sat, 26 Apr 2025 21:41:13 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=1538">Victor-P</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7818</guid>
			<description><![CDATA[Hey guys,<br />
<br />
With Gmail-API-Helper,you can access a Gmail a account. You’ll be able to filter, read, open mails.<br />
That can be helpfull to automate collection and treatement of user’s data.<br />
You will need first Configurate the Google API side, but it is well documented.<br />
<br />
After the configuration you should end up with a json file that we can call credential.json.<br />
<br />
If not already done you’ll have to install Nugets GmailHelper, Google.Apis.Gmail.v1 and Google.Apis.Auth.<br />
<br />
Here is a basic script to use it:<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>/*/ nuget GmailHelper&#92;GmailHelper; nuget GmailHelper&#92;Google.Apis.Gmail.v1; nuget GmailHelper&#92;Google.Apis.Auth; /*/<br />
<br />
<br />
using Google.Apis.Auth.OAuth2;<br />
using Google.Apis.Gmail.v1;<br />
using Google.Apis.Gmail.v1.Data;<br />
using Google.Apis.Services;<br />
using Google.Apis.Util.Store;<br />
using System;<br />
using System.Collections.Generic;<br />
using System.IO;<br />
using System.Linq;<br />
using System.Threading;<br />
<br />
public class GmailHelper<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;static readonly string[] Scopes = { GmailService.Scope.MailGoogleCom };<br />
&nbsp;&nbsp;&nbsp;&nbsp;static readonly string ApplicationName = "Gmail API Helper";<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;public static void Main(string[] args)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserCredential credential;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;using (var stream = new FileStream("path/to/your/credentials.json", FileMode.Open, FileAccess.Read))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string credPath = "token.json"; // path to the tokens<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;credential = GoogleWebAuthorizationBroker.AuthorizeAsync(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GoogleClientSecrets.Load(stream).Secrets,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Scopes,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"user",<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CancellationToken.None,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new FileDataStore(credPath, true)).Result;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine("The user’s credentials have been saved in : " + credPath);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Create the Gmail API service.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var service = new GmailService(new BaseClientService.Initializer()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpClientInitializer = credential,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplicationName = ApplicationName,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// List the 10 first emails of the Incoming.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var request = service.Users.Messages.List("me");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request.MaxResults = 10;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var response = request.Execute();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine("Emails in the Incoming box:");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (response.Messages != null &amp;&amp; response.Messages.Count &gt; 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (var message in response.Messages)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var messageRequest = service.Users.Messages.Get("me", message.Id);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var messageContent = messageRequest.Execute();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(&#36;"- Subject: {messageContent.Snippet}"); // Show a snipet of the subject<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine("Email not found in the Incoming box.");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catch (Exception ex)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine("An error has occured : " + ex.Message);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.ReadKey();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</code></div></div><br />
<br />
<span style="font-family: Calibri;" class="mycode_font"><span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">1 - You’ll have to replace the : </span></span><span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">"path/to/your/credentials.json"</span></span> <span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">by the real path to your </span></span><span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">credentials.json</span></span><span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">.</span></span><br />
<span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">2 - Authenticate the user and stocking the tokens in token.json file.</span></span><br />
<span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">3 - This script will authenticate the user,</span></span><span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">"me"</span></span><span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font"> is the authenticated user.</span></span><br />
<span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">4 - Create an instance of the Gmail API service</span></span><br />
<span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">5 - List the 10 last messages messages: </span></span><span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">request.MaxResults = 10;</span></span><br />
<span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">6 - Grap a snipet of each message to show their topics.</span></span></span>]]></description>
			<content:encoded><![CDATA[Hey guys,<br />
<br />
With Gmail-API-Helper,you can access a Gmail a account. You’ll be able to filter, read, open mails.<br />
That can be helpfull to automate collection and treatement of user’s data.<br />
You will need first Configurate the Google API side, but it is well documented.<br />
<br />
After the configuration you should end up with a json file that we can call credential.json.<br />
<br />
If not already done you’ll have to install Nugets GmailHelper, Google.Apis.Gmail.v1 and Google.Apis.Auth.<br />
<br />
Here is a basic script to use it:<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>/*/ nuget GmailHelper&#92;GmailHelper; nuget GmailHelper&#92;Google.Apis.Gmail.v1; nuget GmailHelper&#92;Google.Apis.Auth; /*/<br />
<br />
<br />
using Google.Apis.Auth.OAuth2;<br />
using Google.Apis.Gmail.v1;<br />
using Google.Apis.Gmail.v1.Data;<br />
using Google.Apis.Services;<br />
using Google.Apis.Util.Store;<br />
using System;<br />
using System.Collections.Generic;<br />
using System.IO;<br />
using System.Linq;<br />
using System.Threading;<br />
<br />
public class GmailHelper<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;static readonly string[] Scopes = { GmailService.Scope.MailGoogleCom };<br />
&nbsp;&nbsp;&nbsp;&nbsp;static readonly string ApplicationName = "Gmail API Helper";<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;public static void Main(string[] args)<br />
&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UserCredential credential;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;using (var stream = new FileStream("path/to/your/credentials.json", FileMode.Open, FileAccess.Read))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string credPath = "token.json"; // path to the tokens<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;credential = GoogleWebAuthorizationBroker.AuthorizeAsync(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GoogleClientSecrets.Load(stream).Secrets,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Scopes,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"user",<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CancellationToken.None,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new FileDataStore(credPath, true)).Result;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine("The user’s credentials have been saved in : " + credPath);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Create the Gmail API service.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var service = new GmailService(new BaseClientService.Initializer()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpClientInitializer = credential,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ApplicationName = ApplicationName,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// List the 10 first emails of the Incoming.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var request = service.Users.Messages.List("me");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request.MaxResults = 10;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var response = request.Execute();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine("Emails in the Incoming box:");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (response.Messages != null &amp;&amp; response.Messages.Count &gt; 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach (var message in response.Messages)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var messageRequest = service.Users.Messages.Get("me", message.Id);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var messageContent = messageRequest.Execute();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine(&#36;"- Subject: {messageContent.Snippet}"); // Show a snipet of the subject<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine("Email not found in the Incoming box.");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catch (Exception ex)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.WriteLine("An error has occured : " + ex.Message);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.ReadKey();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</code></div></div><br />
<br />
<span style="font-family: Calibri;" class="mycode_font"><span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">1 - You’ll have to replace the : </span></span><span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">"path/to/your/credentials.json"</span></span> <span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">by the real path to your </span></span><span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">credentials.json</span></span><span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">.</span></span><br />
<span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">2 - Authenticate the user and stocking the tokens in token.json file.</span></span><br />
<span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">3 - This script will authenticate the user,</span></span><span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">"me"</span></span><span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font"> is the authenticated user.</span></span><br />
<span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">4 - Create an instance of the Gmail API service</span></span><br />
<span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">5 - List the 10 last messages messages: </span></span><span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">request.MaxResults = 10;</span></span><br />
<span style="font-size: medium;" class="mycode_size"><span style="font-family: SimSun;" class="mycode_font">6 - Grap a snipet of each message to show their topics.</span></span></span>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[WebUI4CSharp - first steps in LA]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7817</link>
			<pubDate>Fri, 11 Apr 2025 22:30:14 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=1539">burque505</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7817</guid>
			<description><![CDATA[Another way to use HTML, CSS and JS in your UIs.<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://www.libreautomate.com/forum/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=1370" target="_blank" title="">WebUI4CSharp_new.PNG</a> (Size: 172.89 KB / Downloads: 240)
<!-- end: postbit_attachments_attachment --><br />
<br />
You can find WebUi4CSharp <a href="https://github.com/salvadordf/WebUI4CSharp" target="_blank" rel="noopener" class="mycode_url">here.</a><br />
 From that page:<br />
<blockquote class="mycode_quote"><cite>Quote:</cite>WebUI4CSharp is a <a href="https://github.com/webui-dev/webui" target="_blank" rel="noopener" class="mycode_url">WebUI</a> wrapper, which allows you to use any web browser as a GUI, with C# in the backend and HTML5 in the frontend.<br />
WebUI allows you to link your console, WinForms or WPF application with a web app that runs in a web browser installed in the operating system. Originally WebUI was created to have all the UI code in the web browser and the rest of the code in your hidden C# application. However, you can also decide to have a visible C# application communicating with a HTML5 app. You can get web browser events in your desktop application, call C# functions from JS, call JS functions from C# code, execute JavaScript, etc.<br />
WebUI4CSharp can be used console, WinForms or WPF applications for Windows.<br />
WebUI doesn't embed a web browser in your application. It's used as a bridge between a desktop application and the web browser running an HTML5 app.<br />
Features<ul class="mycode_list"><li>Fully Independent (<span style="font-style: italic;" class="mycode_i">No need for any third-party runtimes</span>)<br />
</li>
<li>Lightweight &amp; Small memory footprint<br />
</li>
<li>Fast binary communication protocol between WebUI and the browser (<span style="font-style: italic;" class="mycode_i">Instead of JSON</span>)<br />
</li>
<li>Multi-platform &amp; Multi-Browser<br />
</li>
<li>Using private profile for safety<br />
</li>
<li>Original library written in Pure C<br />
</li>
<li>XML documentation.<br />
</li>
<li>Help file.<br />
</li>
</ul>
</blockquote>
<br />
I wanted to try this out in LA several months ago, but kept running into snags with window positioning and sizing. Today I discovered that if Firefox is open on my system my code won't work properly, but if it's closed it will. There are no doubt ways around this, and there are 27(!) demo programs for the lib.<br />
<br />
Here are a couple of files you can experiment with (main file and class file). I feel my code quality is poor at the moment, probably with unneeded usings, but it runs.<br />
<br />
I would run it from the LA Editor if I were you, I haven't tried to make it a .exe program yet.<br />
<br />
You should a WebUI window open, which has buttons bound to some C# functions in the 'events' file. Click a button and see the function result displayed in the 'Output' tab of the LA Editor. You should see something like this:<br />
<blockquote class="mycode_quote"><cite>Quote:</cite>my_function_integer: There are 4 arguments in this event<br />
my_function_integer 1: 123<br />
my_function_integer 2: 456<br />
my_function_integer 3: 789<br />
my_function_integer 4: 12345.6789<br />
my_function_boolean 1: True<br />
my_function_boolean 2: False<br />
my_function_raw_binary: 414243<br />
my_function_with_response: 2 * 2 = 4<br />
my_function_with_response: 4 * 2 = 8<br />
my_function_with_response: 8 * 2 = 16</blockquote>
<br />
1) console_call_csharp_from_js.cs<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "console_call_csharp_from_js.cs"<br />
<span class='cm'>/*/ nuget wui&#92;WebUI4CSharp; nuget wui&#92;WebUI4CSharp.Natives; c console_call_csharp_from_js_events.cs; /*/</span><br />
<span class='kw'>using</span> WebUI4CSharp<span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Forms</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Threading</span><span class='pn'>;</span><br />
<br />
<span class='tp'>script</span><span class='op'>.</span><span class='fn'>setup</span><span class='pn'>(</span><span class='vr'>trayIcon</span><span class='pn'>:</span> <span class='kw'>true</span><span class='pn'>,</span> <span class='vr'>sleepExit</span><span class='pn'>:</span> <span class='kw'>true</span><span class='pn'>);</span><br />
<br />
<br />
<span class='kw'>string</span> <span class='vr'>my_html</span> <span class='op'>=</span> <span class='st'>"""<br />
        &lt;!DOCTYPE html&gt;<br />
        &lt;html&gt;<br />
          &lt;head&gt;<br />
            &lt;meta charset="UTF-8"&gt;<br />
            &lt;script src="webui.js"&gt;&lt;/script&gt;<br />
            &lt;title&gt;Call C# from JavaScript Example&lt;/title&gt;<br />
            &lt;style&gt;<br />
               body {<br />
                    font-family: 'Arial', sans-serif;<br />
                    color: white;<br />
                    background: linear-gradient(to right, #507d91, #1c596f, #022737);<br />
                    text-align: center;<br />
                    font-size: 18px;<br />
                }<br />
                button, input {<br />
                    padding: 10px;<br />
                    margin: 10px;<br />
                    border-radius: 3px;<br />
                    border: 1px solid #ccc;<br />
                    box-shadow: 0 3px 5px rgba(0,0,0,0.1);<br />
                    transition: 0.2s;<br />
                }<br />
                button {<br />
                    background: #3498db;<br />
                    color: #fff;<br />
                    cursor: pointer;<br />
                    font-size: 16px;<br />
                }<br />
                h1 { text-shadow: -7px 10px 7px rgb(67 57 57 / 76%); }<br />
                button:hover { background: #c9913d; }" +<br />
                input:focus { outline: none; border-color: #3498db; }<br />
            &lt;/style&gt;<br />
          &lt;/head&gt;<br />
          &lt;body&gt;<br />
            &lt;h1&gt;WebUI - Call C# from JavaScript&lt;/h1&gt;<br />
            &lt;p&gt;Call C# functions with arguments (&lt;em&gt;See the logs in your terminal&lt;/em&gt;)&lt;/p&gt;<br />
            &lt;button onclick="my_function_string('Hello', 'World');"&gt;Call my_function_string()&lt;/button&gt;<br />
            &lt;br&gt;<br />
            &lt;button onclick="my_function_integer(123, 456, 789, 12345.6789);"&gt;Call my_function_integer()&lt;/button&gt;<br />
            &lt;br&gt;<br />
            &lt;button onclick="my_function_boolean(true, false);"&gt;Call my_function_boolean()&lt;/button&gt;<br />
            &lt;br&gt;<br />
            &lt;button onclick="my_function_raw_binary(new Uint8Array([0x41,0x42,0x43]), big_arr);"&gt;<br />
             Call my_function_raw_binary()&lt;/button&gt;<br />
            &lt;br&gt;<br />
            &lt;p&gt;Call a C# function that returns a response&lt;/p&gt;<br />
            &lt;button onclick="MyJS();"&gt;Call my_function_with_response()&lt;/button&gt;<br />
            &lt;div&gt;Double: &lt;input type="text" id="MyInputID" value="2"&gt;&lt;/div&gt;<br />
            &lt;script&gt;<br />
              const arr_size = 512 * 1000;<br />
              const big_arr = new Uint8Array(arr_size);<br />
              big_arr[0] = 0xA1;<br />
              big_arr[arr_size - 1] = 0xA2;<br />
              function MyJS() {<br />
                const MyInput = document.getElementById('MyInputID');<br />
                const number = MyInput.value;<br />
                my_function_with_response(number, 2).then((response) =&gt; {<br />
                    MyInput.value = response;<br />
                });<br />
              }<br />
            &lt;/script&gt;<br />
          &lt;/body&gt;<br />
        &lt;/html&gt;<br />
    """</span><span class='pn'>;</span><br />
        <br />
<br />
WebUIWindow <span class='vr'>window</span> <span class='op'>=</span> <span class='kw'>new</span> WebUIWindow<span class='pn'>();</span><br />
<span class='vr'>window</span><span class='op'>.</span>Bind<span class='pn'>(</span><span class='st'>"my_function_string"</span><span class='pn'>,</span> WebUI_Events<span class='op'>.</span>my_function_string<span class='pn'>);</span><br />
<span class='vr'>window</span><span class='op'>.</span>Bind<span class='pn'>(</span><span class='st'>"my_function_integer"</span><span class='pn'>,</span> WebUI_Events<span class='op'>.</span>my_function_integer<span class='pn'>);</span><br />
<span class='vr'>window</span><span class='op'>.</span>Bind<span class='pn'>(</span><span class='st'>"my_function_boolean"</span><span class='pn'>,</span> WebUI_Events<span class='op'>.</span>my_function_boolean<span class='pn'>);</span><br />
<span class='vr'>window</span><span class='op'>.</span>Bind<span class='pn'>(</span><span class='st'>"my_function_with_response"</span><span class='pn'>,</span> WebUI_Events<span class='op'>.</span>my_function_with_response<span class='pn'>);</span><br />
<span class='vr'>window</span><span class='op'>.</span>Bind<span class='pn'>(</span><span class='st'>"my_function_raw_binary"</span><span class='pn'>,</span> WebUI_Events<span class='op'>.</span>my_function_raw_binary<span class='pn'>);</span><br />
<span class='vr'>window</span><span class='op'>.</span>Show<span class='pn'>(</span><span class='vr'>my_html</span><span class='pn'>);</span><br />
WebUI<span class='op'>.</span>Wait<span class='pn'>();</span><br />
WebUI<span class='op'>.</span>Clean<span class='pn'>();</span></code></div></div><br />
<br />
<br />
<br />
2) console_call_csharp_from_js_events.cs<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// class "console_call_csharp_from_js_events.cs"<br />
<span class='cm'>/*/ nuget wui&#92;WebUI4CSharp; nuget wui&#92;WebUI4CSharp.Natives; /*/</span><br />
<span class='kw'>using</span> WebUI4CSharp<span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Forms</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Threading</span><span class='pn'>;</span><br />
<br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
///</span><span class='xd'> UI Events</span><br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;</span><br />
<span class='kw'>public static class</span> <span class='tp'>WebUI_Events</span> <span class='pn'>{</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> string function</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>e</span><span class='xt'>"&gt;&lt;/param&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>my_function_string</span><span class='pn'>(</span><span class='kw'>ref</span> webui_event_t <span class='vr'>e</span><span class='pn'>) {</span><br />
        <span class='cm'>// JavaScript:<br />
        // my_function_string('Hello', 'World`);</span><br />
        <br />
        WebUIEvent <span class='vr'>lEvent</span> <span class='op'>=</span> <span class='kw'>new</span> WebUIEvent<span class='pn'>(</span><span class='vr'>e</span><span class='pn'>);</span><br />
<span class='pd'>#nullable enable</span><br />
        <span class='kw'>string</span><span class='op'>?</span> <span class='vr'>str_1</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetString<span class='pn'>();</span><br />
        <span class='kw'>string</span><span class='op'>?</span> <span class='vr'>str_2</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetStringAt<span class='pn'>(</span><span class='nr'>1</span><span class='pn'>);</span><br />
<span class='pd'>#nullable disable</span>    <br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_string 1: {0}"</span><span class='pn'>,</span> <span class='vr'>str_1</span><span class='pn'>);</span> <span class='cm'>// Hello</span><br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_string 2: {0}"</span><span class='pn'>,</span> <span class='vr'>str_2</span><span class='pn'>);</span> <span class='cm'>// World</span><br />
    <span class='pn'>}</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> integer function</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>e</span><span class='xt'>"&gt;&lt;/param&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>my_function_integer</span><span class='pn'>(</span><span class='kw'>ref</span> webui_event_t <span class='vr'>e</span><span class='pn'>) {</span><br />
        <span class='cm'>// JavaScript:<br />
        // my_function_integer(123, 456, 789, 12345.6789);</span><br />
        <br />
        WebUIEvent <span class='vr'>lEvent</span> <span class='op'>=</span> <span class='kw'>new</span> WebUIEvent<span class='pn'>(</span><span class='vr'>e</span><span class='pn'>);</span><br />
        <br />
        <span class='tp'>UIntPtr</span> <span class='vr'>count</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetCount<span class='pn'>();</span><br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_integer: There are {0} arguments in this event"</span><span class='pn'>,</span> <span class='vr'>count</span><span class='pn'>);</span> <span class='cm'>// 4</span><br />
        <br />
        <span class='kw'>long</span> <span class='vr'>number_1</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetInt<span class='pn'>();</span><br />
        <span class='kw'>long</span> <span class='vr'>number_2</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetIntAt<span class='pn'>(</span><span class='nr'>1</span><span class='pn'>);</span><br />
        <span class='kw'>long</span> <span class='vr'>number_3</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetIntAt<span class='pn'>(</span><span class='nr'>2</span><span class='pn'>);</span><br />
        <span class='kw'>double</span> <span class='vr'>float_1</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetFloatAt<span class='pn'>(</span><span class='nr'>3</span><span class='pn'>);</span><br />
        <br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_integer 1: {0}"</span><span class='pn'>,</span> <span class='vr'>number_1</span><span class='pn'>);</span> <span class='cm'>// 123</span><br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_integer 2: {0}"</span><span class='pn'>,</span> <span class='vr'>number_2</span><span class='pn'>);</span> <span class='cm'>// 456</span><br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_integer 3: {0}"</span><span class='pn'>,</span> <span class='vr'>number_3</span><span class='pn'>);</span> <span class='cm'>// 789</span><br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_integer 4: {0}"</span><span class='pn'>,</span> <span class='vr'>float_1</span><span class='pn'>);</span> <span class='cm'>// 12345.6789</span><br />
    <span class='pn'>}</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> boolean</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>e</span><span class='xt'>"&gt;&lt;/param&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>my_function_boolean</span><span class='pn'>(</span><span class='kw'>ref</span> webui_event_t <span class='vr'>e</span><span class='pn'>) {</span><br />
        <span class='cm'>// JavaScript:<br />
        // my_function_boolean(true, false);</span><br />
        <br />
        WebUIEvent <span class='vr'>lEvent</span> <span class='op'>=</span> <span class='kw'>new</span> WebUIEvent<span class='pn'>(</span><span class='vr'>e</span><span class='pn'>);</span><br />
        <span class='kw'>bool</span> <span class='vr'>status_1</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetBool<span class='pn'>();</span><br />
        <span class='kw'>bool</span> <span class='vr'>status_2</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetBoolAt<span class='pn'>(</span><span class='nr'>1</span><span class='pn'>);</span><br />
        <br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_boolean 1: {0}"</span><span class='pn'>,</span> <span class='vr'>status_1</span> <span class='op'>?</span> <span class='st'>"True"</span> <span class='op'>:</span> <span class='st'>"False"</span><span class='pn'>);</span> <span class='cm'>// True</span><br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_boolean 2: {0}"</span><span class='pn'>,</span> <span class='vr'>status_2</span> <span class='op'>?</span> <span class='st'>"True"</span> <span class='op'>:</span> <span class='st'>"False"</span><span class='pn'>);</span> <span class='cm'>// False</span><br />
    <span class='pn'>}</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> raw binary function</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>e</span><span class='xt'>"&gt;&lt;/param&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>my_function_raw_binary</span><span class='pn'>(</span><span class='kw'>ref</span> webui_event_t <span class='vr'>e</span><span class='pn'>) {</span><br />
        <span class='cm'>// JavaScript:<br />
        // my_function_raw_binary(new Uint8Array([0x41]), new Uint8Array([0x42, 0x43]));</span><br />
        <br />
        WebUIEvent <span class='vr'>lEvent</span> <span class='op'>=</span> <span class='kw'>new</span> WebUIEvent<span class='pn'>(</span><span class='vr'>e</span><span class='pn'>);</span><br />
<span class='pd'>#nullable enable</span><br />
        <span class='tp'>MemoryStream</span><span class='op'>?</span> <span class='vr'>stream</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetStream<span class='pn'>();</span><br />
<span class='pd'>#nullable disable</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>stream</span> <span class='op'>!=</span> <span class='kw'>null</span><span class='pn'>) {</span><br />
            <span class='kw'>string</span> <span class='vr'>hexstring</span> <span class='op'>=</span> <span class='tp'>Convert</span><span class='op'>.</span><span class='fn'>ToHexString</span><span class='pn'>(</span><span class='vr'>stream</span><span class='op'>.</span><span class='fn'>ToArray</span><span class='pn'>());</span><br />
            <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_raw_binary: "</span> <span class='op'>+</span> <span class='vr'>hexstring</span><span class='pn'>);<br />
        }<br />
    }</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> function with response</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>e</span><span class='xt'>"&gt;&lt;/param&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>my_function_with_response</span><span class='pn'>(</span><span class='kw'>ref</span> webui_event_t <span class='vr'>e</span><span class='pn'>) {</span><br />
        <span class='cm'>// JavaScript:<br />
        // my_function_with_response(number, 2).then(...)</span><br />
        <br />
        WebUIEvent <span class='vr'>lEvent</span> <span class='op'>=</span> <span class='kw'>new</span> WebUIEvent<span class='pn'>(</span><span class='vr'>e</span><span class='pn'>);</span><br />
        <span class='kw'>long</span> <span class='vr'>number</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetInt<span class='pn'>();</span><br />
        <span class='kw'>long</span> <span class='vr'>times</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetIntAt<span class='pn'>(</span><span class='nr'>1</span><span class='pn'>);</span><br />
        <br />
        <span class='kw'>long</span> <span class='vr'>res</span> <span class='op'>=</span> <span class='vr'>number</span> <span class='op'>*</span> <span class='vr'>times</span><span class='pn'>;</span><br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_with_response: {0} * {1} = {2}"</span><span class='pn'>,</span> <span class='vr'>number</span><span class='pn'>,</span> <span class='vr'>times</span><span class='pn'>,</span> <span class='vr'>res</span><span class='pn'>);</span><br />
        <br />
        <span class='cm'>// Send back the response to JavaScript</span><br />
        <span class='vr'>lEvent</span><span class='op'>.</span>ReturnInt<span class='pn'>(</span><span class='vr'>res</span><span class='pn'>);<br />
    }<br />
}</span></code></div></div>]]></description>
			<content:encoded><![CDATA[Another way to use HTML, CSS and JS in your UIs.<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://www.libreautomate.com/forum/images/attachtypes/image.png" title="PNG Image" border="0" alt=".png" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=1370" target="_blank" title="">WebUI4CSharp_new.PNG</a> (Size: 172.89 KB / Downloads: 240)
<!-- end: postbit_attachments_attachment --><br />
<br />
You can find WebUi4CSharp <a href="https://github.com/salvadordf/WebUI4CSharp" target="_blank" rel="noopener" class="mycode_url">here.</a><br />
 From that page:<br />
<blockquote class="mycode_quote"><cite>Quote:</cite>WebUI4CSharp is a <a href="https://github.com/webui-dev/webui" target="_blank" rel="noopener" class="mycode_url">WebUI</a> wrapper, which allows you to use any web browser as a GUI, with C# in the backend and HTML5 in the frontend.<br />
WebUI allows you to link your console, WinForms or WPF application with a web app that runs in a web browser installed in the operating system. Originally WebUI was created to have all the UI code in the web browser and the rest of the code in your hidden C# application. However, you can also decide to have a visible C# application communicating with a HTML5 app. You can get web browser events in your desktop application, call C# functions from JS, call JS functions from C# code, execute JavaScript, etc.<br />
WebUI4CSharp can be used console, WinForms or WPF applications for Windows.<br />
WebUI doesn't embed a web browser in your application. It's used as a bridge between a desktop application and the web browser running an HTML5 app.<br />
Features<ul class="mycode_list"><li>Fully Independent (<span style="font-style: italic;" class="mycode_i">No need for any third-party runtimes</span>)<br />
</li>
<li>Lightweight &amp; Small memory footprint<br />
</li>
<li>Fast binary communication protocol between WebUI and the browser (<span style="font-style: italic;" class="mycode_i">Instead of JSON</span>)<br />
</li>
<li>Multi-platform &amp; Multi-Browser<br />
</li>
<li>Using private profile for safety<br />
</li>
<li>Original library written in Pure C<br />
</li>
<li>XML documentation.<br />
</li>
<li>Help file.<br />
</li>
</ul>
</blockquote>
<br />
I wanted to try this out in LA several months ago, but kept running into snags with window positioning and sizing. Today I discovered that if Firefox is open on my system my code won't work properly, but if it's closed it will. There are no doubt ways around this, and there are 27(!) demo programs for the lib.<br />
<br />
Here are a couple of files you can experiment with (main file and class file). I feel my code quality is poor at the moment, probably with unneeded usings, but it runs.<br />
<br />
I would run it from the LA Editor if I were you, I haven't tried to make it a .exe program yet.<br />
<br />
You should a WebUI window open, which has buttons bound to some C# functions in the 'events' file. Click a button and see the function result displayed in the 'Output' tab of the LA Editor. You should see something like this:<br />
<blockquote class="mycode_quote"><cite>Quote:</cite>my_function_integer: There are 4 arguments in this event<br />
my_function_integer 1: 123<br />
my_function_integer 2: 456<br />
my_function_integer 3: 789<br />
my_function_integer 4: 12345.6789<br />
my_function_boolean 1: True<br />
my_function_boolean 2: False<br />
my_function_raw_binary: 414243<br />
my_function_with_response: 2 * 2 = 4<br />
my_function_with_response: 4 * 2 = 8<br />
my_function_with_response: 8 * 2 = 16</blockquote>
<br />
1) console_call_csharp_from_js.cs<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "console_call_csharp_from_js.cs"<br />
<span class='cm'>/*/ nuget wui&#92;WebUI4CSharp; nuget wui&#92;WebUI4CSharp.Natives; c console_call_csharp_from_js_events.cs; /*/</span><br />
<span class='kw'>using</span> WebUI4CSharp<span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Forms</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Threading</span><span class='pn'>;</span><br />
<br />
<span class='tp'>script</span><span class='op'>.</span><span class='fn'>setup</span><span class='pn'>(</span><span class='vr'>trayIcon</span><span class='pn'>:</span> <span class='kw'>true</span><span class='pn'>,</span> <span class='vr'>sleepExit</span><span class='pn'>:</span> <span class='kw'>true</span><span class='pn'>);</span><br />
<br />
<br />
<span class='kw'>string</span> <span class='vr'>my_html</span> <span class='op'>=</span> <span class='st'>"""<br />
        &lt;!DOCTYPE html&gt;<br />
        &lt;html&gt;<br />
          &lt;head&gt;<br />
            &lt;meta charset="UTF-8"&gt;<br />
            &lt;script src="webui.js"&gt;&lt;/script&gt;<br />
            &lt;title&gt;Call C# from JavaScript Example&lt;/title&gt;<br />
            &lt;style&gt;<br />
               body {<br />
                    font-family: 'Arial', sans-serif;<br />
                    color: white;<br />
                    background: linear-gradient(to right, #507d91, #1c596f, #022737);<br />
                    text-align: center;<br />
                    font-size: 18px;<br />
                }<br />
                button, input {<br />
                    padding: 10px;<br />
                    margin: 10px;<br />
                    border-radius: 3px;<br />
                    border: 1px solid #ccc;<br />
                    box-shadow: 0 3px 5px rgba(0,0,0,0.1);<br />
                    transition: 0.2s;<br />
                }<br />
                button {<br />
                    background: #3498db;<br />
                    color: #fff;<br />
                    cursor: pointer;<br />
                    font-size: 16px;<br />
                }<br />
                h1 { text-shadow: -7px 10px 7px rgb(67 57 57 / 76%); }<br />
                button:hover { background: #c9913d; }" +<br />
                input:focus { outline: none; border-color: #3498db; }<br />
            &lt;/style&gt;<br />
          &lt;/head&gt;<br />
          &lt;body&gt;<br />
            &lt;h1&gt;WebUI - Call C# from JavaScript&lt;/h1&gt;<br />
            &lt;p&gt;Call C# functions with arguments (&lt;em&gt;See the logs in your terminal&lt;/em&gt;)&lt;/p&gt;<br />
            &lt;button onclick="my_function_string('Hello', 'World');"&gt;Call my_function_string()&lt;/button&gt;<br />
            &lt;br&gt;<br />
            &lt;button onclick="my_function_integer(123, 456, 789, 12345.6789);"&gt;Call my_function_integer()&lt;/button&gt;<br />
            &lt;br&gt;<br />
            &lt;button onclick="my_function_boolean(true, false);"&gt;Call my_function_boolean()&lt;/button&gt;<br />
            &lt;br&gt;<br />
            &lt;button onclick="my_function_raw_binary(new Uint8Array([0x41,0x42,0x43]), big_arr);"&gt;<br />
             Call my_function_raw_binary()&lt;/button&gt;<br />
            &lt;br&gt;<br />
            &lt;p&gt;Call a C# function that returns a response&lt;/p&gt;<br />
            &lt;button onclick="MyJS();"&gt;Call my_function_with_response()&lt;/button&gt;<br />
            &lt;div&gt;Double: &lt;input type="text" id="MyInputID" value="2"&gt;&lt;/div&gt;<br />
            &lt;script&gt;<br />
              const arr_size = 512 * 1000;<br />
              const big_arr = new Uint8Array(arr_size);<br />
              big_arr[0] = 0xA1;<br />
              big_arr[arr_size - 1] = 0xA2;<br />
              function MyJS() {<br />
                const MyInput = document.getElementById('MyInputID');<br />
                const number = MyInput.value;<br />
                my_function_with_response(number, 2).then((response) =&gt; {<br />
                    MyInput.value = response;<br />
                });<br />
              }<br />
            &lt;/script&gt;<br />
          &lt;/body&gt;<br />
        &lt;/html&gt;<br />
    """</span><span class='pn'>;</span><br />
        <br />
<br />
WebUIWindow <span class='vr'>window</span> <span class='op'>=</span> <span class='kw'>new</span> WebUIWindow<span class='pn'>();</span><br />
<span class='vr'>window</span><span class='op'>.</span>Bind<span class='pn'>(</span><span class='st'>"my_function_string"</span><span class='pn'>,</span> WebUI_Events<span class='op'>.</span>my_function_string<span class='pn'>);</span><br />
<span class='vr'>window</span><span class='op'>.</span>Bind<span class='pn'>(</span><span class='st'>"my_function_integer"</span><span class='pn'>,</span> WebUI_Events<span class='op'>.</span>my_function_integer<span class='pn'>);</span><br />
<span class='vr'>window</span><span class='op'>.</span>Bind<span class='pn'>(</span><span class='st'>"my_function_boolean"</span><span class='pn'>,</span> WebUI_Events<span class='op'>.</span>my_function_boolean<span class='pn'>);</span><br />
<span class='vr'>window</span><span class='op'>.</span>Bind<span class='pn'>(</span><span class='st'>"my_function_with_response"</span><span class='pn'>,</span> WebUI_Events<span class='op'>.</span>my_function_with_response<span class='pn'>);</span><br />
<span class='vr'>window</span><span class='op'>.</span>Bind<span class='pn'>(</span><span class='st'>"my_function_raw_binary"</span><span class='pn'>,</span> WebUI_Events<span class='op'>.</span>my_function_raw_binary<span class='pn'>);</span><br />
<span class='vr'>window</span><span class='op'>.</span>Show<span class='pn'>(</span><span class='vr'>my_html</span><span class='pn'>);</span><br />
WebUI<span class='op'>.</span>Wait<span class='pn'>();</span><br />
WebUI<span class='op'>.</span>Clean<span class='pn'>();</span></code></div></div><br />
<br />
<br />
<br />
2) console_call_csharp_from_js_events.cs<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// class "console_call_csharp_from_js_events.cs"<br />
<span class='cm'>/*/ nuget wui&#92;WebUI4CSharp; nuget wui&#92;WebUI4CSharp.Natives; /*/</span><br />
<span class='kw'>using</span> WebUI4CSharp<span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Forms</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Threading</span><span class='pn'>;</span><br />
<br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
///</span><span class='xd'> UI Events</span><br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;</span><br />
<span class='kw'>public static class</span> <span class='tp'>WebUI_Events</span> <span class='pn'>{</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> string function</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>e</span><span class='xt'>"&gt;&lt;/param&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>my_function_string</span><span class='pn'>(</span><span class='kw'>ref</span> webui_event_t <span class='vr'>e</span><span class='pn'>) {</span><br />
        <span class='cm'>// JavaScript:<br />
        // my_function_string('Hello', 'World`);</span><br />
        <br />
        WebUIEvent <span class='vr'>lEvent</span> <span class='op'>=</span> <span class='kw'>new</span> WebUIEvent<span class='pn'>(</span><span class='vr'>e</span><span class='pn'>);</span><br />
<span class='pd'>#nullable enable</span><br />
        <span class='kw'>string</span><span class='op'>?</span> <span class='vr'>str_1</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetString<span class='pn'>();</span><br />
        <span class='kw'>string</span><span class='op'>?</span> <span class='vr'>str_2</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetStringAt<span class='pn'>(</span><span class='nr'>1</span><span class='pn'>);</span><br />
<span class='pd'>#nullable disable</span>    <br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_string 1: {0}"</span><span class='pn'>,</span> <span class='vr'>str_1</span><span class='pn'>);</span> <span class='cm'>// Hello</span><br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_string 2: {0}"</span><span class='pn'>,</span> <span class='vr'>str_2</span><span class='pn'>);</span> <span class='cm'>// World</span><br />
    <span class='pn'>}</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> integer function</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>e</span><span class='xt'>"&gt;&lt;/param&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>my_function_integer</span><span class='pn'>(</span><span class='kw'>ref</span> webui_event_t <span class='vr'>e</span><span class='pn'>) {</span><br />
        <span class='cm'>// JavaScript:<br />
        // my_function_integer(123, 456, 789, 12345.6789);</span><br />
        <br />
        WebUIEvent <span class='vr'>lEvent</span> <span class='op'>=</span> <span class='kw'>new</span> WebUIEvent<span class='pn'>(</span><span class='vr'>e</span><span class='pn'>);</span><br />
        <br />
        <span class='tp'>UIntPtr</span> <span class='vr'>count</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetCount<span class='pn'>();</span><br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_integer: There are {0} arguments in this event"</span><span class='pn'>,</span> <span class='vr'>count</span><span class='pn'>);</span> <span class='cm'>// 4</span><br />
        <br />
        <span class='kw'>long</span> <span class='vr'>number_1</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetInt<span class='pn'>();</span><br />
        <span class='kw'>long</span> <span class='vr'>number_2</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetIntAt<span class='pn'>(</span><span class='nr'>1</span><span class='pn'>);</span><br />
        <span class='kw'>long</span> <span class='vr'>number_3</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetIntAt<span class='pn'>(</span><span class='nr'>2</span><span class='pn'>);</span><br />
        <span class='kw'>double</span> <span class='vr'>float_1</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetFloatAt<span class='pn'>(</span><span class='nr'>3</span><span class='pn'>);</span><br />
        <br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_integer 1: {0}"</span><span class='pn'>,</span> <span class='vr'>number_1</span><span class='pn'>);</span> <span class='cm'>// 123</span><br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_integer 2: {0}"</span><span class='pn'>,</span> <span class='vr'>number_2</span><span class='pn'>);</span> <span class='cm'>// 456</span><br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_integer 3: {0}"</span><span class='pn'>,</span> <span class='vr'>number_3</span><span class='pn'>);</span> <span class='cm'>// 789</span><br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_integer 4: {0}"</span><span class='pn'>,</span> <span class='vr'>float_1</span><span class='pn'>);</span> <span class='cm'>// 12345.6789</span><br />
    <span class='pn'>}</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> boolean</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>e</span><span class='xt'>"&gt;&lt;/param&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>my_function_boolean</span><span class='pn'>(</span><span class='kw'>ref</span> webui_event_t <span class='vr'>e</span><span class='pn'>) {</span><br />
        <span class='cm'>// JavaScript:<br />
        // my_function_boolean(true, false);</span><br />
        <br />
        WebUIEvent <span class='vr'>lEvent</span> <span class='op'>=</span> <span class='kw'>new</span> WebUIEvent<span class='pn'>(</span><span class='vr'>e</span><span class='pn'>);</span><br />
        <span class='kw'>bool</span> <span class='vr'>status_1</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetBool<span class='pn'>();</span><br />
        <span class='kw'>bool</span> <span class='vr'>status_2</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetBoolAt<span class='pn'>(</span><span class='nr'>1</span><span class='pn'>);</span><br />
        <br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_boolean 1: {0}"</span><span class='pn'>,</span> <span class='vr'>status_1</span> <span class='op'>?</span> <span class='st'>"True"</span> <span class='op'>:</span> <span class='st'>"False"</span><span class='pn'>);</span> <span class='cm'>// True</span><br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_boolean 2: {0}"</span><span class='pn'>,</span> <span class='vr'>status_2</span> <span class='op'>?</span> <span class='st'>"True"</span> <span class='op'>:</span> <span class='st'>"False"</span><span class='pn'>);</span> <span class='cm'>// False</span><br />
    <span class='pn'>}</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> raw binary function</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>e</span><span class='xt'>"&gt;&lt;/param&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>my_function_raw_binary</span><span class='pn'>(</span><span class='kw'>ref</span> webui_event_t <span class='vr'>e</span><span class='pn'>) {</span><br />
        <span class='cm'>// JavaScript:<br />
        // my_function_raw_binary(new Uint8Array([0x41]), new Uint8Array([0x42, 0x43]));</span><br />
        <br />
        WebUIEvent <span class='vr'>lEvent</span> <span class='op'>=</span> <span class='kw'>new</span> WebUIEvent<span class='pn'>(</span><span class='vr'>e</span><span class='pn'>);</span><br />
<span class='pd'>#nullable enable</span><br />
        <span class='tp'>MemoryStream</span><span class='op'>?</span> <span class='vr'>stream</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetStream<span class='pn'>();</span><br />
<span class='pd'>#nullable disable</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>stream</span> <span class='op'>!=</span> <span class='kw'>null</span><span class='pn'>) {</span><br />
            <span class='kw'>string</span> <span class='vr'>hexstring</span> <span class='op'>=</span> <span class='tp'>Convert</span><span class='op'>.</span><span class='fn'>ToHexString</span><span class='pn'>(</span><span class='vr'>stream</span><span class='op'>.</span><span class='fn'>ToArray</span><span class='pn'>());</span><br />
            <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_raw_binary: "</span> <span class='op'>+</span> <span class='vr'>hexstring</span><span class='pn'>);<br />
        }<br />
    }</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> function with response</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>e</span><span class='xt'>"&gt;&lt;/param&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>my_function_with_response</span><span class='pn'>(</span><span class='kw'>ref</span> webui_event_t <span class='vr'>e</span><span class='pn'>) {</span><br />
        <span class='cm'>// JavaScript:<br />
        // my_function_with_response(number, 2).then(...)</span><br />
        <br />
        WebUIEvent <span class='vr'>lEvent</span> <span class='op'>=</span> <span class='kw'>new</span> WebUIEvent<span class='pn'>(</span><span class='vr'>e</span><span class='pn'>);</span><br />
        <span class='kw'>long</span> <span class='vr'>number</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetInt<span class='pn'>();</span><br />
        <span class='kw'>long</span> <span class='vr'>times</span> <span class='op'>=</span> <span class='vr'>lEvent</span><span class='op'>.</span>GetIntAt<span class='pn'>(</span><span class='nr'>1</span><span class='pn'>);</span><br />
        <br />
        <span class='kw'>long</span> <span class='vr'>res</span> <span class='op'>=</span> <span class='vr'>number</span> <span class='op'>*</span> <span class='vr'>times</span><span class='pn'>;</span><br />
        <span class='tp'>Console</span><span class='op'>.</span><span class='fn'>WriteLine</span><span class='pn'>(</span><span class='st'>"my_function_with_response: {0} * {1} = {2}"</span><span class='pn'>,</span> <span class='vr'>number</span><span class='pn'>,</span> <span class='vr'>times</span><span class='pn'>,</span> <span class='vr'>res</span><span class='pn'>);</span><br />
        <br />
        <span class='cm'>// Send back the response to JavaScript</span><br />
        <span class='vr'>lEvent</span><span class='op'>.</span>ReturnInt<span class='pn'>(</span><span class='vr'>res</span><span class='pn'>);<br />
    }<br />
}</span></code></div></div>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Automation SPY]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7792</link>
			<pubDate>Fri, 03 Jan 2025 15:29:59 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=1537">birdywen</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7792</guid>
			<description><![CDATA[I just came across this interesting C# library which is pretty similar to LA's ui automation. <br />
<a href="http://automationspy.freecluster.eu/" target="_blank" rel="noopener" class="mycode_url">Automation Spy homepage - Inspect UI Automation elements</a>]]></description>
			<content:encoded><![CDATA[I just came across this interesting C# library which is pretty similar to LA's ui automation. <br />
<a href="http://automationspy.freecluster.eu/" target="_blank" rel="noopener" class="mycode_url">Automation Spy homepage - Inspect UI Automation elements</a>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Web browser automation with Playwright]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7779</link>
			<pubDate>Wed, 27 Nov 2024 13:38:14 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=2">Gintaras</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7779</guid>
			<description><![CDATA[<a href="https://playwright.dev/dotnet/docs/input" target="_blank" rel="noopener" class="mycode_url">Playwright</a> automates actions in web browser, such as "click link", "fill text field", "execute JavaScript", "open page".<br />
Actions can be recorded.<br />
Supports Chrome, Edge and other Chromium-based browsers. Partially Firefox.<br />
System requirements: Windows 10+.<br />
<br />
Updated 2025-05-22: added info and <span style="font-weight: bold;" class="mycode_b">PlaywrightRunBrowser</span> parameter to use command line switch <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">--user-data-dir</code> which is now required for <span style="font-weight: bold;" class="mycode_b">Connect</span>.<br />
<br />
Examples<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "Playwright Connect.cs"<br />
<span class='xt'>///</span><span class='xd'> Connects to an existing web browser instance, finds a page by URL, activates the tab, and clicks a link.</span><br />
<br />
<span class='cm'>/*/ c Playwrighter.cs; /*/</span><br />
<span class='kw'>using</span> <span class='ns'>Microsoft</span><span class='op'>.</span>Playwright<span class='pn'>;</span><br />
<br />
<span class='kw'>using var</span> <span class='vr'>play</span> <span class='op'>=</span> Playwrighter<span class='op'>.</span>Connect<span class='pn'>(</span><span class='kw'>out var</span> <span class='vr'>page</span><span class='pn'>,</span> <span class='st'>"https://www.libreautomate.com/forum/*"</span><span class='pn'>);</span><br />
<span class='kw'>await</span> <span class='vr'>page</span><span class='op'>.</span>GetByText<span class='pn'>(</span><span class='st'>"Shared C# code"</span><span class='pn'>)</span><span class='op'>.</span>ClickAsync<span class='pn'>();</span><br />
<span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='vr'>page</span><span class='op'>.</span>Url<span class='pn'>);</span></code></div></div> <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "Playwright Launch.cs"<br />
<span class='xt'>///</span><span class='xd'> Launches a web browser, opens a page, and clicks a link.</span><br />
<br />
<span class='cm'>/*/ c Playwrighter.cs; /*/</span><br />
<span class='kw'>using</span> <span class='ns'>Microsoft</span><span class='op'>.</span>Playwright<span class='pn'>;</span><br />
<br />
<span class='kw'>using var</span> <span class='vr'>play</span> <span class='op'>=</span> Playwrighter<span class='op'>.</span>Launch<span class='pn'>(</span><span class='kw'>out var</span> <span class='vr'>page</span><span class='pn'>);</span> <span class='cm'>//default browser<br />
//using var play = Playwrighter.Launch(out var page, PWBrowser.BundledChrome); //bundled browser (installs if need)</span><br />
<span class='kw'>await</span> <span class='vr'>page</span><span class='op'>.</span>GotoAsync<span class='pn'>(</span><span class='st'>"https://www.libreautomate.com/forum/"</span><span class='pn'>);</span><br />
<span class='kw'>await</span> <span class='vr'>page</span><span class='op'>.</span>GetByText<span class='pn'>(</span><span class='st'>"Shared C# code"</span><span class='pn'>)</span><span class='op'>.</span>ClickAsync<span class='pn'>();</span><br />
<br />
<span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='st'>"Close the browser"</span><span class='pn'>);</span></code></div></div><br /><!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://www.libreautomate.com/forum/images/attachtypes/zip.png" title="ZIP File" border="0" alt=".zip" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=1366" target="_blank" title="">Playwrighter.zip</a> (Size: 11.01 KB / Downloads: 263)
<!-- end: postbit_attachments_attachment -->]]></description>
			<content:encoded><![CDATA[<a href="https://playwright.dev/dotnet/docs/input" target="_blank" rel="noopener" class="mycode_url">Playwright</a> automates actions in web browser, such as "click link", "fill text field", "execute JavaScript", "open page".<br />
Actions can be recorded.<br />
Supports Chrome, Edge and other Chromium-based browsers. Partially Firefox.<br />
System requirements: Windows 10+.<br />
<br />
Updated 2025-05-22: added info and <span style="font-weight: bold;" class="mycode_b">PlaywrightRunBrowser</span> parameter to use command line switch <code style="background:#f4f4f4;border:1px solid #ccc;padding:1px 3px;font-family:monospace;">--user-data-dir</code> which is now required for <span style="font-weight: bold;" class="mycode_b">Connect</span>.<br />
<br />
Examples<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "Playwright Connect.cs"<br />
<span class='xt'>///</span><span class='xd'> Connects to an existing web browser instance, finds a page by URL, activates the tab, and clicks a link.</span><br />
<br />
<span class='cm'>/*/ c Playwrighter.cs; /*/</span><br />
<span class='kw'>using</span> <span class='ns'>Microsoft</span><span class='op'>.</span>Playwright<span class='pn'>;</span><br />
<br />
<span class='kw'>using var</span> <span class='vr'>play</span> <span class='op'>=</span> Playwrighter<span class='op'>.</span>Connect<span class='pn'>(</span><span class='kw'>out var</span> <span class='vr'>page</span><span class='pn'>,</span> <span class='st'>"https://www.libreautomate.com/forum/*"</span><span class='pn'>);</span><br />
<span class='kw'>await</span> <span class='vr'>page</span><span class='op'>.</span>GetByText<span class='pn'>(</span><span class='st'>"Shared C# code"</span><span class='pn'>)</span><span class='op'>.</span>ClickAsync<span class='pn'>();</span><br />
<span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='vr'>page</span><span class='op'>.</span>Url<span class='pn'>);</span></code></div></div> <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "Playwright Launch.cs"<br />
<span class='xt'>///</span><span class='xd'> Launches a web browser, opens a page, and clicks a link.</span><br />
<br />
<span class='cm'>/*/ c Playwrighter.cs; /*/</span><br />
<span class='kw'>using</span> <span class='ns'>Microsoft</span><span class='op'>.</span>Playwright<span class='pn'>;</span><br />
<br />
<span class='kw'>using var</span> <span class='vr'>play</span> <span class='op'>=</span> Playwrighter<span class='op'>.</span>Launch<span class='pn'>(</span><span class='kw'>out var</span> <span class='vr'>page</span><span class='pn'>);</span> <span class='cm'>//default browser<br />
//using var play = Playwrighter.Launch(out var page, PWBrowser.BundledChrome); //bundled browser (installs if need)</span><br />
<span class='kw'>await</span> <span class='vr'>page</span><span class='op'>.</span>GotoAsync<span class='pn'>(</span><span class='st'>"https://www.libreautomate.com/forum/"</span><span class='pn'>);</span><br />
<span class='kw'>await</span> <span class='vr'>page</span><span class='op'>.</span>GetByText<span class='pn'>(</span><span class='st'>"Shared C# code"</span><span class='pn'>)</span><span class='op'>.</span>ClickAsync<span class='pn'>();</span><br />
<br />
<span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='st'>"Close the browser"</span><span class='pn'>);</span></code></div></div><br /><!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://www.libreautomate.com/forum/images/attachtypes/zip.png" title="ZIP File" border="0" alt=".zip" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=1366" target="_blank" title="">Playwrighter.zip</a> (Size: 11.01 KB / Downloads: 263)
<!-- end: postbit_attachments_attachment -->]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Registry variables. Sharing values by multiple script processes.]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7759</link>
			<pubDate>Fri, 27 Sep 2024 08:36:16 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=2">Gintaras</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7759</guid>
			<description><![CDATA[LA scripts run in separate processes. Even the same script runs in new process each time. Each process has its own static variables, and they disappear when the process ends. To share values between processes, can be used a file, registry, shared memory, etc. This class uses the registry.<br />
<br />
Example.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code><span class='cm'>/*/ c &#92;RegistryVariables.cs; /*/</span><br />
<br />
<span class='tp'>print</span><span class='op'>.</span><span class='fn'>clear</span><span class='pn'>();</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>rv</span> <span class='op'>=</span> <span class='kw'>new</span> RegistryVariables<span class='pn'>();</span><br />
<span class='vr'>rv</span><span class='op'>.</span>SetInt<span class='pn'>(</span><span class='st'>"i"</span><span class='pn'>,</span> <span class='nr'>10</span><span class='pn'>);</span><br />
<span class='vr'>rv</span><span class='op'>.</span>SetLong<span class='pn'>(</span><span class='st'>"q"</span><span class='pn'>,</span> <span class='op'>-</span><span class='nr'>2</span><span class='pn'>);</span><br />
<span class='vr'>rv</span><span class='op'>.</span>SetString<span class='pn'>(</span><span class='st'>"s"</span><span class='pn'>,</span> <span class='st'>"test"</span><span class='pn'>);</span><br />
<span class='vr'>rv</span><span class='op'>.</span>SetBinary<span class='pn'>(</span><span class='st'>"b"</span><span class='pn'>, [</span><span class='nr'>1</span><span class='pn'>,</span> <span class='nr'>2</span><span class='pn'>,</span> <span class='nr'>3</span><span class='pn'>,</span> <span class='nr'>4</span><span class='pn'>]);</span><br />
<span class='vr'>rv</span><span class='op'>.</span>SetObject<span class='pn'>(</span><span class='st'>"p"</span><span class='pn'>,</span> <span class='kw'>new</span> <span class='tp'>POINT</span><span class='pn'>(</span><span class='nr'>5</span><span class='pn'>,</span> <span class='nr'>6</span><span class='pn'>));</span><br />
<span class='cm'>//print.it(rv.Exists("p"));<br />
//rv.DeleteAll();<br />
//rv.Delete("s");</span><br />
<span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='vr'>rv</span><span class='op'>.</span>GetInt<span class='pn'>(</span><span class='st'>"i"</span><span class='pn'>),</span> <span class='vr'>rv</span><span class='op'>.</span>GetLong<span class='pn'>(</span><span class='st'>"q"</span><span class='pn'>),</span> <span class='vr'>rv</span><span class='op'>.</span>GetString<span class='pn'>(</span><span class='st'>"s"</span><span class='pn'>),</span> <span class='vr'>rv</span><span class='op'>.</span>GetBinary<span class='pn'>(</span><span class='st'>"b"</span><span class='pn'>),</span> <span class='vr'>rv</span><span class='op'>.</span>GetObject<span class='pn'>(</span><span class='st'>"p"</span><span class='pn'>,</span> <span class='kw'>out</span> <span class='tp'>POINT</span> <span class='vr'>p</span><span class='pn'>),</span> <span class='vr'>p</span><span class='pn'>);</span></code></div></div><br />
To clear the key when LA starts, add this code in a script that runs when LA starts (see Options &gt; Workspace &gt; Run...):<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code><span class='cm'>/*/ role editorExtension; c &#92;RegistryVariables.cs; /*/</span><br />
<span class='kw'>new</span> RegistryVariables<span class='pn'>()</span><span class='op'>.</span>DeleteAll<span class='pn'>();</span></code></div></div> <br />
<hr class="mycode_hr" />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// class "RegistryVariables.cs"<br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Text</span><span class='op'>.</span><span class='ns'>Json</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Text</span><span class='op'>.</span><span class='ns'>Json</span><span class='op'>.</span><span class='ns'>Serialization</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Text</span><span class='op'>.</span><span class='ns'>Encodings</span><span class='op'>.</span><span class='ns'>Web</span><span class='pn'>;</span><br />
<br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
///</span><span class='xd'> Sets/gets values in a registry key.</span><br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
///</span><span class='xd'> </span><span class='xt'>&lt;example&gt;<br />
///</span><span class='xd'> </span><span class='xt'>&lt;code&gt;&lt;![CDATA[<br />
/// /*/ c &#92;RegistryVariables.cs; /*/<br />
/// var rv = new RegistryVariables();<br />
/// rv.SetInt("i", 10);<br />
/// rv.SetLong("q", -2);<br />
/// rv.SetString("s", "test");<br />
/// rv.SetBinary("b", [1, 2, 3, 4]);<br />
/// rv.SetObject("p", new POINT(5, 6));<br />
/// print.it(rv.GetInt("i"), rv.GetLong("q"), rv.GetString("s"), rv.GetBinary("b"), rv.GetObject("p", out POINT p), p);<br />
/// ]]]]]]&gt;&lt;![CDATA[&gt;&lt;![CDATA[&gt;&lt;/code&gt;<br />
///</span><span class='xd'> To clear the key when LA starts, add this code in a script that runs when LA starts (see Options &gt; Workspace &gt; Run...):</span><br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;code&gt;&lt;![CDATA[<br />
/// /*/ role editorExtension; c &#92;RegistryVariables.cs; /*/<br />
/// new RegistryVariables().DeleteAll();<br />
/// ]]]]]]&gt;&lt;![CDATA[&gt;&lt;![CDATA[&gt;&lt;/code&gt;<br />
///</span><span class='xd'> </span><span class='xt'>&lt;/example&gt;</span><br />
<span class='kw'>public class</span> <span class='tp'>RegistryVariables</span> <span class='pn'>{</span><br />
    <span class='kw'>string</span> <span class='vr'>_key</span><span class='pn'>;</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Allows to specify the registry key.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>key</span><span class='xt'>"&gt;</span><span class='xd'>Registry key. Must start with </span><span class='xt'>&lt;c&gt;</span><span class='xd'>@"HKEY_CURRENT_USER&#92;"</span><span class='xt'>&lt;/c&gt;</span><span class='xd'>.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public</span> <span class='tp'>RegistryVariables</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>key</span> <span class='op'>=</span> <span class='st'>@"HKEY_CURRENT_USER&#92;Software&#92;My&#92;Variables"</span><span class='pn'>) {</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>key</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>@"HKEY_CURRENT_USER&#92;"</span><span class='pn'>))</span> <span class='kw'>throw new</span> <span class='tp'>ArgumentException</span><span class='pn'>(</span><span class='st'>"key must start with @</span><span class='se'>&#92;"</span><span class='st'>HKEY_CURRENT_USER</span><span class='se'>&#92;"</span><span class='st'>."</span><span class='pn'>);</span><br />
        <span class='vr'>_key</span> <span class='op'>=</span> <span class='vr'>key</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Sets an int (DWORD) value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>value</span><span class='xt'>"&gt;</span><span class='xd'>Value.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public void</span> <span class='fn'>SetInt</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>int</span> <span class='vr'>value</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>SetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>value</span><span class='pn'>);</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Gets an int (DWORD) value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>defaultValue</span><span class='xt'>"&gt;</span><span class='xd'>Return this value if the registry key or value does not exist or is not DWORD.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public int</span> <span class='fn'>GetInt</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>int</span> <span class='vr'>defaultValue</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>GetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='kw'>is int</span> <span class='vr'>r</span> <span class='op'>?</span> <span class='vr'>r</span> <span class='op'>:</span> <span class='vr'>defaultValue</span><span class='pn'>;</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Sets a long (QWORD) value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>value</span><span class='xt'>"&gt;</span><span class='xd'>Value.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public void</span> <span class='fn'>SetLong</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>long</span> <span class='vr'>value</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>SetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>value</span><span class='pn'>,</span> <span class='tp'>RegistryValueKind</span><span class='op'>.</span><span class='cn'>QWord</span><span class='pn'>);</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Gets a long (QWORD) value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>defaultValue</span><span class='xt'>"&gt;</span><span class='xd'>Return this value if the registry key or value does not exist or is not QWORD.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public long</span> <span class='fn'>GetLong</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>long</span> <span class='vr'>defaultValue</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>GetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='kw'>is long</span> <span class='vr'>r</span> <span class='op'>?</span> <span class='vr'>r</span> <span class='op'>:</span> <span class='vr'>defaultValue</span><span class='pn'>;</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Sets a string value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>value</span><span class='xt'>"&gt;</span><span class='xd'>Value.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public void</span> <span class='fn'>SetString</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>value</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>SetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>value</span><span class='pn'>);</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Gets a string value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;</span><span class='xd'>null if the registry key or value does not exist or is not string.</span><span class='xt'>&lt;/returns&gt;</span><br />
    <span class='kw'>public string</span> <span class='fn'>GetString</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>GetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='kw'>as string</span><span class='pn'>;</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Sets a binary data value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>value</span><span class='xt'>"&gt;</span><span class='xd'>Value.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public void</span> <span class='fn'>SetBinary</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>byte</span><span class='pn'>[]</span> <span class='vr'>value</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>SetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>value</span><span class='pn'>);</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Gets a binary data value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;</span><span class='xd'>null if the registry key or value does not exist or is not binary.</span><span class='xt'>&lt;/returns&gt;</span><br />
    <span class='kw'>public byte</span><span class='pn'>[]</span> <span class='fn'>GetBinary</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>GetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='kw'>as byte</span><span class='pn'>[];</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> JSON-serializes an object of type </span><span class='xt'>&lt;b&gt;</span><span class='xd'>T</span><span class='xt'>&lt;/b&gt;</span><span class='xd'>, and sets a string value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>value</span><span class='xt'>"&gt;</span><span class='xd'>Value.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public void</span> <span class='fn'>SetObject</span><span class='pn'>&lt;</span><span class='tp'>T</span><span class='pn'>&gt;(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='tp'>T</span> <span class='vr'>value</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='tp'>JsonSerializer</span><span class='op'>.</span><span class='fn'>Serialize</span><span class='pn'>(</span><span class='vr'>value</span><span class='pn'>,</span> <span class='vr'>s_jsOptions</span><span class='op'>.</span><span class='fn'>Value</span><span class='pn'>);</span><br />
        <span class='fn'>SetString</span><span class='pn'>(</span><span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>s</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Gets a string value, and JSON-deserializes to object of type </span><span class='xt'>&lt;b&gt;</span><span class='xd'>T</span><span class='xt'>&lt;/b&gt;</span><span class='xd'>.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>value</span><span class='xt'>"&gt;</span><span class='xd'>Receives the object, or </span><span class='xt'>&lt;c&gt;</span><span class='xd'>default(T)</span><span class='xt'>&lt;/c&gt;</span><span class='xd'> if returns false.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;</span><span class='xd'>false if the registry key or value does not exist or is not string.</span><span class='xt'>&lt;/returns&gt;</span><br />
    <span class='kw'>public bool</span> <span class='fn'>GetObject</span><span class='pn'>&lt;</span><span class='tp'>T</span><span class='pn'>&gt;(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>out</span> <span class='tp'>T</span> <span class='vr'>value</span><span class='pn'>) {</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='fn'>GetString</span><span class='pn'>(</span><span class='vr'>name</span><span class='pn'>)</span> <span class='kw'>is not string</span> <span class='vr'>s</span><span class='pn'>) {</span> <span class='vr'>value</span> <span class='op'>=</span> <span class='kw'>default</span><span class='pn'>(</span><span class='tp'>T</span><span class='pn'>);</span> <span class='kw'>return false</span><span class='pn'>; }</span><br />
        <span class='vr'>value</span> <span class='op'>=</span> <span class='tp'>JsonSerializer</span><span class='op'>.</span><span class='fn'>Deserialize</span><span class='pn'>&lt;</span><span class='tp'>T</span><span class='pn'>&gt;(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>s_jsOptions</span><span class='op'>.</span><span class='fn'>Value</span><span class='pn'>);</span><br />
        <span class='kw'>return true</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='kw'>static readonly</span> <span class='tp'>Lazy</span><span class='pn'>&lt;</span><span class='tp'>JsonSerializerOptions</span><span class='pn'>&gt;</span> <span class='vr'>s_jsOptions</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>(()</span> <span class='op'>=&gt;</span> <span class='kw'>new</span><span class='pn'>() {</span><br />
        <span class='fn'>DefaultIgnoreCondition</span> <span class='op'>=</span> <span class='tp'>JsonIgnoreCondition</span><span class='op'>.</span><span class='cn'>WhenWritingNull</span><span class='pn'>,</span><br />
        <span class='fn'>IncludeFields</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>,</span><br />
        <span class='fn'>IgnoreReadOnlyFields</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>,</span><br />
        <span class='fn'>IgnoreReadOnlyProperties</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>,</span><br />
        <span class='fn'>Encoder</span> <span class='op'>=</span> <span class='tp'>JavaScriptEncoder</span><span class='op'>.</span><span class='fn'>UnsafeRelaxedJsonEscaping</span><span class='pn'>,<br />
    });</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Registry value exists?</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;</span><span class='xd'>true if exists.</span><span class='xt'>&lt;/returns&gt;</span><br />
    <span class='kw'>public bool</span> <span class='fn'>Exists</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>GetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='kw'>is not null</span><span class='pn'>;</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Deletes a registry value if exists.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public void</span> <span class='fn'>Delete</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>) {</span><br />
        <span class='kw'>using var</span> <span class='vr'>k</span> <span class='op'>=</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='vr'>CurrentUser</span><span class='op'>.</span><span class='fn'>OpenSubKey</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>[</span><span class='nr'>18</span><span class='pn'>..],</span> <span class='kw'>true</span><span class='pn'>);</span><br />
        <span class='vr'>k</span><span class='op'>?.</span><span class='fn'>DeleteValue</span><span class='pn'>(</span><span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>throwOnMissingValue</span><span class='pn'>:</span> <span class='kw'>false</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Deletes all values from the registry key.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;</span><br />
    <span class='kw'>public void</span> <span class='fn'>DeleteAll</span><span class='pn'>() {</span><br />
        <span class='tp'>Registry</span><span class='op'>.</span><span class='vr'>CurrentUser</span><span class='op'>.</span><span class='fn'>DeleteSubKeyTree</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>[</span><span class='nr'>18</span><span class='pn'>..],</span> <span class='vr'>throwOnMissingSubKey</span><span class='pn'>:</span> <span class='kw'>false</span><span class='pn'>);<br />
    }<br />
}</span></code></div></div>]]></description>
			<content:encoded><![CDATA[LA scripts run in separate processes. Even the same script runs in new process each time. Each process has its own static variables, and they disappear when the process ends. To share values between processes, can be used a file, registry, shared memory, etc. This class uses the registry.<br />
<br />
Example.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code><span class='cm'>/*/ c &#92;RegistryVariables.cs; /*/</span><br />
<br />
<span class='tp'>print</span><span class='op'>.</span><span class='fn'>clear</span><span class='pn'>();</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>rv</span> <span class='op'>=</span> <span class='kw'>new</span> RegistryVariables<span class='pn'>();</span><br />
<span class='vr'>rv</span><span class='op'>.</span>SetInt<span class='pn'>(</span><span class='st'>"i"</span><span class='pn'>,</span> <span class='nr'>10</span><span class='pn'>);</span><br />
<span class='vr'>rv</span><span class='op'>.</span>SetLong<span class='pn'>(</span><span class='st'>"q"</span><span class='pn'>,</span> <span class='op'>-</span><span class='nr'>2</span><span class='pn'>);</span><br />
<span class='vr'>rv</span><span class='op'>.</span>SetString<span class='pn'>(</span><span class='st'>"s"</span><span class='pn'>,</span> <span class='st'>"test"</span><span class='pn'>);</span><br />
<span class='vr'>rv</span><span class='op'>.</span>SetBinary<span class='pn'>(</span><span class='st'>"b"</span><span class='pn'>, [</span><span class='nr'>1</span><span class='pn'>,</span> <span class='nr'>2</span><span class='pn'>,</span> <span class='nr'>3</span><span class='pn'>,</span> <span class='nr'>4</span><span class='pn'>]);</span><br />
<span class='vr'>rv</span><span class='op'>.</span>SetObject<span class='pn'>(</span><span class='st'>"p"</span><span class='pn'>,</span> <span class='kw'>new</span> <span class='tp'>POINT</span><span class='pn'>(</span><span class='nr'>5</span><span class='pn'>,</span> <span class='nr'>6</span><span class='pn'>));</span><br />
<span class='cm'>//print.it(rv.Exists("p"));<br />
//rv.DeleteAll();<br />
//rv.Delete("s");</span><br />
<span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='vr'>rv</span><span class='op'>.</span>GetInt<span class='pn'>(</span><span class='st'>"i"</span><span class='pn'>),</span> <span class='vr'>rv</span><span class='op'>.</span>GetLong<span class='pn'>(</span><span class='st'>"q"</span><span class='pn'>),</span> <span class='vr'>rv</span><span class='op'>.</span>GetString<span class='pn'>(</span><span class='st'>"s"</span><span class='pn'>),</span> <span class='vr'>rv</span><span class='op'>.</span>GetBinary<span class='pn'>(</span><span class='st'>"b"</span><span class='pn'>),</span> <span class='vr'>rv</span><span class='op'>.</span>GetObject<span class='pn'>(</span><span class='st'>"p"</span><span class='pn'>,</span> <span class='kw'>out</span> <span class='tp'>POINT</span> <span class='vr'>p</span><span class='pn'>),</span> <span class='vr'>p</span><span class='pn'>);</span></code></div></div><br />
To clear the key when LA starts, add this code in a script that runs when LA starts (see Options &gt; Workspace &gt; Run...):<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code><span class='cm'>/*/ role editorExtension; c &#92;RegistryVariables.cs; /*/</span><br />
<span class='kw'>new</span> RegistryVariables<span class='pn'>()</span><span class='op'>.</span>DeleteAll<span class='pn'>();</span></code></div></div> <br />
<hr class="mycode_hr" />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// class "RegistryVariables.cs"<br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Text</span><span class='op'>.</span><span class='ns'>Json</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Text</span><span class='op'>.</span><span class='ns'>Json</span><span class='op'>.</span><span class='ns'>Serialization</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Text</span><span class='op'>.</span><span class='ns'>Encodings</span><span class='op'>.</span><span class='ns'>Web</span><span class='pn'>;</span><br />
<br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
///</span><span class='xd'> Sets/gets values in a registry key.</span><br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
///</span><span class='xd'> </span><span class='xt'>&lt;example&gt;<br />
///</span><span class='xd'> </span><span class='xt'>&lt;code&gt;&lt;![CDATA[<br />
/// /*/ c &#92;RegistryVariables.cs; /*/<br />
/// var rv = new RegistryVariables();<br />
/// rv.SetInt("i", 10);<br />
/// rv.SetLong("q", -2);<br />
/// rv.SetString("s", "test");<br />
/// rv.SetBinary("b", [1, 2, 3, 4]);<br />
/// rv.SetObject("p", new POINT(5, 6));<br />
/// print.it(rv.GetInt("i"), rv.GetLong("q"), rv.GetString("s"), rv.GetBinary("b"), rv.GetObject("p", out POINT p), p);<br />
/// ]]]]]]&gt;&lt;![CDATA[&gt;&lt;![CDATA[&gt;&lt;/code&gt;<br />
///</span><span class='xd'> To clear the key when LA starts, add this code in a script that runs when LA starts (see Options &gt; Workspace &gt; Run...):</span><br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;code&gt;&lt;![CDATA[<br />
/// /*/ role editorExtension; c &#92;RegistryVariables.cs; /*/<br />
/// new RegistryVariables().DeleteAll();<br />
/// ]]]]]]&gt;&lt;![CDATA[&gt;&lt;![CDATA[&gt;&lt;/code&gt;<br />
///</span><span class='xd'> </span><span class='xt'>&lt;/example&gt;</span><br />
<span class='kw'>public class</span> <span class='tp'>RegistryVariables</span> <span class='pn'>{</span><br />
    <span class='kw'>string</span> <span class='vr'>_key</span><span class='pn'>;</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Allows to specify the registry key.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>key</span><span class='xt'>"&gt;</span><span class='xd'>Registry key. Must start with </span><span class='xt'>&lt;c&gt;</span><span class='xd'>@"HKEY_CURRENT_USER&#92;"</span><span class='xt'>&lt;/c&gt;</span><span class='xd'>.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public</span> <span class='tp'>RegistryVariables</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>key</span> <span class='op'>=</span> <span class='st'>@"HKEY_CURRENT_USER&#92;Software&#92;My&#92;Variables"</span><span class='pn'>) {</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>key</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>@"HKEY_CURRENT_USER&#92;"</span><span class='pn'>))</span> <span class='kw'>throw new</span> <span class='tp'>ArgumentException</span><span class='pn'>(</span><span class='st'>"key must start with @</span><span class='se'>&#92;"</span><span class='st'>HKEY_CURRENT_USER</span><span class='se'>&#92;"</span><span class='st'>."</span><span class='pn'>);</span><br />
        <span class='vr'>_key</span> <span class='op'>=</span> <span class='vr'>key</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Sets an int (DWORD) value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>value</span><span class='xt'>"&gt;</span><span class='xd'>Value.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public void</span> <span class='fn'>SetInt</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>int</span> <span class='vr'>value</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>SetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>value</span><span class='pn'>);</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Gets an int (DWORD) value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>defaultValue</span><span class='xt'>"&gt;</span><span class='xd'>Return this value if the registry key or value does not exist or is not DWORD.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public int</span> <span class='fn'>GetInt</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>int</span> <span class='vr'>defaultValue</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>GetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='kw'>is int</span> <span class='vr'>r</span> <span class='op'>?</span> <span class='vr'>r</span> <span class='op'>:</span> <span class='vr'>defaultValue</span><span class='pn'>;</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Sets a long (QWORD) value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>value</span><span class='xt'>"&gt;</span><span class='xd'>Value.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public void</span> <span class='fn'>SetLong</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>long</span> <span class='vr'>value</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>SetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>value</span><span class='pn'>,</span> <span class='tp'>RegistryValueKind</span><span class='op'>.</span><span class='cn'>QWord</span><span class='pn'>);</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Gets a long (QWORD) value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>defaultValue</span><span class='xt'>"&gt;</span><span class='xd'>Return this value if the registry key or value does not exist or is not QWORD.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public long</span> <span class='fn'>GetLong</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>long</span> <span class='vr'>defaultValue</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>GetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='kw'>is long</span> <span class='vr'>r</span> <span class='op'>?</span> <span class='vr'>r</span> <span class='op'>:</span> <span class='vr'>defaultValue</span><span class='pn'>;</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Sets a string value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>value</span><span class='xt'>"&gt;</span><span class='xd'>Value.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public void</span> <span class='fn'>SetString</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>value</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>SetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>value</span><span class='pn'>);</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Gets a string value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;</span><span class='xd'>null if the registry key or value does not exist or is not string.</span><span class='xt'>&lt;/returns&gt;</span><br />
    <span class='kw'>public string</span> <span class='fn'>GetString</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>GetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='kw'>as string</span><span class='pn'>;</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Sets a binary data value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>value</span><span class='xt'>"&gt;</span><span class='xd'>Value.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public void</span> <span class='fn'>SetBinary</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>byte</span><span class='pn'>[]</span> <span class='vr'>value</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>SetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>value</span><span class='pn'>);</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Gets a binary data value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;</span><span class='xd'>null if the registry key or value does not exist or is not binary.</span><span class='xt'>&lt;/returns&gt;</span><br />
    <span class='kw'>public byte</span><span class='pn'>[]</span> <span class='fn'>GetBinary</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>GetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='kw'>as byte</span><span class='pn'>[];</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> JSON-serializes an object of type </span><span class='xt'>&lt;b&gt;</span><span class='xd'>T</span><span class='xt'>&lt;/b&gt;</span><span class='xd'>, and sets a string value.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>value</span><span class='xt'>"&gt;</span><span class='xd'>Value.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public void</span> <span class='fn'>SetObject</span><span class='pn'>&lt;</span><span class='tp'>T</span><span class='pn'>&gt;(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='tp'>T</span> <span class='vr'>value</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='tp'>JsonSerializer</span><span class='op'>.</span><span class='fn'>Serialize</span><span class='pn'>(</span><span class='vr'>value</span><span class='pn'>,</span> <span class='vr'>s_jsOptions</span><span class='op'>.</span><span class='fn'>Value</span><span class='pn'>);</span><br />
        <span class='fn'>SetString</span><span class='pn'>(</span><span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>s</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Gets a string value, and JSON-deserializes to object of type </span><span class='xt'>&lt;b&gt;</span><span class='xd'>T</span><span class='xt'>&lt;/b&gt;</span><span class='xd'>.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>value</span><span class='xt'>"&gt;</span><span class='xd'>Receives the object, or </span><span class='xt'>&lt;c&gt;</span><span class='xd'>default(T)</span><span class='xt'>&lt;/c&gt;</span><span class='xd'> if returns false.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;</span><span class='xd'>false if the registry key or value does not exist or is not string.</span><span class='xt'>&lt;/returns&gt;</span><br />
    <span class='kw'>public bool</span> <span class='fn'>GetObject</span><span class='pn'>&lt;</span><span class='tp'>T</span><span class='pn'>&gt;(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>out</span> <span class='tp'>T</span> <span class='vr'>value</span><span class='pn'>) {</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='fn'>GetString</span><span class='pn'>(</span><span class='vr'>name</span><span class='pn'>)</span> <span class='kw'>is not string</span> <span class='vr'>s</span><span class='pn'>) {</span> <span class='vr'>value</span> <span class='op'>=</span> <span class='kw'>default</span><span class='pn'>(</span><span class='tp'>T</span><span class='pn'>);</span> <span class='kw'>return false</span><span class='pn'>; }</span><br />
        <span class='vr'>value</span> <span class='op'>=</span> <span class='tp'>JsonSerializer</span><span class='op'>.</span><span class='fn'>Deserialize</span><span class='pn'>&lt;</span><span class='tp'>T</span><span class='pn'>&gt;(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>s_jsOptions</span><span class='op'>.</span><span class='fn'>Value</span><span class='pn'>);</span><br />
        <span class='kw'>return true</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='kw'>static readonly</span> <span class='tp'>Lazy</span><span class='pn'>&lt;</span><span class='tp'>JsonSerializerOptions</span><span class='pn'>&gt;</span> <span class='vr'>s_jsOptions</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>(()</span> <span class='op'>=&gt;</span> <span class='kw'>new</span><span class='pn'>() {</span><br />
        <span class='fn'>DefaultIgnoreCondition</span> <span class='op'>=</span> <span class='tp'>JsonIgnoreCondition</span><span class='op'>.</span><span class='cn'>WhenWritingNull</span><span class='pn'>,</span><br />
        <span class='fn'>IncludeFields</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>,</span><br />
        <span class='fn'>IgnoreReadOnlyFields</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>,</span><br />
        <span class='fn'>IgnoreReadOnlyProperties</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>,</span><br />
        <span class='fn'>Encoder</span> <span class='op'>=</span> <span class='tp'>JavaScriptEncoder</span><span class='op'>.</span><span class='fn'>UnsafeRelaxedJsonEscaping</span><span class='pn'>,<br />
    });</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Registry value exists?</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;</span><span class='xd'>true if exists.</span><span class='xt'>&lt;/returns&gt;</span><br />
    <span class='kw'>public bool</span> <span class='fn'>Exists</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>GetValue</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='kw'>is not null</span><span class='pn'>;</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Deletes a registry value if exists.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>Registry value name.</span><span class='xt'>&lt;/param&gt;</span><br />
    <span class='kw'>public void</span> <span class='fn'>Delete</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>) {</span><br />
        <span class='kw'>using var</span> <span class='vr'>k</span> <span class='op'>=</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='vr'>CurrentUser</span><span class='op'>.</span><span class='fn'>OpenSubKey</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>[</span><span class='nr'>18</span><span class='pn'>..],</span> <span class='kw'>true</span><span class='pn'>);</span><br />
        <span class='vr'>k</span><span class='op'>?.</span><span class='fn'>DeleteValue</span><span class='pn'>(</span><span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>throwOnMissingValue</span><span class='pn'>:</span> <span class='kw'>false</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Deletes all values from the registry key.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;</span><br />
    <span class='kw'>public void</span> <span class='fn'>DeleteAll</span><span class='pn'>() {</span><br />
        <span class='tp'>Registry</span><span class='op'>.</span><span class='vr'>CurrentUser</span><span class='op'>.</span><span class='fn'>DeleteSubKeyTree</span><span class='pn'>(</span><span class='vr'>_key</span><span class='pn'>[</span><span class='nr'>18</span><span class='pn'>..],</span> <span class='vr'>throwOnMissingSubKey</span><span class='pn'>:</span> <span class='kw'>false</span><span class='pn'>);<br />
    }<br />
}</span></code></div></div>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Simple password manager for scripts]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7741</link>
			<pubDate>Mon, 12 Aug 2024 07:42:44 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=2">Gintaras</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7741</guid>
			<description><![CDATA[Examples.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "Passwords examples.cs"<br />
<span class='cm'>/*/ c &#92;Passwords.cs; /*/<br />
<br />
//get password named "test".<br />
//    The first time it shows a password input dialog.<br />
//    Also shows a key input dialog if a key for this computer/user still not saved.<br />
//    To avoid password/key input dialogs at an inconvenient time, call any Passwords method at a convenient time. Or run file "Passwords.cs" (it calls Passwords.ShowManagerUI).</span><br />
<span class='kw'>string</span> <span class='vr'>pw</span> <span class='op'>=</span> Passwords<span class='op'>.</span>Get<span class='pn'>(</span><span class='st'>"test"</span><span class='pn'>);</span><br />
<span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='vr'>pw</span><span class='pn'>);</span><br />
<br />
<span class='cm'>//save new password with name "test2", or change password named "test2"</span><br />
Passwords<span class='op'>.</span>Save<span class='pn'>(</span><span class='st'>"test2"</span><span class='pn'>,</span> <span class='st'>"99564Jk-"</span><span class='pn'>);</span><br />
<br />
<span class='cm'>//delete password named "test3" if exists</span><br />
Passwords<span class='op'>.</span>Delete<span class='pn'>(</span><span class='st'>"test3"</span><span class='pn'>);</span><br />
<br />
<span class='cm'>//show the password manager dialog. Or you can run the "Passwords" file.</span><br />
Passwords<span class='op'>.</span>ShowManagerUI<span class='pn'>();</span></code></div></div><br />
Class.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// class "Passwords.cs"<br />
<span class='cm'>/*/ role miniProgram; define SCRIPT; /*/</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Security</span><span class='op'>.</span><span class='ns'>Cryptography</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Controls</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Data</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Media</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Input</span><span class='pn'>;</span><br />
<br />
<span class='pd'>#if</span> SCRIPT<br />
<span class='ex'>//Passwords.Save("saved", "test");<br />
//Passwords.Delete("Saved");<br />
//print.it(Passwords.Get("saved"));<br />
<br />
Passwords.ShowManagerUI();<br />
</span><span class='pd'>#endif</span><br />
<br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
///</span><span class='xd'> Simple password manager for scripts.</span><br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
///</span><span class='xd'> </span><span class='xt'>&lt;remarks&gt;<br />
///</span><span class='xd'> In scripts, you often need to use passwords, API keys, and other kinds of credentials. This class helps to protect them. In script you use password names, not passwords directly; passwords are saved encrypted.</span><br />
<span class='xt'>///</span><span class='xd'> To encrypt passwords uses a user-provided encryption key (aka master password). Saves the key encrypted using Windows data protection API, which makes it undecryptable on other computers and user accounts.</span><br />
<span class='xt'>///</span><span class='xd'> Saves everything in </span><span class='xt'>&lt;see cref="</span><span class='fn'>Folder</span><span class='xt'>"/&gt;</span><span class='xd'>.</span><br />
<span class='xt'>///</span><span class='xd'> Can be used in portable apps too; more info in </span><span class='xt'>&lt;see cref="</span><span class='fn'>Folder</span><span class='xt'>"/&gt;</span><span class='xd'> remarks.</span><br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;para&gt;<br />
///</span><span class='xd'> Obviously, passwords managed by this class are not completely secure. And less secure than when using a password manager software.</span><br />
<span class='xt'>///</span><span class='xd'> To use in scripts conveniently, this class saves encrypted passwords (and key) so you don't have to enter them manually each time. They can be decrypted only on the same computer/account.</span><br />
<span class='xt'>///</span><span class='xd'> A determined hacker can steal the passwords if he can access that computer/account and somehow know about this class and find the saved data and this code (or just run this code). It's unlikely, but possible.</span><br />
<span class='xt'>///</span><span class='xd'> Also this class does not encrypt passwords in memory, and does not clear the password string memory.</span><br />
<span class='xt'>///</span><span class='xd'> But this class gives full protection from leaking passwords through script sharing or github etc.</span><br />
<span class='xt'>///</span><span class='xd'> Another benefit: when you change a password, will not need to find and update it in each script that uses the password. Instead you can run this script to show the password manager UI and update the password there.</span><br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/para&gt;<br />
///</span><span class='xd'> </span><span class='xt'>&lt;para&gt;<br />
///</span><span class='xd'> To avoid password/key input dialogs at an inconvenient time, call any </span><span class='xt'>&lt;b&gt;</span><span class='xd'>Passwords</span><span class='xt'>&lt;/b&gt;</span><span class='xd'> method at a convenient time. Or run this file (it calls </span><span class='xt'>&lt;b&gt;</span><span class='xd'>Passwords.ShowManagerUI</span><span class='xt'>&lt;/b&gt;</span><span class='xd'>).</span><br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/para&gt;<br />
///</span><span class='xd'> </span><span class='xt'>&lt;/remarks&gt;</span><br />
<span class='kw'>public static class</span> <span class='tp'>Passwords</span> <span class='pn'>{</span><br />
    <span class='pd'>#region</span> settings. You can edit this code.<br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Full path of the password manager data folder.</span><br />
    <span class='xt'>///</span><span class='xd'> Default: </span><span class='xt'>&lt;c&gt;</span><span class='xd'>folders.ThisAppDataLocal + "Pm"</span><span class='xt'>&lt;/c&gt;</span><span class='xd'>.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;remarks&gt;<br />
    ///</span><span class='xd'> In portable LA the folder is in its drive. The portable setup tool does not copy/update it, unless the path is changed like </span><span class='xt'>&lt;c&gt;</span><span class='xd'>folders.ThisAppDataRoaming + "Pm"</span><span class='xt'>&lt;/c&gt;</span><span class='xd'> (the tool copies the roaming folder).</span><br />
    <span class='xt'>///</span><span class='xd'> Note: the </span><span class='xt'>&lt;b&gt;</span><span class='xd'>folders.ThisAppDataLocal/Roaming</span><span class='xt'>&lt;/b&gt;</span><span class='xd'> path is different if script role is </span><span class='xt'>&lt;b&gt;</span><span class='xd'>editorExtension</span><span class='xt'>&lt;/b&gt;</span><span class='xd'>.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/remarks&gt;</span><br />
    <span class='kw'>public static string</span> <span class='fn'>Folder</span> <span class='pn'>{</span> <span class='kw'>get</span><span class='pn'>;</span> <span class='kw'>set</span><span class='pn'>; }</span> <span class='op'>=</span> <span class='tp'>folders</span><span class='op'>.</span><span class='fn'>ThisAppDataLocal +</span> <span class='st'>"Pm"</span><span class='pn'>;</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Parameter </span><span class='xt'>&lt;i&gt;</span><span class='xd'>optionalEntropy</span><span class='xt'>&lt;/i&gt;</span><span class='xd'> of </span><span class='xt'>&lt;see cref="</span><span class='tp'>ProtectedData</span><span class='xt'>"/&gt;</span><span class='xd'> class functions.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;remarks&gt;<br />
    ///</span><span class='xd'> Used when encrypting and decrypting the password encryption key. The key cannot be decrypted with different entropy than when encrypting it.</span><br />
    <span class='xt'>///</span><span class='xd'> Recommended: replace the default value with a unique value of any length. It adds some more security and separates your passwords from passwords of other apps that use this class and same folder.</span><br />
    <span class='xt'>///</span><span class='xd'> The names of password data and key files depend on this value. It allows multiple apps (or scripts) to save their passwords separately. Just change the default value used by your app.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/remarks&gt;</span><br />
    <span class='kw'>static byte</span><span class='pn'>[]</span> <span class='vr'>_entropy</span> <span class='op'>=</span> <span class='pn'>[</span><span class='nr'>39</span><span class='pn'>,</span> <span class='nr'>212</span><span class='pn'>,</span> <span class='nr'>196</span><span class='pn'>,</span> <span class='nr'>74</span><span class='pn'>];</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Edit this if want to customize the password input dialog. For example localize the text.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>password</span><span class='xt'>"&gt;&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>save</span><span class='xt'>"&gt;</span><span class='xd'>true to save the password.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;&lt;c&gt;</span><span class='xd'>true</span><span class='xt'>&lt;/c&gt;</span><span class='xd'> if OK.</span><span class='xt'>&lt;/returns&gt;</span><br />
    <span class='kw'>static bool</span> <span class='fn'>_PasswordInputDialog</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>out string</span> <span class='vr'>password</span><span class='pn'>,</span> <span class='kw'>out bool</span> <span class='vr'>save</span><span class='pn'>) {</span><br />
        <span class='tp'>DControls</span> <span class='vr'>c</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>() {</span> <span class='fn'>Checkbox</span> <span class='op'>=</span> <span class='st'>"Save"</span><span class='pn'>,</span> <span class='fn'>IsChecked</span> <span class='op'>=</span> <span class='kw'>true</span> <span class='pn'>};</span><br />
        <span class='kw'>bool</span> <span class='vr'>ok</span> <span class='op'>=</span> <span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>showInput</span><span class='pn'>(</span><span class='kw'>out</span> <span class='vr'>password</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>,</span> <span class='st'>"Password for "</span> <span class='op'>+</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='tp'>DEdit</span><span class='op'>.</span><span class='cn'>Password</span><span class='pn'>,</span> <span class='vr'>controls</span><span class='pn'>:</span> <span class='vr'>c</span><span class='pn'>);</span><br />
        <span class='vr'>save</span> <span class='op'>=</span> <span class='vr'>c</span><span class='op'>.</span><span class='fn'>IsChecked</span><span class='pn'>;</span><br />
        <span class='kw'>return</span> <span class='vr'>ok</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Edit this if want to customize the key input dialog. For example localize the text.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>key</span><span class='xt'>"&gt;&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>newKey</span><span class='xt'>"&gt;</span><span class='xd'>If </span><span class='xt'>&lt;c&gt;</span><span class='xd'>true</span><span class='xt'>&lt;/c&gt;</span><span class='xd'>, the user can enter a new key (there are no saved passwords). If </span><span class='xt'>&lt;c&gt;</span><span class='xd'>false</span><span class='xt'>&lt;/c&gt;</span><span class='xd'>, the user must enter the same key.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;&lt;c&gt;</span><span class='xd'>true</span><span class='xt'>&lt;/c&gt;</span><span class='xd'> if OK.</span><span class='xt'>&lt;/returns&gt;</span><br />
    <span class='kw'>static bool</span> <span class='fn'>_KeyInputDialog</span><span class='pn'>(</span><span class='kw'>out string</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='kw'>bool</span> <span class='vr'>newKey</span><span class='pn'>) {</span><br />
        <span class='kw'>string</span> <span class='vr'>info</span> <span class='op'>=</span> <span class='vr'>newKey</span><br />
            <span class='op'>?</span> <span class='st'>"Please enter a key (any text) that will be used to encrypt saved passwords.</span><span class='se'>&#92;n</span><span class='st'>It will be saved encrypted for this computer/user (undecryptable elsewhere)."</span><br />
            <span class='op'>:</span> <span class='st'>"Please enter the same key that was used to encrypt the saved passwords."</span><span class='pn'>;</span><br />
        <span class='kw'>return</span> <span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>showInput</span><span class='pn'>(</span><span class='kw'>out</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='st'>"Password manager key"</span><span class='pn'>,</span> <span class='vr'>info</span><span class='pn'>,</span> <span class='tp'>DEdit</span><span class='op'>.</span><span class='cn'>Password</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Edit this if want to customize the "Delete all saved passwords?" dialog. For example localize the text.</span><br />
    <span class='xt'>///</span><span class='xd'> Called when </span><span class='xt'>&lt;b&gt;</span><span class='xd'>_KeyInputDialog</span><span class='xt'>&lt;/b&gt;</span><span class='xd'> returns </span><span class='xt'>&lt;c&gt;</span><span class='xd'>false</span><span class='xt'>&lt;/c&gt;</span><span class='xd'> when </span><span class='xt'>&lt;i&gt;</span><span class='xd'>newKey</span><span class='xt'>&lt;/i&gt;</span><span class='xd'> </span><span class='xt'>&lt;c&gt;</span><span class='xd'>false</span><span class='xt'>&lt;/c&gt;</span><span class='xd'>. </span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;&lt;c&gt;</span><span class='xd'>true</span><span class='xt'>&lt;/c&gt;</span><span class='xd'> to delete.</span><span class='xt'>&lt;/returns&gt;</span><br />
    <span class='kw'>static bool</span> <span class='fn'>_DeletePasswordsDialog</span><span class='pn'>() {</span><br />
        <span class='kw'>return</span> <span class='nr'>1</span> <span class='op'>==</span> <span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='st'>"Delete all saved passwords?"</span><span class='pn'>,</span><br />
            <span class='st'>"Saved passwords cannot be decrypted without the key.</span><span class='se'>&#92;n</span><span class='st'>If you have lost the key, you can delete saved passwords and set a new key.</span><span class='se'>&#92;n</span><span class='st'>Do you want to delete all saved passwords?"</span><span class='pn'>,</span><br />
            <span class='st'>"1 Delete|2 Cancel"</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='tp'>DIcon</span><span class='op'>.</span><span class='cn'>Warning</span><span class='pn'>,</span> <span class='vr'>defaultButton</span><span class='pn'>:</span> <span class='nr'>2</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='pd'>#endregion</span><br />
    <br />
    <span class='kw'>static string</span> <span class='fn'>_PasswordsFile</span> <span class='op'>=&gt;</span> <span class='fn'>Folder</span> <span class='op'>+</span> <span class='st'>"</span><span class='se'>&#92;&#92;</span><span class='st'>"</span> <span class='op'>+</span> <span class='pn'>(</span><span class='vr'>_filename</span> <span class='op'>??=</span> <span class='tp'>Hash</span><span class='op'>.</span><span class='fn'>MD5</span><span class='pn'>(</span><span class='vr'>_entropy</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>())</span> <span class='op'>+</span> <span class='st'>".csv"</span><span class='pn'>;</span><br />
    <span class='kw'>static string</span> <span class='vr'>_filename</span><span class='pn'>;</span><br />
    <br />
    <span class='kw'>static string</span> <span class='fn'>_KeyFile</span> <span class='op'>=&gt;</span> <span class='fn'>_PasswordsFile</span> <span class='op'>+</span> <span class='st'>".key"</span><span class='pn'>;</span><br />
    <br />
    <span class='kw'>static</span> <span class='tp'>csvTable</span> <span class='fn'>_LoadCsv</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>file</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>) {</span><br />
        <span class='vr'>file</span> <span class='op'>??=</span> <span class='fn'>_PasswordsFile</span><span class='pn'>;</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>exists</span><span class='pn'>(</span><span class='vr'>file</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>File</span><span class='pn'>)</span> <span class='kw'>return new</span><span class='pn'>() {</span> <span class='fn'>ColumnCount</span> <span class='op'>=</span> <span class='nr'>2</span> <span class='pn'>};</span><br />
        <span class='kw'>var</span> <span class='vr'>t</span> <span class='op'>=</span> <span class='tp'>csvTable</span><span class='op'>.</span><span class='fn'>load</span><span class='pn'>(</span><span class='vr'>file</span><span class='pn'>);</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>t</span><span class='op'>.</span><span class='fn'>ColumnCount</span> <span class='op'>&lt;</span> <span class='nr'>2</span><span class='pn'>)</span> <span class='vr'>t</span><span class='op'>.</span><span class='fn'>ColumnCount</span> <span class='op'>=</span> <span class='nr'>2</span><span class='pn'>;</span><br />
        <span class='kw'>return</span> <span class='vr'>t</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='kw'>static</span> <span class='tp'>Dictionary</span><span class='pn'>&lt;</span><span class='kw'>string</span><span class='pn'>,</span> <span class='kw'>string</span><span class='pn'>&gt;</span> <span class='fn'>_LoadDict</span><span class='pn'>()</span> <span class='op'>=&gt;</span> <span class='fn'>_LoadCsv</span><span class='pn'>()</span><span class='op'>.</span><span class='fn'>ToDictionary</span><span class='pn'>(</span><span class='kw'>true</span><span class='pn'>,</span> <span class='kw'>true</span><span class='pn'>);</span><br />
    <br />
    <span class='kw'>static void</span> <span class='fn'>_SaveCsv</span><span class='pn'>(</span><span class='tp'>csvTable</span> <span class='vr'>t</span><span class='pn'>) {</span><br />
        <span class='vr'>t</span><span class='op'>.</span><span class='fn'>Save</span><span class='pn'>(</span><span class='fn'>_PasswordsFile</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='kw'>static void</span> <span class='fn'>_SaveDict</span><span class='pn'>(</span><span class='tp'>Dictionary</span><span class='pn'>&lt;</span><span class='kw'>string</span><span class='pn'>,</span> <span class='kw'>string</span><span class='pn'>&gt;</span> <span class='vr'>d</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='fn'>_SaveCsv</span><span class='pn'>(</span><span class='tp'>csvTable</span><span class='op'>.</span><span class='fn'>fromDictionary</span><span class='pn'>(</span><span class='vr'>d</span><span class='pn'>));</span><br />
    <br />
    <span class='kw'>static byte</span><span class='pn'>[]</span> <span class='fn'>_GetKey</span><span class='pn'>() {</span><br />
        <span class='kw'>try</span> <span class='pn'>{</span><br />
            <span class='kw'>var</span> <span class='vr'>data</span> <span class='op'>=</span> <span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>loadBytes</span><span class='pn'>(</span><span class='fn'>_KeyFile</span><span class='pn'>);</span><br />
            <span class='kw'>var</span> <span class='vr'>r1</span> <span class='op'>=</span> <span class='tp'>ProtectedData</span><span class='op'>.</span><span class='fn'>Unprotect</span><span class='pn'>(</span><span class='vr'>data</span><span class='pn'>,</span> <span class='vr'>_entropy</span><span class='pn'>,</span> <span class='tp'>DataProtectionScope</span><span class='op'>.</span><span class='cn'>CurrentUser</span><span class='pn'>);</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>r1</span><span class='op'>.</span><span class='fn'>Length</span> <span class='op'>==</span> <span class='nr'>16</span><span class='pn'>)</span> <span class='kw'>return</span> <span class='vr'>r1</span><span class='pn'>;<br />
        }</span><br />
        <span class='kw'>catch</span> <span class='pn'>{ }</span><br />
        <br />
        <span class='kw'>return</span> <span class='fn'>_SetKey</span><span class='pn'>();<br />
    }</span><br />
    <br />
    <span class='kw'>static bool</span> <span class='fn'>_KeyInputDialog2</span><span class='pn'>(</span><span class='kw'>out byte</span><span class='pn'>[]</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='kw'>bool</span> <span class='vr'>newKey</span><span class='pn'>) {</span><br />
        <span class='vr'>key</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>;</span><br />
        <span class='kw'>string</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='st'>""</span><span class='pn'>;</span><br />
        <span class='kw'>while</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Length</span> <span class='op'>&lt;</span> <span class='nr'>1</span><span class='pn'>) {</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='fn'>_KeyInputDialog</span><span class='pn'>(</span><span class='kw'>out</span> <span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>newKey</span><span class='pn'>))</span> <span class='kw'>return false</span><span class='pn'>;<br />
        }</span><br />
        <br />
        <span class='vr'>key</span> <span class='op'>=</span> <span class='tp'>Hash</span><span class='op'>.</span><span class='fn'>MD5</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>ToArray</span><span class='pn'>();</span><br />
        <span class='kw'>return true</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='kw'>static byte</span><span class='pn'>[]</span> <span class='fn'>_SetKey</span><span class='pn'>() {</span><br />
        <span class='kw'>var</span> <span class='vr'>t</span> <span class='op'>=</span> <span class='fn'>_LoadCsv</span><span class='pn'>();</span><br />
        <span class='kw'>bool</span> <span class='vr'>newKey</span> <span class='op'>=</span> <span class='vr'>t</span><span class='op'>.</span><span class='fn'>RowCount</span> <span class='op'>==</span> <span class='nr'>0</span><span class='pn'>;</span><br />
        <span class='lb'>g1</span><span class='pn'>:</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='fn'>_KeyInputDialog2</span><span class='pn'>(</span><span class='kw'>out var</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='vr'>newKey</span><span class='pn'>)) {</span> <span class='cm'>//canceled</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>newKey</span> <span class='op'>|| !</span><span class='fn'>_DeletePasswordsDialog</span><span class='pn'>())</span> <span class='kw'>throw new</span> <span class='tp'>OperationCanceledException</span><span class='pn'>();</span><br />
            <span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>delete</span><span class='pn'>(</span><span class='fn'>_PasswordsFile</span><span class='pn'>);</span><br />
            <span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>delete</span><span class='pn'>(</span><span class='fn'>_KeyFile</span><span class='pn'>);</span><br />
            <span class='vr'>newKey</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>;</span><br />
            <span class='kw'>goto</span> <span class='lb'>g1</span><span class='pn'>;<br />
        }</span><br />
        <br />
        <span class='cm'>//is data encrypted with this key?</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>newKey</span> <span class='op'>&amp;&amp;</span> <span class='vr'>t</span><span class='op'>.</span><span class='fn'>Rows</span><span class='op'>.</span><span class='fn'>All</span><span class='pn'>(</span><span class='vr'>a</span> <span class='op'>=&gt; !</span><span class='fn'>_Decrypt</span><span class='pn'>(</span><span class='vr'>a</span><span class='pn'>[</span><span class='nr'>1</span><span class='pn'>],</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='kw'>out _</span><span class='pn'>))) {</span> <span class='cm'>//if fails to decrypt all passwords, the key is incorrect</span><br />
            <span class='nr'>500</span><span class='op'>.</span><span class='fn'>ms</span><span class='pn'>();</span><br />
            <span class='kw'>goto</span> <span class='lb'>g1</span><span class='pn'>;<br />
        }</span><br />
        <br />
        <span class='kw'>var</span> <span class='vr'>p</span> <span class='op'>=</span> <span class='tp'>ProtectedData</span><span class='op'>.</span><span class='fn'>Protect</span><span class='pn'>(</span><span class='vr'>key</span><span class='pn'>,</span> <span class='vr'>_entropy</span><span class='pn'>,</span> <span class='tp'>DataProtectionScope</span><span class='op'>.</span><span class='cn'>CurrentUser</span><span class='pn'>);</span><br />
        <span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>saveBytes</span><span class='pn'>(</span><span class='fn'>_KeyFile</span><span class='pn'>,</span> <span class='vr'>p</span><span class='pn'>);</span><br />
        <span class='kw'>return</span> <span class='vr'>key</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='kw'>static bool</span> <span class='fn'>_ChangeKey</span><span class='pn'>(</span><span class='kw'>ref byte</span><span class='pn'>[]</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='tp'>List</span><span class='pn'>&lt;</span><span class='tp'>_Item</span><span class='pn'>&gt;</span> <span class='vr'>a</span><span class='pn'>) {</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='fn'>_KeyInputDialog2</span><span class='pn'>(</span><span class='kw'>out var</span> <span class='vr'>key2</span><span class='pn'>,</span> <span class='vr'>newKey</span><span class='pn'>:</span> <span class='kw'>true</span><span class='pn'>))</span> <span class='kw'>return false</span><span class='pn'>;</span><br />
        <br />
        <span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>var</span> <span class='vr'>v</span> <span class='kw'>in</span> <span class='vr'>a</span><span class='pn'>) {</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='fn'>_Decrypt</span><span class='pn'>(</span><span class='vr'>v</span><span class='op'>.</span><span class='vr'>EncryptedPassword</span><span class='pn'>,</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='kw'>out string</span> <span class='vr'>pw</span><span class='pn'>))</span><br />
                <span class='vr'>v</span><span class='op'>.</span><span class='fn'>SetNewPassword</span><span class='pn'>(</span><span class='vr'>pw</span><span class='pn'>,</span> <span class='vr'>key2</span><span class='pn'>);<br />
        }</span><br />
        <br />
        <span class='kw'>var</span> <span class='vr'>p</span> <span class='op'>=</span> <span class='tp'>ProtectedData</span><span class='op'>.</span><span class='fn'>Protect</span><span class='pn'>(</span><span class='vr'>key2</span><span class='pn'>,</span> <span class='vr'>_entropy</span><span class='pn'>,</span> <span class='tp'>DataProtectionScope</span><span class='op'>.</span><span class='cn'>CurrentUser</span><span class='pn'>);</span><br />
        <span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>saveBytes</span><span class='pn'>(</span><span class='fn'>_KeyFile</span><span class='pn'>,</span> <span class='vr'>p</span><span class='pn'>);</span><br />
        <span class='vr'>key</span> <span class='op'>=</span> <span class='vr'>key2</span><span class='pn'>;</span><br />
        <span class='kw'>return true</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='kw'>static string</span> <span class='fn'>_Encrypt</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>s</span><span class='pn'>,</span> <span class='kw'>byte</span><span class='pn'>[]</span> <span class='vr'>key</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>) {</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>NE</span><span class='pn'>())</span> <span class='kw'>return</span> <span class='st'>""</span><span class='pn'>;</span><br />
        <span class='kw'>return</span> <span class='tp'>Convert2</span><span class='op'>.</span><span class='fn'>AesEncryptS</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>key</span> <span class='op'>??</span> <span class='fn'>_GetKey</span><span class='pn'>());<br />
    }</span><br />
    <br />
    <span class='kw'>static bool</span> <span class='fn'>_Decrypt</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>s</span><span class='pn'>,</span> <span class='kw'>byte</span><span class='pn'>[]</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='kw'>out string</span> <span class='vr'>r</span><span class='pn'>) {</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>NE</span><span class='pn'>())</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='st'>""</span><span class='pn'>;</span><br />
        <span class='kw'>else</span> <span class='pn'>{</span><br />
            <span class='kw'>try</span> <span class='pn'>{</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='tp'>Convert2</span><span class='op'>.</span><span class='fn'>AesDecryptS</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>key</span><span class='pn'>); }</span><br />
            <span class='kw'>catch</span> <span class='pn'>{</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>;</span> <span class='kw'>return false</span><span class='pn'>; }<br />
        }</span><br />
        <span class='kw'>return true</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='cm'>//TODO: in name, replace substring &lt;user&gt; with user SID.</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Encrypts </span><span class='xt'>&lt;i&gt;</span><span class='xd'>password</span><span class='xt'>&lt;/i&gt;</span><span class='xd'> and saves in the passwords file. Can add or replace.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>A name for the password. Case-insensitive.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>password</span><span class='xt'>"&gt;&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>OperationCanceledException</span><span class='xt'>"&gt;</span><span class='xd'>Key input dialog canceled.</span><span class='xt'>&lt;/exception&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>Exception</span><span class='xt'>"&gt;</span><span class='xd'>Failed to load (if exists) or save the passwords file.</span><span class='xt'>&lt;/exception&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>Save</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>password</span><span class='pn'>) {</span><br />
        <span class='fn'>_Save</span><span class='pn'>(</span><span class='fn'>_LoadDict</span><span class='pn'>(),</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>password</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='kw'>static void</span> <span class='fn'>_Save</span><span class='pn'>(</span><span class='tp'>Dictionary</span><span class='pn'>&lt;</span><span class='kw'>string</span><span class='pn'>,</span> <span class='kw'>string</span><span class='pn'>&gt;</span> <span class='vr'>d</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>password</span><span class='pn'>) {</span><br />
        <span class='vr'>d</span><span class='pn'>[</span><span class='vr'>name</span><span class='pn'>]</span> <span class='op'>=</span> <span class='fn'>_Encrypt</span><span class='pn'>(</span><span class='vr'>password</span><span class='pn'>);</span><br />
        <span class='fn'>_SaveDict</span><span class='pn'>(</span><span class='vr'>d</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Deletes one or more passwords from the passwords file.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>names</span><span class='xt'>"&gt;</span><span class='xd'>Password names. Case-insensitive.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>Exception</span><span class='xt'>"&gt;</span><span class='xd'>Failed to load (if exists) or save the passwords file.</span><span class='xt'>&lt;/exception&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>Delete</span><span class='pn'>(</span><span class='kw'>params string</span><span class='pn'>[]</span> <span class='vr'>names</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>d</span> <span class='op'>=</span> <span class='fn'>_LoadDict</span><span class='pn'>();</span><br />
        <span class='kw'>bool</span> <span class='vr'>deleted</span> <span class='op'>=</span> <span class='kw'>false</span><span class='pn'>;</span><br />
        <span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>var</span> <span class='vr'>v</span> <span class='kw'>in</span> <span class='vr'>names</span><span class='pn'>)</span> <span class='vr'>deleted</span> <span class='op'>|=</span> <span class='vr'>d</span><span class='op'>.</span><span class='fn'>Remove</span><span class='pn'>(</span><span class='vr'>v</span><span class='pn'>);</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>deleted</span><span class='pn'>)</span> <span class='fn'>_SaveDict</span><span class='pn'>(</span><span class='vr'>d</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Gets a password from the passwords file and decrypts.</span><br />
    <span class='xt'>///</span><span class='xd'> If not found, shows a password input dialog and calls </span><span class='xt'>&lt;see cref="</span><span class='fn'>Save</span><span class='xt'>"/&gt;</span><span class='xd'> (optionally).</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>The password's name. Case-insensitive.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;</span><span class='xd'>Password.</span><span class='xt'>&lt;/returns&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>OperationCanceledException</span><span class='xt'>"&gt;</span><span class='xd'>Password input dialog canceled. Or key input dialog canceled.</span><span class='xt'>&lt;/exception&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>Exception</span><span class='xt'>"&gt;</span><span class='xd'>Failed to load (if exists) or save the passwords file.</span><span class='xt'>&lt;/exception&gt;</span><br />
    <span class='kw'>public static string</span> <span class='fn'>Get</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>d</span> <span class='op'>=</span> <span class='fn'>_LoadDict</span><span class='pn'>();</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>d</span><span class='op'>.</span><span class='fn'>TryGetValue</span><span class='pn'>(</span><span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>out var</span> <span class='vr'>s</span><span class='pn'>)) {</span><br />
            <span class='kw'>var</span> <span class='vr'>key</span> <span class='op'>=</span> <span class='fn'>_GetKey</span><span class='pn'>();</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='fn'>_Decrypt</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='kw'>out var</span> <span class='vr'>r</span><span class='pn'>))</span> <span class='kw'>return</span> <span class='vr'>r</span><span class='pn'>;<br />
        }</span><br />
        <br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='fn'>_PasswordInputDialog</span><span class='pn'>(</span><span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>out</span> <span class='vr'>s</span><span class='pn'>,</span> <span class='kw'>out bool</span> <span class='vr'>save</span><span class='pn'>))</span> <span class='kw'>throw new</span> <span class='tp'>OperationCanceledException</span><span class='pn'>();</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>save</span><span class='pn'>)</span> <span class='fn'>_Save</span><span class='pn'>(</span><span class='vr'>d</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>s</span><span class='pn'>);</span><br />
        <span class='kw'>return</span> <span class='vr'>s</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Gets all names.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>Exception</span><span class='xt'>"&gt;</span><span class='xd'>Failed to load (if exists) the passwords file.</span><span class='xt'>&lt;/exception&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;example&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;code&gt;&lt;![CDATA[<br />
    /// //show menu with all password names<br />
    /// var a = Passwords.GetList();<br />
    /// if (a.Any()) {<br />
    ///     var m = new popupMenu("0cfd5f9c-8a23-4534-ad93-5af3ba8c2b41");<br />
    ///     foreach (var v in a) {<br />
    ///         m[v] = o =&gt; { print.it(Passwords.Get(v)); };<br />
    ///     }<br />
    ///     m.Show();<br />
    /// }<br />
    /// ]]]]]]&gt;&lt;![CDATA[&gt;&lt;![CDATA[&gt;&lt;/code&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;/example&gt;</span><br />
    <span class='kw'>public static string</span><span class='pn'>[]</span> <span class='fn'>GetList</span><span class='pn'>() {</span><br />
        <span class='kw'>var</span> <span class='vr'>d</span> <span class='op'>=</span> <span class='fn'>_LoadDict</span><span class='pn'>();</span><br />
        <span class='kw'>return</span> <span class='vr'>d</span><span class='op'>.</span><span class='fn'>Keys</span><span class='op'>.</span><span class='fn'>ToArray</span><span class='pn'>();<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Shows a dialog window with a data grid where you can add, delete and edit names and passwords.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>OperationCanceledException</span><span class='xt'>"&gt;</span><span class='xd'>Key input dialog canceled.</span><span class='xt'>&lt;/exception&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>Exception</span><span class='xt'>"&gt;</span><span class='xd'>Failed to load (if exists) or save the passwords file.</span><span class='xt'>&lt;/exception&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>ShowManagerUI</span><span class='pn'>(</span><span class='tp'>Window</span> <span class='vr'>owner</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>b</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>wpfBuilder</span><span class='pn'>(</span><span class='st'>"Passwords"</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>WinSize</span><span class='pn'>(</span><span class='nr'>500</span><span class='pn'>,</span> <span class='nr'>500</span><span class='pn'>);</span><br />
        <span class='kw'>var</span> <span class='vr'>w</span> <span class='op'>=</span> <span class='vr'>b</span><span class='op'>.</span><span class='fn'>Window</span><span class='pn'>;</span><br />
        <span class='vr'>b</span><span class='op'>.</span><span class='fn'>R</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='kw'>out</span> <span class='tp'>Menu</span> <span class='vr'>menu</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Margin</span><span class='pn'>(</span><span class='st'>"T-2 B0"</span><span class='pn'>);</span><br />
        <span class='kw'>var</span> <span class='vr'>g</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>DataGrid</span> <span class='pn'>{</span><br />
            <span class='fn'>AutoGenerateColumns</span> <span class='op'>=</span> <span class='kw'>false</span><span class='pn'>,</span><br />
            <span class='fn'>CanUserAddRows</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>,</span><br />
            <span class='fn'>VerticalGridLinesBrush</span> <span class='op'>=</span> <span class='tp'>Brushes</span><span class='op'>.</span><span class='fn'>LightGray</span><span class='pn'>,</span><br />
            <span class='fn'>HorizontalGridLinesBrush</span> <span class='op'>=</span> <span class='tp'>Brushes</span><span class='op'>.</span><span class='fn'>LightGray</span><span class='pn'>,<br />
        };</span><br />
        <span class='vr'>b</span><span class='op'>.</span><span class='fn'>Row</span><span class='pn'>(</span><span class='op'>-</span><span class='nr'>1</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>g</span><span class='pn'>);</span><br />
        <span class='vr'>b</span><span class='op'>.</span><span class='fn'>R</span><span class='op'>.</span><span class='fn'>AddOkCancel</span><span class='pn'>();</span><br />
        <span class='vr'>b</span><span class='op'>.</span><span class='fn'>End</span><span class='pn'>();</span><br />
        <br />
        <span class='kw'>var</span> <span class='vr'>colName</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>DataGridTextColumn</span> <span class='pn'>{</span><br />
            <span class='fn'>Header</span> <span class='op'>=</span> <span class='st'>"Name"</span><span class='pn'>,</span><br />
            <span class='fn'>Binding</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>Binding</span><span class='pn'>(</span><span class='st'>"Name"</span><span class='pn'>),</span><br />
            <span class='fn'>Width</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>(</span><span class='nr'>1</span><span class='pn'>,</span> <span class='tp'>DataGridLengthUnitType</span><span class='op'>.</span><span class='cn'>Star</span><span class='pn'>),</span><br />
            <span class='fn'>CanUserReorder</span> <span class='op'>=</span> <span class='kw'>false</span><br />
        <span class='pn'>};</span><br />
        <span class='vr'>g</span><span class='op'>.</span><span class='fn'>Columns</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>colName</span><span class='pn'>);</span><br />
        <br />
        <span class='kw'>var</span> <span class='vr'>colPw</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>DataGridTextColumn</span> <span class='pn'>{</span><br />
            <span class='fn'>Header</span> <span class='op'>=</span> <span class='st'>"Password"</span><span class='pn'>,</span><br />
            <span class='fn'>Binding</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>Binding</span><span class='pn'>(</span><span class='st'>"Password"</span><span class='pn'>),</span><br />
            <span class='fn'>Width</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>(</span><span class='nr'>1</span><span class='pn'>,</span> <span class='tp'>DataGridLengthUnitType</span><span class='op'>.</span><span class='cn'>Star</span><span class='pn'>),</span><br />
            <span class='fn'>CanUserReorder</span> <span class='op'>=</span> <span class='kw'>false</span><br />
        <span class='pn'>};</span><br />
        <span class='vr'>g</span><span class='op'>.</span><span class='fn'>Columns</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>colPw</span><span class='pn'>);</span><br />
        <br />
        <span class='kw'>var</span> <span class='vr'>key</span> <span class='op'>=</span> <span class='fn'>_GetKey</span><span class='pn'>();</span> <span class='cm'>//OperationCanceledException</span><br />
        <span class='kw'>var</span> <span class='vr'>t</span> <span class='op'>=</span> <span class='fn'>_LoadCsv</span><span class='pn'>();</span><br />
        <span class='kw'>var</span> <span class='vr'>a</span> <span class='op'>=</span> <span class='vr'>t</span><span class='op'>.</span><span class='fn'>Rows</span><span class='op'>.</span><span class='fn'>Select</span><span class='pn'>(</span><span class='vr'>o</span> <span class='op'>=&gt;</span> <span class='kw'>new</span> <span class='tp'>_Item</span><span class='pn'>(</span><span class='vr'>o</span><span class='pn'>[</span><span class='nr'>0</span><span class='pn'>],</span> <span class='vr'>o</span><span class='pn'>[</span><span class='nr'>1</span><span class='pn'>],</span> <span class='vr'>key</span><span class='pn'>))</span><span class='op'>.</span><span class='fn'>ToList</span><span class='pn'>();</span><br />
        <span class='vr'>g</span><span class='op'>.</span><span class='fn'>ItemsSource</span> <span class='op'>=</span> <span class='vr'>a</span><span class='pn'>;</span><br />
        <br />
        <span class='cm'>//clear the displayed password placeholder text when started editing</span><br />
        <span class='vr'>g</span><span class='op'>.</span><span class='fn'>PreparingCellForEdit</span> <span class='op'>+=</span> <span class='pn'>(</span><span class='vr'>o</span><span class='pn'>,</span> <span class='vr'>e</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>e</span><span class='op'>.</span><span class='fn'>Column</span> <span class='op'>==</span> <span class='vr'>colPw</span><span class='pn'>) {</span><br />
                <span class='kw'>var</span> <span class='vr'>tb</span> <span class='op'>=</span> <span class='pn'>(</span><span class='tp'>TextBox</span><span class='pn'>)</span><span class='vr'>e</span><span class='op'>.</span><span class='fn'>EditingElement</span><span class='pn'>;</span><br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>e</span><span class='op'>.</span><span class='fn'>EditingEventArgs</span> <span class='kw'>is not</span> <span class='tp'>TextCompositionEventArgs</span><span class='pn'>)</span> <span class='vr'>tb</span><span class='op'>.</span><span class='fn'>Clear</span><span class='pn'>();</span><br />
                <span class='cm'>//tb.Foreground = tb.Background; //hide password</span><br />
            <span class='pn'>}<br />
        };</span><br />
        <br />
        <span class='cm'>//validate name when ending editing</span><br />
        <span class='vr'>g</span><span class='op'>.</span><span class='fn'>CellEditEnding</span> <span class='op'>+=</span> <span class='pn'>(</span><span class='vr'>o</span><span class='pn'>,</span> <span class='vr'>e</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>e</span><span class='op'>.</span><span class='fn'>EditAction</span> <span class='op'>==</span> <span class='tp'>DataGridEditAction</span><span class='op'>.</span><span class='cn'>Commit</span><span class='pn'>) {</span><br />
                <span class='kw'>var</span> <span class='vr'>tb</span> <span class='op'>=</span> <span class='pn'>(</span><span class='tp'>TextBox</span><span class='pn'>)</span><span class='vr'>e</span><span class='op'>.</span><span class='fn'>EditingElement</span><span class='pn'>;</span><br />
                <span class='kw'>var</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='vr'>tb</span><span class='op'>.</span><span class='fn'>Text</span><span class='pn'>;</span><br />
                <br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Trim</span><span class='pn'>()</span> <span class='kw'>is var</span> <span class='vr'>s2</span> <span class='op'>&amp;&amp;</span> <span class='vr'>s2</span> <span class='op'>!=</span> <span class='vr'>s</span><span class='pn'>) {</span><br />
                    <span class='kw'>switch</span> <span class='pn'>(</span><span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='kw'>null</span><span class='pn'>,</span> <span class='st'>"The texts starts or ends with spaces."</span><span class='pn'>,</span> <span class='st'>"1 Trim spaces|2 Don't trim|3 Cancel"</span><span class='pn'>,</span> <span class='vr'>owner</span><span class='pn'>:</span> <span class='vr'>w</span><span class='pn'>,</span> <span class='vr'>defaultButton</span><span class='pn'>:</span> <span class='nr'>3</span><span class='pn'>)) {</span><br />
                    <span class='kw'>case</span> <span class='nr'>3</span><span class='pn'>:</span> <span class='fn'>_Cancel</span><span class='pn'>();</span> <span class='kw'>return</span><span class='pn'>;</span><br />
                    <span class='kw'>case</span> <span class='nr'>1</span><span class='pn'>:</span> <span class='vr'>tb</span><span class='op'>.</span><span class='fn'>Text</span> <span class='op'>=</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='vr'>s2</span><span class='pn'>;</span> <span class='kw'>break</span><span class='pn'>;<br />
                    }<br />
                }</span><br />
                <br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>e</span><span class='op'>.</span><span class='fn'>Column</span> <span class='op'>==</span> <span class='vr'>colName</span><span class='pn'>) {</span><br />
                    <span class='kw'>var</span> <span class='vr'>item</span> <span class='op'>=</span> <span class='vr'>e</span><span class='op'>.</span><span class='fn'>Row</span><span class='op'>.</span><span class='fn'>Item</span> <span class='kw'>as</span> <span class='tp'>_Item</span><span class='pn'>;</span><br />
                    <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>a</span><span class='op'>.</span><span class='fn'>Any</span><span class='pn'>(</span><span class='vr'>v</span> <span class='op'>=&gt;</span> <span class='vr'>v</span> <span class='fn'>!=</span> <span class='vr'>item</span> <span class='op'>&amp;&amp;</span> <span class='vr'>v</span><span class='op'>.</span><span class='fn'>Name</span><span class='op'>.</span><span class='fn'>Eqi</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>))) {</span><br />
                        <span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='st'>"Error"</span><span class='pn'>,</span> <span class='st'>&#36;"Name '</span><span class='pn'>{</span><span class='vr'>s</span><span class='pn'>}</span><span class='st'>' already exists."</span><span class='pn'>,</span> <span class='vr'>owner</span><span class='pn'>:</span> <span class='vr'>w</span><span class='pn'>);</span><br />
                        <span class='fn'>_Cancel</span><span class='pn'>();<br />
                    }<br />
                }</span><br />
                <br />
                <span class='kw'>void</span> <span class='fn'>_Cancel</span><span class='pn'>() {</span><br />
                    <span class='vr'>e</span><span class='op'>.</span><span class='fn'>Cancel</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>;</span><br />
                    <br />
                    <span class='cm'>//workaround for DataGrid bug: on Tab key starts editing next cell. Then 2 cells are in edit mode.</span><br />
                    <span class='tp'>EventHandler</span><span class='pn'>&lt;</span><span class='tp'>DataGridBeginningEditEventArgs</span><span class='pn'>&gt;</span> <span class='vr'>eh1</span> <span class='op'>=</span> <span class='pn'>(</span><span class='vr'>o</span><span class='pn'>,</span> <span class='vr'>e</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='pn'>{</span> <span class='vr'>e</span><span class='op'>.</span><span class='fn'>Cancel</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>;</span> <span class='vr'>tb</span><span class='op'>.</span><span class='fn'>Focus</span><span class='pn'>(); };</span><br />
                    <span class='vr'>g</span><span class='op'>.</span><span class='fn'>BeginningEdit</span> <span class='op'>+=</span> <span class='vr'>eh1</span><span class='pn'>;</span><br />
                    <span class='tp'>timer</span><span class='op'>.</span><span class='fn'>after</span><span class='pn'>(</span><span class='nr'>1</span><span class='pn'>,</span> <span class='vr'>_</span> <span class='op'>=&gt;</span> <span class='pn'>{</span> <span class='vr'>g</span><span class='op'>.</span><span class='fn'>BeginningEdit</span> <span class='op'>-=</span> <span class='vr'>eh1</span><span class='pn'>; });<br />
                }<br />
            }<br />
        };</span><br />
        <br />
        <span class='fn'>_CreateMenu</span><span class='pn'>();</span><br />
        <br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>owner</span> <span class='op'>!=</span> <span class='kw'>null</span><span class='pn'>) {</span><br />
            <span class='vr'>w</span><span class='op'>.</span><span class='fn'>Owner</span> <span class='op'>=</span> <span class='vr'>owner</span><span class='pn'>;</span><br />
            <span class='vr'>w</span><span class='op'>.</span><span class='fn'>ShowInTaskbar</span> <span class='op'>=</span> <span class='kw'>false</span><span class='pn'>;<br />
        }</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>b</span><span class='op'>.</span><span class='fn'>ShowDialog</span><span class='pn'>(</span><span class='vr'>owner</span><span class='pn'>))</span> <span class='kw'>return</span><span class='pn'>;</span><br />
        <br />
        <span class='fn'>_SaveItems</span><span class='pn'>();</span><br />
        <br />
        <span class='kw'>void</span> <span class='fn'>_SaveItems</span><span class='pn'>() {</span><br />
            <span class='kw'>var</span> <span class='vr'>t</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>csvTable</span> <span class='pn'>{</span> <span class='fn'>ColumnCount</span> <span class='op'>=</span> <span class='nr'>2</span> <span class='pn'>};</span><br />
            <span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>var</span> <span class='vr'>v</span> <span class='kw'>in</span> <span class='vr'>a</span><span class='pn'>)</span> <span class='vr'>t</span><span class='op'>.</span><span class='fn'>AddRow</span><span class='pn'>(</span><span class='vr'>v</span><span class='op'>.</span><span class='fn'>Name</span><span class='pn'>,</span> <span class='vr'>v</span><span class='op'>.</span><span class='vr'>EncryptedPassword</span><span class='pn'>);</span><br />
            <span class='fn'>_SaveCsv</span><span class='pn'>(</span><span class='vr'>t</span><span class='pn'>);<br />
        }</span><br />
        <br />
        <span class='kw'>void</span> <span class='fn'>_CreateMenu</span><span class='pn'>() {</span><br />
            <span class='cm'>//File</span><br />
            <span class='kw'>var</span> <span class='vr'>mFile</span> <span class='op'>=</span> <span class='fn'>_TopItem</span><span class='pn'>(</span><span class='st'>"_Menu"</span><span class='pn'>);</span><br />
            <span class='fn'>_Item</span><span class='pn'>(</span><span class='vr'>mFile</span><span class='pn'>,</span> <span class='st'>"Change key..."</span><span class='pn'>,</span> <span class='vr'>o</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
                <span class='vr'>g</span><span class='op'>.</span><span class='fn'>CancelEdit</span><span class='pn'>();</span><br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>a</span><span class='op'>.</span><span class='fn'>Any</span><span class='pn'>(</span><span class='vr'>o</span> <span class='op'>=&gt;</span> <span class='vr'>o</span><span class='op'>.</span><span class='fn'>Password</span> <span class='op'>==</span> <span class='st'>"&lt;error&gt;"</span><span class='pn'>)) {</span> <span class='cm'>//unlikely</span><br />
                    <span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>showError</span><span class='pn'>(</span><span class='st'>"Cannot change the key"</span><span class='pn'>,</span> <span class='st'>"Failed to decrypt some passwords (&lt;error&gt;), therefore cannot encrypt them with a new key.</span><span class='se'>&#92;n</span><span class='st'>Edit or delete the &lt;error&gt; passwords."</span><span class='pn'>);</span><br />
                    <span class='kw'>return</span><span class='pn'>;<br />
                }</span><br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='fn'>_ChangeKey</span><span class='pn'>(</span><span class='kw'>ref</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='vr'>a</span><span class='pn'>))</span><br />
                    <span class='fn'>_SaveItems</span><span class='pn'>();<br />
            });</span><br />
            <span class='cm'>//TODO2: UI help.</span><br />
            <br />
            <span class='tp'>MenuItem</span> <span class='fn'>_Item</span><span class='pn'>(</span><span class='tp'>ItemsControl</span> <span class='vr'>parent</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='tp'>Action</span><span class='pn'>&lt;</span><span class='tp'>MenuItem</span><span class='pn'>&gt;</span> <span class='vr'>click</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>tooltip</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>) {</span><br />
                <span class='kw'>var</span> <span class='vr'>mi</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>MenuItem</span> <span class='pn'>{</span> <span class='fn'>Header</span> <span class='op'>=</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='fn'>ToolTip</span> <span class='op'>=</span> <span class='vr'>tooltip</span> <span class='pn'>};</span><br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>click</span> <span class='op'>!=</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='vr'>mi</span><span class='op'>.</span><span class='fn'>Click</span> <span class='op'>+=</span> <span class='pn'>(</span><span class='vr'>sender</span><span class='pn'>,</span> <span class='vr'>_</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='vr'>click</span><span class='pn'>(</span><span class='vr'>sender</span> <span class='kw'>as</span> <span class='tp'>MenuItem</span><span class='pn'>);</span><br />
                <span class='vr'>parent</span><span class='op'>.</span><span class='fn'>Items</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>mi</span><span class='pn'>);</span><br />
                <span class='kw'>return</span> <span class='vr'>mi</span><span class='pn'>;<br />
            }</span><br />
            <br />
            <span class='tp'>MenuItem</span> <span class='fn'>_TopItem</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='fn'>_Item</span><span class='pn'>(</span><span class='vr'>menu</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>);</span><br />
            <br />
            <span class='cm'>//void _Separator(ItemsControl parent) { parent.Items.Add(new Separator()); }</span><br />
        <span class='pn'>}<br />
    }</span><br />
    <br />
    <span class='kw'>record class</span> <span class='tp'>_Item</span> <span class='pn'>{</span><br />
        <span class='kw'>string</span> <span class='vr'>_pwDisplay</span><span class='pn'>;</span><br />
        <br />
        <span class='kw'>public</span> <span class='tp'>_Item</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>encPw</span><span class='pn'>,</span> <span class='kw'>byte</span><span class='pn'>[]</span> <span class='vr'>key</span><span class='pn'>) {</span><br />
            <span class='fn'>Name</span> <span class='op'>=</span> <span class='vr'>name</span><span class='pn'>;</span><br />
            <span class='vr'>EncryptedPassword</span> <span class='op'>=</span> <span class='vr'>encPw</span><span class='pn'>;</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>encPw</span><span class='op'>.</span><span class='fn'>NE</span><span class='pn'>())</span> <span class='vr'>_pwDisplay</span> <span class='op'>=</span> <span class='fn'>_Decrypt</span><span class='pn'>(</span><span class='vr'>EncryptedPassword</span><span class='pn'>,</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='kw'>out _</span><span class='pn'>)</span> <span class='op'>?</span> <span class='st'>"•…"</span> <span class='op'>:</span> <span class='st'>"&lt;error&gt;"</span><span class='pn'>;<br />
        }</span><br />
        <span class='kw'>public</span> <span class='tp'>_Item</span><span class='pn'>() { }</span><br />
        <br />
        <span class='kw'>public string</span> <span class='fn'>Name</span> <span class='pn'>{</span> <span class='kw'>get</span><span class='pn'>;</span> <span class='kw'>set</span><span class='pn'>; }</span><br />
        <span class='kw'>public string</span> <span class='vr'>EncryptedPassword</span><span class='pn'>;</span><br />
        <br />
        <span class='kw'>public string</span> <span class='fn'>Password</span> <span class='pn'>{</span><br />
            <span class='kw'>get</span> <span class='op'>=&gt;</span> <span class='vr'>_pwDisplay</span><span class='pn'>;</span><br />
            <span class='kw'>set</span> <span class='op'>=&gt;</span> <span class='fn'>SetNewPassword</span><span class='pn'>(</span><span class='kw'>value</span><span class='pn'>);<br />
        }</span><br />
        <br />
        <span class='kw'>public void</span> <span class='fn'>SetNewPassword</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>s</span><span class='pn'>,</span> <span class='kw'>byte</span><span class='pn'>[]</span> <span class='vr'>key</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>) {</span><br />
            <span class='vr'>EncryptedPassword</span> <span class='op'>=</span> <span class='fn'>_Encrypt</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>key</span><span class='pn'>);</span><br />
            <span class='vr'>_pwDisplay</span> <span class='op'>=</span> <span class='vr'>s</span><span class='op'>.</span><span class='fn'>NE</span><span class='pn'>()</span> <span class='op'>?</span> <span class='kw'>null</span> <span class='op'>:</span> <span class='st'>"•…"</span><span class='pn'>;<br />
        }<br />
    }<br />
}</span></code></div></div>]]></description>
			<content:encoded><![CDATA[Examples.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "Passwords examples.cs"<br />
<span class='cm'>/*/ c &#92;Passwords.cs; /*/<br />
<br />
//get password named "test".<br />
//    The first time it shows a password input dialog.<br />
//    Also shows a key input dialog if a key for this computer/user still not saved.<br />
//    To avoid password/key input dialogs at an inconvenient time, call any Passwords method at a convenient time. Or run file "Passwords.cs" (it calls Passwords.ShowManagerUI).</span><br />
<span class='kw'>string</span> <span class='vr'>pw</span> <span class='op'>=</span> Passwords<span class='op'>.</span>Get<span class='pn'>(</span><span class='st'>"test"</span><span class='pn'>);</span><br />
<span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='vr'>pw</span><span class='pn'>);</span><br />
<br />
<span class='cm'>//save new password with name "test2", or change password named "test2"</span><br />
Passwords<span class='op'>.</span>Save<span class='pn'>(</span><span class='st'>"test2"</span><span class='pn'>,</span> <span class='st'>"99564Jk-"</span><span class='pn'>);</span><br />
<br />
<span class='cm'>//delete password named "test3" if exists</span><br />
Passwords<span class='op'>.</span>Delete<span class='pn'>(</span><span class='st'>"test3"</span><span class='pn'>);</span><br />
<br />
<span class='cm'>//show the password manager dialog. Or you can run the "Passwords" file.</span><br />
Passwords<span class='op'>.</span>ShowManagerUI<span class='pn'>();</span></code></div></div><br />
Class.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// class "Passwords.cs"<br />
<span class='cm'>/*/ role miniProgram; define SCRIPT; /*/</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Security</span><span class='op'>.</span><span class='ns'>Cryptography</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Controls</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Data</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Media</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Input</span><span class='pn'>;</span><br />
<br />
<span class='pd'>#if</span> SCRIPT<br />
<span class='ex'>//Passwords.Save("saved", "test");<br />
//Passwords.Delete("Saved");<br />
//print.it(Passwords.Get("saved"));<br />
<br />
Passwords.ShowManagerUI();<br />
</span><span class='pd'>#endif</span><br />
<br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
///</span><span class='xd'> Simple password manager for scripts.</span><br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
///</span><span class='xd'> </span><span class='xt'>&lt;remarks&gt;<br />
///</span><span class='xd'> In scripts, you often need to use passwords, API keys, and other kinds of credentials. This class helps to protect them. In script you use password names, not passwords directly; passwords are saved encrypted.</span><br />
<span class='xt'>///</span><span class='xd'> To encrypt passwords uses a user-provided encryption key (aka master password). Saves the key encrypted using Windows data protection API, which makes it undecryptable on other computers and user accounts.</span><br />
<span class='xt'>///</span><span class='xd'> Saves everything in </span><span class='xt'>&lt;see cref="</span><span class='fn'>Folder</span><span class='xt'>"/&gt;</span><span class='xd'>.</span><br />
<span class='xt'>///</span><span class='xd'> Can be used in portable apps too; more info in </span><span class='xt'>&lt;see cref="</span><span class='fn'>Folder</span><span class='xt'>"/&gt;</span><span class='xd'> remarks.</span><br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;para&gt;<br />
///</span><span class='xd'> Obviously, passwords managed by this class are not completely secure. And less secure than when using a password manager software.</span><br />
<span class='xt'>///</span><span class='xd'> To use in scripts conveniently, this class saves encrypted passwords (and key) so you don't have to enter them manually each time. They can be decrypted only on the same computer/account.</span><br />
<span class='xt'>///</span><span class='xd'> A determined hacker can steal the passwords if he can access that computer/account and somehow know about this class and find the saved data and this code (or just run this code). It's unlikely, but possible.</span><br />
<span class='xt'>///</span><span class='xd'> Also this class does not encrypt passwords in memory, and does not clear the password string memory.</span><br />
<span class='xt'>///</span><span class='xd'> But this class gives full protection from leaking passwords through script sharing or github etc.</span><br />
<span class='xt'>///</span><span class='xd'> Another benefit: when you change a password, will not need to find and update it in each script that uses the password. Instead you can run this script to show the password manager UI and update the password there.</span><br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/para&gt;<br />
///</span><span class='xd'> </span><span class='xt'>&lt;para&gt;<br />
///</span><span class='xd'> To avoid password/key input dialogs at an inconvenient time, call any </span><span class='xt'>&lt;b&gt;</span><span class='xd'>Passwords</span><span class='xt'>&lt;/b&gt;</span><span class='xd'> method at a convenient time. Or run this file (it calls </span><span class='xt'>&lt;b&gt;</span><span class='xd'>Passwords.ShowManagerUI</span><span class='xt'>&lt;/b&gt;</span><span class='xd'>).</span><br />
<span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/para&gt;<br />
///</span><span class='xd'> </span><span class='xt'>&lt;/remarks&gt;</span><br />
<span class='kw'>public static class</span> <span class='tp'>Passwords</span> <span class='pn'>{</span><br />
    <span class='pd'>#region</span> settings. You can edit this code.<br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Full path of the password manager data folder.</span><br />
    <span class='xt'>///</span><span class='xd'> Default: </span><span class='xt'>&lt;c&gt;</span><span class='xd'>folders.ThisAppDataLocal + "Pm"</span><span class='xt'>&lt;/c&gt;</span><span class='xd'>.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;remarks&gt;<br />
    ///</span><span class='xd'> In portable LA the folder is in its drive. The portable setup tool does not copy/update it, unless the path is changed like </span><span class='xt'>&lt;c&gt;</span><span class='xd'>folders.ThisAppDataRoaming + "Pm"</span><span class='xt'>&lt;/c&gt;</span><span class='xd'> (the tool copies the roaming folder).</span><br />
    <span class='xt'>///</span><span class='xd'> Note: the </span><span class='xt'>&lt;b&gt;</span><span class='xd'>folders.ThisAppDataLocal/Roaming</span><span class='xt'>&lt;/b&gt;</span><span class='xd'> path is different if script role is </span><span class='xt'>&lt;b&gt;</span><span class='xd'>editorExtension</span><span class='xt'>&lt;/b&gt;</span><span class='xd'>.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/remarks&gt;</span><br />
    <span class='kw'>public static string</span> <span class='fn'>Folder</span> <span class='pn'>{</span> <span class='kw'>get</span><span class='pn'>;</span> <span class='kw'>set</span><span class='pn'>; }</span> <span class='op'>=</span> <span class='tp'>folders</span><span class='op'>.</span><span class='fn'>ThisAppDataLocal +</span> <span class='st'>"Pm"</span><span class='pn'>;</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Parameter </span><span class='xt'>&lt;i&gt;</span><span class='xd'>optionalEntropy</span><span class='xt'>&lt;/i&gt;</span><span class='xd'> of </span><span class='xt'>&lt;see cref="</span><span class='tp'>ProtectedData</span><span class='xt'>"/&gt;</span><span class='xd'> class functions.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;remarks&gt;<br />
    ///</span><span class='xd'> Used when encrypting and decrypting the password encryption key. The key cannot be decrypted with different entropy than when encrypting it.</span><br />
    <span class='xt'>///</span><span class='xd'> Recommended: replace the default value with a unique value of any length. It adds some more security and separates your passwords from passwords of other apps that use this class and same folder.</span><br />
    <span class='xt'>///</span><span class='xd'> The names of password data and key files depend on this value. It allows multiple apps (or scripts) to save their passwords separately. Just change the default value used by your app.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/remarks&gt;</span><br />
    <span class='kw'>static byte</span><span class='pn'>[]</span> <span class='vr'>_entropy</span> <span class='op'>=</span> <span class='pn'>[</span><span class='nr'>39</span><span class='pn'>,</span> <span class='nr'>212</span><span class='pn'>,</span> <span class='nr'>196</span><span class='pn'>,</span> <span class='nr'>74</span><span class='pn'>];</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Edit this if want to customize the password input dialog. For example localize the text.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>password</span><span class='xt'>"&gt;&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>save</span><span class='xt'>"&gt;</span><span class='xd'>true to save the password.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;&lt;c&gt;</span><span class='xd'>true</span><span class='xt'>&lt;/c&gt;</span><span class='xd'> if OK.</span><span class='xt'>&lt;/returns&gt;</span><br />
    <span class='kw'>static bool</span> <span class='fn'>_PasswordInputDialog</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>out string</span> <span class='vr'>password</span><span class='pn'>,</span> <span class='kw'>out bool</span> <span class='vr'>save</span><span class='pn'>) {</span><br />
        <span class='tp'>DControls</span> <span class='vr'>c</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>() {</span> <span class='fn'>Checkbox</span> <span class='op'>=</span> <span class='st'>"Save"</span><span class='pn'>,</span> <span class='fn'>IsChecked</span> <span class='op'>=</span> <span class='kw'>true</span> <span class='pn'>};</span><br />
        <span class='kw'>bool</span> <span class='vr'>ok</span> <span class='op'>=</span> <span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>showInput</span><span class='pn'>(</span><span class='kw'>out</span> <span class='vr'>password</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>,</span> <span class='st'>"Password for "</span> <span class='op'>+</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='tp'>DEdit</span><span class='op'>.</span><span class='cn'>Password</span><span class='pn'>,</span> <span class='vr'>controls</span><span class='pn'>:</span> <span class='vr'>c</span><span class='pn'>);</span><br />
        <span class='vr'>save</span> <span class='op'>=</span> <span class='vr'>c</span><span class='op'>.</span><span class='fn'>IsChecked</span><span class='pn'>;</span><br />
        <span class='kw'>return</span> <span class='vr'>ok</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Edit this if want to customize the key input dialog. For example localize the text.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>key</span><span class='xt'>"&gt;&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>newKey</span><span class='xt'>"&gt;</span><span class='xd'>If </span><span class='xt'>&lt;c&gt;</span><span class='xd'>true</span><span class='xt'>&lt;/c&gt;</span><span class='xd'>, the user can enter a new key (there are no saved passwords). If </span><span class='xt'>&lt;c&gt;</span><span class='xd'>false</span><span class='xt'>&lt;/c&gt;</span><span class='xd'>, the user must enter the same key.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;&lt;c&gt;</span><span class='xd'>true</span><span class='xt'>&lt;/c&gt;</span><span class='xd'> if OK.</span><span class='xt'>&lt;/returns&gt;</span><br />
    <span class='kw'>static bool</span> <span class='fn'>_KeyInputDialog</span><span class='pn'>(</span><span class='kw'>out string</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='kw'>bool</span> <span class='vr'>newKey</span><span class='pn'>) {</span><br />
        <span class='kw'>string</span> <span class='vr'>info</span> <span class='op'>=</span> <span class='vr'>newKey</span><br />
            <span class='op'>?</span> <span class='st'>"Please enter a key (any text) that will be used to encrypt saved passwords.</span><span class='se'>&#92;n</span><span class='st'>It will be saved encrypted for this computer/user (undecryptable elsewhere)."</span><br />
            <span class='op'>:</span> <span class='st'>"Please enter the same key that was used to encrypt the saved passwords."</span><span class='pn'>;</span><br />
        <span class='kw'>return</span> <span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>showInput</span><span class='pn'>(</span><span class='kw'>out</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='st'>"Password manager key"</span><span class='pn'>,</span> <span class='vr'>info</span><span class='pn'>,</span> <span class='tp'>DEdit</span><span class='op'>.</span><span class='cn'>Password</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Edit this if want to customize the "Delete all saved passwords?" dialog. For example localize the text.</span><br />
    <span class='xt'>///</span><span class='xd'> Called when </span><span class='xt'>&lt;b&gt;</span><span class='xd'>_KeyInputDialog</span><span class='xt'>&lt;/b&gt;</span><span class='xd'> returns </span><span class='xt'>&lt;c&gt;</span><span class='xd'>false</span><span class='xt'>&lt;/c&gt;</span><span class='xd'> when </span><span class='xt'>&lt;i&gt;</span><span class='xd'>newKey</span><span class='xt'>&lt;/i&gt;</span><span class='xd'> </span><span class='xt'>&lt;c&gt;</span><span class='xd'>false</span><span class='xt'>&lt;/c&gt;</span><span class='xd'>. </span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;&lt;c&gt;</span><span class='xd'>true</span><span class='xt'>&lt;/c&gt;</span><span class='xd'> to delete.</span><span class='xt'>&lt;/returns&gt;</span><br />
    <span class='kw'>static bool</span> <span class='fn'>_DeletePasswordsDialog</span><span class='pn'>() {</span><br />
        <span class='kw'>return</span> <span class='nr'>1</span> <span class='op'>==</span> <span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='st'>"Delete all saved passwords?"</span><span class='pn'>,</span><br />
            <span class='st'>"Saved passwords cannot be decrypted without the key.</span><span class='se'>&#92;n</span><span class='st'>If you have lost the key, you can delete saved passwords and set a new key.</span><span class='se'>&#92;n</span><span class='st'>Do you want to delete all saved passwords?"</span><span class='pn'>,</span><br />
            <span class='st'>"1 Delete|2 Cancel"</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='tp'>DIcon</span><span class='op'>.</span><span class='cn'>Warning</span><span class='pn'>,</span> <span class='vr'>defaultButton</span><span class='pn'>:</span> <span class='nr'>2</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='pd'>#endregion</span><br />
    <br />
    <span class='kw'>static string</span> <span class='fn'>_PasswordsFile</span> <span class='op'>=&gt;</span> <span class='fn'>Folder</span> <span class='op'>+</span> <span class='st'>"</span><span class='se'>&#92;&#92;</span><span class='st'>"</span> <span class='op'>+</span> <span class='pn'>(</span><span class='vr'>_filename</span> <span class='op'>??=</span> <span class='tp'>Hash</span><span class='op'>.</span><span class='fn'>MD5</span><span class='pn'>(</span><span class='vr'>_entropy</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>())</span> <span class='op'>+</span> <span class='st'>".csv"</span><span class='pn'>;</span><br />
    <span class='kw'>static string</span> <span class='vr'>_filename</span><span class='pn'>;</span><br />
    <br />
    <span class='kw'>static string</span> <span class='fn'>_KeyFile</span> <span class='op'>=&gt;</span> <span class='fn'>_PasswordsFile</span> <span class='op'>+</span> <span class='st'>".key"</span><span class='pn'>;</span><br />
    <br />
    <span class='kw'>static</span> <span class='tp'>csvTable</span> <span class='fn'>_LoadCsv</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>file</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>) {</span><br />
        <span class='vr'>file</span> <span class='op'>??=</span> <span class='fn'>_PasswordsFile</span><span class='pn'>;</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>exists</span><span class='pn'>(</span><span class='vr'>file</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>File</span><span class='pn'>)</span> <span class='kw'>return new</span><span class='pn'>() {</span> <span class='fn'>ColumnCount</span> <span class='op'>=</span> <span class='nr'>2</span> <span class='pn'>};</span><br />
        <span class='kw'>var</span> <span class='vr'>t</span> <span class='op'>=</span> <span class='tp'>csvTable</span><span class='op'>.</span><span class='fn'>load</span><span class='pn'>(</span><span class='vr'>file</span><span class='pn'>);</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>t</span><span class='op'>.</span><span class='fn'>ColumnCount</span> <span class='op'>&lt;</span> <span class='nr'>2</span><span class='pn'>)</span> <span class='vr'>t</span><span class='op'>.</span><span class='fn'>ColumnCount</span> <span class='op'>=</span> <span class='nr'>2</span><span class='pn'>;</span><br />
        <span class='kw'>return</span> <span class='vr'>t</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='kw'>static</span> <span class='tp'>Dictionary</span><span class='pn'>&lt;</span><span class='kw'>string</span><span class='pn'>,</span> <span class='kw'>string</span><span class='pn'>&gt;</span> <span class='fn'>_LoadDict</span><span class='pn'>()</span> <span class='op'>=&gt;</span> <span class='fn'>_LoadCsv</span><span class='pn'>()</span><span class='op'>.</span><span class='fn'>ToDictionary</span><span class='pn'>(</span><span class='kw'>true</span><span class='pn'>,</span> <span class='kw'>true</span><span class='pn'>);</span><br />
    <br />
    <span class='kw'>static void</span> <span class='fn'>_SaveCsv</span><span class='pn'>(</span><span class='tp'>csvTable</span> <span class='vr'>t</span><span class='pn'>) {</span><br />
        <span class='vr'>t</span><span class='op'>.</span><span class='fn'>Save</span><span class='pn'>(</span><span class='fn'>_PasswordsFile</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='kw'>static void</span> <span class='fn'>_SaveDict</span><span class='pn'>(</span><span class='tp'>Dictionary</span><span class='pn'>&lt;</span><span class='kw'>string</span><span class='pn'>,</span> <span class='kw'>string</span><span class='pn'>&gt;</span> <span class='vr'>d</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='fn'>_SaveCsv</span><span class='pn'>(</span><span class='tp'>csvTable</span><span class='op'>.</span><span class='fn'>fromDictionary</span><span class='pn'>(</span><span class='vr'>d</span><span class='pn'>));</span><br />
    <br />
    <span class='kw'>static byte</span><span class='pn'>[]</span> <span class='fn'>_GetKey</span><span class='pn'>() {</span><br />
        <span class='kw'>try</span> <span class='pn'>{</span><br />
            <span class='kw'>var</span> <span class='vr'>data</span> <span class='op'>=</span> <span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>loadBytes</span><span class='pn'>(</span><span class='fn'>_KeyFile</span><span class='pn'>);</span><br />
            <span class='kw'>var</span> <span class='vr'>r1</span> <span class='op'>=</span> <span class='tp'>ProtectedData</span><span class='op'>.</span><span class='fn'>Unprotect</span><span class='pn'>(</span><span class='vr'>data</span><span class='pn'>,</span> <span class='vr'>_entropy</span><span class='pn'>,</span> <span class='tp'>DataProtectionScope</span><span class='op'>.</span><span class='cn'>CurrentUser</span><span class='pn'>);</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>r1</span><span class='op'>.</span><span class='fn'>Length</span> <span class='op'>==</span> <span class='nr'>16</span><span class='pn'>)</span> <span class='kw'>return</span> <span class='vr'>r1</span><span class='pn'>;<br />
        }</span><br />
        <span class='kw'>catch</span> <span class='pn'>{ }</span><br />
        <br />
        <span class='kw'>return</span> <span class='fn'>_SetKey</span><span class='pn'>();<br />
    }</span><br />
    <br />
    <span class='kw'>static bool</span> <span class='fn'>_KeyInputDialog2</span><span class='pn'>(</span><span class='kw'>out byte</span><span class='pn'>[]</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='kw'>bool</span> <span class='vr'>newKey</span><span class='pn'>) {</span><br />
        <span class='vr'>key</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>;</span><br />
        <span class='kw'>string</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='st'>""</span><span class='pn'>;</span><br />
        <span class='kw'>while</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Length</span> <span class='op'>&lt;</span> <span class='nr'>1</span><span class='pn'>) {</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='fn'>_KeyInputDialog</span><span class='pn'>(</span><span class='kw'>out</span> <span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>newKey</span><span class='pn'>))</span> <span class='kw'>return false</span><span class='pn'>;<br />
        }</span><br />
        <br />
        <span class='vr'>key</span> <span class='op'>=</span> <span class='tp'>Hash</span><span class='op'>.</span><span class='fn'>MD5</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>ToArray</span><span class='pn'>();</span><br />
        <span class='kw'>return true</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='kw'>static byte</span><span class='pn'>[]</span> <span class='fn'>_SetKey</span><span class='pn'>() {</span><br />
        <span class='kw'>var</span> <span class='vr'>t</span> <span class='op'>=</span> <span class='fn'>_LoadCsv</span><span class='pn'>();</span><br />
        <span class='kw'>bool</span> <span class='vr'>newKey</span> <span class='op'>=</span> <span class='vr'>t</span><span class='op'>.</span><span class='fn'>RowCount</span> <span class='op'>==</span> <span class='nr'>0</span><span class='pn'>;</span><br />
        <span class='lb'>g1</span><span class='pn'>:</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='fn'>_KeyInputDialog2</span><span class='pn'>(</span><span class='kw'>out var</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='vr'>newKey</span><span class='pn'>)) {</span> <span class='cm'>//canceled</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>newKey</span> <span class='op'>|| !</span><span class='fn'>_DeletePasswordsDialog</span><span class='pn'>())</span> <span class='kw'>throw new</span> <span class='tp'>OperationCanceledException</span><span class='pn'>();</span><br />
            <span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>delete</span><span class='pn'>(</span><span class='fn'>_PasswordsFile</span><span class='pn'>);</span><br />
            <span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>delete</span><span class='pn'>(</span><span class='fn'>_KeyFile</span><span class='pn'>);</span><br />
            <span class='vr'>newKey</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>;</span><br />
            <span class='kw'>goto</span> <span class='lb'>g1</span><span class='pn'>;<br />
        }</span><br />
        <br />
        <span class='cm'>//is data encrypted with this key?</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>newKey</span> <span class='op'>&amp;&amp;</span> <span class='vr'>t</span><span class='op'>.</span><span class='fn'>Rows</span><span class='op'>.</span><span class='fn'>All</span><span class='pn'>(</span><span class='vr'>a</span> <span class='op'>=&gt; !</span><span class='fn'>_Decrypt</span><span class='pn'>(</span><span class='vr'>a</span><span class='pn'>[</span><span class='nr'>1</span><span class='pn'>],</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='kw'>out _</span><span class='pn'>))) {</span> <span class='cm'>//if fails to decrypt all passwords, the key is incorrect</span><br />
            <span class='nr'>500</span><span class='op'>.</span><span class='fn'>ms</span><span class='pn'>();</span><br />
            <span class='kw'>goto</span> <span class='lb'>g1</span><span class='pn'>;<br />
        }</span><br />
        <br />
        <span class='kw'>var</span> <span class='vr'>p</span> <span class='op'>=</span> <span class='tp'>ProtectedData</span><span class='op'>.</span><span class='fn'>Protect</span><span class='pn'>(</span><span class='vr'>key</span><span class='pn'>,</span> <span class='vr'>_entropy</span><span class='pn'>,</span> <span class='tp'>DataProtectionScope</span><span class='op'>.</span><span class='cn'>CurrentUser</span><span class='pn'>);</span><br />
        <span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>saveBytes</span><span class='pn'>(</span><span class='fn'>_KeyFile</span><span class='pn'>,</span> <span class='vr'>p</span><span class='pn'>);</span><br />
        <span class='kw'>return</span> <span class='vr'>key</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='kw'>static bool</span> <span class='fn'>_ChangeKey</span><span class='pn'>(</span><span class='kw'>ref byte</span><span class='pn'>[]</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='tp'>List</span><span class='pn'>&lt;</span><span class='tp'>_Item</span><span class='pn'>&gt;</span> <span class='vr'>a</span><span class='pn'>) {</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='fn'>_KeyInputDialog2</span><span class='pn'>(</span><span class='kw'>out var</span> <span class='vr'>key2</span><span class='pn'>,</span> <span class='vr'>newKey</span><span class='pn'>:</span> <span class='kw'>true</span><span class='pn'>))</span> <span class='kw'>return false</span><span class='pn'>;</span><br />
        <br />
        <span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>var</span> <span class='vr'>v</span> <span class='kw'>in</span> <span class='vr'>a</span><span class='pn'>) {</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='fn'>_Decrypt</span><span class='pn'>(</span><span class='vr'>v</span><span class='op'>.</span><span class='vr'>EncryptedPassword</span><span class='pn'>,</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='kw'>out string</span> <span class='vr'>pw</span><span class='pn'>))</span><br />
                <span class='vr'>v</span><span class='op'>.</span><span class='fn'>SetNewPassword</span><span class='pn'>(</span><span class='vr'>pw</span><span class='pn'>,</span> <span class='vr'>key2</span><span class='pn'>);<br />
        }</span><br />
        <br />
        <span class='kw'>var</span> <span class='vr'>p</span> <span class='op'>=</span> <span class='tp'>ProtectedData</span><span class='op'>.</span><span class='fn'>Protect</span><span class='pn'>(</span><span class='vr'>key2</span><span class='pn'>,</span> <span class='vr'>_entropy</span><span class='pn'>,</span> <span class='tp'>DataProtectionScope</span><span class='op'>.</span><span class='cn'>CurrentUser</span><span class='pn'>);</span><br />
        <span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>saveBytes</span><span class='pn'>(</span><span class='fn'>_KeyFile</span><span class='pn'>,</span> <span class='vr'>p</span><span class='pn'>);</span><br />
        <span class='vr'>key</span> <span class='op'>=</span> <span class='vr'>key2</span><span class='pn'>;</span><br />
        <span class='kw'>return true</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='kw'>static string</span> <span class='fn'>_Encrypt</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>s</span><span class='pn'>,</span> <span class='kw'>byte</span><span class='pn'>[]</span> <span class='vr'>key</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>) {</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>NE</span><span class='pn'>())</span> <span class='kw'>return</span> <span class='st'>""</span><span class='pn'>;</span><br />
        <span class='kw'>return</span> <span class='tp'>Convert2</span><span class='op'>.</span><span class='fn'>AesEncryptS</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>key</span> <span class='op'>??</span> <span class='fn'>_GetKey</span><span class='pn'>());<br />
    }</span><br />
    <br />
    <span class='kw'>static bool</span> <span class='fn'>_Decrypt</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>s</span><span class='pn'>,</span> <span class='kw'>byte</span><span class='pn'>[]</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='kw'>out string</span> <span class='vr'>r</span><span class='pn'>) {</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>NE</span><span class='pn'>())</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='st'>""</span><span class='pn'>;</span><br />
        <span class='kw'>else</span> <span class='pn'>{</span><br />
            <span class='kw'>try</span> <span class='pn'>{</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='tp'>Convert2</span><span class='op'>.</span><span class='fn'>AesDecryptS</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>key</span><span class='pn'>); }</span><br />
            <span class='kw'>catch</span> <span class='pn'>{</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>;</span> <span class='kw'>return false</span><span class='pn'>; }<br />
        }</span><br />
        <span class='kw'>return true</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='cm'>//TODO: in name, replace substring &lt;user&gt; with user SID.</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Encrypts </span><span class='xt'>&lt;i&gt;</span><span class='xd'>password</span><span class='xt'>&lt;/i&gt;</span><span class='xd'> and saves in the passwords file. Can add or replace.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>A name for the password. Case-insensitive.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>password</span><span class='xt'>"&gt;&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>OperationCanceledException</span><span class='xt'>"&gt;</span><span class='xd'>Key input dialog canceled.</span><span class='xt'>&lt;/exception&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>Exception</span><span class='xt'>"&gt;</span><span class='xd'>Failed to load (if exists) or save the passwords file.</span><span class='xt'>&lt;/exception&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>Save</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>password</span><span class='pn'>) {</span><br />
        <span class='fn'>_Save</span><span class='pn'>(</span><span class='fn'>_LoadDict</span><span class='pn'>(),</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>password</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='kw'>static void</span> <span class='fn'>_Save</span><span class='pn'>(</span><span class='tp'>Dictionary</span><span class='pn'>&lt;</span><span class='kw'>string</span><span class='pn'>,</span> <span class='kw'>string</span><span class='pn'>&gt;</span> <span class='vr'>d</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>password</span><span class='pn'>) {</span><br />
        <span class='vr'>d</span><span class='pn'>[</span><span class='vr'>name</span><span class='pn'>]</span> <span class='op'>=</span> <span class='fn'>_Encrypt</span><span class='pn'>(</span><span class='vr'>password</span><span class='pn'>);</span><br />
        <span class='fn'>_SaveDict</span><span class='pn'>(</span><span class='vr'>d</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Deletes one or more passwords from the passwords file.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>names</span><span class='xt'>"&gt;</span><span class='xd'>Password names. Case-insensitive.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>Exception</span><span class='xt'>"&gt;</span><span class='xd'>Failed to load (if exists) or save the passwords file.</span><span class='xt'>&lt;/exception&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>Delete</span><span class='pn'>(</span><span class='kw'>params string</span><span class='pn'>[]</span> <span class='vr'>names</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>d</span> <span class='op'>=</span> <span class='fn'>_LoadDict</span><span class='pn'>();</span><br />
        <span class='kw'>bool</span> <span class='vr'>deleted</span> <span class='op'>=</span> <span class='kw'>false</span><span class='pn'>;</span><br />
        <span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>var</span> <span class='vr'>v</span> <span class='kw'>in</span> <span class='vr'>names</span><span class='pn'>)</span> <span class='vr'>deleted</span> <span class='op'>|=</span> <span class='vr'>d</span><span class='op'>.</span><span class='fn'>Remove</span><span class='pn'>(</span><span class='vr'>v</span><span class='pn'>);</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>deleted</span><span class='pn'>)</span> <span class='fn'>_SaveDict</span><span class='pn'>(</span><span class='vr'>d</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Gets a password from the passwords file and decrypts.</span><br />
    <span class='xt'>///</span><span class='xd'> If not found, shows a password input dialog and calls </span><span class='xt'>&lt;see cref="</span><span class='fn'>Save</span><span class='xt'>"/&gt;</span><span class='xd'> (optionally).</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;param name="</span><span class='vr'>name</span><span class='xt'>"&gt;</span><span class='xd'>The password's name. Case-insensitive.</span><span class='xt'>&lt;/param&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;returns&gt;</span><span class='xd'>Password.</span><span class='xt'>&lt;/returns&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>OperationCanceledException</span><span class='xt'>"&gt;</span><span class='xd'>Password input dialog canceled. Or key input dialog canceled.</span><span class='xt'>&lt;/exception&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>Exception</span><span class='xt'>"&gt;</span><span class='xd'>Failed to load (if exists) or save the passwords file.</span><span class='xt'>&lt;/exception&gt;</span><br />
    <span class='kw'>public static string</span> <span class='fn'>Get</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>d</span> <span class='op'>=</span> <span class='fn'>_LoadDict</span><span class='pn'>();</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>d</span><span class='op'>.</span><span class='fn'>TryGetValue</span><span class='pn'>(</span><span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>out var</span> <span class='vr'>s</span><span class='pn'>)) {</span><br />
            <span class='kw'>var</span> <span class='vr'>key</span> <span class='op'>=</span> <span class='fn'>_GetKey</span><span class='pn'>();</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='fn'>_Decrypt</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='kw'>out var</span> <span class='vr'>r</span><span class='pn'>))</span> <span class='kw'>return</span> <span class='vr'>r</span><span class='pn'>;<br />
        }</span><br />
        <br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='fn'>_PasswordInputDialog</span><span class='pn'>(</span><span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>out</span> <span class='vr'>s</span><span class='pn'>,</span> <span class='kw'>out bool</span> <span class='vr'>save</span><span class='pn'>))</span> <span class='kw'>throw new</span> <span class='tp'>OperationCanceledException</span><span class='pn'>();</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>save</span><span class='pn'>)</span> <span class='fn'>_Save</span><span class='pn'>(</span><span class='vr'>d</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>s</span><span class='pn'>);</span><br />
        <span class='kw'>return</span> <span class='vr'>s</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Gets all names.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>Exception</span><span class='xt'>"&gt;</span><span class='xd'>Failed to load (if exists) the passwords file.</span><span class='xt'>&lt;/exception&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;example&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;code&gt;&lt;![CDATA[<br />
    /// //show menu with all password names<br />
    /// var a = Passwords.GetList();<br />
    /// if (a.Any()) {<br />
    ///     var m = new popupMenu("0cfd5f9c-8a23-4534-ad93-5af3ba8c2b41");<br />
    ///     foreach (var v in a) {<br />
    ///         m[v] = o =&gt; { print.it(Passwords.Get(v)); };<br />
    ///     }<br />
    ///     m.Show();<br />
    /// }<br />
    /// ]]]]]]&gt;&lt;![CDATA[&gt;&lt;![CDATA[&gt;&lt;/code&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;/example&gt;</span><br />
    <span class='kw'>public static string</span><span class='pn'>[]</span> <span class='fn'>GetList</span><span class='pn'>() {</span><br />
        <span class='kw'>var</span> <span class='vr'>d</span> <span class='op'>=</span> <span class='fn'>_LoadDict</span><span class='pn'>();</span><br />
        <span class='kw'>return</span> <span class='vr'>d</span><span class='op'>.</span><span class='fn'>Keys</span><span class='op'>.</span><span class='fn'>ToArray</span><span class='pn'>();<br />
    }</span><br />
    <br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;summary&gt;<br />
    ///</span><span class='xd'> Shows a dialog window with a data grid where you can add, delete and edit names and passwords.</span><br />
    <span class='xt'>///</span><span class='xd'> </span><span class='xt'>&lt;/summary&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>OperationCanceledException</span><span class='xt'>"&gt;</span><span class='xd'>Key input dialog canceled.</span><span class='xt'>&lt;/exception&gt;<br />
    ///</span><span class='xd'> </span><span class='xt'>&lt;exception cref="</span><span class='tp'>Exception</span><span class='xt'>"&gt;</span><span class='xd'>Failed to load (if exists) or save the passwords file.</span><span class='xt'>&lt;/exception&gt;</span><br />
    <span class='kw'>public static void</span> <span class='fn'>ShowManagerUI</span><span class='pn'>(</span><span class='tp'>Window</span> <span class='vr'>owner</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>b</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>wpfBuilder</span><span class='pn'>(</span><span class='st'>"Passwords"</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>WinSize</span><span class='pn'>(</span><span class='nr'>500</span><span class='pn'>,</span> <span class='nr'>500</span><span class='pn'>);</span><br />
        <span class='kw'>var</span> <span class='vr'>w</span> <span class='op'>=</span> <span class='vr'>b</span><span class='op'>.</span><span class='fn'>Window</span><span class='pn'>;</span><br />
        <span class='vr'>b</span><span class='op'>.</span><span class='fn'>R</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='kw'>out</span> <span class='tp'>Menu</span> <span class='vr'>menu</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Margin</span><span class='pn'>(</span><span class='st'>"T-2 B0"</span><span class='pn'>);</span><br />
        <span class='kw'>var</span> <span class='vr'>g</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>DataGrid</span> <span class='pn'>{</span><br />
            <span class='fn'>AutoGenerateColumns</span> <span class='op'>=</span> <span class='kw'>false</span><span class='pn'>,</span><br />
            <span class='fn'>CanUserAddRows</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>,</span><br />
            <span class='fn'>VerticalGridLinesBrush</span> <span class='op'>=</span> <span class='tp'>Brushes</span><span class='op'>.</span><span class='fn'>LightGray</span><span class='pn'>,</span><br />
            <span class='fn'>HorizontalGridLinesBrush</span> <span class='op'>=</span> <span class='tp'>Brushes</span><span class='op'>.</span><span class='fn'>LightGray</span><span class='pn'>,<br />
        };</span><br />
        <span class='vr'>b</span><span class='op'>.</span><span class='fn'>Row</span><span class='pn'>(</span><span class='op'>-</span><span class='nr'>1</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>g</span><span class='pn'>);</span><br />
        <span class='vr'>b</span><span class='op'>.</span><span class='fn'>R</span><span class='op'>.</span><span class='fn'>AddOkCancel</span><span class='pn'>();</span><br />
        <span class='vr'>b</span><span class='op'>.</span><span class='fn'>End</span><span class='pn'>();</span><br />
        <br />
        <span class='kw'>var</span> <span class='vr'>colName</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>DataGridTextColumn</span> <span class='pn'>{</span><br />
            <span class='fn'>Header</span> <span class='op'>=</span> <span class='st'>"Name"</span><span class='pn'>,</span><br />
            <span class='fn'>Binding</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>Binding</span><span class='pn'>(</span><span class='st'>"Name"</span><span class='pn'>),</span><br />
            <span class='fn'>Width</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>(</span><span class='nr'>1</span><span class='pn'>,</span> <span class='tp'>DataGridLengthUnitType</span><span class='op'>.</span><span class='cn'>Star</span><span class='pn'>),</span><br />
            <span class='fn'>CanUserReorder</span> <span class='op'>=</span> <span class='kw'>false</span><br />
        <span class='pn'>};</span><br />
        <span class='vr'>g</span><span class='op'>.</span><span class='fn'>Columns</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>colName</span><span class='pn'>);</span><br />
        <br />
        <span class='kw'>var</span> <span class='vr'>colPw</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>DataGridTextColumn</span> <span class='pn'>{</span><br />
            <span class='fn'>Header</span> <span class='op'>=</span> <span class='st'>"Password"</span><span class='pn'>,</span><br />
            <span class='fn'>Binding</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>Binding</span><span class='pn'>(</span><span class='st'>"Password"</span><span class='pn'>),</span><br />
            <span class='fn'>Width</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>(</span><span class='nr'>1</span><span class='pn'>,</span> <span class='tp'>DataGridLengthUnitType</span><span class='op'>.</span><span class='cn'>Star</span><span class='pn'>),</span><br />
            <span class='fn'>CanUserReorder</span> <span class='op'>=</span> <span class='kw'>false</span><br />
        <span class='pn'>};</span><br />
        <span class='vr'>g</span><span class='op'>.</span><span class='fn'>Columns</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>colPw</span><span class='pn'>);</span><br />
        <br />
        <span class='kw'>var</span> <span class='vr'>key</span> <span class='op'>=</span> <span class='fn'>_GetKey</span><span class='pn'>();</span> <span class='cm'>//OperationCanceledException</span><br />
        <span class='kw'>var</span> <span class='vr'>t</span> <span class='op'>=</span> <span class='fn'>_LoadCsv</span><span class='pn'>();</span><br />
        <span class='kw'>var</span> <span class='vr'>a</span> <span class='op'>=</span> <span class='vr'>t</span><span class='op'>.</span><span class='fn'>Rows</span><span class='op'>.</span><span class='fn'>Select</span><span class='pn'>(</span><span class='vr'>o</span> <span class='op'>=&gt;</span> <span class='kw'>new</span> <span class='tp'>_Item</span><span class='pn'>(</span><span class='vr'>o</span><span class='pn'>[</span><span class='nr'>0</span><span class='pn'>],</span> <span class='vr'>o</span><span class='pn'>[</span><span class='nr'>1</span><span class='pn'>],</span> <span class='vr'>key</span><span class='pn'>))</span><span class='op'>.</span><span class='fn'>ToList</span><span class='pn'>();</span><br />
        <span class='vr'>g</span><span class='op'>.</span><span class='fn'>ItemsSource</span> <span class='op'>=</span> <span class='vr'>a</span><span class='pn'>;</span><br />
        <br />
        <span class='cm'>//clear the displayed password placeholder text when started editing</span><br />
        <span class='vr'>g</span><span class='op'>.</span><span class='fn'>PreparingCellForEdit</span> <span class='op'>+=</span> <span class='pn'>(</span><span class='vr'>o</span><span class='pn'>,</span> <span class='vr'>e</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>e</span><span class='op'>.</span><span class='fn'>Column</span> <span class='op'>==</span> <span class='vr'>colPw</span><span class='pn'>) {</span><br />
                <span class='kw'>var</span> <span class='vr'>tb</span> <span class='op'>=</span> <span class='pn'>(</span><span class='tp'>TextBox</span><span class='pn'>)</span><span class='vr'>e</span><span class='op'>.</span><span class='fn'>EditingElement</span><span class='pn'>;</span><br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>e</span><span class='op'>.</span><span class='fn'>EditingEventArgs</span> <span class='kw'>is not</span> <span class='tp'>TextCompositionEventArgs</span><span class='pn'>)</span> <span class='vr'>tb</span><span class='op'>.</span><span class='fn'>Clear</span><span class='pn'>();</span><br />
                <span class='cm'>//tb.Foreground = tb.Background; //hide password</span><br />
            <span class='pn'>}<br />
        };</span><br />
        <br />
        <span class='cm'>//validate name when ending editing</span><br />
        <span class='vr'>g</span><span class='op'>.</span><span class='fn'>CellEditEnding</span> <span class='op'>+=</span> <span class='pn'>(</span><span class='vr'>o</span><span class='pn'>,</span> <span class='vr'>e</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>e</span><span class='op'>.</span><span class='fn'>EditAction</span> <span class='op'>==</span> <span class='tp'>DataGridEditAction</span><span class='op'>.</span><span class='cn'>Commit</span><span class='pn'>) {</span><br />
                <span class='kw'>var</span> <span class='vr'>tb</span> <span class='op'>=</span> <span class='pn'>(</span><span class='tp'>TextBox</span><span class='pn'>)</span><span class='vr'>e</span><span class='op'>.</span><span class='fn'>EditingElement</span><span class='pn'>;</span><br />
                <span class='kw'>var</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='vr'>tb</span><span class='op'>.</span><span class='fn'>Text</span><span class='pn'>;</span><br />
                <br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Trim</span><span class='pn'>()</span> <span class='kw'>is var</span> <span class='vr'>s2</span> <span class='op'>&amp;&amp;</span> <span class='vr'>s2</span> <span class='op'>!=</span> <span class='vr'>s</span><span class='pn'>) {</span><br />
                    <span class='kw'>switch</span> <span class='pn'>(</span><span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='kw'>null</span><span class='pn'>,</span> <span class='st'>"The texts starts or ends with spaces."</span><span class='pn'>,</span> <span class='st'>"1 Trim spaces|2 Don't trim|3 Cancel"</span><span class='pn'>,</span> <span class='vr'>owner</span><span class='pn'>:</span> <span class='vr'>w</span><span class='pn'>,</span> <span class='vr'>defaultButton</span><span class='pn'>:</span> <span class='nr'>3</span><span class='pn'>)) {</span><br />
                    <span class='kw'>case</span> <span class='nr'>3</span><span class='pn'>:</span> <span class='fn'>_Cancel</span><span class='pn'>();</span> <span class='kw'>return</span><span class='pn'>;</span><br />
                    <span class='kw'>case</span> <span class='nr'>1</span><span class='pn'>:</span> <span class='vr'>tb</span><span class='op'>.</span><span class='fn'>Text</span> <span class='op'>=</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='vr'>s2</span><span class='pn'>;</span> <span class='kw'>break</span><span class='pn'>;<br />
                    }<br />
                }</span><br />
                <br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>e</span><span class='op'>.</span><span class='fn'>Column</span> <span class='op'>==</span> <span class='vr'>colName</span><span class='pn'>) {</span><br />
                    <span class='kw'>var</span> <span class='vr'>item</span> <span class='op'>=</span> <span class='vr'>e</span><span class='op'>.</span><span class='fn'>Row</span><span class='op'>.</span><span class='fn'>Item</span> <span class='kw'>as</span> <span class='tp'>_Item</span><span class='pn'>;</span><br />
                    <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>a</span><span class='op'>.</span><span class='fn'>Any</span><span class='pn'>(</span><span class='vr'>v</span> <span class='op'>=&gt;</span> <span class='vr'>v</span> <span class='fn'>!=</span> <span class='vr'>item</span> <span class='op'>&amp;&amp;</span> <span class='vr'>v</span><span class='op'>.</span><span class='fn'>Name</span><span class='op'>.</span><span class='fn'>Eqi</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>))) {</span><br />
                        <span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='st'>"Error"</span><span class='pn'>,</span> <span class='st'>&#36;"Name '</span><span class='pn'>{</span><span class='vr'>s</span><span class='pn'>}</span><span class='st'>' already exists."</span><span class='pn'>,</span> <span class='vr'>owner</span><span class='pn'>:</span> <span class='vr'>w</span><span class='pn'>);</span><br />
                        <span class='fn'>_Cancel</span><span class='pn'>();<br />
                    }<br />
                }</span><br />
                <br />
                <span class='kw'>void</span> <span class='fn'>_Cancel</span><span class='pn'>() {</span><br />
                    <span class='vr'>e</span><span class='op'>.</span><span class='fn'>Cancel</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>;</span><br />
                    <br />
                    <span class='cm'>//workaround for DataGrid bug: on Tab key starts editing next cell. Then 2 cells are in edit mode.</span><br />
                    <span class='tp'>EventHandler</span><span class='pn'>&lt;</span><span class='tp'>DataGridBeginningEditEventArgs</span><span class='pn'>&gt;</span> <span class='vr'>eh1</span> <span class='op'>=</span> <span class='pn'>(</span><span class='vr'>o</span><span class='pn'>,</span> <span class='vr'>e</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='pn'>{</span> <span class='vr'>e</span><span class='op'>.</span><span class='fn'>Cancel</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>;</span> <span class='vr'>tb</span><span class='op'>.</span><span class='fn'>Focus</span><span class='pn'>(); };</span><br />
                    <span class='vr'>g</span><span class='op'>.</span><span class='fn'>BeginningEdit</span> <span class='op'>+=</span> <span class='vr'>eh1</span><span class='pn'>;</span><br />
                    <span class='tp'>timer</span><span class='op'>.</span><span class='fn'>after</span><span class='pn'>(</span><span class='nr'>1</span><span class='pn'>,</span> <span class='vr'>_</span> <span class='op'>=&gt;</span> <span class='pn'>{</span> <span class='vr'>g</span><span class='op'>.</span><span class='fn'>BeginningEdit</span> <span class='op'>-=</span> <span class='vr'>eh1</span><span class='pn'>; });<br />
                }<br />
            }<br />
        };</span><br />
        <br />
        <span class='fn'>_CreateMenu</span><span class='pn'>();</span><br />
        <br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>owner</span> <span class='op'>!=</span> <span class='kw'>null</span><span class='pn'>) {</span><br />
            <span class='vr'>w</span><span class='op'>.</span><span class='fn'>Owner</span> <span class='op'>=</span> <span class='vr'>owner</span><span class='pn'>;</span><br />
            <span class='vr'>w</span><span class='op'>.</span><span class='fn'>ShowInTaskbar</span> <span class='op'>=</span> <span class='kw'>false</span><span class='pn'>;<br />
        }</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>b</span><span class='op'>.</span><span class='fn'>ShowDialog</span><span class='pn'>(</span><span class='vr'>owner</span><span class='pn'>))</span> <span class='kw'>return</span><span class='pn'>;</span><br />
        <br />
        <span class='fn'>_SaveItems</span><span class='pn'>();</span><br />
        <br />
        <span class='kw'>void</span> <span class='fn'>_SaveItems</span><span class='pn'>() {</span><br />
            <span class='kw'>var</span> <span class='vr'>t</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>csvTable</span> <span class='pn'>{</span> <span class='fn'>ColumnCount</span> <span class='op'>=</span> <span class='nr'>2</span> <span class='pn'>};</span><br />
            <span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>var</span> <span class='vr'>v</span> <span class='kw'>in</span> <span class='vr'>a</span><span class='pn'>)</span> <span class='vr'>t</span><span class='op'>.</span><span class='fn'>AddRow</span><span class='pn'>(</span><span class='vr'>v</span><span class='op'>.</span><span class='fn'>Name</span><span class='pn'>,</span> <span class='vr'>v</span><span class='op'>.</span><span class='vr'>EncryptedPassword</span><span class='pn'>);</span><br />
            <span class='fn'>_SaveCsv</span><span class='pn'>(</span><span class='vr'>t</span><span class='pn'>);<br />
        }</span><br />
        <br />
        <span class='kw'>void</span> <span class='fn'>_CreateMenu</span><span class='pn'>() {</span><br />
            <span class='cm'>//File</span><br />
            <span class='kw'>var</span> <span class='vr'>mFile</span> <span class='op'>=</span> <span class='fn'>_TopItem</span><span class='pn'>(</span><span class='st'>"_Menu"</span><span class='pn'>);</span><br />
            <span class='fn'>_Item</span><span class='pn'>(</span><span class='vr'>mFile</span><span class='pn'>,</span> <span class='st'>"Change key..."</span><span class='pn'>,</span> <span class='vr'>o</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
                <span class='vr'>g</span><span class='op'>.</span><span class='fn'>CancelEdit</span><span class='pn'>();</span><br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>a</span><span class='op'>.</span><span class='fn'>Any</span><span class='pn'>(</span><span class='vr'>o</span> <span class='op'>=&gt;</span> <span class='vr'>o</span><span class='op'>.</span><span class='fn'>Password</span> <span class='op'>==</span> <span class='st'>"&lt;error&gt;"</span><span class='pn'>)) {</span> <span class='cm'>//unlikely</span><br />
                    <span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>showError</span><span class='pn'>(</span><span class='st'>"Cannot change the key"</span><span class='pn'>,</span> <span class='st'>"Failed to decrypt some passwords (&lt;error&gt;), therefore cannot encrypt them with a new key.</span><span class='se'>&#92;n</span><span class='st'>Edit or delete the &lt;error&gt; passwords."</span><span class='pn'>);</span><br />
                    <span class='kw'>return</span><span class='pn'>;<br />
                }</span><br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='fn'>_ChangeKey</span><span class='pn'>(</span><span class='kw'>ref</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='vr'>a</span><span class='pn'>))</span><br />
                    <span class='fn'>_SaveItems</span><span class='pn'>();<br />
            });</span><br />
            <span class='cm'>//TODO2: UI help.</span><br />
            <br />
            <span class='tp'>MenuItem</span> <span class='fn'>_Item</span><span class='pn'>(</span><span class='tp'>ItemsControl</span> <span class='vr'>parent</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='tp'>Action</span><span class='pn'>&lt;</span><span class='tp'>MenuItem</span><span class='pn'>&gt;</span> <span class='vr'>click</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>tooltip</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>) {</span><br />
                <span class='kw'>var</span> <span class='vr'>mi</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>MenuItem</span> <span class='pn'>{</span> <span class='fn'>Header</span> <span class='op'>=</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='fn'>ToolTip</span> <span class='op'>=</span> <span class='vr'>tooltip</span> <span class='pn'>};</span><br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>click</span> <span class='op'>!=</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='vr'>mi</span><span class='op'>.</span><span class='fn'>Click</span> <span class='op'>+=</span> <span class='pn'>(</span><span class='vr'>sender</span><span class='pn'>,</span> <span class='vr'>_</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='vr'>click</span><span class='pn'>(</span><span class='vr'>sender</span> <span class='kw'>as</span> <span class='tp'>MenuItem</span><span class='pn'>);</span><br />
                <span class='vr'>parent</span><span class='op'>.</span><span class='fn'>Items</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>mi</span><span class='pn'>);</span><br />
                <span class='kw'>return</span> <span class='vr'>mi</span><span class='pn'>;<br />
            }</span><br />
            <br />
            <span class='tp'>MenuItem</span> <span class='fn'>_TopItem</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='fn'>_Item</span><span class='pn'>(</span><span class='vr'>menu</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>);</span><br />
            <br />
            <span class='cm'>//void _Separator(ItemsControl parent) { parent.Items.Add(new Separator()); }</span><br />
        <span class='pn'>}<br />
    }</span><br />
    <br />
    <span class='kw'>record class</span> <span class='tp'>_Item</span> <span class='pn'>{</span><br />
        <span class='kw'>string</span> <span class='vr'>_pwDisplay</span><span class='pn'>;</span><br />
        <br />
        <span class='kw'>public</span> <span class='tp'>_Item</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>encPw</span><span class='pn'>,</span> <span class='kw'>byte</span><span class='pn'>[]</span> <span class='vr'>key</span><span class='pn'>) {</span><br />
            <span class='fn'>Name</span> <span class='op'>=</span> <span class='vr'>name</span><span class='pn'>;</span><br />
            <span class='vr'>EncryptedPassword</span> <span class='op'>=</span> <span class='vr'>encPw</span><span class='pn'>;</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>encPw</span><span class='op'>.</span><span class='fn'>NE</span><span class='pn'>())</span> <span class='vr'>_pwDisplay</span> <span class='op'>=</span> <span class='fn'>_Decrypt</span><span class='pn'>(</span><span class='vr'>EncryptedPassword</span><span class='pn'>,</span> <span class='vr'>key</span><span class='pn'>,</span> <span class='kw'>out _</span><span class='pn'>)</span> <span class='op'>?</span> <span class='st'>"•…"</span> <span class='op'>:</span> <span class='st'>"&lt;error&gt;"</span><span class='pn'>;<br />
        }</span><br />
        <span class='kw'>public</span> <span class='tp'>_Item</span><span class='pn'>() { }</span><br />
        <br />
        <span class='kw'>public string</span> <span class='fn'>Name</span> <span class='pn'>{</span> <span class='kw'>get</span><span class='pn'>;</span> <span class='kw'>set</span><span class='pn'>; }</span><br />
        <span class='kw'>public string</span> <span class='vr'>EncryptedPassword</span><span class='pn'>;</span><br />
        <br />
        <span class='kw'>public string</span> <span class='fn'>Password</span> <span class='pn'>{</span><br />
            <span class='kw'>get</span> <span class='op'>=&gt;</span> <span class='vr'>_pwDisplay</span><span class='pn'>;</span><br />
            <span class='kw'>set</span> <span class='op'>=&gt;</span> <span class='fn'>SetNewPassword</span><span class='pn'>(</span><span class='kw'>value</span><span class='pn'>);<br />
        }</span><br />
        <br />
        <span class='kw'>public void</span> <span class='fn'>SetNewPassword</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>s</span><span class='pn'>,</span> <span class='kw'>byte</span><span class='pn'>[]</span> <span class='vr'>key</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>) {</span><br />
            <span class='vr'>EncryptedPassword</span> <span class='op'>=</span> <span class='fn'>_Encrypt</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>key</span><span class='pn'>);</span><br />
            <span class='vr'>_pwDisplay</span> <span class='op'>=</span> <span class='vr'>s</span><span class='op'>.</span><span class='fn'>NE</span><span class='pn'>()</span> <span class='op'>?</span> <span class='kw'>null</span> <span class='op'>:</span> <span class='st'>"•…"</span><span class='pn'>;<br />
        }<br />
    }<br />
}</span></code></div></div>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Remote contol of XYplorer]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7608</link>
			<pubDate>Sun, 03 Mar 2024 13:40:31 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=1471">MBaas</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7608</guid>
			<description><![CDATA[With gintara's <a href="https://www.libreautomate.com/forum/showthread.php?tid=4773&amp;pid=37344#pid37344" target="_blank" rel="noopener" class="mycode_url">help</a>, I was able to modify an <a href="https://www.libreautomate.com/forum/showthread.php?tid=4773" target="_blank" rel="noopener" class="mycode_url">old QM script</a> that can be used to send msgs to my favorite filemanager <a href="https://xyplorer.com" target="_blank" rel="noopener" class="mycode_url">XYplorer</a>:<br />
<br />
    void XYSend (string script) {<br />
        var w = wnd.findOrRun(of: "xyplorer.exe",run: () =&gt; run.it("xyplorer.exe"));<br />
        if (w.Is0) return;<br />
        WndCopyData.Send&lt;char&gt;(w, 0x00400001, script);            <br />
    }<br />
 <br />
NB: check XY's help for "Remote Control"]]></description>
			<content:encoded><![CDATA[With gintara's <a href="https://www.libreautomate.com/forum/showthread.php?tid=4773&amp;pid=37344#pid37344" target="_blank" rel="noopener" class="mycode_url">help</a>, I was able to modify an <a href="https://www.libreautomate.com/forum/showthread.php?tid=4773" target="_blank" rel="noopener" class="mycode_url">old QM script</a> that can be used to send msgs to my favorite filemanager <a href="https://xyplorer.com" target="_blank" rel="noopener" class="mycode_url">XYplorer</a>:<br />
<br />
    void XYSend (string script) {<br />
        var w = wnd.findOrRun(of: "xyplorer.exe",run: () =&gt; run.it("xyplorer.exe"));<br />
        if (w.Is0) return;<br />
        WndCopyData.Send&lt;char&gt;(w, 0x00400001, script);            <br />
    }<br />
 <br />
NB: check XY's help for "Remote Control"]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Translate Cookbook with Google Translate or OpenAI ChatGPT]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7592</link>
			<pubDate>Mon, 12 Feb 2024 08:46:21 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=2">Gintaras</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7592</guid>
			<description><![CDATA[Need LA 1.1 or later.<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "Translate cookbook recipe.cs"<br />
<span class='xt'>///</span><span class='xd'> Run this script. It translates Cookbook articles as you open them.</span><br />
<span class='xt'>///</span><span class='xd'> At first edit the `language` value below (replace "lt").</span><br />
<span class='xt'>///</span><span class='xd'> </span><br />
<span class='xt'>///</span><span class='xd'> To translate, this script can use Google Translate (Cloud or free) or Microsoft Translator or OpenAI ChatGPT.</span><br />
<span class='xt'>///</span><span class='xd'> By default uses a free Google Translate endpoint. Don't need an account etc. It is undocumented and may stop working in the future, therefore this script also can use Google Cloud, which has the same translation speed and quality.</span><br />
<span class='xt'>///</span><span class='xd'> To use Microsoft Translator:</span><br />
<span class='xt'>///</span><span class='xd'>     Change the `translator` value below in `#region settings`.</span><br />
<span class='xt'>///</span><span class='xd'>     Change the `language` value in the `microsoft` block.</span><br />
<span class='xt'>///</span><span class='xd'> To use OpenAI or Google Cloud:</span><br />
<span class='xt'>///</span><span class='xd'>     Change the `translator` value below in `#region settings`.</span><br />
<span class='xt'>///</span><span class='xd'>     Change the `language` value in the `google` or `openai` block.</span><br />
<span class='xt'>///</span><span class='xd'>     Create account at https://platform.openai.com or Google Cloud. Generate an API key. For OpenAI at first need to upgrade to a paid tier (&#36;5 one-time). For Google Cloud need a project etc. You can find more info and tutorials on the Internet.</span><br />
<span class='xt'>///</span><span class='xd'>     Save the API key it the Registry (see `registryApiKey` below).</span><br />
<span class='xt'>///</span><span class='xd'>     How much it costs? Both services can translate more than enough text / month for free. Entire Cookbook can be translated for free.</span><br />
<span class='xt'>///</span><span class='xd'> </span><br />
<span class='xt'>///</span><span class='xd'> To run this script at startup, add its name in Options &gt; Workspace.</span><br />
<span class='xt'>///</span><span class='xd'> After editing this script, run it again.</span><br />
<span class='xt'>///</span><span class='xd'> </span><br />
<span class='xt'>///</span><span class='xd'> This script caches translated texts. If you want to re-translate a recipe, open it with Ctrl.</span><br />
<span class='xt'>///</span><span class='xd'> OpenAI is slow. If the recipe contains much text, may need to wait 30 minutes or more. During that time LA is hung. Because of the cache, next time will not need to wait for the same recipe.</span><br />
<span class='xt'>///</span><span class='xd'> Google is fast, usually 100-300 ms, but incorrectly translates many words.</span><br />
<span class='xt'>///</span><span class='xd'> Microsoft is just slightly slower than Google, and it seems its translation quality is better.</span><br />
<br />
<span class='cm'>/*/ role editorExtension; testInternal Au.Editor; r Au.Editor.dll; /*/<br />
/*/ role editorExtension; testInternal Au.Editor; r Au.Editor.dll; nuget -&#92;WeCantSpell.Hunspell; /*/<br />
<br />
//#define DEBUG_PRINT<br />
//#define SPELLCHECK</span><br />
<br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Text</span><span class='op'>.</span><span class='ns'>Json</span><span class='op'>.</span><span class='ns'>Nodes</span><span class='pn'>;</span><br />
<br />
<span class='pd'>#region</span> settings<br />
<br />
<span class='kw'>const</span> <span class='tp'>Translator</span> <span class='cn'>translator</span> <span class='op'>=</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>GoogleFree</span><span class='pn'>;</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>google</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='pn'>{</span><br />
    <span class='fn'>language</span> <span class='op'>=</span> <span class='st'>"lt"</span><span class='pn'>,</span> <span class='cm'>//https://cloud.google.com/translate/docs/languages</span><br />
    <span class='fn'>registryApiKey</span> <span class='op'>=</span> <span class='pn'>(</span><span class='vr'>key</span><span class='pn'>:</span> <span class='st'>@"HKEY_CURRENT_USER&#92;Software&#92;Au"</span><span class='pn'>,</span> <span class='vr'>value</span><span class='pn'>:</span> <span class='st'>"Google Cloud API key"</span><span class='pn'>),</span> <span class='cm'>//for Translator.GoogleCloud only. Don't need to edit these strings, just create the Registry key and value.</span><br />
<span class='pn'>};</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>microsoft</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='pn'>{</span><br />
    <span class='fn'>language</span> <span class='op'>=</span> <span class='st'>"lt"</span><span class='pn'>,<br />
};</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>openai</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='pn'>{</span><br />
    <span class='fn'>language</span> <span class='op'>=</span> <span class='st'>"Lithuanian"</span><span class='pn'>,</span><br />
    <span class='fn'>model</span> <span class='op'>=</span> <span class='st'>"gpt-4-turbo-preview"</span><span class='pn'>,</span> <span class='cm'>//"gpt-3.5-turbo", "gpt-4", "gpt-4-turbo-preview", etc</span><br />
    <span class='fn'>registryApiKey</span> <span class='op'>=</span> <span class='pn'>(</span><span class='vr'>key</span><span class='pn'>:</span> <span class='st'>@"HKEY_CURRENT_USER&#92;Software&#92;Au"</span><span class='pn'>,</span> <span class='vr'>value</span><span class='pn'>:</span> <span class='st'>"OpenAI API key"</span><span class='pn'>),</span> <span class='cm'>//Don't need to edit these strings, just create the Registry key and value.</span><br />
<span class='pn'>};</span><br />
<br />
<span class='pd'>#endregion<br />
<br />
<br />
#if</span> SPELLCHECK<br />
<span class='ex'>var dict = @"C:&#92;Program Files&#92;LibreOffice&#92;share&#92;extensions&#92;dict-en&#92;en_US.dic";<br />
//var dict = @"C:&#92;Program Files&#92;LibreOffice&#92;share&#92;wordbook&#92;technical.dic";<br />
var dictionary = WeCantSpell.Hunspell.WordList.CreateFromFiles(dict);<br />
</span><span class='pd'>#endif</span><br />
<br />
EditorExtension<span class='op'>.</span>WindowReady <span class='op'>+=</span> <span class='pn'>()</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
    Panels<span class='op'>.</span>Recipe<span class='op'>.</span>OpeningRecipe <span class='op'>=</span> <span class='fn'>_OpeningRecipe</span><span class='pn'>;<br />
};</span><br />
<br />
<span class='kw'>void</span> <span class='fn'>_OpeningRecipe</span><span class='pn'>(</span>PanelRecipe<span class='op'>.</span>OpeningRecipeArgs <span class='vr'>e</span><span class='pn'>) {</span><br />
    <span class='cm'>//convert e to text:<br />
    //Name<br />
    //<br />
    //e.parts[0] //text with replaced tags<br />
    //<br />
    //#&lt;1&gt;# //replaced code<br />
    //<br />
    //e.parts[2] //text with replaced tags<br />
    //<br />
    //#&lt;3&gt;# //replaced code<br />
    //<br />
    //...</span><br />
    <br />
<span class='pd'>#if</span> DEBUG_PRINT<br />
<span class='ex'>    print.clear();<br />
</span><span class='pd'>#endif</span><br />
    <span class='tp'>List</span><span class='pn'>&lt;(</span><span class='kw'>string</span> s<span class='pn'>,</span> <span class='kw'>int</span> lenEndTag<span class='pn'>,</span> <span class='kw'>int</span> lenText<span class='pn'>)&gt;</span> <span class='vr'>tags</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>();</span><br />
    <span class='tp'>regexp</span> <span class='vr'>rx</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>(</span><span class='st'>@"&lt;_&gt;(?&lt;g1&gt;[&#92;s&#92;S]*?)&lt;/_&gt;|&lt;image </span><span class='se'>""</span><span class='st'>[^</span><span class='se'>""</span><span class='st'>]*</span><span class='se'>""</span><span class='st'>&gt;|&lt;([+&#92;.]?[a-z]+)(?&lt;g3&gt; [^&gt;]+)?&gt;(?&lt;g2&gt;[^&lt;]++|(?R))+&lt;(?:/&#92;1)?&gt;"</span><span class='pn'>);</span><br />
    <span class='kw'>var</span> <span class='vr'>sb</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>StringBuilder</span><span class='pn'>(</span><span class='fn'>_PrepareTitle</span><span class='pn'>(</span><span class='vr'>e</span><span class='op'>.</span>name<span class='pn'>,</span> <span class='vr'>tags</span><span class='pn'>)</span> <span class='op'>+</span> <span class='st'>"</span><span class='se'>&#92;r&#92;n&#92;r&#92;n</span><span class='st'>"</span><span class='pn'>);</span><br />
    <span class='kw'>int</span> <span class='vr'>ipart</span> <span class='op'>= -</span><span class='nr'>1</span><span class='pn'>;</span><br />
    <span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>var</span> <span class='vr'>v</span> <span class='kw'>in</span> <span class='vr'>e</span><span class='op'>.</span>parts<span class='pn'>) {</span><br />
        <span class='vr'>ipart</span><span class='op'>++</span><span class='pn'>;</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>v</span><span class='op'>.</span>isText<span class='pn'>) {</span><br />
            <span class='cm'>//print.it(v.s); print.it("----");</span><br />
            <span class='vr'>sb</span><span class='op'>.</span><span class='fn'>AppendLine</span><span class='pn'>(</span><span class='vr'>rx</span><span class='op'>.</span><span class='fn'>Replace</span><span class='pn'>(</span><span class='vr'>v</span><span class='op'>.</span>s<span class='pn'>,</span> <span class='fn'>_Repl1</span><span class='pn'>));</span><br />
            <br />
            <span class='kw'>string</span> <span class='fn'>_Repl1</span><span class='pn'>(</span><span class='tp'>RXMatch</span> <span class='vr'>m</span><span class='pn'>) {</span><br />
                <span class='kw'>int</span> <span class='vr'>i</span> <span class='op'>=</span> <span class='vr'>tags</span><span class='op'>.</span><span class='fn'>Count</span><span class='pn'>;</span><br />
                <span class='kw'>var</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='vr'>m</span><span class='op'>.</span><span class='fn'>Value</span><span class='pn'>;</span><br />
                <span class='kw'>var</span> <span class='vr'>g</span> <span class='op'>=</span> <span class='vr'>m</span><span class='pn'>[</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;_&gt;"</span><span class='pn'>)</span> <span class='op'>?</span> <span class='st'>"g1"</span> <span class='op'>:</span> <span class='st'>"g2"</span><span class='pn'>];</span><br />
                <span class='kw'>var</span> <span class='vr'>t</span> <span class='op'>=</span> <span class='vr'>g</span><span class='op'>.</span><span class='fn'>Value</span><span class='pn'>;</span><br />
                <span class='kw'>bool</span> <span class='vr'>translate</span> <span class='op'>=</span> <span class='kw'>false</span><span class='pn'>;</span><br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>t</span><span class='op'>.</span><span class='fn'>Contains</span><span class='pn'>(</span><span class='st'>'&lt;'</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//nested tags (mostly keywords, identifiers)</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;_&gt;"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//don't translate</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;image"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;."</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//code, keyword</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;b&gt;"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//mostly identifiers and UI</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;i&gt;"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//mostly parameters and terms</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;mono&gt;"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//keys, hotkeys</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;+see"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//identifiers<br />
                //else if (s.Starts("&lt;+recipe")) { /*print.it(t);*/ } //</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;+nuget"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>t</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"http"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//URL</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>t</span><span class='op'>.</span><span class='fn'>FindAny</span><span class='pn'>(</span><span class='st'>".;"</span><span class='pn'>)</span> <span class='op'>&gt;=</span> <span class='nr'>0</span><span class='pn'>) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//avoid breaking the sentence</span><br />
                <span class='kw'>else</span> <span class='pn'>{</span><br />
                    <span class='cm'>//print.it(s);</span><br />
                    <span class='vr'>translate</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>;</span><br />
                    <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>m</span><span class='pn'>[</span><span class='st'>"g3"</span><span class='pn'>]</span><span class='op'>.</span><span class='fn'>Exists</span><span class='pn'>) {</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='st'>&#36;"</span><span class='pn'>{</span><span class='vr'>s</span><span class='pn'>[..(</span><span class='vr'>g</span><span class='op'>.</span><span class='fn'>Start</span> <span class='op'>-</span> <span class='vr'>m</span><span class='op'>.</span><span class='fn'>Start</span> <span class='op'>-</span> <span class='nr'>1</span><span class='pn'>)]}</span><span class='st'> </span><span class='se'>&#92;"</span><span class='pn'>{</span><span class='vr'>t</span><span class='pn'>}</span><span class='se'>&#92;"</span><span class='st'>&gt;</span><span class='pn'>{</span><span class='vr'>t</span><span class='pn'>}{</span><span class='vr'>s</span><span class='pn'>[(</span><span class='vr'>g</span><span class='op'>.</span><span class='fn'>End</span> <span class='op'>-</span> <span class='vr'>m</span><span class='op'>.</span><span class='fn'>Start</span><span class='pn'>)..]}</span><span class='st'>"</span><span class='pn'>;</span> <span class='cm'>/*print.it(t, s);*/</span> <span class='pn'>}</span> <span class='cm'>//&lt;tag&gt;text&lt;&gt; -&gt; &lt;tag "text"&gt;translatedText&lt;&gt;</span><br />
                <span class='pn'>}</span><br />
                <span class='cm'>//print.it(s, t);</span><br />
                <span class='vr'>tags</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>translate</span> <span class='op'>?</span> <span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>m</span><span class='op'>.</span><span class='fn'>End</span> <span class='op'>-</span> <span class='vr'>g</span><span class='op'>.</span><span class='fn'>End</span><span class='pn'>,</span> <span class='vr'>t</span><span class='op'>.</span><span class='fn'>Length</span><span class='pn'>)</span> <span class='op'>:</span> <span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>));</span><br />
                <span class='kw'>return</span> <span class='vr'>translate</span> <span class='op'>?</span> <span class='st'>&#36;"&lt;Q</span><span class='pn'>{</span><span class='vr'>i</span><span class='pn'>}</span><span class='st'>&gt;</span><span class='pn'>{</span><span class='vr'>t</span><span class='pn'>}</span><span class='st'>&lt;/Q</span><span class='pn'>{</span><span class='vr'>i</span><span class='pn'>}</span><span class='st'>&gt;"</span> <span class='op'>:</span> <span class='st'>&#36;"[Q</span><span class='pn'>{</span><span class='vr'>i</span><span class='pn'>}</span><span class='st'>]"</span><span class='pn'>;<br />
            }<br />
        }</span> <span class='kw'>else</span> <span class='pn'>{</span><br />
            <span class='vr'>sb</span><span class='op'>.</span><span class='fn'>AppendLine</span><span class='pn'>(</span><span class='st'>&#36;"</span><span class='se'>&#92;r&#92;n</span><span class='st'>#&lt;</span><span class='pn'>{</span><span class='vr'>ipart</span><span class='pn'>}</span><span class='st'>&gt;#</span><span class='se'>&#92;r&#92;n</span><span class='st'>"</span><span class='pn'>);<br />
        }<br />
    }</span><br />
    <span class='kw'>var</span> <span class='vr'>text</span> <span class='op'>=</span> <span class='vr'>sb</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>();</span><br />
    <br />
<span class='pd'>#if</span> DEBUG_PRINT<br />
<span class='ex'>    //if (text.RxIsMatch(@"&lt;[+&#92;.]?[a-z_]")) {<br />
    //    print.it(&#36;"&lt;&gt;&lt;lc yellow&gt;{e.name}    &lt;c red&gt;not all tags replaced&lt;&gt;&lt;&gt;");<br />
    //    print.it(text);<br />
    //    print.it("----");<br />
    //    print.it(e.parts.Select(o =&gt; o.s));<br />
    //    return;<br />
    //}<br />
    <br />
    //if (text.RxIsMatch(@"(?i)(?&lt;![a-z])'")) {<br />
    //    print.it(&#36;"&lt;&gt;&lt;lc yellow&gt;{e.name}&lt;&gt;");<br />
    //    print.it(text);<br />
    //}<br />
    <br />
    //var s1 = text.RxReplace(@""".+?""", &#36;"&lt;bc yellow&gt;&#36;0&lt;&gt;");<br />
    //if (s1 != text) {<br />
    //    print.it(&#36;"&lt;&gt;&lt;lc yellow&gt;{e.name}&lt;&gt;&#92;r&#92;n{s1}");<br />
    //}<br />
    <br />
    //print.it(&#36;"&lt;&gt;&lt;lc yellowgreen&gt;{e.name}&lt;&gt;");<br />
    //print.it(text);<br />
    //if (text.Length &gt; 1500) print.it(text.Length);<br />
    //if (text.Length &gt; 1900) {<br />
    //    print.it(&#36;"&lt;&gt;&lt;lc yellowgreen&gt;{e.name}&lt;&gt;");<br />
    //    print.it(text);<br />
    //}<br />
</span><span class='pd'>#endif<br />
    <br />
#if</span> SPELLCHECK<br />
<span class='ex'>    text = text.RxReplace(@"&#92;b[A-Za-z][a-z']+&#92;b", m =&gt; {<br />
        var s = m.Value;<br />
        bool ok = dictionary.Check(s);<br />
        if (!ok) {<br />
            s = &#36;"&lt;bc yellow&gt;{s}&lt;&gt;";<br />
        }<br />
        return s;<br />
    });<br />
</span><span class='pd'>#else</span><br />
    <br />
    <span class='cm'>//translate<br />
    <br />
    //print.it(text); print.it("-------");<br />
    //return;<br />
    <br />
    //perf.first();</span><br />
    <br />
    <span class='vr'>text</span> <span class='op'>=</span> <span class='fn'>_Translate</span><span class='pn'>(</span><span class='vr'>e</span><span class='op'>.</span>name<span class='pn'>,</span> <span class='vr'>text</span><span class='pn'>,</span> <span class='kw'>true</span><span class='pn'>);</span><br />
    <br />
    <span class='cm'>//perf.nw();<br />
    //print.it(text);<br />
    //return;</span><br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>text</span> <span class='op'>==</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='kw'>return</span><span class='pn'>;</span><br />
<span class='pd'>#endif</span><br />
    <br />
    <span class='tp'>regexp</span> <span class='vr'>rxRT1</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>,</span> <span class='vr'>rxRT2</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>;</span><br />
    <br />
    <span class='cm'>//get translated name</span><br />
    <br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>text</span><span class='op'>.</span><span class='fn'>RxMatch</span><span class='pn'>(</span><span class='st'>@"&#92;R&#92;R"</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='kw'>out</span> <span class='tp'>RXGroup</span> <span class='vr'>g1</span><span class='pn'>)) {</span> <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>"Failed to translate. Result:</span><span class='se'>&#92;r&#92;n</span><span class='st'>"</span> <span class='op'>+</span> <span class='vr'>text</span><span class='pn'>);</span> <span class='kw'>return</span><span class='pn'>; }</span><br />
    <span class='vr'>e</span><span class='op'>.</span>name <span class='op'>=</span> <span class='fn'>_RestoreTags</span><span class='pn'>(</span><span class='vr'>text</span><span class='pn'>[..</span><span class='vr'>g1</span><span class='op'>.</span><span class='fn'>Start</span><span class='pn'>]</span><span class='op'>.</span><span class='fn'>Trim</span><span class='pn'>(),</span> <span class='op'>-</span><span class='nr'>1</span><span class='pn'>);</span><br />
    <span class='kw'>int</span> <span class='vr'>i</span> <span class='op'>=</span> <span class='vr'>g1</span><span class='op'>.</span><span class='fn'>End</span><span class='pn'>;</span><br />
    <br />
    <span class='cm'>//parse translated text parts and create new e.parts with translated texts (with restored tags) and restored codes</span><br />
    <br />
    <span class='tp'>List</span><span class='pn'>&lt;(</span><span class='kw'>bool</span> isText<span class='pn'>,</span> <span class='kw'>string</span> s<span class='pn'>)&gt;</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>();</span><br />
    <span class='tp'>regexp</span> <span class='vr'>rx2</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>(</span><span class='st'>@"&#92;R+#&lt;(&#92;d+)&gt;#&#92;R*"</span><span class='pn'>);</span><br />
    <span class='vr'>ipart</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>;</span><br />
    <span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>var</span> <span class='vr'>m</span> <span class='kw'>in</span> <span class='vr'>rx2</span><span class='op'>.</span><span class='fn'>FindAll</span><span class='pn'>(</span><span class='vr'>text</span><span class='pn'>)) {</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>m</span><span class='op'>.</span><span class='fn'>Start</span> <span class='op'>&gt;</span> <span class='vr'>i</span><span class='pn'>)</span> <span class='vr'>r</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>((</span><span class='kw'>true</span><span class='pn'>,</span> <span class='fn'>_RestoreTags</span><span class='pn'>(</span><span class='vr'>text</span><span class='pn'>[</span><span class='vr'>i</span><span class='pn'>..</span><span class='vr'>m</span><span class='op'>.</span><span class='fn'>Start</span><span class='pn'>],</span> <span class='vr'>ipart</span><span class='op'>++</span><span class='pn'>)));</span><br />
        <span class='vr'>r</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>e</span><span class='op'>.</span>parts<span class='pn'>[</span><span class='vr'>m</span><span class='pn'>[</span><span class='nr'>1</span><span class='pn'>]</span><span class='op'>.</span><span class='fn'>Value</span><span class='op'>.</span><span class='fn'>ToInt</span><span class='pn'>()]);</span><br />
        <span class='vr'>ipart</span><span class='op'>++</span><span class='pn'>;</span><br />
        <span class='vr'>i</span> <span class='op'>=</span> <span class='vr'>m</span><span class='op'>.</span><span class='fn'>End</span><span class='pn'>;<br />
    }</span><br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>text</span><span class='op'>.</span><span class='fn'>Length</span> <span class='op'>&gt;</span> <span class='vr'>i</span><span class='pn'>)</span> <span class='vr'>r</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>((</span><span class='kw'>true</span><span class='pn'>,</span> <span class='fn'>_RestoreTags</span><span class='pn'>(</span><span class='vr'>text</span><span class='pn'>[</span><span class='vr'>i</span><span class='pn'>..],</span> <span class='vr'>ipart</span><span class='pn'>)));</span><br />
    <br />
<span class='pd'>#if</span> DEBUG_PRINT<br />
<span class='ex'>    //print.it(&#36;"&lt;&gt;&lt;lc yellowgreen&gt;{e.name}&lt;&gt;");<br />
    //print.it(r);<br />
</span><span class='pd'>#endif</span><br />
    <br />
    <span class='vr'>e</span><span class='op'>.</span>parts <span class='op'>=</span> <span class='vr'>r</span><span class='pn'>;</span><br />
    <br />
    <span class='kw'>string</span> <span class='fn'>_RestoreTags</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>s</span><span class='pn'>,</span> <span class='kw'>int</span> <span class='vr'>ipart</span><span class='pn'>) {</span><br />
        <span class='vr'>rxRT1</span> <span class='op'>??=</span> <span class='kw'>new</span><span class='pn'>(</span><span class='st'>@"&#92;[Q&#92;d+&#92;]"</span><span class='pn'>);</span><br />
        <span class='vr'>s</span> <span class='op'>=</span> <span class='vr'>rxRT1</span><span class='op'>.</span><span class='fn'>Replace</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>m</span> <span class='op'>=&gt;</span> <span class='vr'>tags</span><span class='pn'>[</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>ToInt</span><span class='pn'>(</span><span class='vr'>m</span><span class='op'>.</span><span class='fn'>Start</span> <span class='op'>+</span> <span class='nr'>2</span><span class='pn'>)]</span><span class='op'>.</span><span class='vr'>s</span><span class='pn'>);</span><br />
        <span class='vr'>rxRT2</span> <span class='op'>??=</span> <span class='kw'>new</span><span class='pn'>(</span><span class='st'>@"&lt;Q(&#92;d+)&gt;([^&lt;]++)&lt;/Q&#92;1&gt;"</span><span class='pn'>);</span><br />
        <span class='vr'>s</span> <span class='op'>=</span> <span class='vr'>rxRT2</span><span class='op'>.</span><span class='fn'>Replace</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>m</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
            <span class='kw'>var</span> <span class='vr'>translatedLinkText</span> <span class='op'>=</span> <span class='vr'>m</span><span class='pn'>[</span><span class='nr'>2</span><span class='pn'>]</span><span class='op'>.</span><span class='fn'>Value</span><span class='pn'>;</span><br />
            <span class='kw'>var</span> <span class='vr'>tag</span> <span class='op'>=</span> <span class='vr'>tags</span><span class='pn'>[</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>ToInt</span><span class='pn'>(</span><span class='vr'>m</span><span class='op'>.</span><span class='fn'>Start</span> <span class='op'>+</span> <span class='nr'>2</span><span class='pn'>)];</span><br />
            <span class='kw'>var</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='vr'>tag</span><span class='op'>.</span><span class='vr'>s</span><span class='pn'>[..</span><span class='op'>^</span><span class='pn'>(</span><span class='vr'>tag</span><span class='op'>.</span><span class='vr'>lenEndTag</span> <span class='op'>+</span> <span class='vr'>tag</span><span class='op'>.</span><span class='vr'>lenText</span><span class='pn'>)]</span> <span class='op'>+</span> <span class='vr'>translatedLinkText</span> <span class='op'>+</span> <span class='vr'>tag</span><span class='op'>.</span><span class='vr'>s</span><span class='pn'>[</span><span class='op'>^</span><span class='vr'>tag</span><span class='op'>.</span><span class='vr'>lenEndTag</span><span class='pn'>..];</span><br />
            <span class='cm'>//print.it(r);</span><br />
            <span class='kw'>return</span> <span class='vr'>r</span><span class='pn'>;<br />
        });</span><br />
        <br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>ipart</span> <span class='op'>&gt;=</span> <span class='nr'>0</span><span class='pn'>)</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='st'>&#36;"&lt;lc #e0ffe0&gt;</span><span class='pn'>{</span><span class='vr'>e</span><span class='op'>.</span>parts<span class='pn'>[</span><span class='vr'>ipart</span><span class='pn'>]</span><span class='op'>.</span>s<span class='pn'>}</span><span class='st'>&lt;&gt;</span><span class='se'>&#92;r&#92;n&#92;r&#92;n</span><span class='st'>&lt;lc #ffffe0&gt;</span><span class='pn'>{</span><span class='vr'>s</span><span class='pn'>}</span><span class='st'>&lt;&gt;"</span><span class='pn'>;</span><br />
        <br />
        <span class='kw'>return</span> <span class='vr'>s</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='kw'>static string</span> <span class='fn'>_PrepareTitle</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>s</span><span class='pn'>,</span> <span class='tp'>List</span><span class='pn'>&lt;(</span><span class='kw'>string</span> s<span class='pn'>,</span> <span class='kw'>int</span> i1<span class='pn'>,</span> <span class='kw'>int</span> i2<span class='pn'>)&gt;</span> <span class='vr'>tags</span><span class='pn'>) {</span><br />
        <span class='tp'>HashSet</span><span class='pn'>&lt;</span><span class='kw'>string</span><span class='pn'>&gt;</span> <span class='vr'>dontTranslate</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>() {</span> <span class='st'>"bool"</span><span class='pn'>,</span> <span class='st'>"null"</span><span class='pn'>,</span> <span class='st'>"default"</span><span class='pn'>,</span> <span class='st'>"class"</span><span class='pn'>,</span> <span class='st'>"struct"</span><span class='pn'>,</span> <span class='st'>"generic"</span><span class='pn'>,</span> <span class='st'>"nullable"</span><span class='pn'>,</span> <span class='st'>"tuple"</span><span class='pn'>,</span> <span class='st'>"using"</span><span class='pn'>,</span> <span class='st'>"if"</span><span class='pn'>,</span> <span class='st'>"else"</span><span class='pn'>,</span> <span class='st'>"switch"</span><span class='pn'>,</span> <span class='st'>"for"</span><span class='pn'>,</span> <span class='st'>"return"</span><span class='pn'>,</span> <span class='st'>"try"</span><span class='pn'>,</span> <span class='st'>"catch"</span><span class='pn'>,</span> <span class='st'>"finally"</span><span class='pn'>,</span> <span class='st'>"throw"</span><span class='pn'>,</span> <span class='st'>"delegate"</span><span class='pn'>,</span> <span class='st'>"event"</span><span class='pn'>, };</span><br />
        <br />
        <span class='vr'>s</span> <span class='op'>=</span> <span class='vr'>s</span><span class='op'>.</span><span class='fn'>RxReplace</span><span class='pn'>(</span><span class='st'>@"&#92;b[a-zA-Z]+&#92;b"</span><span class='pn'>,</span> <span class='vr'>m</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
            <span class='kw'>var</span> <span class='vr'>k</span> <span class='op'>=</span> <span class='vr'>m</span><span class='op'>.</span><span class='fn'>Value</span><span class='pn'>;</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>dontTranslate</span><span class='op'>.</span><span class='fn'>Contains</span><span class='pn'>(</span><span class='vr'>k</span><span class='pn'>)</span> <span class='op'>||</span> <span class='pn'>(</span><span class='vr'>m</span><span class='op'>.</span><span class='fn'>Start</span> <span class='op'>&gt;</span> <span class='nr'>0</span> <span class='op'>&amp;&amp;</span> <span class='kw'>char</span><span class='op'>.</span><span class='fn'>IsUpper</span><span class='pn'>(</span><span class='vr'>k</span><span class='pn'>[</span><span class='nr'>0</span><span class='pn'>]))) {</span><br />
                <span class='kw'>int</span> <span class='vr'>i</span> <span class='op'>=</span> <span class='vr'>tags</span><span class='op'>.</span><span class='fn'>Count</span><span class='pn'>;</span><br />
                <span class='vr'>tags</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>((</span><span class='vr'>k</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>));</span><br />
                <span class='kw'>return</span> <span class='st'>&#36;"[Q</span><span class='pn'>{</span><span class='vr'>i</span><span class='pn'>}</span><span class='st'>]"</span><span class='pn'>;<br />
            }</span><br />
            <span class='kw'>return</span> <span class='vr'>k</span><span class='pn'>;<br />
        });</span><br />
        <span class='kw'>return</span> <span class='vr'>s</span><span class='pn'>;<br />
    }<br />
}</span><br />
<br />
<span class='pd'>#pragma warning disable</span> CS0162 <span class='cm'>//Unreachable code detected</span><br />
<span class='kw'>string</span> <span class='fn'>_Translate</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>text</span><span class='pn'>,</span> <span class='kw'>bool</span> <span class='vr'>debugPrint</span><span class='pn'>) {</span><br />
    <span class='kw'>string</span> <span class='vr'>targetLang</span> <span class='op'>=</span> <span class='cn'>translator</span> <span class='kw'>switch</span> <span class='pn'>{</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>OpenAI</span> <span class='op'>=&gt;</span> <span class='vr'>openai</span><span class='op'>.</span><span class='fn'>language</span><span class='pn'>,</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>Microsoft</span> <span class='op'>=&gt;</span> <span class='vr'>microsoft</span><span class='op'>.</span><span class='fn'>language</span><span class='pn'>,</span> <span class='kw'>_</span> <span class='op'>=&gt;</span> <span class='vr'>google</span><span class='op'>.</span><span class='fn'>language</span> <span class='pn'>};</span><br />
    <br />
    <span class='tp'>_Cache</span> <span class='vr'>cache</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>();</span><br />
    <span class='kw'>string</span> <span class='vr'>cacheDir</span> <span class='op'>=</span> <span class='cn'>translator</span> <span class='kw'>switch</span> <span class='pn'>{</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>OpenAI</span> <span class='op'>=&gt;</span> <span class='st'>"openai "</span><span class='pn'>,</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>Microsoft</span> <span class='op'>=&gt;</span> <span class='st'>"microsoft "</span><span class='pn'>,</span> <span class='kw'>_</span> <span class='op'>=&gt;</span> <span class='st'>"google "</span> <span class='pn'>};</span><br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>cache</span><span class='op'>.</span><span class='fn'>Get</span><span class='pn'>(</span><span class='vr'>cacheDir</span> <span class='op'>+</span> <span class='vr'>targetLang</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>text</span><span class='pn'>,</span> <span class='kw'>out var</span> <span class='vr'>translated</span><span class='pn'>))</span> <span class='kw'>return</span> <span class='vr'>translated</span><span class='pn'>;</span><br />
    <br />
    <span class='kw'>string</span> <span class='vr'>apiKey</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>;</span><br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='cn'>translator</span> <span class='op'>==</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>Microsoft</span><span class='pn'>) {</span><br />
        <span class='vr'>apiKey</span> <span class='op'>=</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>http</span><span class='op'>.</span><span class='fn'>Get</span><span class='pn'>(</span><span class='st'>"https://edge.microsoft.com/translate/auth"</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Text</span><span class='pn'>();<br />
    }</span> <span class='kw'>else if</span> <span class='pn'>(</span><span class='cn'>translator</span> <span class='op'>!=</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>GoogleFree</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>rkv</span> <span class='op'>=</span> <span class='cn'>translator</span> <span class='op'>==</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>OpenAI</span> <span class='op'>?</span> <span class='vr'>openai</span><span class='op'>.</span><span class='fn'>registryApiKey</span> <span class='op'>:</span> <span class='vr'>google</span><span class='op'>.</span><span class='fn'>registryApiKey</span><span class='pn'>;</span><br />
        <span class='vr'>apiKey</span> <span class='op'>=</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>GetValue</span><span class='pn'>(</span><span class='vr'>rkv</span><span class='op'>.</span><span class='vr'>key</span><span class='pn'>,</span> <span class='vr'>rkv</span><span class='op'>.</span><span class='vr'>value</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='kw'>as string</span> <span class='op'>??</span> <span class='kw'>throw new</span> <span class='tp'>AuException</span><span class='pn'>(</span><span class='st'>"no API key in registry"</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='cn'>translator</span> <span class='op'>==</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>OpenAI</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>json</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>JsonObject</span> <span class='pn'>{<br />
            [</span><span class='st'>"model"</span><span class='pn'>]</span> <span class='op'>=</span> <span class='vr'>openai</span><span class='op'>.</span><span class='fn'>model</span><span class='pn'>,<br />
            [</span><span class='st'>"messages"</span><span class='pn'>]</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>JsonArray</span><span class='pn'>(</span><span class='kw'>new</span> <span class='tp'>JsonObject</span> <span class='pn'>{ [</span><span class='st'>"role"</span><span class='pn'>]</span> <span class='op'>=</span> <span class='st'>"user"</span><span class='pn'>, [</span><span class='st'>"content"</span><span class='pn'>]</span> <span class='op'>=</span> <span class='st'>&#36;"Translate English to </span><span class='pn'>{</span><span class='vr'>targetLang</span><span class='pn'>}</span><span class='st'>:</span><span class='se'>&#92;r&#92;n</span><span class='pn'>{</span><span class='vr'>text</span><span class='pn'>}</span><span class='st'>"</span> <span class='pn'>})<br />
        };</span><br />
        <br />
        <span class='kw'>var</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>http</span><span class='op'>.</span><span class='fn'>Post</span><span class='pn'>(</span><span class='st'>"https://api.openai.com/v1/chat/completions"</span><span class='pn'>,</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>jsonContent</span><span class='pn'>(</span><span class='vr'>json</span><span class='op'>.</span><span class='fn'>ToJsonString</span><span class='pn'>()), [</span><span class='st'>&#36;"Authorization: Bearer </span><span class='pn'>{</span><span class='vr'>apiKey</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>]);</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>IsSuccessStatusCode</span><span class='pn'>) {</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>debugPrint</span><span class='pn'>)</span> <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>&#36;"api.openai.com error: </span><span class='pn'>{(</span><span class='kw'>int</span><span class='pn'>)</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>StatusCode</span><span class='pn'>}</span><span class='st'> </span><span class='pn'>{</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>ReasonPhrase</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>);</span><br />
            <span class='kw'>return null</span><span class='pn'>;<br />
        }</span><br />
        <span class='kw'>var</span> <span class='vr'>j</span> <span class='op'>=</span> <span class='vr'>r</span><span class='op'>.</span><span class='fn'>Json</span><span class='pn'>();</span><br />
        <br />
        <span class='cm'>//print.it(j.ToJsonString());</span><br />
        <span class='vr'>translated</span> <span class='op'>=</span> <span class='pn'>(</span><span class='kw'>string</span><span class='pn'>)</span><span class='vr'>j</span><span class='pn'>[</span><span class='st'>"choices"</span><span class='pn'>][</span><span class='nr'>0</span><span class='pn'>][</span><span class='st'>"message"</span><span class='pn'>][</span><span class='st'>"content"</span><span class='pn'>];<br />
    }</span> <span class='kw'>else if</span> <span class='pn'>(</span><span class='cn'>translator</span> <span class='op'>==</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>Microsoft</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>json</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>JsonArray</span><span class='pn'>(</span><span class='kw'>new</span> <span class='tp'>JsonObject</span> <span class='pn'>{ [</span><span class='st'>"Text"</span><span class='pn'>]</span> <span class='op'>=</span> <span class='vr'>text</span> <span class='pn'>});</span><br />
        <span class='kw'>var</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>http</span><span class='op'>.</span><span class='fn'>Post</span><span class='pn'>(</span><span class='st'>&#36;"https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&amp;from=en&amp;to=</span><span class='pn'>{</span><span class='vr'>targetLang</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>,</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>jsonContent</span><span class='pn'>(</span><span class='vr'>json</span><span class='op'>.</span><span class='fn'>ToJsonString</span><span class='pn'>()),</span> <span class='vr'>headers</span><span class='pn'>: [</span><span class='st'>&#36;"Authorization: Bearer </span><span class='pn'>{</span><span class='vr'>apiKey</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>]);</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>IsSuccessStatusCode</span><span class='pn'>) {</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>debugPrint</span><span class='pn'>)</span> <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>&#36;"api.cognitive.microsofttranslator.com error: </span><span class='pn'>{(</span><span class='kw'>int</span><span class='pn'>)</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>StatusCode</span><span class='pn'>}</span><span class='st'> </span><span class='pn'>{</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>ReasonPhrase</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>);</span><br />
            <span class='kw'>return null</span><span class='pn'>;<br />
        }</span><br />
        <span class='kw'>var</span> <span class='vr'>j</span> <span class='op'>=</span> <span class='vr'>r</span><span class='op'>.</span><span class='fn'>Json</span><span class='pn'>();</span><br />
        <br />
        <span class='vr'>translated</span> <span class='op'>=</span> <span class='pn'>(</span><span class='kw'>string</span><span class='pn'>)</span><span class='vr'>j</span><span class='pn'>[</span><span class='nr'>0</span><span class='pn'>][</span><span class='st'>"translations"</span><span class='pn'>][</span><span class='nr'>0</span><span class='pn'>][</span><span class='st'>"text"</span><span class='pn'>];<br />
    }</span> <span class='kw'>else if</span> <span class='pn'>(</span><span class='cn'>translator</span> <span class='op'>==</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>GoogleCloud</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>url</span> <span class='op'>=</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>urlAppend</span><span class='pn'>(</span><span class='st'>"https://translation.googleapis.com/language/translate/v2"</span><span class='pn'>,</span> <span class='st'>"key="</span> <span class='op'>+</span> <span class='vr'>apiKey</span><span class='pn'>);</span><br />
        <span class='kw'>var</span> <span class='vr'>json</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='pn'>{</span> <span class='fn'>q</span> <span class='op'>=</span> <span class='vr'>text</span><span class='pn'>,</span> <span class='fn'>source</span> <span class='op'>=</span> <span class='st'>"en"</span><span class='pn'>,</span> <span class='fn'>target</span> <span class='op'>=</span> <span class='vr'>targetLang</span><span class='pn'>,</span> <span class='fn'>format</span> <span class='op'>=</span> <span class='st'>"text"</span> <span class='pn'>};</span><br />
        <span class='kw'>var</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>http</span><span class='op'>.</span><span class='fn'>Post</span><span class='pn'>(</span><span class='vr'>url</span><span class='pn'>,</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>jsonContent</span><span class='pn'>(</span><span class='vr'>json</span><span class='pn'>));</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>IsSuccessStatusCode</span><span class='pn'>) {</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>debugPrint</span><span class='pn'>)</span> <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>&#36;"translation.googleapis.com error: </span><span class='pn'>{(</span><span class='kw'>int</span><span class='pn'>)</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>StatusCode</span><span class='pn'>}</span><span class='st'> </span><span class='pn'>{</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>ReasonPhrase</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>);</span><br />
            <span class='kw'>return null</span><span class='pn'>;<br />
        }</span><br />
        <span class='kw'>var</span> <span class='vr'>j</span> <span class='op'>=</span> <span class='vr'>r</span><span class='op'>.</span><span class='fn'>Json</span><span class='pn'>();</span><br />
        <br />
        <span class='vr'>translated</span> <span class='op'>=</span> <span class='pn'>(</span><span class='kw'>string</span><span class='pn'>)</span><span class='vr'>j</span><span class='pn'>[</span><span class='st'>"data"</span><span class='pn'>][</span><span class='st'>"translations"</span><span class='pn'>][</span><span class='nr'>0</span><span class='pn'>][</span><span class='st'>"translatedText"</span><span class='pn'>];<br />
    }</span> <span class='kw'>else</span> <span class='pn'>{</span><br />
        <span class='kw'>var</span> <span class='vr'>url</span> <span class='op'>=</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>urlAppend</span><span class='pn'>(</span><span class='st'>"https://translate.googleapis.com/translate_a/single"</span><span class='pn'>,</span> <span class='st'>"client=gtx"</span><span class='pn'>,</span> <span class='st'>"sl=en"</span><span class='pn'>,</span> <span class='st'>"tl="</span> <span class='op'>+</span> <span class='vr'>targetLang</span><span class='pn'>,</span> <span class='st'>"dt=t"</span><span class='pn'>,</span> <span class='st'>"q="</span> <span class='op'>+</span> <span class='vr'>text</span><span class='pn'>);</span><br />
        <span class='kw'>var</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>http</span><span class='op'>.</span><span class='fn'>Get</span><span class='pn'>(</span><span class='vr'>url</span><span class='pn'>);</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>IsSuccessStatusCode</span><span class='pn'>) {</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>debugPrint</span><span class='pn'>)</span> <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>&#36;"translate.googleapis.com error: </span><span class='pn'>{(</span><span class='kw'>int</span><span class='pn'>)</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>StatusCode</span><span class='pn'>}</span><span class='st'> </span><span class='pn'>{</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>ReasonPhrase</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>);</span><br />
            <span class='kw'>return null</span><span class='pn'>;<br />
        }</span><br />
        <span class='kw'>var</span> <span class='vr'>j</span> <span class='op'>=</span> <span class='vr'>r</span><span class='op'>.</span><span class='fn'>Json</span><span class='pn'>();</span><br />
        <br />
        <span class='kw'>var</span> <span class='vr'>sb</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>StringBuilder</span><span class='pn'>();</span><br />
        <span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>var</span> <span class='vr'>v</span> <span class='kw'>in</span> <span class='vr'>j</span><span class='pn'>[</span><span class='nr'>0</span><span class='pn'>]</span><span class='op'>.</span><span class='fn'>AsArray</span><span class='pn'>())</span> <span class='vr'>sb</span><span class='op'>.</span><span class='fn'>Append</span><span class='pn'>((</span><span class='kw'>string</span><span class='pn'>)</span><span class='vr'>v</span><span class='pn'>[</span><span class='nr'>0</span><span class='pn'>]);</span><br />
        <span class='vr'>translated</span> <span class='op'>=</span> <span class='vr'>sb</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>();<br />
    }</span><br />
    <br />
    <span class='vr'>cache</span><span class='op'>.</span><span class='fn'>Save</span><span class='pn'>(</span><span class='vr'>translated</span><span class='pn'>);</span><br />
    <br />
    <span class='kw'>return</span> <span class='vr'>translated</span><span class='pn'>;<br />
}</span><br />
<span class='pd'>#pragma warning restore</span> CS0162 <span class='cm'>//Unreachable code detected</span><br />
<br />
<span class='kw'>class</span> <span class='tp'>_Cache</span> <span class='pn'>{</span><br />
    <span class='kw'>string</span> <span class='vr'>_file</span><span class='pn'>,</span> <span class='vr'>_hash</span><span class='pn'>;</span><br />
    <br />
    <span class='kw'>public bool</span> <span class='fn'>Get</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>folder</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>text</span><span class='pn'>,</span> <span class='kw'>out string</span> <span class='vr'>translated</span><span class='pn'>) {</span><br />
        <span class='vr'>_hash</span> <span class='op'>=</span> <span class='tp'>Hash</span><span class='op'>.</span><span class='fn'>MD5</span><span class='pn'>(</span><span class='vr'>text</span><span class='pn'>,</span> <span class='kw'>true</span><span class='pn'>);</span><br />
        <span class='vr'>_file</span> <span class='op'>=</span> <span class='tp'>folders</span><span class='op'>.</span><span class='fn'>ThisAppDataRoaming +</span> <span class='st'>@"translate&#92;"</span> <span class='op'>+</span> <span class='vr'>folder</span> <span class='op'>+</span> <span class='st'>@"&#92;"</span> <span class='op'>+</span> <span class='vr'>name</span> <span class='op'>+</span> <span class='st'>".txt"</span><span class='pn'>;</span><br />
        <span class='cm'>//run.selectInExplorer(_file);</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='tp'>keys</span><span class='op'>.</span><span class='fn'>isCtrl</span><span class='pn'>)</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>exists</span><span class='pn'>(</span><span class='vr'>_file</span><span class='pn'>)) {</span><br />
                <span class='kw'>var</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>loadText</span><span class='pn'>(</span><span class='vr'>_file</span><span class='pn'>);</span><br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='vr'>_hash</span><span class='pn'>)) {</span><br />
                    <span class='vr'>translated</span> <span class='op'>=</span> <span class='vr'>s</span><span class='pn'>[</span><span class='vr'>_hash</span><span class='op'>.</span><span class='fn'>Length</span><span class='pn'>..];</span><br />
                    <span class='kw'>return true</span><span class='pn'>;<br />
                }<br />
            }</span><br />
        <span class='vr'>translated</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>;</span><br />
        <span class='kw'>return false</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='kw'>public void</span> <span class='fn'>Save</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>translated</span><span class='pn'>) {</span><br />
        <span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>saveText</span><span class='pn'>(</span><span class='vr'>_file</span><span class='pn'>,</span> <span class='vr'>_hash</span> <span class='op'>+</span> <span class='vr'>translated</span><span class='pn'>);<br />
    }<br />
}</span><br />
<br />
<span class='kw'>enum</span> <span class='tp'>Translator</span> <span class='pn'>{</span> <span class='cn'>GoogleFree</span><span class='pn'>,</span> <span class='cn'>GoogleCloud</span><span class='pn'>,</span> <span class='cn'>Microsoft</span><span class='pn'>,</span> <span class='cn'>OpenAI</span> <span class='pn'>}</span></code></div></div><br />
Changes:<br />
2024-03-03: Added Misrosoft Translator.]]></description>
			<content:encoded><![CDATA[Need LA 1.1 or later.<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "Translate cookbook recipe.cs"<br />
<span class='xt'>///</span><span class='xd'> Run this script. It translates Cookbook articles as you open them.</span><br />
<span class='xt'>///</span><span class='xd'> At first edit the `language` value below (replace "lt").</span><br />
<span class='xt'>///</span><span class='xd'> </span><br />
<span class='xt'>///</span><span class='xd'> To translate, this script can use Google Translate (Cloud or free) or Microsoft Translator or OpenAI ChatGPT.</span><br />
<span class='xt'>///</span><span class='xd'> By default uses a free Google Translate endpoint. Don't need an account etc. It is undocumented and may stop working in the future, therefore this script also can use Google Cloud, which has the same translation speed and quality.</span><br />
<span class='xt'>///</span><span class='xd'> To use Microsoft Translator:</span><br />
<span class='xt'>///</span><span class='xd'>     Change the `translator` value below in `#region settings`.</span><br />
<span class='xt'>///</span><span class='xd'>     Change the `language` value in the `microsoft` block.</span><br />
<span class='xt'>///</span><span class='xd'> To use OpenAI or Google Cloud:</span><br />
<span class='xt'>///</span><span class='xd'>     Change the `translator` value below in `#region settings`.</span><br />
<span class='xt'>///</span><span class='xd'>     Change the `language` value in the `google` or `openai` block.</span><br />
<span class='xt'>///</span><span class='xd'>     Create account at https://platform.openai.com or Google Cloud. Generate an API key. For OpenAI at first need to upgrade to a paid tier (&#36;5 one-time). For Google Cloud need a project etc. You can find more info and tutorials on the Internet.</span><br />
<span class='xt'>///</span><span class='xd'>     Save the API key it the Registry (see `registryApiKey` below).</span><br />
<span class='xt'>///</span><span class='xd'>     How much it costs? Both services can translate more than enough text / month for free. Entire Cookbook can be translated for free.</span><br />
<span class='xt'>///</span><span class='xd'> </span><br />
<span class='xt'>///</span><span class='xd'> To run this script at startup, add its name in Options &gt; Workspace.</span><br />
<span class='xt'>///</span><span class='xd'> After editing this script, run it again.</span><br />
<span class='xt'>///</span><span class='xd'> </span><br />
<span class='xt'>///</span><span class='xd'> This script caches translated texts. If you want to re-translate a recipe, open it with Ctrl.</span><br />
<span class='xt'>///</span><span class='xd'> OpenAI is slow. If the recipe contains much text, may need to wait 30 minutes or more. During that time LA is hung. Because of the cache, next time will not need to wait for the same recipe.</span><br />
<span class='xt'>///</span><span class='xd'> Google is fast, usually 100-300 ms, but incorrectly translates many words.</span><br />
<span class='xt'>///</span><span class='xd'> Microsoft is just slightly slower than Google, and it seems its translation quality is better.</span><br />
<br />
<span class='cm'>/*/ role editorExtension; testInternal Au.Editor; r Au.Editor.dll; /*/<br />
/*/ role editorExtension; testInternal Au.Editor; r Au.Editor.dll; nuget -&#92;WeCantSpell.Hunspell; /*/<br />
<br />
//#define DEBUG_PRINT<br />
//#define SPELLCHECK</span><br />
<br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Text</span><span class='op'>.</span><span class='ns'>Json</span><span class='op'>.</span><span class='ns'>Nodes</span><span class='pn'>;</span><br />
<br />
<span class='pd'>#region</span> settings<br />
<br />
<span class='kw'>const</span> <span class='tp'>Translator</span> <span class='cn'>translator</span> <span class='op'>=</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>GoogleFree</span><span class='pn'>;</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>google</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='pn'>{</span><br />
    <span class='fn'>language</span> <span class='op'>=</span> <span class='st'>"lt"</span><span class='pn'>,</span> <span class='cm'>//https://cloud.google.com/translate/docs/languages</span><br />
    <span class='fn'>registryApiKey</span> <span class='op'>=</span> <span class='pn'>(</span><span class='vr'>key</span><span class='pn'>:</span> <span class='st'>@"HKEY_CURRENT_USER&#92;Software&#92;Au"</span><span class='pn'>,</span> <span class='vr'>value</span><span class='pn'>:</span> <span class='st'>"Google Cloud API key"</span><span class='pn'>),</span> <span class='cm'>//for Translator.GoogleCloud only. Don't need to edit these strings, just create the Registry key and value.</span><br />
<span class='pn'>};</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>microsoft</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='pn'>{</span><br />
    <span class='fn'>language</span> <span class='op'>=</span> <span class='st'>"lt"</span><span class='pn'>,<br />
};</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>openai</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='pn'>{</span><br />
    <span class='fn'>language</span> <span class='op'>=</span> <span class='st'>"Lithuanian"</span><span class='pn'>,</span><br />
    <span class='fn'>model</span> <span class='op'>=</span> <span class='st'>"gpt-4-turbo-preview"</span><span class='pn'>,</span> <span class='cm'>//"gpt-3.5-turbo", "gpt-4", "gpt-4-turbo-preview", etc</span><br />
    <span class='fn'>registryApiKey</span> <span class='op'>=</span> <span class='pn'>(</span><span class='vr'>key</span><span class='pn'>:</span> <span class='st'>@"HKEY_CURRENT_USER&#92;Software&#92;Au"</span><span class='pn'>,</span> <span class='vr'>value</span><span class='pn'>:</span> <span class='st'>"OpenAI API key"</span><span class='pn'>),</span> <span class='cm'>//Don't need to edit these strings, just create the Registry key and value.</span><br />
<span class='pn'>};</span><br />
<br />
<span class='pd'>#endregion<br />
<br />
<br />
#if</span> SPELLCHECK<br />
<span class='ex'>var dict = @"C:&#92;Program Files&#92;LibreOffice&#92;share&#92;extensions&#92;dict-en&#92;en_US.dic";<br />
//var dict = @"C:&#92;Program Files&#92;LibreOffice&#92;share&#92;wordbook&#92;technical.dic";<br />
var dictionary = WeCantSpell.Hunspell.WordList.CreateFromFiles(dict);<br />
</span><span class='pd'>#endif</span><br />
<br />
EditorExtension<span class='op'>.</span>WindowReady <span class='op'>+=</span> <span class='pn'>()</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
    Panels<span class='op'>.</span>Recipe<span class='op'>.</span>OpeningRecipe <span class='op'>=</span> <span class='fn'>_OpeningRecipe</span><span class='pn'>;<br />
};</span><br />
<br />
<span class='kw'>void</span> <span class='fn'>_OpeningRecipe</span><span class='pn'>(</span>PanelRecipe<span class='op'>.</span>OpeningRecipeArgs <span class='vr'>e</span><span class='pn'>) {</span><br />
    <span class='cm'>//convert e to text:<br />
    //Name<br />
    //<br />
    //e.parts[0] //text with replaced tags<br />
    //<br />
    //#&lt;1&gt;# //replaced code<br />
    //<br />
    //e.parts[2] //text with replaced tags<br />
    //<br />
    //#&lt;3&gt;# //replaced code<br />
    //<br />
    //...</span><br />
    <br />
<span class='pd'>#if</span> DEBUG_PRINT<br />
<span class='ex'>    print.clear();<br />
</span><span class='pd'>#endif</span><br />
    <span class='tp'>List</span><span class='pn'>&lt;(</span><span class='kw'>string</span> s<span class='pn'>,</span> <span class='kw'>int</span> lenEndTag<span class='pn'>,</span> <span class='kw'>int</span> lenText<span class='pn'>)&gt;</span> <span class='vr'>tags</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>();</span><br />
    <span class='tp'>regexp</span> <span class='vr'>rx</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>(</span><span class='st'>@"&lt;_&gt;(?&lt;g1&gt;[&#92;s&#92;S]*?)&lt;/_&gt;|&lt;image </span><span class='se'>""</span><span class='st'>[^</span><span class='se'>""</span><span class='st'>]*</span><span class='se'>""</span><span class='st'>&gt;|&lt;([+&#92;.]?[a-z]+)(?&lt;g3&gt; [^&gt;]+)?&gt;(?&lt;g2&gt;[^&lt;]++|(?R))+&lt;(?:/&#92;1)?&gt;"</span><span class='pn'>);</span><br />
    <span class='kw'>var</span> <span class='vr'>sb</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>StringBuilder</span><span class='pn'>(</span><span class='fn'>_PrepareTitle</span><span class='pn'>(</span><span class='vr'>e</span><span class='op'>.</span>name<span class='pn'>,</span> <span class='vr'>tags</span><span class='pn'>)</span> <span class='op'>+</span> <span class='st'>"</span><span class='se'>&#92;r&#92;n&#92;r&#92;n</span><span class='st'>"</span><span class='pn'>);</span><br />
    <span class='kw'>int</span> <span class='vr'>ipart</span> <span class='op'>= -</span><span class='nr'>1</span><span class='pn'>;</span><br />
    <span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>var</span> <span class='vr'>v</span> <span class='kw'>in</span> <span class='vr'>e</span><span class='op'>.</span>parts<span class='pn'>) {</span><br />
        <span class='vr'>ipart</span><span class='op'>++</span><span class='pn'>;</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>v</span><span class='op'>.</span>isText<span class='pn'>) {</span><br />
            <span class='cm'>//print.it(v.s); print.it("----");</span><br />
            <span class='vr'>sb</span><span class='op'>.</span><span class='fn'>AppendLine</span><span class='pn'>(</span><span class='vr'>rx</span><span class='op'>.</span><span class='fn'>Replace</span><span class='pn'>(</span><span class='vr'>v</span><span class='op'>.</span>s<span class='pn'>,</span> <span class='fn'>_Repl1</span><span class='pn'>));</span><br />
            <br />
            <span class='kw'>string</span> <span class='fn'>_Repl1</span><span class='pn'>(</span><span class='tp'>RXMatch</span> <span class='vr'>m</span><span class='pn'>) {</span><br />
                <span class='kw'>int</span> <span class='vr'>i</span> <span class='op'>=</span> <span class='vr'>tags</span><span class='op'>.</span><span class='fn'>Count</span><span class='pn'>;</span><br />
                <span class='kw'>var</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='vr'>m</span><span class='op'>.</span><span class='fn'>Value</span><span class='pn'>;</span><br />
                <span class='kw'>var</span> <span class='vr'>g</span> <span class='op'>=</span> <span class='vr'>m</span><span class='pn'>[</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;_&gt;"</span><span class='pn'>)</span> <span class='op'>?</span> <span class='st'>"g1"</span> <span class='op'>:</span> <span class='st'>"g2"</span><span class='pn'>];</span><br />
                <span class='kw'>var</span> <span class='vr'>t</span> <span class='op'>=</span> <span class='vr'>g</span><span class='op'>.</span><span class='fn'>Value</span><span class='pn'>;</span><br />
                <span class='kw'>bool</span> <span class='vr'>translate</span> <span class='op'>=</span> <span class='kw'>false</span><span class='pn'>;</span><br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>t</span><span class='op'>.</span><span class='fn'>Contains</span><span class='pn'>(</span><span class='st'>'&lt;'</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//nested tags (mostly keywords, identifiers)</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;_&gt;"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//don't translate</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;image"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;."</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//code, keyword</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;b&gt;"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//mostly identifiers and UI</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;i&gt;"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//mostly parameters and terms</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;mono&gt;"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//keys, hotkeys</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;+see"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//identifiers<br />
                //else if (s.Starts("&lt;+recipe")) { /*print.it(t);*/ } //</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"&lt;+nuget"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>t</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='st'>"http"</span><span class='pn'>)) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//URL</span><br />
                <span class='kw'>else if</span> <span class='pn'>(</span><span class='vr'>t</span><span class='op'>.</span><span class='fn'>FindAny</span><span class='pn'>(</span><span class='st'>".;"</span><span class='pn'>)</span> <span class='op'>&gt;=</span> <span class='nr'>0</span><span class='pn'>) {</span> <span class='cm'>/*print.it(t);*/</span> <span class='pn'>}</span> <span class='cm'>//avoid breaking the sentence</span><br />
                <span class='kw'>else</span> <span class='pn'>{</span><br />
                    <span class='cm'>//print.it(s);</span><br />
                    <span class='vr'>translate</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>;</span><br />
                    <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>m</span><span class='pn'>[</span><span class='st'>"g3"</span><span class='pn'>]</span><span class='op'>.</span><span class='fn'>Exists</span><span class='pn'>) {</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='st'>&#36;"</span><span class='pn'>{</span><span class='vr'>s</span><span class='pn'>[..(</span><span class='vr'>g</span><span class='op'>.</span><span class='fn'>Start</span> <span class='op'>-</span> <span class='vr'>m</span><span class='op'>.</span><span class='fn'>Start</span> <span class='op'>-</span> <span class='nr'>1</span><span class='pn'>)]}</span><span class='st'> </span><span class='se'>&#92;"</span><span class='pn'>{</span><span class='vr'>t</span><span class='pn'>}</span><span class='se'>&#92;"</span><span class='st'>&gt;</span><span class='pn'>{</span><span class='vr'>t</span><span class='pn'>}{</span><span class='vr'>s</span><span class='pn'>[(</span><span class='vr'>g</span><span class='op'>.</span><span class='fn'>End</span> <span class='op'>-</span> <span class='vr'>m</span><span class='op'>.</span><span class='fn'>Start</span><span class='pn'>)..]}</span><span class='st'>"</span><span class='pn'>;</span> <span class='cm'>/*print.it(t, s);*/</span> <span class='pn'>}</span> <span class='cm'>//&lt;tag&gt;text&lt;&gt; -&gt; &lt;tag "text"&gt;translatedText&lt;&gt;</span><br />
                <span class='pn'>}</span><br />
                <span class='cm'>//print.it(s, t);</span><br />
                <span class='vr'>tags</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>translate</span> <span class='op'>?</span> <span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>m</span><span class='op'>.</span><span class='fn'>End</span> <span class='op'>-</span> <span class='vr'>g</span><span class='op'>.</span><span class='fn'>End</span><span class='pn'>,</span> <span class='vr'>t</span><span class='op'>.</span><span class='fn'>Length</span><span class='pn'>)</span> <span class='op'>:</span> <span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>));</span><br />
                <span class='kw'>return</span> <span class='vr'>translate</span> <span class='op'>?</span> <span class='st'>&#36;"&lt;Q</span><span class='pn'>{</span><span class='vr'>i</span><span class='pn'>}</span><span class='st'>&gt;</span><span class='pn'>{</span><span class='vr'>t</span><span class='pn'>}</span><span class='st'>&lt;/Q</span><span class='pn'>{</span><span class='vr'>i</span><span class='pn'>}</span><span class='st'>&gt;"</span> <span class='op'>:</span> <span class='st'>&#36;"[Q</span><span class='pn'>{</span><span class='vr'>i</span><span class='pn'>}</span><span class='st'>]"</span><span class='pn'>;<br />
            }<br />
        }</span> <span class='kw'>else</span> <span class='pn'>{</span><br />
            <span class='vr'>sb</span><span class='op'>.</span><span class='fn'>AppendLine</span><span class='pn'>(</span><span class='st'>&#36;"</span><span class='se'>&#92;r&#92;n</span><span class='st'>#&lt;</span><span class='pn'>{</span><span class='vr'>ipart</span><span class='pn'>}</span><span class='st'>&gt;#</span><span class='se'>&#92;r&#92;n</span><span class='st'>"</span><span class='pn'>);<br />
        }<br />
    }</span><br />
    <span class='kw'>var</span> <span class='vr'>text</span> <span class='op'>=</span> <span class='vr'>sb</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>();</span><br />
    <br />
<span class='pd'>#if</span> DEBUG_PRINT<br />
<span class='ex'>    //if (text.RxIsMatch(@"&lt;[+&#92;.]?[a-z_]")) {<br />
    //    print.it(&#36;"&lt;&gt;&lt;lc yellow&gt;{e.name}    &lt;c red&gt;not all tags replaced&lt;&gt;&lt;&gt;");<br />
    //    print.it(text);<br />
    //    print.it("----");<br />
    //    print.it(e.parts.Select(o =&gt; o.s));<br />
    //    return;<br />
    //}<br />
    <br />
    //if (text.RxIsMatch(@"(?i)(?&lt;![a-z])'")) {<br />
    //    print.it(&#36;"&lt;&gt;&lt;lc yellow&gt;{e.name}&lt;&gt;");<br />
    //    print.it(text);<br />
    //}<br />
    <br />
    //var s1 = text.RxReplace(@""".+?""", &#36;"&lt;bc yellow&gt;&#36;0&lt;&gt;");<br />
    //if (s1 != text) {<br />
    //    print.it(&#36;"&lt;&gt;&lt;lc yellow&gt;{e.name}&lt;&gt;&#92;r&#92;n{s1}");<br />
    //}<br />
    <br />
    //print.it(&#36;"&lt;&gt;&lt;lc yellowgreen&gt;{e.name}&lt;&gt;");<br />
    //print.it(text);<br />
    //if (text.Length &gt; 1500) print.it(text.Length);<br />
    //if (text.Length &gt; 1900) {<br />
    //    print.it(&#36;"&lt;&gt;&lt;lc yellowgreen&gt;{e.name}&lt;&gt;");<br />
    //    print.it(text);<br />
    //}<br />
</span><span class='pd'>#endif<br />
    <br />
#if</span> SPELLCHECK<br />
<span class='ex'>    text = text.RxReplace(@"&#92;b[A-Za-z][a-z']+&#92;b", m =&gt; {<br />
        var s = m.Value;<br />
        bool ok = dictionary.Check(s);<br />
        if (!ok) {<br />
            s = &#36;"&lt;bc yellow&gt;{s}&lt;&gt;";<br />
        }<br />
        return s;<br />
    });<br />
</span><span class='pd'>#else</span><br />
    <br />
    <span class='cm'>//translate<br />
    <br />
    //print.it(text); print.it("-------");<br />
    //return;<br />
    <br />
    //perf.first();</span><br />
    <br />
    <span class='vr'>text</span> <span class='op'>=</span> <span class='fn'>_Translate</span><span class='pn'>(</span><span class='vr'>e</span><span class='op'>.</span>name<span class='pn'>,</span> <span class='vr'>text</span><span class='pn'>,</span> <span class='kw'>true</span><span class='pn'>);</span><br />
    <br />
    <span class='cm'>//perf.nw();<br />
    //print.it(text);<br />
    //return;</span><br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>text</span> <span class='op'>==</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='kw'>return</span><span class='pn'>;</span><br />
<span class='pd'>#endif</span><br />
    <br />
    <span class='tp'>regexp</span> <span class='vr'>rxRT1</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>,</span> <span class='vr'>rxRT2</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>;</span><br />
    <br />
    <span class='cm'>//get translated name</span><br />
    <br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>text</span><span class='op'>.</span><span class='fn'>RxMatch</span><span class='pn'>(</span><span class='st'>@"&#92;R&#92;R"</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='kw'>out</span> <span class='tp'>RXGroup</span> <span class='vr'>g1</span><span class='pn'>)) {</span> <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>"Failed to translate. Result:</span><span class='se'>&#92;r&#92;n</span><span class='st'>"</span> <span class='op'>+</span> <span class='vr'>text</span><span class='pn'>);</span> <span class='kw'>return</span><span class='pn'>; }</span><br />
    <span class='vr'>e</span><span class='op'>.</span>name <span class='op'>=</span> <span class='fn'>_RestoreTags</span><span class='pn'>(</span><span class='vr'>text</span><span class='pn'>[..</span><span class='vr'>g1</span><span class='op'>.</span><span class='fn'>Start</span><span class='pn'>]</span><span class='op'>.</span><span class='fn'>Trim</span><span class='pn'>(),</span> <span class='op'>-</span><span class='nr'>1</span><span class='pn'>);</span><br />
    <span class='kw'>int</span> <span class='vr'>i</span> <span class='op'>=</span> <span class='vr'>g1</span><span class='op'>.</span><span class='fn'>End</span><span class='pn'>;</span><br />
    <br />
    <span class='cm'>//parse translated text parts and create new e.parts with translated texts (with restored tags) and restored codes</span><br />
    <br />
    <span class='tp'>List</span><span class='pn'>&lt;(</span><span class='kw'>bool</span> isText<span class='pn'>,</span> <span class='kw'>string</span> s<span class='pn'>)&gt;</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>();</span><br />
    <span class='tp'>regexp</span> <span class='vr'>rx2</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>(</span><span class='st'>@"&#92;R+#&lt;(&#92;d+)&gt;#&#92;R*"</span><span class='pn'>);</span><br />
    <span class='vr'>ipart</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>;</span><br />
    <span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>var</span> <span class='vr'>m</span> <span class='kw'>in</span> <span class='vr'>rx2</span><span class='op'>.</span><span class='fn'>FindAll</span><span class='pn'>(</span><span class='vr'>text</span><span class='pn'>)) {</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>m</span><span class='op'>.</span><span class='fn'>Start</span> <span class='op'>&gt;</span> <span class='vr'>i</span><span class='pn'>)</span> <span class='vr'>r</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>((</span><span class='kw'>true</span><span class='pn'>,</span> <span class='fn'>_RestoreTags</span><span class='pn'>(</span><span class='vr'>text</span><span class='pn'>[</span><span class='vr'>i</span><span class='pn'>..</span><span class='vr'>m</span><span class='op'>.</span><span class='fn'>Start</span><span class='pn'>],</span> <span class='vr'>ipart</span><span class='op'>++</span><span class='pn'>)));</span><br />
        <span class='vr'>r</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>e</span><span class='op'>.</span>parts<span class='pn'>[</span><span class='vr'>m</span><span class='pn'>[</span><span class='nr'>1</span><span class='pn'>]</span><span class='op'>.</span><span class='fn'>Value</span><span class='op'>.</span><span class='fn'>ToInt</span><span class='pn'>()]);</span><br />
        <span class='vr'>ipart</span><span class='op'>++</span><span class='pn'>;</span><br />
        <span class='vr'>i</span> <span class='op'>=</span> <span class='vr'>m</span><span class='op'>.</span><span class='fn'>End</span><span class='pn'>;<br />
    }</span><br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>text</span><span class='op'>.</span><span class='fn'>Length</span> <span class='op'>&gt;</span> <span class='vr'>i</span><span class='pn'>)</span> <span class='vr'>r</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>((</span><span class='kw'>true</span><span class='pn'>,</span> <span class='fn'>_RestoreTags</span><span class='pn'>(</span><span class='vr'>text</span><span class='pn'>[</span><span class='vr'>i</span><span class='pn'>..],</span> <span class='vr'>ipart</span><span class='pn'>)));</span><br />
    <br />
<span class='pd'>#if</span> DEBUG_PRINT<br />
<span class='ex'>    //print.it(&#36;"&lt;&gt;&lt;lc yellowgreen&gt;{e.name}&lt;&gt;");<br />
    //print.it(r);<br />
</span><span class='pd'>#endif</span><br />
    <br />
    <span class='vr'>e</span><span class='op'>.</span>parts <span class='op'>=</span> <span class='vr'>r</span><span class='pn'>;</span><br />
    <br />
    <span class='kw'>string</span> <span class='fn'>_RestoreTags</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>s</span><span class='pn'>,</span> <span class='kw'>int</span> <span class='vr'>ipart</span><span class='pn'>) {</span><br />
        <span class='vr'>rxRT1</span> <span class='op'>??=</span> <span class='kw'>new</span><span class='pn'>(</span><span class='st'>@"&#92;[Q&#92;d+&#92;]"</span><span class='pn'>);</span><br />
        <span class='vr'>s</span> <span class='op'>=</span> <span class='vr'>rxRT1</span><span class='op'>.</span><span class='fn'>Replace</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>m</span> <span class='op'>=&gt;</span> <span class='vr'>tags</span><span class='pn'>[</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>ToInt</span><span class='pn'>(</span><span class='vr'>m</span><span class='op'>.</span><span class='fn'>Start</span> <span class='op'>+</span> <span class='nr'>2</span><span class='pn'>)]</span><span class='op'>.</span><span class='vr'>s</span><span class='pn'>);</span><br />
        <span class='vr'>rxRT2</span> <span class='op'>??=</span> <span class='kw'>new</span><span class='pn'>(</span><span class='st'>@"&lt;Q(&#92;d+)&gt;([^&lt;]++)&lt;/Q&#92;1&gt;"</span><span class='pn'>);</span><br />
        <span class='vr'>s</span> <span class='op'>=</span> <span class='vr'>rxRT2</span><span class='op'>.</span><span class='fn'>Replace</span><span class='pn'>(</span><span class='vr'>s</span><span class='pn'>,</span> <span class='vr'>m</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
            <span class='kw'>var</span> <span class='vr'>translatedLinkText</span> <span class='op'>=</span> <span class='vr'>m</span><span class='pn'>[</span><span class='nr'>2</span><span class='pn'>]</span><span class='op'>.</span><span class='fn'>Value</span><span class='pn'>;</span><br />
            <span class='kw'>var</span> <span class='vr'>tag</span> <span class='op'>=</span> <span class='vr'>tags</span><span class='pn'>[</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>ToInt</span><span class='pn'>(</span><span class='vr'>m</span><span class='op'>.</span><span class='fn'>Start</span> <span class='op'>+</span> <span class='nr'>2</span><span class='pn'>)];</span><br />
            <span class='kw'>var</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='vr'>tag</span><span class='op'>.</span><span class='vr'>s</span><span class='pn'>[..</span><span class='op'>^</span><span class='pn'>(</span><span class='vr'>tag</span><span class='op'>.</span><span class='vr'>lenEndTag</span> <span class='op'>+</span> <span class='vr'>tag</span><span class='op'>.</span><span class='vr'>lenText</span><span class='pn'>)]</span> <span class='op'>+</span> <span class='vr'>translatedLinkText</span> <span class='op'>+</span> <span class='vr'>tag</span><span class='op'>.</span><span class='vr'>s</span><span class='pn'>[</span><span class='op'>^</span><span class='vr'>tag</span><span class='op'>.</span><span class='vr'>lenEndTag</span><span class='pn'>..];</span><br />
            <span class='cm'>//print.it(r);</span><br />
            <span class='kw'>return</span> <span class='vr'>r</span><span class='pn'>;<br />
        });</span><br />
        <br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>ipart</span> <span class='op'>&gt;=</span> <span class='nr'>0</span><span class='pn'>)</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='st'>&#36;"&lt;lc #e0ffe0&gt;</span><span class='pn'>{</span><span class='vr'>e</span><span class='op'>.</span>parts<span class='pn'>[</span><span class='vr'>ipart</span><span class='pn'>]</span><span class='op'>.</span>s<span class='pn'>}</span><span class='st'>&lt;&gt;</span><span class='se'>&#92;r&#92;n&#92;r&#92;n</span><span class='st'>&lt;lc #ffffe0&gt;</span><span class='pn'>{</span><span class='vr'>s</span><span class='pn'>}</span><span class='st'>&lt;&gt;"</span><span class='pn'>;</span><br />
        <br />
        <span class='kw'>return</span> <span class='vr'>s</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='kw'>static string</span> <span class='fn'>_PrepareTitle</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>s</span><span class='pn'>,</span> <span class='tp'>List</span><span class='pn'>&lt;(</span><span class='kw'>string</span> s<span class='pn'>,</span> <span class='kw'>int</span> i1<span class='pn'>,</span> <span class='kw'>int</span> i2<span class='pn'>)&gt;</span> <span class='vr'>tags</span><span class='pn'>) {</span><br />
        <span class='tp'>HashSet</span><span class='pn'>&lt;</span><span class='kw'>string</span><span class='pn'>&gt;</span> <span class='vr'>dontTranslate</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>() {</span> <span class='st'>"bool"</span><span class='pn'>,</span> <span class='st'>"null"</span><span class='pn'>,</span> <span class='st'>"default"</span><span class='pn'>,</span> <span class='st'>"class"</span><span class='pn'>,</span> <span class='st'>"struct"</span><span class='pn'>,</span> <span class='st'>"generic"</span><span class='pn'>,</span> <span class='st'>"nullable"</span><span class='pn'>,</span> <span class='st'>"tuple"</span><span class='pn'>,</span> <span class='st'>"using"</span><span class='pn'>,</span> <span class='st'>"if"</span><span class='pn'>,</span> <span class='st'>"else"</span><span class='pn'>,</span> <span class='st'>"switch"</span><span class='pn'>,</span> <span class='st'>"for"</span><span class='pn'>,</span> <span class='st'>"return"</span><span class='pn'>,</span> <span class='st'>"try"</span><span class='pn'>,</span> <span class='st'>"catch"</span><span class='pn'>,</span> <span class='st'>"finally"</span><span class='pn'>,</span> <span class='st'>"throw"</span><span class='pn'>,</span> <span class='st'>"delegate"</span><span class='pn'>,</span> <span class='st'>"event"</span><span class='pn'>, };</span><br />
        <br />
        <span class='vr'>s</span> <span class='op'>=</span> <span class='vr'>s</span><span class='op'>.</span><span class='fn'>RxReplace</span><span class='pn'>(</span><span class='st'>@"&#92;b[a-zA-Z]+&#92;b"</span><span class='pn'>,</span> <span class='vr'>m</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
            <span class='kw'>var</span> <span class='vr'>k</span> <span class='op'>=</span> <span class='vr'>m</span><span class='op'>.</span><span class='fn'>Value</span><span class='pn'>;</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>dontTranslate</span><span class='op'>.</span><span class='fn'>Contains</span><span class='pn'>(</span><span class='vr'>k</span><span class='pn'>)</span> <span class='op'>||</span> <span class='pn'>(</span><span class='vr'>m</span><span class='op'>.</span><span class='fn'>Start</span> <span class='op'>&gt;</span> <span class='nr'>0</span> <span class='op'>&amp;&amp;</span> <span class='kw'>char</span><span class='op'>.</span><span class='fn'>IsUpper</span><span class='pn'>(</span><span class='vr'>k</span><span class='pn'>[</span><span class='nr'>0</span><span class='pn'>]))) {</span><br />
                <span class='kw'>int</span> <span class='vr'>i</span> <span class='op'>=</span> <span class='vr'>tags</span><span class='op'>.</span><span class='fn'>Count</span><span class='pn'>;</span><br />
                <span class='vr'>tags</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>((</span><span class='vr'>k</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>));</span><br />
                <span class='kw'>return</span> <span class='st'>&#36;"[Q</span><span class='pn'>{</span><span class='vr'>i</span><span class='pn'>}</span><span class='st'>]"</span><span class='pn'>;<br />
            }</span><br />
            <span class='kw'>return</span> <span class='vr'>k</span><span class='pn'>;<br />
        });</span><br />
        <span class='kw'>return</span> <span class='vr'>s</span><span class='pn'>;<br />
    }<br />
}</span><br />
<br />
<span class='pd'>#pragma warning disable</span> CS0162 <span class='cm'>//Unreachable code detected</span><br />
<span class='kw'>string</span> <span class='fn'>_Translate</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>text</span><span class='pn'>,</span> <span class='kw'>bool</span> <span class='vr'>debugPrint</span><span class='pn'>) {</span><br />
    <span class='kw'>string</span> <span class='vr'>targetLang</span> <span class='op'>=</span> <span class='cn'>translator</span> <span class='kw'>switch</span> <span class='pn'>{</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>OpenAI</span> <span class='op'>=&gt;</span> <span class='vr'>openai</span><span class='op'>.</span><span class='fn'>language</span><span class='pn'>,</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>Microsoft</span> <span class='op'>=&gt;</span> <span class='vr'>microsoft</span><span class='op'>.</span><span class='fn'>language</span><span class='pn'>,</span> <span class='kw'>_</span> <span class='op'>=&gt;</span> <span class='vr'>google</span><span class='op'>.</span><span class='fn'>language</span> <span class='pn'>};</span><br />
    <br />
    <span class='tp'>_Cache</span> <span class='vr'>cache</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>();</span><br />
    <span class='kw'>string</span> <span class='vr'>cacheDir</span> <span class='op'>=</span> <span class='cn'>translator</span> <span class='kw'>switch</span> <span class='pn'>{</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>OpenAI</span> <span class='op'>=&gt;</span> <span class='st'>"openai "</span><span class='pn'>,</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>Microsoft</span> <span class='op'>=&gt;</span> <span class='st'>"microsoft "</span><span class='pn'>,</span> <span class='kw'>_</span> <span class='op'>=&gt;</span> <span class='st'>"google "</span> <span class='pn'>};</span><br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>cache</span><span class='op'>.</span><span class='fn'>Get</span><span class='pn'>(</span><span class='vr'>cacheDir</span> <span class='op'>+</span> <span class='vr'>targetLang</span><span class='pn'>,</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='vr'>text</span><span class='pn'>,</span> <span class='kw'>out var</span> <span class='vr'>translated</span><span class='pn'>))</span> <span class='kw'>return</span> <span class='vr'>translated</span><span class='pn'>;</span><br />
    <br />
    <span class='kw'>string</span> <span class='vr'>apiKey</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>;</span><br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='cn'>translator</span> <span class='op'>==</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>Microsoft</span><span class='pn'>) {</span><br />
        <span class='vr'>apiKey</span> <span class='op'>=</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>http</span><span class='op'>.</span><span class='fn'>Get</span><span class='pn'>(</span><span class='st'>"https://edge.microsoft.com/translate/auth"</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Text</span><span class='pn'>();<br />
    }</span> <span class='kw'>else if</span> <span class='pn'>(</span><span class='cn'>translator</span> <span class='op'>!=</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>GoogleFree</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>rkv</span> <span class='op'>=</span> <span class='cn'>translator</span> <span class='op'>==</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>OpenAI</span> <span class='op'>?</span> <span class='vr'>openai</span><span class='op'>.</span><span class='fn'>registryApiKey</span> <span class='op'>:</span> <span class='vr'>google</span><span class='op'>.</span><span class='fn'>registryApiKey</span><span class='pn'>;</span><br />
        <span class='vr'>apiKey</span> <span class='op'>=</span> <span class='tp'>Registry</span><span class='op'>.</span><span class='fn'>GetValue</span><span class='pn'>(</span><span class='vr'>rkv</span><span class='op'>.</span><span class='vr'>key</span><span class='pn'>,</span> <span class='vr'>rkv</span><span class='op'>.</span><span class='vr'>value</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>)</span> <span class='kw'>as string</span> <span class='op'>??</span> <span class='kw'>throw new</span> <span class='tp'>AuException</span><span class='pn'>(</span><span class='st'>"no API key in registry"</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='cn'>translator</span> <span class='op'>==</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>OpenAI</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>json</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>JsonObject</span> <span class='pn'>{<br />
            [</span><span class='st'>"model"</span><span class='pn'>]</span> <span class='op'>=</span> <span class='vr'>openai</span><span class='op'>.</span><span class='fn'>model</span><span class='pn'>,<br />
            [</span><span class='st'>"messages"</span><span class='pn'>]</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>JsonArray</span><span class='pn'>(</span><span class='kw'>new</span> <span class='tp'>JsonObject</span> <span class='pn'>{ [</span><span class='st'>"role"</span><span class='pn'>]</span> <span class='op'>=</span> <span class='st'>"user"</span><span class='pn'>, [</span><span class='st'>"content"</span><span class='pn'>]</span> <span class='op'>=</span> <span class='st'>&#36;"Translate English to </span><span class='pn'>{</span><span class='vr'>targetLang</span><span class='pn'>}</span><span class='st'>:</span><span class='se'>&#92;r&#92;n</span><span class='pn'>{</span><span class='vr'>text</span><span class='pn'>}</span><span class='st'>"</span> <span class='pn'>})<br />
        };</span><br />
        <br />
        <span class='kw'>var</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>http</span><span class='op'>.</span><span class='fn'>Post</span><span class='pn'>(</span><span class='st'>"https://api.openai.com/v1/chat/completions"</span><span class='pn'>,</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>jsonContent</span><span class='pn'>(</span><span class='vr'>json</span><span class='op'>.</span><span class='fn'>ToJsonString</span><span class='pn'>()), [</span><span class='st'>&#36;"Authorization: Bearer </span><span class='pn'>{</span><span class='vr'>apiKey</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>]);</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>IsSuccessStatusCode</span><span class='pn'>) {</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>debugPrint</span><span class='pn'>)</span> <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>&#36;"api.openai.com error: </span><span class='pn'>{(</span><span class='kw'>int</span><span class='pn'>)</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>StatusCode</span><span class='pn'>}</span><span class='st'> </span><span class='pn'>{</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>ReasonPhrase</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>);</span><br />
            <span class='kw'>return null</span><span class='pn'>;<br />
        }</span><br />
        <span class='kw'>var</span> <span class='vr'>j</span> <span class='op'>=</span> <span class='vr'>r</span><span class='op'>.</span><span class='fn'>Json</span><span class='pn'>();</span><br />
        <br />
        <span class='cm'>//print.it(j.ToJsonString());</span><br />
        <span class='vr'>translated</span> <span class='op'>=</span> <span class='pn'>(</span><span class='kw'>string</span><span class='pn'>)</span><span class='vr'>j</span><span class='pn'>[</span><span class='st'>"choices"</span><span class='pn'>][</span><span class='nr'>0</span><span class='pn'>][</span><span class='st'>"message"</span><span class='pn'>][</span><span class='st'>"content"</span><span class='pn'>];<br />
    }</span> <span class='kw'>else if</span> <span class='pn'>(</span><span class='cn'>translator</span> <span class='op'>==</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>Microsoft</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>json</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>JsonArray</span><span class='pn'>(</span><span class='kw'>new</span> <span class='tp'>JsonObject</span> <span class='pn'>{ [</span><span class='st'>"Text"</span><span class='pn'>]</span> <span class='op'>=</span> <span class='vr'>text</span> <span class='pn'>});</span><br />
        <span class='kw'>var</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>http</span><span class='op'>.</span><span class='fn'>Post</span><span class='pn'>(</span><span class='st'>&#36;"https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&amp;from=en&amp;to=</span><span class='pn'>{</span><span class='vr'>targetLang</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>,</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>jsonContent</span><span class='pn'>(</span><span class='vr'>json</span><span class='op'>.</span><span class='fn'>ToJsonString</span><span class='pn'>()),</span> <span class='vr'>headers</span><span class='pn'>: [</span><span class='st'>&#36;"Authorization: Bearer </span><span class='pn'>{</span><span class='vr'>apiKey</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>]);</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>IsSuccessStatusCode</span><span class='pn'>) {</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>debugPrint</span><span class='pn'>)</span> <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>&#36;"api.cognitive.microsofttranslator.com error: </span><span class='pn'>{(</span><span class='kw'>int</span><span class='pn'>)</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>StatusCode</span><span class='pn'>}</span><span class='st'> </span><span class='pn'>{</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>ReasonPhrase</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>);</span><br />
            <span class='kw'>return null</span><span class='pn'>;<br />
        }</span><br />
        <span class='kw'>var</span> <span class='vr'>j</span> <span class='op'>=</span> <span class='vr'>r</span><span class='op'>.</span><span class='fn'>Json</span><span class='pn'>();</span><br />
        <br />
        <span class='vr'>translated</span> <span class='op'>=</span> <span class='pn'>(</span><span class='kw'>string</span><span class='pn'>)</span><span class='vr'>j</span><span class='pn'>[</span><span class='nr'>0</span><span class='pn'>][</span><span class='st'>"translations"</span><span class='pn'>][</span><span class='nr'>0</span><span class='pn'>][</span><span class='st'>"text"</span><span class='pn'>];<br />
    }</span> <span class='kw'>else if</span> <span class='pn'>(</span><span class='cn'>translator</span> <span class='op'>==</span> <span class='tp'>Translator</span><span class='op'>.</span><span class='cn'>GoogleCloud</span><span class='pn'>) {</span><br />
        <span class='kw'>var</span> <span class='vr'>url</span> <span class='op'>=</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>urlAppend</span><span class='pn'>(</span><span class='st'>"https://translation.googleapis.com/language/translate/v2"</span><span class='pn'>,</span> <span class='st'>"key="</span> <span class='op'>+</span> <span class='vr'>apiKey</span><span class='pn'>);</span><br />
        <span class='kw'>var</span> <span class='vr'>json</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='pn'>{</span> <span class='fn'>q</span> <span class='op'>=</span> <span class='vr'>text</span><span class='pn'>,</span> <span class='fn'>source</span> <span class='op'>=</span> <span class='st'>"en"</span><span class='pn'>,</span> <span class='fn'>target</span> <span class='op'>=</span> <span class='vr'>targetLang</span><span class='pn'>,</span> <span class='fn'>format</span> <span class='op'>=</span> <span class='st'>"text"</span> <span class='pn'>};</span><br />
        <span class='kw'>var</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>http</span><span class='op'>.</span><span class='fn'>Post</span><span class='pn'>(</span><span class='vr'>url</span><span class='pn'>,</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>jsonContent</span><span class='pn'>(</span><span class='vr'>json</span><span class='pn'>));</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>IsSuccessStatusCode</span><span class='pn'>) {</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>debugPrint</span><span class='pn'>)</span> <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>&#36;"translation.googleapis.com error: </span><span class='pn'>{(</span><span class='kw'>int</span><span class='pn'>)</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>StatusCode</span><span class='pn'>}</span><span class='st'> </span><span class='pn'>{</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>ReasonPhrase</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>);</span><br />
            <span class='kw'>return null</span><span class='pn'>;<br />
        }</span><br />
        <span class='kw'>var</span> <span class='vr'>j</span> <span class='op'>=</span> <span class='vr'>r</span><span class='op'>.</span><span class='fn'>Json</span><span class='pn'>();</span><br />
        <br />
        <span class='vr'>translated</span> <span class='op'>=</span> <span class='pn'>(</span><span class='kw'>string</span><span class='pn'>)</span><span class='vr'>j</span><span class='pn'>[</span><span class='st'>"data"</span><span class='pn'>][</span><span class='st'>"translations"</span><span class='pn'>][</span><span class='nr'>0</span><span class='pn'>][</span><span class='st'>"translatedText"</span><span class='pn'>];<br />
    }</span> <span class='kw'>else</span> <span class='pn'>{</span><br />
        <span class='kw'>var</span> <span class='vr'>url</span> <span class='op'>=</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>urlAppend</span><span class='pn'>(</span><span class='st'>"https://translate.googleapis.com/translate_a/single"</span><span class='pn'>,</span> <span class='st'>"client=gtx"</span><span class='pn'>,</span> <span class='st'>"sl=en"</span><span class='pn'>,</span> <span class='st'>"tl="</span> <span class='op'>+</span> <span class='vr'>targetLang</span><span class='pn'>,</span> <span class='st'>"dt=t"</span><span class='pn'>,</span> <span class='st'>"q="</span> <span class='op'>+</span> <span class='vr'>text</span><span class='pn'>);</span><br />
        <span class='kw'>var</span> <span class='vr'>r</span> <span class='op'>=</span> <span class='tp'>internet</span><span class='op'>.</span><span class='fn'>http</span><span class='op'>.</span><span class='fn'>Get</span><span class='pn'>(</span><span class='vr'>url</span><span class='pn'>);</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>IsSuccessStatusCode</span><span class='pn'>) {</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>debugPrint</span><span class='pn'>)</span> <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>&#36;"translate.googleapis.com error: </span><span class='pn'>{(</span><span class='kw'>int</span><span class='pn'>)</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>StatusCode</span><span class='pn'>}</span><span class='st'> </span><span class='pn'>{</span><span class='vr'>r</span><span class='op'>.</span><span class='fn'>ReasonPhrase</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>);</span><br />
            <span class='kw'>return null</span><span class='pn'>;<br />
        }</span><br />
        <span class='kw'>var</span> <span class='vr'>j</span> <span class='op'>=</span> <span class='vr'>r</span><span class='op'>.</span><span class='fn'>Json</span><span class='pn'>();</span><br />
        <br />
        <span class='kw'>var</span> <span class='vr'>sb</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>StringBuilder</span><span class='pn'>();</span><br />
        <span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>var</span> <span class='vr'>v</span> <span class='kw'>in</span> <span class='vr'>j</span><span class='pn'>[</span><span class='nr'>0</span><span class='pn'>]</span><span class='op'>.</span><span class='fn'>AsArray</span><span class='pn'>())</span> <span class='vr'>sb</span><span class='op'>.</span><span class='fn'>Append</span><span class='pn'>((</span><span class='kw'>string</span><span class='pn'>)</span><span class='vr'>v</span><span class='pn'>[</span><span class='nr'>0</span><span class='pn'>]);</span><br />
        <span class='vr'>translated</span> <span class='op'>=</span> <span class='vr'>sb</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>();<br />
    }</span><br />
    <br />
    <span class='vr'>cache</span><span class='op'>.</span><span class='fn'>Save</span><span class='pn'>(</span><span class='vr'>translated</span><span class='pn'>);</span><br />
    <br />
    <span class='kw'>return</span> <span class='vr'>translated</span><span class='pn'>;<br />
}</span><br />
<span class='pd'>#pragma warning restore</span> CS0162 <span class='cm'>//Unreachable code detected</span><br />
<br />
<span class='kw'>class</span> <span class='tp'>_Cache</span> <span class='pn'>{</span><br />
    <span class='kw'>string</span> <span class='vr'>_file</span><span class='pn'>,</span> <span class='vr'>_hash</span><span class='pn'>;</span><br />
    <br />
    <span class='kw'>public bool</span> <span class='fn'>Get</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>folder</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>name</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>text</span><span class='pn'>,</span> <span class='kw'>out string</span> <span class='vr'>translated</span><span class='pn'>) {</span><br />
        <span class='vr'>_hash</span> <span class='op'>=</span> <span class='tp'>Hash</span><span class='op'>.</span><span class='fn'>MD5</span><span class='pn'>(</span><span class='vr'>text</span><span class='pn'>,</span> <span class='kw'>true</span><span class='pn'>);</span><br />
        <span class='vr'>_file</span> <span class='op'>=</span> <span class='tp'>folders</span><span class='op'>.</span><span class='fn'>ThisAppDataRoaming +</span> <span class='st'>@"translate&#92;"</span> <span class='op'>+</span> <span class='vr'>folder</span> <span class='op'>+</span> <span class='st'>@"&#92;"</span> <span class='op'>+</span> <span class='vr'>name</span> <span class='op'>+</span> <span class='st'>".txt"</span><span class='pn'>;</span><br />
        <span class='cm'>//run.selectInExplorer(_file);</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='tp'>keys</span><span class='op'>.</span><span class='fn'>isCtrl</span><span class='pn'>)</span><br />
            <span class='kw'>if</span> <span class='pn'>(</span><span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>exists</span><span class='pn'>(</span><span class='vr'>_file</span><span class='pn'>)) {</span><br />
                <span class='kw'>var</span> <span class='vr'>s</span> <span class='op'>=</span> <span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>loadText</span><span class='pn'>(</span><span class='vr'>_file</span><span class='pn'>);</span><br />
                <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>s</span><span class='op'>.</span><span class='fn'>Starts</span><span class='pn'>(</span><span class='vr'>_hash</span><span class='pn'>)) {</span><br />
                    <span class='vr'>translated</span> <span class='op'>=</span> <span class='vr'>s</span><span class='pn'>[</span><span class='vr'>_hash</span><span class='op'>.</span><span class='fn'>Length</span><span class='pn'>..];</span><br />
                    <span class='kw'>return true</span><span class='pn'>;<br />
                }<br />
            }</span><br />
        <span class='vr'>translated</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>;</span><br />
        <span class='kw'>return false</span><span class='pn'>;<br />
    }</span><br />
    <br />
    <span class='kw'>public void</span> <span class='fn'>Save</span><span class='pn'>(</span><span class='kw'>string</span> <span class='vr'>translated</span><span class='pn'>) {</span><br />
        <span class='tp'>filesystem</span><span class='op'>.</span><span class='fn'>saveText</span><span class='pn'>(</span><span class='vr'>_file</span><span class='pn'>,</span> <span class='vr'>_hash</span> <span class='op'>+</span> <span class='vr'>translated</span><span class='pn'>);<br />
    }<br />
}</span><br />
<br />
<span class='kw'>enum</span> <span class='tp'>Translator</span> <span class='pn'>{</span> <span class='cn'>GoogleFree</span><span class='pn'>,</span> <span class='cn'>GoogleCloud</span><span class='pn'>,</span> <span class='cn'>Microsoft</span><span class='pn'>,</span> <span class='cn'>OpenAI</span> <span class='pn'>}</span></code></div></div><br />
Changes:<br />
2024-03-03: Added Misrosoft Translator.]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Lua in LA using MoonSharp]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7575</link>
			<pubDate>Tue, 02 Jan 2024 14:12:21 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=1539">burque505</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7575</guid>
			<description><![CDATA[Happy New Year to all. Here's a couple of Lua scripts running in LA using MoonSharp. You can get MoonSharp with NuGet. <a href="https://www.moonsharp.org/getting_started.html" target="_blank" rel="noopener" class="mycode_url">(I cloned code from here).</a> You can return really large results with this factorial code, by the way.<br />
<br />
First (using implicit conversion):<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "lua3c2.cs"<br />
<span class='cm'>/*/ nuget lua&#92;MoonSharp; /*/ //.</span><br />
 <br />
<span class='kw'>using</span> MoonSharp<span class='op'>.</span>Interpreter<span class='pn'>;</span><br />
<span class='tp'>script</span><span class='op'>.</span><span class='fn'>setup</span><span class='pn'>(</span><span class='vr'>trayIcon</span><span class='pn'>:</span> <span class='kw'>true</span><span class='pn'>,</span> <span class='vr'>sleepExit</span><span class='pn'>:</span> <span class='kw'>true</span><span class='pn'>);</span><br />
<span class='cm'>//..</span><br />
<span class='kw'>int</span> <span class='vr'>myNumber</span><span class='pn'>;</span><br />
<br />
<span class='tp'>dialog</span><span class='op'>.</span><span class='tp'>options</span><span class='op'>.</span><span class='fn'>defaultTitle</span> <span class='op'>=</span> <span class='st'>"MoonSharp Testing"</span><span class='pn'>;</span><br />
<span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>showInputNumber</span><span class='pn'>(</span><span class='kw'>out</span> <span class='vr'>myNumber</span><span class='pn'>,</span> <span class='st'>"Lua Factorial Test"</span><span class='pn'>,</span> <span class='st'>"Enter an integer"</span><span class='pn'>);</span><br />
<br />
<span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='fn'>MoonSharpFactorial2</span><span class='pn'>(</span><span class='vr'>myNumber</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>(),</span> <span class='st'>"Back from Lua"</span><span class='pn'>,</span> <span class='vr'>title</span><span class='pn'>:</span> <span class='st'>"Lua Factorial Output"</span><span class='pn'>);</span><br />
<br />
<span class='kw'>double</span> <span class='fn'>MoonSharpFactorial2</span><span class='pn'>(</span><span class='kw'>int</span> <span class='vr'>aNumber</span><span class='pn'>)<br />
{</span><br />
    <span class='kw'>string</span> <span class='vr'>scriptCode</span> <span class='op'>=</span> <span class='st'>@"    <br />
        -- defines a factorial function<br />
        function fact (n)<br />
            if (n == 0) then<br />
                return 1<br />
            else<br />
                return n*fact(n - 1)<br />
            end<br />
        end"</span><span class='pn'>;</span><br />
<br />
    Script <span class='vr'>script</span> <span class='op'>=</span> <span class='kw'>new</span> Script<span class='pn'>();</span><br />
<br />
    <span class='vr'>script</span><span class='op'>.</span>DoString<span class='pn'>(</span><span class='vr'>scriptCode</span><span class='pn'>);</span><br />
    <span class='cm'>// Here we get the script.Globals function by index, i.e. ["fact"]<br />
    // This returns a System.Object. Sometimes that's not what's wanted.</span><br />
    DynValue <span class='vr'>res</span> <span class='op'>=</span> <span class='vr'>script</span><span class='op'>.</span>Call<span class='pn'>(</span><span class='vr'>script</span><span class='op'>.</span>Globals<span class='pn'>[</span><span class='st'>"fact"</span><span class='pn'>],</span> <span class='vr'>myNumber</span><span class='pn'>);</span><br />
<br />
    <span class='kw'>return</span> <span class='vr'>res</span><span class='op'>.</span>Number<span class='pn'>;<br />
}</span></code></div></div><br />
Second, explicit conversion:<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "lua3c3.cs"<br />
<span class='cm'>/*/ nuget lua&#92;MoonSharp; /*/ //.</span><br />
 <br />
<span class='kw'>using</span> MoonSharp<span class='op'>.</span>Interpreter<span class='pn'>;</span><br />
<span class='tp'>script</span><span class='op'>.</span><span class='fn'>setup</span><span class='pn'>(</span><span class='vr'>trayIcon</span><span class='pn'>:</span> <span class='kw'>true</span><span class='pn'>,</span> <span class='vr'>sleepExit</span><span class='pn'>:</span> <span class='kw'>true</span><span class='pn'>);</span><br />
<span class='cm'>//..</span><br />
<span class='kw'>int</span> <span class='vr'>myNumber</span><span class='pn'>;</span><br />
<br />
<span class='tp'>dialog</span><span class='op'>.</span><span class='tp'>options</span><span class='op'>.</span><span class='fn'>defaultTitle</span> <span class='op'>=</span> <span class='st'>"MoonSharp Testing"</span><span class='pn'>;</span><br />
<span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>showInputNumber</span><span class='pn'>(</span><span class='kw'>out</span> <span class='vr'>myNumber</span><span class='pn'>,</span> <span class='st'>"Lua Factorial Test"</span><span class='pn'>,</span> <span class='st'>"Enter an integer"</span><span class='pn'>);</span><br />
<br />
<span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='fn'>MoonSharpFactorial2</span><span class='pn'>(</span><span class='vr'>myNumber</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>(),</span> <span class='st'>"Back from Lua"</span><span class='pn'>,</span> <span class='vr'>title</span><span class='pn'>:</span> <span class='st'>"Lua Factorial Output"</span><span class='pn'>);</span><br />
<br />
<span class='kw'>double</span> <span class='fn'>MoonSharpFactorial2</span><span class='pn'>(</span><span class='kw'>int</span> <span class='vr'>aNumber</span><span class='pn'>)<br />
{</span><br />
    <span class='kw'>string</span> <span class='vr'>scriptCode</span> <span class='op'>=</span> <span class='st'>@"    <br />
        -- defines a factorial function<br />
        function fact (n)<br />
            if (n == 0) then<br />
                return 1<br />
            else<br />
                return n*fact(n - 1)<br />
            end<br />
        end"</span><span class='pn'>;</span><br />
<br />
    Script <span class='vr'>script</span> <span class='op'>=</span> <span class='kw'>new</span> Script<span class='pn'>();</span><br />
<br />
    <span class='vr'>script</span><span class='op'>.</span>DoString<span class='pn'>(</span><span class='vr'>scriptCode</span><span class='pn'>);</span><br />
    DynValue <span class='vr'>luaFactFunction</span> <span class='op'>=</span> <span class='vr'>script</span><span class='op'>.</span>Globals<span class='op'>.</span>Get<span class='pn'>(</span><span class='st'>"fact"</span><span class='pn'>);</span><br />
<span class='cm'>/*<br />
If we are interested in avoiding conversion, we can pass a number directly instead of using the implicit conversions the Call method provides.<br />
It’s not really needed for this but somewhere else it might be. Not all implicit conversions give the desired results.<br />
*/</span><br />
    DynValue <span class='vr'>res</span> <span class='op'>=</span> <span class='vr'>script</span><span class='op'>.</span>Call<span class='pn'>(</span><span class='vr'>luaFactFunction</span><span class='pn'>,</span> DynValue<span class='op'>.</span>NewNumber<span class='pn'>(</span><span class='vr'>aNumber</span><span class='pn'>));</span><br />
<br />
    <span class='kw'>return</span> <span class='vr'>res</span><span class='op'>.</span>Number<span class='pn'>;<br />
}</span></code></div></div><br />
<br />
<br />
Best regards,<br />
burque505]]></description>
			<content:encoded><![CDATA[Happy New Year to all. Here's a couple of Lua scripts running in LA using MoonSharp. You can get MoonSharp with NuGet. <a href="https://www.moonsharp.org/getting_started.html" target="_blank" rel="noopener" class="mycode_url">(I cloned code from here).</a> You can return really large results with this factorial code, by the way.<br />
<br />
First (using implicit conversion):<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "lua3c2.cs"<br />
<span class='cm'>/*/ nuget lua&#92;MoonSharp; /*/ //.</span><br />
 <br />
<span class='kw'>using</span> MoonSharp<span class='op'>.</span>Interpreter<span class='pn'>;</span><br />
<span class='tp'>script</span><span class='op'>.</span><span class='fn'>setup</span><span class='pn'>(</span><span class='vr'>trayIcon</span><span class='pn'>:</span> <span class='kw'>true</span><span class='pn'>,</span> <span class='vr'>sleepExit</span><span class='pn'>:</span> <span class='kw'>true</span><span class='pn'>);</span><br />
<span class='cm'>//..</span><br />
<span class='kw'>int</span> <span class='vr'>myNumber</span><span class='pn'>;</span><br />
<br />
<span class='tp'>dialog</span><span class='op'>.</span><span class='tp'>options</span><span class='op'>.</span><span class='fn'>defaultTitle</span> <span class='op'>=</span> <span class='st'>"MoonSharp Testing"</span><span class='pn'>;</span><br />
<span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>showInputNumber</span><span class='pn'>(</span><span class='kw'>out</span> <span class='vr'>myNumber</span><span class='pn'>,</span> <span class='st'>"Lua Factorial Test"</span><span class='pn'>,</span> <span class='st'>"Enter an integer"</span><span class='pn'>);</span><br />
<br />
<span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='fn'>MoonSharpFactorial2</span><span class='pn'>(</span><span class='vr'>myNumber</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>(),</span> <span class='st'>"Back from Lua"</span><span class='pn'>,</span> <span class='vr'>title</span><span class='pn'>:</span> <span class='st'>"Lua Factorial Output"</span><span class='pn'>);</span><br />
<br />
<span class='kw'>double</span> <span class='fn'>MoonSharpFactorial2</span><span class='pn'>(</span><span class='kw'>int</span> <span class='vr'>aNumber</span><span class='pn'>)<br />
{</span><br />
    <span class='kw'>string</span> <span class='vr'>scriptCode</span> <span class='op'>=</span> <span class='st'>@"    <br />
        -- defines a factorial function<br />
        function fact (n)<br />
            if (n == 0) then<br />
                return 1<br />
            else<br />
                return n*fact(n - 1)<br />
            end<br />
        end"</span><span class='pn'>;</span><br />
<br />
    Script <span class='vr'>script</span> <span class='op'>=</span> <span class='kw'>new</span> Script<span class='pn'>();</span><br />
<br />
    <span class='vr'>script</span><span class='op'>.</span>DoString<span class='pn'>(</span><span class='vr'>scriptCode</span><span class='pn'>);</span><br />
    <span class='cm'>// Here we get the script.Globals function by index, i.e. ["fact"]<br />
    // This returns a System.Object. Sometimes that's not what's wanted.</span><br />
    DynValue <span class='vr'>res</span> <span class='op'>=</span> <span class='vr'>script</span><span class='op'>.</span>Call<span class='pn'>(</span><span class='vr'>script</span><span class='op'>.</span>Globals<span class='pn'>[</span><span class='st'>"fact"</span><span class='pn'>],</span> <span class='vr'>myNumber</span><span class='pn'>);</span><br />
<br />
    <span class='kw'>return</span> <span class='vr'>res</span><span class='op'>.</span>Number<span class='pn'>;<br />
}</span></code></div></div><br />
Second, explicit conversion:<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "lua3c3.cs"<br />
<span class='cm'>/*/ nuget lua&#92;MoonSharp; /*/ //.</span><br />
 <br />
<span class='kw'>using</span> MoonSharp<span class='op'>.</span>Interpreter<span class='pn'>;</span><br />
<span class='tp'>script</span><span class='op'>.</span><span class='fn'>setup</span><span class='pn'>(</span><span class='vr'>trayIcon</span><span class='pn'>:</span> <span class='kw'>true</span><span class='pn'>,</span> <span class='vr'>sleepExit</span><span class='pn'>:</span> <span class='kw'>true</span><span class='pn'>);</span><br />
<span class='cm'>//..</span><br />
<span class='kw'>int</span> <span class='vr'>myNumber</span><span class='pn'>;</span><br />
<br />
<span class='tp'>dialog</span><span class='op'>.</span><span class='tp'>options</span><span class='op'>.</span><span class='fn'>defaultTitle</span> <span class='op'>=</span> <span class='st'>"MoonSharp Testing"</span><span class='pn'>;</span><br />
<span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>showInputNumber</span><span class='pn'>(</span><span class='kw'>out</span> <span class='vr'>myNumber</span><span class='pn'>,</span> <span class='st'>"Lua Factorial Test"</span><span class='pn'>,</span> <span class='st'>"Enter an integer"</span><span class='pn'>);</span><br />
<br />
<span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='fn'>MoonSharpFactorial2</span><span class='pn'>(</span><span class='vr'>myNumber</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>(),</span> <span class='st'>"Back from Lua"</span><span class='pn'>,</span> <span class='vr'>title</span><span class='pn'>:</span> <span class='st'>"Lua Factorial Output"</span><span class='pn'>);</span><br />
<br />
<span class='kw'>double</span> <span class='fn'>MoonSharpFactorial2</span><span class='pn'>(</span><span class='kw'>int</span> <span class='vr'>aNumber</span><span class='pn'>)<br />
{</span><br />
    <span class='kw'>string</span> <span class='vr'>scriptCode</span> <span class='op'>=</span> <span class='st'>@"    <br />
        -- defines a factorial function<br />
        function fact (n)<br />
            if (n == 0) then<br />
                return 1<br />
            else<br />
                return n*fact(n - 1)<br />
            end<br />
        end"</span><span class='pn'>;</span><br />
<br />
    Script <span class='vr'>script</span> <span class='op'>=</span> <span class='kw'>new</span> Script<span class='pn'>();</span><br />
<br />
    <span class='vr'>script</span><span class='op'>.</span>DoString<span class='pn'>(</span><span class='vr'>scriptCode</span><span class='pn'>);</span><br />
    DynValue <span class='vr'>luaFactFunction</span> <span class='op'>=</span> <span class='vr'>script</span><span class='op'>.</span>Globals<span class='op'>.</span>Get<span class='pn'>(</span><span class='st'>"fact"</span><span class='pn'>);</span><br />
<span class='cm'>/*<br />
If we are interested in avoiding conversion, we can pass a number directly instead of using the implicit conversions the Call method provides.<br />
It’s not really needed for this but somewhere else it might be. Not all implicit conversions give the desired results.<br />
*/</span><br />
    DynValue <span class='vr'>res</span> <span class='op'>=</span> <span class='vr'>script</span><span class='op'>.</span>Call<span class='pn'>(</span><span class='vr'>luaFactFunction</span><span class='pn'>,</span> DynValue<span class='op'>.</span>NewNumber<span class='pn'>(</span><span class='vr'>aNumber</span><span class='pn'>));</span><br />
<br />
    <span class='kw'>return</span> <span class='vr'>res</span><span class='op'>.</span>Number<span class='pn'>;<br />
}</span></code></div></div><br />
<br />
<br />
Best regards,<br />
burque505]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Query Alpha Vantage API]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7573</link>
			<pubDate>Wed, 27 Dec 2023 21:49:10 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=1539">burque505</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7573</guid>
			<description><![CDATA[I hope everyone had/is having a great holiday season.<br />
I wanted to use <a href="https://www.libreautomate.com/cookbook/Http%20post%20web%20form%2C%20JSON.html" target="_blank" rel="noopener" class="mycode_url">HttpClient</a>  for <a href="https://alphavantage.co" target="_blank" rel="noopener" class="mycode_url">Alpha Vantage</a>'s API instead of WebClient (deprecated). Alpha Vantage's docs use WebClient, which doesn't seem to me to be a great idea since it's deprecated, even though perhaps easier. Here's some code that gets Intraday quotes. You'll need to get a free API key and replace 'demo' in the code with that key.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code><span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Net</span><span class='op'>.</span><span class='ns'>Http</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Text</span><span class='op'>.</span><span class='ns'>Json</span><span class='pn'>;</span><br />
<br />
<span class='cm'>// Replace the "demo" apikey below with your own key from https://www.alphavantage.co/support/#api-key</span><br />
<span class='kw'>string</span> <span class='vr'>QUERY_URL</span> <span class='op'>=</span> <span class='st'>"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&amp;symbol=IBM&amp;interval=5min&amp;apikey=demo"</span><span class='pn'>;</span><br />
<span class='tp'>Uri</span> <span class='vr'>queryUri</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>Uri</span><span class='pn'>(</span><span class='vr'>QUERY_URL</span><span class='pn'>);</span><br />
<br />
<span class='kw'>using</span> <span class='pn'>(</span><span class='tp'>HttpClient</span> <span class='vr'>client</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>HttpClient</span><span class='pn'>()) {</span><br />
    <span class='kw'>try</span> <span class='pn'>{</span><br />
        <span class='cm'>// Send the GET request</span><br />
        <span class='tp'>HttpResponseMessage</span> <span class='vr'>response</span> <span class='op'>=</span> <span class='kw'>await</span> <span class='vr'>client</span><span class='op'>.</span><span class='fn'>GetAsync</span><span class='pn'>(</span><span class='vr'>queryUri</span><span class='pn'>);</span><br />
        <br />
        <span class='cm'>// Check if the request was successful</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>response</span><span class='op'>.</span><span class='fn'>IsSuccessStatusCode</span><span class='pn'>) {</span><br />
            <span class='cm'>// Read and parse the response content</span><br />
            <span class='kw'>string</span> <span class='vr'>jsonContent</span> <span class='op'>=</span> <span class='kw'>await</span> <span class='vr'>response</span><span class='op'>.</span><span class='fn'>Content</span><span class='op'>.</span><span class='fn'>ReadAsStringAsync</span><span class='pn'>();</span><br />
            <br />
            <span class='cm'>// Deserialize JSON using System.Text.Json</span><br />
            <span class='kw'>dynamic</span> <span class='vr'>json_data</span> <span class='op'>=</span> <span class='tp'>JsonSerializer</span><span class='op'>.</span><span class='fn'>Deserialize</span><span class='pn'>&lt;</span><span class='tp'>Dictionary</span><span class='pn'>&lt;</span><span class='kw'>string</span><span class='pn'>,</span> <span class='kw'>dynamic</span><span class='pn'>&gt;&gt;(</span><span class='vr'>jsonContent</span><span class='pn'>);</span><br />
            <br />
            <span class='cm'>// Do something with the json_data besides printing, if you like.</span><br />
            <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='vr'>json_data</span><span class='pn'>);<br />
        }</span> <span class='kw'>else</span> <span class='pn'>{</span><br />
            <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>&#36;"Error: </span><span class='pn'>{</span><span class='vr'>response</span><span class='op'>.</span><span class='fn'>StatusCode</span><span class='pn'>}</span><span class='st'> - </span><span class='pn'>{</span><span class='vr'>response</span><span class='op'>.</span><span class='fn'>ReasonPhrase</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>);<br />
        }<br />
    }</span><br />
    <span class='kw'>catch</span> <span class='pn'>(</span><span class='tp'>Exception</span> <span class='vr'>ex</span><span class='pn'>) {</span><br />
        <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>&#36;"An error occurred: </span><span class='pn'>{</span><span class='vr'>ex</span><span class='op'>.</span><span class='fn'>Message</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>);<br />
    }<br />
}</span></code></div></div><br />
<br />
<br />
Best regards and Happy New Year,<br />
burque505]]></description>
			<content:encoded><![CDATA[I hope everyone had/is having a great holiday season.<br />
I wanted to use <a href="https://www.libreautomate.com/cookbook/Http%20post%20web%20form%2C%20JSON.html" target="_blank" rel="noopener" class="mycode_url">HttpClient</a>  for <a href="https://alphavantage.co" target="_blank" rel="noopener" class="mycode_url">Alpha Vantage</a>'s API instead of WebClient (deprecated). Alpha Vantage's docs use WebClient, which doesn't seem to me to be a great idea since it's deprecated, even though perhaps easier. Here's some code that gets Intraday quotes. You'll need to get a free API key and replace 'demo' in the code with that key.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code><span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Net</span><span class='op'>.</span><span class='ns'>Http</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Text</span><span class='op'>.</span><span class='ns'>Json</span><span class='pn'>;</span><br />
<br />
<span class='cm'>// Replace the "demo" apikey below with your own key from https://www.alphavantage.co/support/#api-key</span><br />
<span class='kw'>string</span> <span class='vr'>QUERY_URL</span> <span class='op'>=</span> <span class='st'>"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&amp;symbol=IBM&amp;interval=5min&amp;apikey=demo"</span><span class='pn'>;</span><br />
<span class='tp'>Uri</span> <span class='vr'>queryUri</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>Uri</span><span class='pn'>(</span><span class='vr'>QUERY_URL</span><span class='pn'>);</span><br />
<br />
<span class='kw'>using</span> <span class='pn'>(</span><span class='tp'>HttpClient</span> <span class='vr'>client</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>HttpClient</span><span class='pn'>()) {</span><br />
    <span class='kw'>try</span> <span class='pn'>{</span><br />
        <span class='cm'>// Send the GET request</span><br />
        <span class='tp'>HttpResponseMessage</span> <span class='vr'>response</span> <span class='op'>=</span> <span class='kw'>await</span> <span class='vr'>client</span><span class='op'>.</span><span class='fn'>GetAsync</span><span class='pn'>(</span><span class='vr'>queryUri</span><span class='pn'>);</span><br />
        <br />
        <span class='cm'>// Check if the request was successful</span><br />
        <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>response</span><span class='op'>.</span><span class='fn'>IsSuccessStatusCode</span><span class='pn'>) {</span><br />
            <span class='cm'>// Read and parse the response content</span><br />
            <span class='kw'>string</span> <span class='vr'>jsonContent</span> <span class='op'>=</span> <span class='kw'>await</span> <span class='vr'>response</span><span class='op'>.</span><span class='fn'>Content</span><span class='op'>.</span><span class='fn'>ReadAsStringAsync</span><span class='pn'>();</span><br />
            <br />
            <span class='cm'>// Deserialize JSON using System.Text.Json</span><br />
            <span class='kw'>dynamic</span> <span class='vr'>json_data</span> <span class='op'>=</span> <span class='tp'>JsonSerializer</span><span class='op'>.</span><span class='fn'>Deserialize</span><span class='pn'>&lt;</span><span class='tp'>Dictionary</span><span class='pn'>&lt;</span><span class='kw'>string</span><span class='pn'>,</span> <span class='kw'>dynamic</span><span class='pn'>&gt;&gt;(</span><span class='vr'>jsonContent</span><span class='pn'>);</span><br />
            <br />
            <span class='cm'>// Do something with the json_data besides printing, if you like.</span><br />
            <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='vr'>json_data</span><span class='pn'>);<br />
        }</span> <span class='kw'>else</span> <span class='pn'>{</span><br />
            <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>&#36;"Error: </span><span class='pn'>{</span><span class='vr'>response</span><span class='op'>.</span><span class='fn'>StatusCode</span><span class='pn'>}</span><span class='st'> - </span><span class='pn'>{</span><span class='vr'>response</span><span class='op'>.</span><span class='fn'>ReasonPhrase</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>);<br />
        }<br />
    }</span><br />
    <span class='kw'>catch</span> <span class='pn'>(</span><span class='tp'>Exception</span> <span class='vr'>ex</span><span class='pn'>) {</span><br />
        <span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>&#36;"An error occurred: </span><span class='pn'>{</span><span class='vr'>ex</span><span class='op'>.</span><span class='fn'>Message</span><span class='pn'>}</span><span class='st'>"</span><span class='pn'>);<br />
    }<br />
}</span></code></div></div><br />
<br />
<br />
Best regards and Happy New Year,<br />
burque505]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Interact with AutoHotkey from LA]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7567</link>
			<pubDate>Thu, 23 Nov 2023 18:38:43 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=1539">burque505</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7567</guid>
			<description><![CDATA[Hi all, Happy Thanksgiving to everyone. I still use AutoHotkey quite a bit. My experience is mostly with v1, and v2 is rapidly taking over.<br />
amazing-andrew wrote an interop for the AutoHotkey dll (version 1) which you can find <a href="https://github.com/amazing-andrew/AutoHotkey.Interop" target="_blank" rel="noopener" class="mycode_url">here, AutoHotkey.Interop</a>.<br />
LucidMethod wrote a cool library called <a href="https://github.com/LucidMethod/sharpAHK" target="_blank" rel="noopener" class="mycode_url">SharpAHK</a>, which also uses AutoHotkey.Interop. As it was missing a function critical to displaying GUIs, I added one and created a Nuget package, <a href="https://www.nuget.org/packages/sharpAHK_505/" target="_blank" rel="noopener" class="mycode_url">sharpAHK_505</a>.<br />
That's what I used in the script below.<br />
<br />
You can pass variables back and forth between LA and AutoHotkey, as you'll see.<br />
I realize there may be little interest among LA users for AutoHotkey, since LA can do so much, but you may find some limited application for it.<br />
<br />
I haven't done much testing with this - there may be memory leaks or other bugs.<br />
<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "SharpAHKtest6.cs"<br />
<span class='cm'>/*/ nuget -&#92;sharpahk_505; /*/ //.</span><br />
<span class='kw'>using</span> AutoHotkey<span class='op'>.</span>Interop<span class='pn'>;</span><br />
<br />
<span class='kw'>string</span> <span class='vr'>myContent</span> <span class='op'>=</span> <span class='st'>"Not updated!"</span><span class='pn'>;</span> <span class='cm'>// Initialized for debugging<br />
<br />
// Create AutoHotkey script to be loaded by<br />
// AutoHotkey engine instance.<br />
// (I suggest you avoid AHK comments in the script</span><br />
<br />
<span class='kw'>string</span> <span class='vr'>scriptString</span> <span class='op'>=</span> <span class='st'>@"#NoEnv<br />
#SingleInstance force<br />
<br />
Gui, Add, Edit, r9 vMyEdit w135, Text to appear inside control.<br />
Gui, Add, Button, w80 gOutputz, Submit<br />
gui, show<br />
<br />
return<br />
<br />
Outputz:<br />
    Gui, Submit ; Submit the data from the GUI to update variables<br />
    MsgBox, ,Testing AHK,% MyEdit<br />
    ExitApp<br />
return<br />
"</span><span class='pn'>;</span><br />
<br />
<span class='cm'>// Create the AutoHotkey engine</span><br />
<span class='kw'>var</span> <span class='vr'>ahk</span> <span class='op'>=</span> AutoHotkeyEngine<span class='op'>.</span>Instance<span class='pn'>;</span><br />
<br />
<span class='cm'>// Load the script</span><br />
<span class='vr'>ahk</span><span class='op'>.</span>LoadScript<span class='pn'>(</span><span class='vr'>scriptString</span><span class='pn'>);</span><br />
<br />
<span class='cm'>// Critical - if this 'while' loop is omitted,<br />
// the script will exit immediately after the<br />
// GUI very briefly appears.<br />
// sharpAHK_505 has the isReady function,<br />
// but AFAIK the original sharpAHK does not.</span><br />
<span class='kw'>while</span> <span class='pn'>(</span><span class='vr'>ahk</span><span class='op'>.</span>IsReady<span class='pn'>()</span> <span class='op'>==</span> <span class='kw'>true</span><span class='pn'>) {</span><br />
    <span class='cm'>// Use LA's 'Intellisense' to explore other<br />
    // engine functions.<br />
    // For example, 'getter' GetVar also has a 'setter',<br />
    // SetVar</span><br />
    <span class='vr'>myContent</span> <span class='op'>=</span> <span class='vr'>ahk</span><span class='op'>.</span>GetVar<span class='pn'>(</span><span class='st'>"MyEdit"</span><span class='pn'>);</span><br />
    <span class='tp'>Thread</span><span class='op'>.</span><span class='fn'>Sleep</span><span class='pn'>(</span><span class='nr'>100</span><span class='pn'>);<br />
}</span><br />
<br />
<span class='cm'>// Do something with the variable retrieved </span><br />
<span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>"Contents is: "</span> <span class='op'>+</span> <span class='vr'>myContent</span><span class='pn'>);</span><br />
<br />
<span class='vr'>ahk</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>;</span></code></div></div>]]></description>
			<content:encoded><![CDATA[Hi all, Happy Thanksgiving to everyone. I still use AutoHotkey quite a bit. My experience is mostly with v1, and v2 is rapidly taking over.<br />
amazing-andrew wrote an interop for the AutoHotkey dll (version 1) which you can find <a href="https://github.com/amazing-andrew/AutoHotkey.Interop" target="_blank" rel="noopener" class="mycode_url">here, AutoHotkey.Interop</a>.<br />
LucidMethod wrote a cool library called <a href="https://github.com/LucidMethod/sharpAHK" target="_blank" rel="noopener" class="mycode_url">SharpAHK</a>, which also uses AutoHotkey.Interop. As it was missing a function critical to displaying GUIs, I added one and created a Nuget package, <a href="https://www.nuget.org/packages/sharpAHK_505/" target="_blank" rel="noopener" class="mycode_url">sharpAHK_505</a>.<br />
That's what I used in the script below.<br />
<br />
You can pass variables back and forth between LA and AutoHotkey, as you'll see.<br />
I realize there may be little interest among LA users for AutoHotkey, since LA can do so much, but you may find some limited application for it.<br />
<br />
I haven't done much testing with this - there may be memory leaks or other bugs.<br />
<br />
<br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "SharpAHKtest6.cs"<br />
<span class='cm'>/*/ nuget -&#92;sharpahk_505; /*/ //.</span><br />
<span class='kw'>using</span> AutoHotkey<span class='op'>.</span>Interop<span class='pn'>;</span><br />
<br />
<span class='kw'>string</span> <span class='vr'>myContent</span> <span class='op'>=</span> <span class='st'>"Not updated!"</span><span class='pn'>;</span> <span class='cm'>// Initialized for debugging<br />
<br />
// Create AutoHotkey script to be loaded by<br />
// AutoHotkey engine instance.<br />
// (I suggest you avoid AHK comments in the script</span><br />
<br />
<span class='kw'>string</span> <span class='vr'>scriptString</span> <span class='op'>=</span> <span class='st'>@"#NoEnv<br />
#SingleInstance force<br />
<br />
Gui, Add, Edit, r9 vMyEdit w135, Text to appear inside control.<br />
Gui, Add, Button, w80 gOutputz, Submit<br />
gui, show<br />
<br />
return<br />
<br />
Outputz:<br />
    Gui, Submit ; Submit the data from the GUI to update variables<br />
    MsgBox, ,Testing AHK,% MyEdit<br />
    ExitApp<br />
return<br />
"</span><span class='pn'>;</span><br />
<br />
<span class='cm'>// Create the AutoHotkey engine</span><br />
<span class='kw'>var</span> <span class='vr'>ahk</span> <span class='op'>=</span> AutoHotkeyEngine<span class='op'>.</span>Instance<span class='pn'>;</span><br />
<br />
<span class='cm'>// Load the script</span><br />
<span class='vr'>ahk</span><span class='op'>.</span>LoadScript<span class='pn'>(</span><span class='vr'>scriptString</span><span class='pn'>);</span><br />
<br />
<span class='cm'>// Critical - if this 'while' loop is omitted,<br />
// the script will exit immediately after the<br />
// GUI very briefly appears.<br />
// sharpAHK_505 has the isReady function,<br />
// but AFAIK the original sharpAHK does not.</span><br />
<span class='kw'>while</span> <span class='pn'>(</span><span class='vr'>ahk</span><span class='op'>.</span>IsReady<span class='pn'>()</span> <span class='op'>==</span> <span class='kw'>true</span><span class='pn'>) {</span><br />
    <span class='cm'>// Use LA's 'Intellisense' to explore other<br />
    // engine functions.<br />
    // For example, 'getter' GetVar also has a 'setter',<br />
    // SetVar</span><br />
    <span class='vr'>myContent</span> <span class='op'>=</span> <span class='vr'>ahk</span><span class='op'>.</span>GetVar<span class='pn'>(</span><span class='st'>"MyEdit"</span><span class='pn'>);</span><br />
    <span class='tp'>Thread</span><span class='op'>.</span><span class='fn'>Sleep</span><span class='pn'>(</span><span class='nr'>100</span><span class='pn'>);<br />
}</span><br />
<br />
<span class='cm'>// Do something with the variable retrieved </span><br />
<span class='tp'>print</span><span class='op'>.</span><span class='fn'>it</span><span class='pn'>(</span><span class='st'>"Contents is: "</span> <span class='op'>+</span> <span class='vr'>myContent</span><span class='pn'>);</span><br />
<br />
<span class='vr'>ahk</span> <span class='op'>=</span> <span class='kw'>null</span><span class='pn'>;</span></code></div></div>]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Unloading file AuCpp.dll from other processes]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7557</link>
			<pubDate>Sun, 29 Oct 2023 18:44:09 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=2">Gintaras</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7557</guid>
			<description><![CDATA[The Au library uses a dll file <span style="font-family: Courier New;" class="mycode_font">AuCpp.dll</span>. If your program uses the Au library, sometimes it may want to delete or replace the dll, for example when installing new version. But it may fail because the dll may be loaded into some seemingly unrelated processes.<br />
<br />
Class <span style="font-weight: bold;" class="mycode_b">elm</span> and some other Au library functions load the dll into the target process to make "find UI element" faster or/and enable some features.<br />
<br />
There are 2 versions of <span style="font-family: Courier New;" class="mycode_font">AuCpp.dll</span> files: 64-bit dll for 64-bit processes and 32-bit dll for 32-bit processes.<br />
<br />
If need to delete the dll, you can:<br />
- With <span style="font-weight: bold;" class="mycode_b">elm </span>functions use flag <span style="font-weight: bold;" class="mycode_b">NotInProc</span>. Then the dll will not be loaded into the target process. But will be slower etc.<br />
- Or unload the dll from other processes. This script shows how.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "Unload AuCpp.dll from other processes.cs"<br />
<span class='cm'>//AuCpp.dll exports function Cpp_Unload. It unloads AuCpp.dll from other processes. You can use it in any program (C#, C++, etc).<br />
//Or, to avoid loading AuCpp.dll into your process, use Cpp_Unload code converted to C#.<br />
//Or, if you use C++, copy the function from GitHub to your C++ program.</span><br />
<br />
<span class='pd'>#if</span> <span class='op'>!</span><span class='kw'>true</span> <span class='cm'>//use function Cpp_Unload of AuCpp.dll</span><br />
<span class='ex'>api.Cpp_Unload(1);<br />
<br />
unsafe class api : NativeApi {<br />
    /// &lt;param name="flags"&gt;1 - wait less.&lt;/param&gt;<br />
    [DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]<br />
    internal static extern void Cpp_Unload(uint flags);<br />
}<br />
</span><span class='pd'>#else</span> <span class='cm'>//use Cpp_Unload code converted to C#</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Collections</span><span class='op'>.</span><span class='ns'>Generic</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Runtime</span><span class='op'>.</span><span class='ns'>InteropServices</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Threading</span><span class='pn'>;</span><br />
<br />
<span class='fn'>UnloadAuCppDll</span><span class='pn'>(</span><span class='op'>!</span><span class='kw'>true</span><span class='pn'>);</span><br />
<br />
<span class='kw'>void</span> <span class='fn'>UnloadAuCppDll</span><span class='pn'>(</span><span class='kw'>bool</span> <span class='vr'>waitLess</span><span class='pn'>) {</span><br />
    <span class='kw'>int</span> <span class='vr'>less</span> <span class='op'>=</span> <span class='vr'>waitLess</span> <span class='op'>?</span> <span class='nr'>5</span> <span class='op'>:</span> <span class='nr'>1</span><span class='pn'>;</span><br />
    <span class='tp'>List</span><span class='pn'>&lt;</span><span class='kw'>nint</span><span class='pn'>&gt;</span> <span class='vr'>a</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>();</span><br />
    <br />
    <span class='cm'>//close elm agent windows</span><br />
    <span class='kw'>for</span> <span class='pn'>(</span><span class='kw'>nint</span> <span class='vr'>w</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>; (</span><span class='vr'>w</span> <span class='op'>=</span> <span class='tp'>api</span><span class='op'>.</span><span class='fn'>FindWindowEx</span><span class='pn'>(</span><span class='tp'>api</span><span class='op'>.</span><span class='cn'>HWND_MESSAGE</span><span class='pn'>,</span> <span class='vr'>w</span><span class='pn'>,</span> <span class='st'>"AuCpp_IPA_1"</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>))</span> <span class='op'>!=</span> <span class='nr'>0</span><span class='pn'>;)</span> <span class='vr'>a</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>w</span><span class='pn'>);</span><br />
    <span class='kw'>int</span> <span class='vr'>n</span> <span class='op'>=</span> <span class='vr'>a</span><span class='op'>.</span><span class='fn'>Count</span><span class='pn'>;</span><br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>n</span> <span class='op'>&gt;</span> <span class='nr'>0</span><span class='pn'>) {</span><br />
        <span class='kw'>for</span> <span class='pn'>(</span><span class='kw'>int</span> <span class='vr'>i</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>;</span> <span class='vr'>i</span> <span class='op'>&lt;</span> <span class='vr'>n</span><span class='pn'>;</span> <span class='vr'>i</span><span class='op'>++</span><span class='pn'>)</span> <span class='tp'>api</span><span class='op'>.</span><span class='fn'>SendMessageTimeout</span><span class='pn'>(</span><span class='vr'>a</span><span class='pn'>[</span><span class='vr'>i</span><span class='pn'>],</span> <span class='tp'>api</span><span class='op'>.</span><span class='cn'>WM_CLOSE</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='tp'>api</span><span class='op'>.</span><span class='cn'>SMTO_ABORTIFHUNG</span><span class='pn'>,</span> <span class='nr'>5000</span> <span class='op'>/</span> <span class='vr'>less</span><span class='pn'>,</span> <span class='kw'>out _</span><span class='pn'>);</span><br />
        <span class='vr'>a</span><span class='op'>.</span><span class='fn'>Clear</span><span class='pn'>();</span><br />
        <span class='tp'>Thread</span><span class='op'>.</span><span class='fn'>Sleep</span><span class='pn'>(</span><span class='vr'>n</span> <span class='op'>*</span> <span class='nr'>50</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='cm'>//unload from processes where loaded by the clipboard hook</span><br />
    <span class='tp'>api</span><span class='op'>.</span><span class='fn'>SendMessageTimeout</span><span class='pn'>(</span><span class='tp'>api</span><span class='op'>.</span><span class='cn'>HWND_BROADCAST</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='tp'>api</span><span class='op'>.</span><span class='cn'>SMTO_ABORTIFHUNG</span><span class='pn'>,</span> <span class='nr'>1000</span> <span class='op'>/</span> <span class='vr'>less</span><span class='pn'>,</span> <span class='kw'>out _</span><span class='pn'>);</span><br />
    <span class='kw'>for</span> <span class='pn'>(</span><span class='kw'>nint</span> <span class='vr'>w</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>; (</span><span class='vr'>w</span> <span class='op'>=</span> <span class='tp'>api</span><span class='op'>.</span><span class='fn'>FindWindowEx</span><span class='pn'>(</span><span class='tp'>api</span><span class='op'>.</span><span class='cn'>HWND_MESSAGE</span><span class='pn'>,</span> <span class='vr'>w</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>))</span> <span class='op'>!=</span> <span class='nr'>0</span><span class='pn'>;)</span> <span class='vr'>a</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>w</span><span class='pn'>);</span><br />
    <span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>var</span> <span class='vr'>w</span> <span class='kw'>in</span> <span class='vr'>a</span><span class='pn'>)</span> <span class='tp'>api</span><span class='op'>.</span><span class='fn'>SendMessageTimeout</span><span class='pn'>(</span><span class='vr'>w</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='tp'>api</span><span class='op'>.</span><span class='cn'>SMTO_ABORTIFHUNG</span><span class='pn'>,</span> <span class='nr'>1000</span> <span class='op'>/</span> <span class='vr'>less</span><span class='pn'>,</span> <span class='kw'>out _</span><span class='pn'>);</span><br />
    <span class='tp'>Thread</span><span class='op'>.</span><span class='fn'>Sleep</span><span class='pn'>(</span><span class='nr'>500</span> <span class='op'>/</span> <span class='vr'>less</span><span class='pn'>);<br />
}</span><br />
<br />
<span class='kw'>unsafe class</span> <span class='tp'>api</span> <span class='pn'>{<br />
    [</span><span class='tp'>DllImport</span><span class='pn'>(</span><span class='st'>"user32.dll"</span><span class='pn'>,</span> <span class='vr'>EntryPoint</span> <span class='op'>=</span> <span class='st'>"FindWindowExW"</span><span class='pn'>)]</span><br />
    <span class='kw'>internal static extern nint</span> <span class='fn'>FindWindowEx</span><span class='pn'>(</span><span class='kw'>nint</span> <span class='vr'>hWndParent</span><span class='pn'>,</span> <span class='kw'>nint</span> <span class='vr'>hWndChildAfter</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>lpszClass</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>lpszWindow</span><span class='pn'>);<br />
    <br />
    [</span><span class='tp'>DllImport</span><span class='pn'>(</span><span class='st'>"user32.dll"</span><span class='pn'>,</span> <span class='vr'>EntryPoint</span> <span class='op'>=</span> <span class='st'>"SendMessageTimeoutW"</span><span class='pn'>)]</span><br />
    <span class='kw'>internal static extern nint</span> <span class='fn'>SendMessageTimeout</span><span class='pn'>(</span><span class='kw'>nint</span> <span class='vr'>hWnd</span><span class='pn'>,</span> <span class='kw'>int</span> <span class='vr'>Msg</span><span class='pn'>,</span> <span class='kw'>nint</span> <span class='vr'>wParam</span><span class='pn'>,</span> <span class='kw'>nint</span> <span class='vr'>lParam</span><span class='pn'>,</span> <span class='kw'>uint</span> <span class='vr'>fuFlags</span><span class='pn'>,</span> <span class='kw'>int</span> <span class='vr'>uTimeout</span><span class='pn'>,</span> <span class='kw'>out nint</span> <span class='vr'>lpdwResult</span><span class='pn'>);</span><br />
    <br />
    <span class='kw'>internal const nint</span> <span class='cn'>HWND_MESSAGE</span> <span class='op'>= -</span><span class='nr'>3</span><span class='pn'>;</span><br />
    <span class='kw'>internal const nint</span> <span class='cn'>HWND_BROADCAST</span> <span class='op'>=</span> <span class='nr'>0xFFFF</span><span class='pn'>;</span><br />
    <span class='kw'>internal const uint</span> <span class='cn'>SMTO_ABORTIFHUNG</span> <span class='op'>=</span> <span class='nr'>0x2</span><span class='pn'>;</span><br />
    <span class='kw'>internal const int</span> <span class='cn'>WM_CLOSE</span> <span class='op'>=</span> <span class='nr'>0x10</span><span class='pn'>;<br />
}</span><br />
<span class='pd'>#endif</span></code></div></div><br />
In LA 1.2 and later the dll also exports function <span style="font-weight: bold;" class="mycode_b">UnloadAuCppDll</span>. It can be called by <span style="font-family: Courier New,Courier,monospace;" class="mycode_font">rundll32.exe</span>. Example in cmd:<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>rundll32.exe "C:&#92;path&#92;to&#92;AuCpp.dll",UnloadAuCppDll 0</code></div></div><br />
See also: <a href="https://github.com/qgindi/LibreAutomate/blob/master/Au.Editor/LibreAutomate.iss" target="_blank" rel="noopener" class="mycode_url">Pascal code for Inno Setup</a><br />
<br />
Notes:<br />
- Cannot unload from hung or suspended processes.<br />
- Cannot unload from admin processes if this process isn't admin.<br />
- Cannot unload if some process still uses COM objects of that process (eg UI elements).<br />
- May need to wait more.<br />
- Test with AuCpp.dll of your program. Not with AuCpp.dll of LibreAutomate while it is running.]]></description>
			<content:encoded><![CDATA[The Au library uses a dll file <span style="font-family: Courier New;" class="mycode_font">AuCpp.dll</span>. If your program uses the Au library, sometimes it may want to delete or replace the dll, for example when installing new version. But it may fail because the dll may be loaded into some seemingly unrelated processes.<br />
<br />
Class <span style="font-weight: bold;" class="mycode_b">elm</span> and some other Au library functions load the dll into the target process to make "find UI element" faster or/and enable some features.<br />
<br />
There are 2 versions of <span style="font-family: Courier New;" class="mycode_font">AuCpp.dll</span> files: 64-bit dll for 64-bit processes and 32-bit dll for 32-bit processes.<br />
<br />
If need to delete the dll, you can:<br />
- With <span style="font-weight: bold;" class="mycode_b">elm </span>functions use flag <span style="font-weight: bold;" class="mycode_b">NotInProc</span>. Then the dll will not be loaded into the target process. But will be slower etc.<br />
- Or unload the dll from other processes. This script shows how.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "Unload AuCpp.dll from other processes.cs"<br />
<span class='cm'>//AuCpp.dll exports function Cpp_Unload. It unloads AuCpp.dll from other processes. You can use it in any program (C#, C++, etc).<br />
//Or, to avoid loading AuCpp.dll into your process, use Cpp_Unload code converted to C#.<br />
//Or, if you use C++, copy the function from GitHub to your C++ program.</span><br />
<br />
<span class='pd'>#if</span> <span class='op'>!</span><span class='kw'>true</span> <span class='cm'>//use function Cpp_Unload of AuCpp.dll</span><br />
<span class='ex'>api.Cpp_Unload(1);<br />
<br />
unsafe class api : NativeApi {<br />
    /// &lt;param name="flags"&gt;1 - wait less.&lt;/param&gt;<br />
    [DllImport("AuCpp.dll", CallingConvention = CallingConvention.Cdecl)]<br />
    internal static extern void Cpp_Unload(uint flags);<br />
}<br />
</span><span class='pd'>#else</span> <span class='cm'>//use Cpp_Unload code converted to C#</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Collections</span><span class='op'>.</span><span class='ns'>Generic</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Runtime</span><span class='op'>.</span><span class='ns'>InteropServices</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Threading</span><span class='pn'>;</span><br />
<br />
<span class='fn'>UnloadAuCppDll</span><span class='pn'>(</span><span class='op'>!</span><span class='kw'>true</span><span class='pn'>);</span><br />
<br />
<span class='kw'>void</span> <span class='fn'>UnloadAuCppDll</span><span class='pn'>(</span><span class='kw'>bool</span> <span class='vr'>waitLess</span><span class='pn'>) {</span><br />
    <span class='kw'>int</span> <span class='vr'>less</span> <span class='op'>=</span> <span class='vr'>waitLess</span> <span class='op'>?</span> <span class='nr'>5</span> <span class='op'>:</span> <span class='nr'>1</span><span class='pn'>;</span><br />
    <span class='tp'>List</span><span class='pn'>&lt;</span><span class='kw'>nint</span><span class='pn'>&gt;</span> <span class='vr'>a</span> <span class='op'>=</span> <span class='kw'>new</span><span class='pn'>();</span><br />
    <br />
    <span class='cm'>//close elm agent windows</span><br />
    <span class='kw'>for</span> <span class='pn'>(</span><span class='kw'>nint</span> <span class='vr'>w</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>; (</span><span class='vr'>w</span> <span class='op'>=</span> <span class='tp'>api</span><span class='op'>.</span><span class='fn'>FindWindowEx</span><span class='pn'>(</span><span class='tp'>api</span><span class='op'>.</span><span class='cn'>HWND_MESSAGE</span><span class='pn'>,</span> <span class='vr'>w</span><span class='pn'>,</span> <span class='st'>"AuCpp_IPA_1"</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>))</span> <span class='op'>!=</span> <span class='nr'>0</span><span class='pn'>;)</span> <span class='vr'>a</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>w</span><span class='pn'>);</span><br />
    <span class='kw'>int</span> <span class='vr'>n</span> <span class='op'>=</span> <span class='vr'>a</span><span class='op'>.</span><span class='fn'>Count</span><span class='pn'>;</span><br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>n</span> <span class='op'>&gt;</span> <span class='nr'>0</span><span class='pn'>) {</span><br />
        <span class='kw'>for</span> <span class='pn'>(</span><span class='kw'>int</span> <span class='vr'>i</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>;</span> <span class='vr'>i</span> <span class='op'>&lt;</span> <span class='vr'>n</span><span class='pn'>;</span> <span class='vr'>i</span><span class='op'>++</span><span class='pn'>)</span> <span class='tp'>api</span><span class='op'>.</span><span class='fn'>SendMessageTimeout</span><span class='pn'>(</span><span class='vr'>a</span><span class='pn'>[</span><span class='vr'>i</span><span class='pn'>],</span> <span class='tp'>api</span><span class='op'>.</span><span class='cn'>WM_CLOSE</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='tp'>api</span><span class='op'>.</span><span class='cn'>SMTO_ABORTIFHUNG</span><span class='pn'>,</span> <span class='nr'>5000</span> <span class='op'>/</span> <span class='vr'>less</span><span class='pn'>,</span> <span class='kw'>out _</span><span class='pn'>);</span><br />
        <span class='vr'>a</span><span class='op'>.</span><span class='fn'>Clear</span><span class='pn'>();</span><br />
        <span class='tp'>Thread</span><span class='op'>.</span><span class='fn'>Sleep</span><span class='pn'>(</span><span class='vr'>n</span> <span class='op'>*</span> <span class='nr'>50</span><span class='pn'>);<br />
    }</span><br />
    <br />
    <span class='cm'>//unload from processes where loaded by the clipboard hook</span><br />
    <span class='tp'>api</span><span class='op'>.</span><span class='fn'>SendMessageTimeout</span><span class='pn'>(</span><span class='tp'>api</span><span class='op'>.</span><span class='cn'>HWND_BROADCAST</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='tp'>api</span><span class='op'>.</span><span class='cn'>SMTO_ABORTIFHUNG</span><span class='pn'>,</span> <span class='nr'>1000</span> <span class='op'>/</span> <span class='vr'>less</span><span class='pn'>,</span> <span class='kw'>out _</span><span class='pn'>);</span><br />
    <span class='kw'>for</span> <span class='pn'>(</span><span class='kw'>nint</span> <span class='vr'>w</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>; (</span><span class='vr'>w</span> <span class='op'>=</span> <span class='tp'>api</span><span class='op'>.</span><span class='fn'>FindWindowEx</span><span class='pn'>(</span><span class='tp'>api</span><span class='op'>.</span><span class='cn'>HWND_MESSAGE</span><span class='pn'>,</span> <span class='vr'>w</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>,</span> <span class='kw'>null</span><span class='pn'>))</span> <span class='op'>!=</span> <span class='nr'>0</span><span class='pn'>;)</span> <span class='vr'>a</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>w</span><span class='pn'>);</span><br />
    <span class='kw'>foreach</span> <span class='pn'>(</span><span class='kw'>var</span> <span class='vr'>w</span> <span class='kw'>in</span> <span class='vr'>a</span><span class='pn'>)</span> <span class='tp'>api</span><span class='op'>.</span><span class='fn'>SendMessageTimeout</span><span class='pn'>(</span><span class='vr'>w</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='nr'>0</span><span class='pn'>,</span> <span class='tp'>api</span><span class='op'>.</span><span class='cn'>SMTO_ABORTIFHUNG</span><span class='pn'>,</span> <span class='nr'>1000</span> <span class='op'>/</span> <span class='vr'>less</span><span class='pn'>,</span> <span class='kw'>out _</span><span class='pn'>);</span><br />
    <span class='tp'>Thread</span><span class='op'>.</span><span class='fn'>Sleep</span><span class='pn'>(</span><span class='nr'>500</span> <span class='op'>/</span> <span class='vr'>less</span><span class='pn'>);<br />
}</span><br />
<br />
<span class='kw'>unsafe class</span> <span class='tp'>api</span> <span class='pn'>{<br />
    [</span><span class='tp'>DllImport</span><span class='pn'>(</span><span class='st'>"user32.dll"</span><span class='pn'>,</span> <span class='vr'>EntryPoint</span> <span class='op'>=</span> <span class='st'>"FindWindowExW"</span><span class='pn'>)]</span><br />
    <span class='kw'>internal static extern nint</span> <span class='fn'>FindWindowEx</span><span class='pn'>(</span><span class='kw'>nint</span> <span class='vr'>hWndParent</span><span class='pn'>,</span> <span class='kw'>nint</span> <span class='vr'>hWndChildAfter</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>lpszClass</span><span class='pn'>,</span> <span class='kw'>string</span> <span class='vr'>lpszWindow</span><span class='pn'>);<br />
    <br />
    [</span><span class='tp'>DllImport</span><span class='pn'>(</span><span class='st'>"user32.dll"</span><span class='pn'>,</span> <span class='vr'>EntryPoint</span> <span class='op'>=</span> <span class='st'>"SendMessageTimeoutW"</span><span class='pn'>)]</span><br />
    <span class='kw'>internal static extern nint</span> <span class='fn'>SendMessageTimeout</span><span class='pn'>(</span><span class='kw'>nint</span> <span class='vr'>hWnd</span><span class='pn'>,</span> <span class='kw'>int</span> <span class='vr'>Msg</span><span class='pn'>,</span> <span class='kw'>nint</span> <span class='vr'>wParam</span><span class='pn'>,</span> <span class='kw'>nint</span> <span class='vr'>lParam</span><span class='pn'>,</span> <span class='kw'>uint</span> <span class='vr'>fuFlags</span><span class='pn'>,</span> <span class='kw'>int</span> <span class='vr'>uTimeout</span><span class='pn'>,</span> <span class='kw'>out nint</span> <span class='vr'>lpdwResult</span><span class='pn'>);</span><br />
    <br />
    <span class='kw'>internal const nint</span> <span class='cn'>HWND_MESSAGE</span> <span class='op'>= -</span><span class='nr'>3</span><span class='pn'>;</span><br />
    <span class='kw'>internal const nint</span> <span class='cn'>HWND_BROADCAST</span> <span class='op'>=</span> <span class='nr'>0xFFFF</span><span class='pn'>;</span><br />
    <span class='kw'>internal const uint</span> <span class='cn'>SMTO_ABORTIFHUNG</span> <span class='op'>=</span> <span class='nr'>0x2</span><span class='pn'>;</span><br />
    <span class='kw'>internal const int</span> <span class='cn'>WM_CLOSE</span> <span class='op'>=</span> <span class='nr'>0x10</span><span class='pn'>;<br />
}</span><br />
<span class='pd'>#endif</span></code></div></div><br />
In LA 1.2 and later the dll also exports function <span style="font-weight: bold;" class="mycode_b">UnloadAuCppDll</span>. It can be called by <span style="font-family: Courier New,Courier,monospace;" class="mycode_font">rundll32.exe</span>. Example in cmd:<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>rundll32.exe "C:&#92;path&#92;to&#92;AuCpp.dll",UnloadAuCppDll 0</code></div></div><br />
See also: <a href="https://github.com/qgindi/LibreAutomate/blob/master/Au.Editor/LibreAutomate.iss" target="_blank" rel="noopener" class="mycode_url">Pascal code for Inno Setup</a><br />
<br />
Notes:<br />
- Cannot unload from hung or suspended processes.<br />
- Cannot unload from admin processes if this process isn't admin.<br />
- Cannot unload if some process still uses COM objects of that process (eg UI elements).<br />
- May need to wait more.<br />
- Test with AuCpp.dll of your program. Not with AuCpp.dll of LibreAutomate while it is running.]]></content:encoded>
		</item>
		<item>
			<title><![CDATA[Dynamic controls experiment]]></title>
			<link>https://www.libreautomate.com/forum/showthread.php?tid=7537</link>
			<pubDate>Tue, 12 Sep 2023 18:33:57 +0000</pubDate>
			<dc:creator><![CDATA[<a href="https://www.libreautomate.com/forum/member.php?action=profile&uid=1539">burque505</a>]]></dc:creator>
			<guid isPermaLink="false">https://www.libreautomate.com/forum/showthread.php?tid=7537</guid>
			<description><![CDATA[Although this doesn't really do anything useful, it may be helpful as an example of dynamically creating controls.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "owned_d3.cs"<br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Controls</span><span class='pn'>;</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>rows</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>;</span><br />
<span class='kw'>var</span> <span class='vr'>b</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>wpfBuilder</span><span class='pn'>(</span><span class='st'>"Select Sections"</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Width</span><span class='pn'>(</span><span class='nr'>185</span><span class='pn'>);</span><br />
<span class='vr'>b</span><span class='op'>.</span><span class='fn'>R</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='kw'>out</span> <span class='tp'>Button</span> <span class='vr'>btn1</span><span class='pn'>,</span> <span class='st'>"Sections?"</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='kw'>out</span> <span class='tp'>TextBox</span> <span class='vr'>tb1</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Width</span><span class='pn'>(</span><span class='nr'>80</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Focus</span><span class='pn'>();</span><br />
<span class='vr'>btn1</span><span class='op'>.</span><span class='fn'>IsDefault</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>;</span><br />
<span class='vr'>btn1</span><span class='op'>.</span><span class='fn'>ToolTip</span> <span class='op'>=</span> <span class='st'>"No. sections in field to right."</span><span class='pn'>;</span><br />
<span class='vr'>tb1</span><span class='op'>.</span><span class='fn'>ToolTip</span> <span class='op'>=</span> <span class='st'>"Enter number of sections."</span> <span class='op'>+</span> <span class='tp'>Environment</span><span class='op'>.</span><span class='fn'>NewLine</span> <span class='op'>+</span> <span class='st'>"Then press Enter or button."</span><span class='pn'>;</span><br />
<br />
<span class='cm'>// Click handler: throw error if entry doesn't evaluate to int</span><br />
<span class='vr'>btn1</span><span class='op'>.</span><span class='fn'>Click</span> <span class='op'>+=</span> <span class='pn'>(</span><span class='vr'>o</span><span class='pn'>,</span> <span class='vr'>e</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
    <span class='kw'>int</span> <span class='vr'>value</span><span class='pn'>;</span><br />
    <span class='kw'>bool</span> <span class='vr'>success</span> <span class='op'>=</span> <span class='kw'>int</span><span class='op'>.</span><span class='fn'>TryParse</span><span class='pn'>(</span><span class='vr'>tb1</span><span class='op'>.</span><span class='fn'>Text</span><span class='pn'>,</span> <span class='kw'>out</span> <span class='vr'>value</span><span class='pn'>);</span><br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>success</span><span class='pn'>) {</span><br />
        <span class='vr'>rows</span> <span class='op'>=</span> <span class='vr'>tb1</span><span class='op'>.</span><span class='fn'>Text</span><span class='op'>.</span><span class='fn'>ToInt</span><span class='pn'>();</span><br />
        <span class='cm'>// https://www.libreautomate.com/cookbook/Dialog%20-%20owned%2C%20non-modal%20(don%27t%20wait).html</span><br />
        <span class='fn'>_Dialog2</span><span class='pn'>(</span><span class='vr'>b</span><span class='op'>.</span><span class='fn'>Window</span><span class='pn'>);<br />
    }</span> <span class='kw'>else</span> <span class='pn'>{</span><br />
        <span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='st'>"Error - not an integer!"</span><span class='pn'>,</span> <span class='vr'>title</span><span class='pn'>:</span> <span class='st'>"Error"</span><span class='pn'>,</span> <span class='vr'>secondsTimeout</span><span class='pn'>:</span> <span class='nr'>2</span><span class='pn'>);</span><br />
        <span class='vr'>tb1</span><span class='op'>.</span><span class='fn'>Text</span> <span class='op'>=</span> <span class='st'>""</span><span class='pn'>;</span><br />
        <span class='kw'>return</span><span class='pn'>;<br />
    }<br />
};</span><br />
<br />
<span class='vr'>b</span><span class='op'>.</span><span class='fn'>End</span><span class='pn'>();</span><br />
<span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>b</span><span class='op'>.</span><span class='fn'>ShowDialog</span><span class='pn'>())</span> <span class='kw'>return</span><span class='pn'>;</span><br />
<br />
<span class='kw'>void</span> <span class='fn'>_Dialog2</span><span class='pn'>(</span><span class='tp'>Window</span> <span class='vr'>owner</span><span class='pn'>) {</span><br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>tb1</span><span class='op'>.</span><span class='fn'>Text</span> <span class='op'>==</span> <span class='st'>""</span><span class='pn'>) {</span><br />
        <span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='st'>"Please select # of sections"</span><span class='pn'>,</span> <span class='vr'>title</span><span class='pn'>:</span> <span class='st'>"Error"</span><span class='pn'>,</span> <span class='vr'>secondsTimeout</span><span class='pn'>:</span> <span class='nr'>2</span><span class='pn'>);</span><br />
        <span class='kw'>return</span><span class='pn'>;<br />
    }</span><br />
    <span class='kw'>var</span> <span class='vr'>b</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>wpfBuilder</span><span class='pn'>(</span><span class='st'>"Dynamic controls"</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>WinSize</span><span class='pn'>(</span><span class='nr'>300</span><span class='pn'>);</span><br />
    <span class='kw'>for</span> <span class='pn'>(</span><span class='kw'>int</span> <span class='vr'>i</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>;</span> <span class='vr'>i</span> <span class='op'>&lt;</span> <span class='vr'>rows</span><span class='pn'>;</span> <span class='vr'>i</span><span class='op'>++</span><span class='pn'>) {</span><br />
        <br />
        <span class='cm'>// Add buttons</span><br />
        <span class='tp'>Button</span> <span class='vr'>btnD</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>Button</span><span class='pn'>();</span><br />
        <span class='vr'>btnD</span><span class='op'>.</span><span class='fn'>Name</span> <span class='op'>=</span> <span class='st'>"btn_"</span> <span class='op'>+</span> <span class='vr'>i</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>();</span><br />
        <span class='vr'>btnD</span><span class='op'>.</span><span class='fn'>Width</span> <span class='op'>=</span> <span class='nr'>80</span><span class='pn'>;</span><br />
        <span class='vr'>btnD</span><span class='op'>.</span><span class='fn'>Height</span> <span class='op'>=</span> <span class='nr'>30</span><span class='pn'>;</span><br />
        <span class='vr'>btnD</span><span class='op'>.</span><span class='fn'>Content</span> <span class='op'>=</span> <span class='st'>"Button "</span> <span class='op'>+</span> <span class='pn'>(</span><span class='vr'>i</span> <span class='op'>+</span> <span class='nr'>1</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>();</span><br />
        <br />
        <span class='cm'>// Add textboxes</span><br />
        <span class='tp'>TextBox</span> <span class='vr'>txtD</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>TextBox</span><span class='pn'>();</span><br />
        <span class='vr'>txtD</span><span class='op'>.</span><span class='fn'>Name</span> <span class='op'>=</span> <span class='st'>"txt_"</span> <span class='op'>+</span> <span class='vr'>i</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>();</span><br />
        <span class='vr'>txtD</span><span class='op'>.</span><span class='fn'>Width</span> <span class='op'>=</span> <span class='nr'>200</span><span class='pn'>;</span><br />
        <br />
        <span class='cm'>// Add simple functionality</span><br />
        <span class='vr'>btnD</span><span class='op'>.</span><span class='fn'>Click</span> <span class='op'>+=</span> <span class='pn'>(</span><span class='vr'>o</span><span class='pn'>,</span> <span class='vr'>e</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='fn'>btnTestClick</span><span class='pn'>(</span><span class='vr'>o</span><span class='pn'>,</span> <span class='vr'>txtD</span><span class='pn'>,</span> <span class='vr'>btnD</span><span class='pn'>);</span><br />
        <br />
        <span class='cm'>// Add dynamically created controls to window</span><br />
        <span class='vr'>b</span><span class='op'>.</span><span class='fn'>R</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>btnD</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>txtD</span><span class='pn'>);<br />
    }</span><br />
    <span class='vr'>b</span><span class='op'>.</span><span class='fn'>End</span><span class='pn'>();</span><br />
    <span class='cm'>// see https://www.libreautomate.com/cookbook/Dialog%20-%20owned%2C%20non-modal%20(don%27t%20wait).html</span><br />
    <span class='kw'>var</span> <span class='vr'>w</span> <span class='op'>=</span> <span class='vr'>b</span><span class='op'>.</span><span class='fn'>Window</span><span class='pn'>;</span><br />
    <span class='vr'>w</span><span class='op'>.</span><span class='fn'>Owner</span> <span class='op'>=</span> <span class='vr'>owner</span><span class='pn'>;</span><br />
    <span class='vr'>w</span><span class='op'>.</span><span class='fn'>Show</span><span class='pn'>();</span><br />
    <span class='vr'>tb1</span><span class='op'>.</span><span class='fn'>Text</span> <span class='op'>=</span> <span class='st'>""</span><span class='pn'>;<br />
}</span><br />
<br />
<span class='cm'>// Click handler with simple code</span><br />
<span class='kw'>void</span> <span class='fn'>btnTestClick</span><span class='pn'>(</span><span class='tp'>Object</span> <span class='vr'>o</span><span class='pn'>,</span> <span class='tp'>TextBox</span> <span class='vr'>textbox</span><span class='pn'>,</span> <span class='tp'>Button</span> <span class='vr'>btn</span><span class='pn'>) {</span><br />
    <span class='vr'>textbox</span><span class='op'>.</span><span class='fn'>Text</span> <span class='op'>=</span> <span class='st'>"This text was added by "</span> <span class='op'>+</span> <span class='vr'>btn</span><span class='op'>.</span><span class='fn'>Content</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>();<br />
}</span></code></div></div><br />
Best regards, burque505<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://www.libreautomate.com/forum/images/attachtypes/image.png" title="GIF Image" border="0" alt=".gif" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=1306" target="_blank" title="">dynamic_controls.gif</a> (Size: 310.7 KB / Downloads: 280)
<!-- end: postbit_attachments_attachment -->]]></description>
			<content:encoded><![CDATA[Although this doesn't really do anything useful, it may be helpful as an example of dynamically creating controls.<br />
 <br />
<div class="codeblock"><div class="title">Code:</div><div class="body" dir="ltr"><code>// script "owned_d3.cs"<br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='pn'>;</span><br />
<span class='kw'>using</span> <span class='ns'>System</span><span class='op'>.</span><span class='ns'>Windows</span><span class='op'>.</span><span class='ns'>Controls</span><span class='pn'>;</span><br />
<br />
<span class='kw'>var</span> <span class='vr'>rows</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>;</span><br />
<span class='kw'>var</span> <span class='vr'>b</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>wpfBuilder</span><span class='pn'>(</span><span class='st'>"Select Sections"</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Width</span><span class='pn'>(</span><span class='nr'>185</span><span class='pn'>);</span><br />
<span class='vr'>b</span><span class='op'>.</span><span class='fn'>R</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='kw'>out</span> <span class='tp'>Button</span> <span class='vr'>btn1</span><span class='pn'>,</span> <span class='st'>"Sections?"</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='kw'>out</span> <span class='tp'>TextBox</span> <span class='vr'>tb1</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Width</span><span class='pn'>(</span><span class='nr'>80</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Focus</span><span class='pn'>();</span><br />
<span class='vr'>btn1</span><span class='op'>.</span><span class='fn'>IsDefault</span> <span class='op'>=</span> <span class='kw'>true</span><span class='pn'>;</span><br />
<span class='vr'>btn1</span><span class='op'>.</span><span class='fn'>ToolTip</span> <span class='op'>=</span> <span class='st'>"No. sections in field to right."</span><span class='pn'>;</span><br />
<span class='vr'>tb1</span><span class='op'>.</span><span class='fn'>ToolTip</span> <span class='op'>=</span> <span class='st'>"Enter number of sections."</span> <span class='op'>+</span> <span class='tp'>Environment</span><span class='op'>.</span><span class='fn'>NewLine</span> <span class='op'>+</span> <span class='st'>"Then press Enter or button."</span><span class='pn'>;</span><br />
<br />
<span class='cm'>// Click handler: throw error if entry doesn't evaluate to int</span><br />
<span class='vr'>btn1</span><span class='op'>.</span><span class='fn'>Click</span> <span class='op'>+=</span> <span class='pn'>(</span><span class='vr'>o</span><span class='pn'>,</span> <span class='vr'>e</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='pn'>{</span><br />
    <span class='kw'>int</span> <span class='vr'>value</span><span class='pn'>;</span><br />
    <span class='kw'>bool</span> <span class='vr'>success</span> <span class='op'>=</span> <span class='kw'>int</span><span class='op'>.</span><span class='fn'>TryParse</span><span class='pn'>(</span><span class='vr'>tb1</span><span class='op'>.</span><span class='fn'>Text</span><span class='pn'>,</span> <span class='kw'>out</span> <span class='vr'>value</span><span class='pn'>);</span><br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>success</span><span class='pn'>) {</span><br />
        <span class='vr'>rows</span> <span class='op'>=</span> <span class='vr'>tb1</span><span class='op'>.</span><span class='fn'>Text</span><span class='op'>.</span><span class='fn'>ToInt</span><span class='pn'>();</span><br />
        <span class='cm'>// https://www.libreautomate.com/cookbook/Dialog%20-%20owned%2C%20non-modal%20(don%27t%20wait).html</span><br />
        <span class='fn'>_Dialog2</span><span class='pn'>(</span><span class='vr'>b</span><span class='op'>.</span><span class='fn'>Window</span><span class='pn'>);<br />
    }</span> <span class='kw'>else</span> <span class='pn'>{</span><br />
        <span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='st'>"Error - not an integer!"</span><span class='pn'>,</span> <span class='vr'>title</span><span class='pn'>:</span> <span class='st'>"Error"</span><span class='pn'>,</span> <span class='vr'>secondsTimeout</span><span class='pn'>:</span> <span class='nr'>2</span><span class='pn'>);</span><br />
        <span class='vr'>tb1</span><span class='op'>.</span><span class='fn'>Text</span> <span class='op'>=</span> <span class='st'>""</span><span class='pn'>;</span><br />
        <span class='kw'>return</span><span class='pn'>;<br />
    }<br />
};</span><br />
<br />
<span class='vr'>b</span><span class='op'>.</span><span class='fn'>End</span><span class='pn'>();</span><br />
<span class='kw'>if</span> <span class='pn'>(</span><span class='op'>!</span><span class='vr'>b</span><span class='op'>.</span><span class='fn'>ShowDialog</span><span class='pn'>())</span> <span class='kw'>return</span><span class='pn'>;</span><br />
<br />
<span class='kw'>void</span> <span class='fn'>_Dialog2</span><span class='pn'>(</span><span class='tp'>Window</span> <span class='vr'>owner</span><span class='pn'>) {</span><br />
    <span class='kw'>if</span> <span class='pn'>(</span><span class='vr'>tb1</span><span class='op'>.</span><span class='fn'>Text</span> <span class='op'>==</span> <span class='st'>""</span><span class='pn'>) {</span><br />
        <span class='tp'>dialog</span><span class='op'>.</span><span class='fn'>show</span><span class='pn'>(</span><span class='st'>"Please select # of sections"</span><span class='pn'>,</span> <span class='vr'>title</span><span class='pn'>:</span> <span class='st'>"Error"</span><span class='pn'>,</span> <span class='vr'>secondsTimeout</span><span class='pn'>:</span> <span class='nr'>2</span><span class='pn'>);</span><br />
        <span class='kw'>return</span><span class='pn'>;<br />
    }</span><br />
    <span class='kw'>var</span> <span class='vr'>b</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>wpfBuilder</span><span class='pn'>(</span><span class='st'>"Dynamic controls"</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>WinSize</span><span class='pn'>(</span><span class='nr'>300</span><span class='pn'>);</span><br />
    <span class='kw'>for</span> <span class='pn'>(</span><span class='kw'>int</span> <span class='vr'>i</span> <span class='op'>=</span> <span class='nr'>0</span><span class='pn'>;</span> <span class='vr'>i</span> <span class='op'>&lt;</span> <span class='vr'>rows</span><span class='pn'>;</span> <span class='vr'>i</span><span class='op'>++</span><span class='pn'>) {</span><br />
        <br />
        <span class='cm'>// Add buttons</span><br />
        <span class='tp'>Button</span> <span class='vr'>btnD</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>Button</span><span class='pn'>();</span><br />
        <span class='vr'>btnD</span><span class='op'>.</span><span class='fn'>Name</span> <span class='op'>=</span> <span class='st'>"btn_"</span> <span class='op'>+</span> <span class='vr'>i</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>();</span><br />
        <span class='vr'>btnD</span><span class='op'>.</span><span class='fn'>Width</span> <span class='op'>=</span> <span class='nr'>80</span><span class='pn'>;</span><br />
        <span class='vr'>btnD</span><span class='op'>.</span><span class='fn'>Height</span> <span class='op'>=</span> <span class='nr'>30</span><span class='pn'>;</span><br />
        <span class='vr'>btnD</span><span class='op'>.</span><span class='fn'>Content</span> <span class='op'>=</span> <span class='st'>"Button "</span> <span class='op'>+</span> <span class='pn'>(</span><span class='vr'>i</span> <span class='op'>+</span> <span class='nr'>1</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>();</span><br />
        <br />
        <span class='cm'>// Add textboxes</span><br />
        <span class='tp'>TextBox</span> <span class='vr'>txtD</span> <span class='op'>=</span> <span class='kw'>new</span> <span class='tp'>TextBox</span><span class='pn'>();</span><br />
        <span class='vr'>txtD</span><span class='op'>.</span><span class='fn'>Name</span> <span class='op'>=</span> <span class='st'>"txt_"</span> <span class='op'>+</span> <span class='vr'>i</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>();</span><br />
        <span class='vr'>txtD</span><span class='op'>.</span><span class='fn'>Width</span> <span class='op'>=</span> <span class='nr'>200</span><span class='pn'>;</span><br />
        <br />
        <span class='cm'>// Add simple functionality</span><br />
        <span class='vr'>btnD</span><span class='op'>.</span><span class='fn'>Click</span> <span class='op'>+=</span> <span class='pn'>(</span><span class='vr'>o</span><span class='pn'>,</span> <span class='vr'>e</span><span class='pn'>)</span> <span class='op'>=&gt;</span> <span class='fn'>btnTestClick</span><span class='pn'>(</span><span class='vr'>o</span><span class='pn'>,</span> <span class='vr'>txtD</span><span class='pn'>,</span> <span class='vr'>btnD</span><span class='pn'>);</span><br />
        <br />
        <span class='cm'>// Add dynamically created controls to window</span><br />
        <span class='vr'>b</span><span class='op'>.</span><span class='fn'>R</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>btnD</span><span class='pn'>)</span><span class='op'>.</span><span class='fn'>Add</span><span class='pn'>(</span><span class='vr'>txtD</span><span class='pn'>);<br />
    }</span><br />
    <span class='vr'>b</span><span class='op'>.</span><span class='fn'>End</span><span class='pn'>();</span><br />
    <span class='cm'>// see https://www.libreautomate.com/cookbook/Dialog%20-%20owned%2C%20non-modal%20(don%27t%20wait).html</span><br />
    <span class='kw'>var</span> <span class='vr'>w</span> <span class='op'>=</span> <span class='vr'>b</span><span class='op'>.</span><span class='fn'>Window</span><span class='pn'>;</span><br />
    <span class='vr'>w</span><span class='op'>.</span><span class='fn'>Owner</span> <span class='op'>=</span> <span class='vr'>owner</span><span class='pn'>;</span><br />
    <span class='vr'>w</span><span class='op'>.</span><span class='fn'>Show</span><span class='pn'>();</span><br />
    <span class='vr'>tb1</span><span class='op'>.</span><span class='fn'>Text</span> <span class='op'>=</span> <span class='st'>""</span><span class='pn'>;<br />
}</span><br />
<br />
<span class='cm'>// Click handler with simple code</span><br />
<span class='kw'>void</span> <span class='fn'>btnTestClick</span><span class='pn'>(</span><span class='tp'>Object</span> <span class='vr'>o</span><span class='pn'>,</span> <span class='tp'>TextBox</span> <span class='vr'>textbox</span><span class='pn'>,</span> <span class='tp'>Button</span> <span class='vr'>btn</span><span class='pn'>) {</span><br />
    <span class='vr'>textbox</span><span class='op'>.</span><span class='fn'>Text</span> <span class='op'>=</span> <span class='st'>"This text was added by "</span> <span class='op'>+</span> <span class='vr'>btn</span><span class='op'>.</span><span class='fn'>Content</span><span class='op'>.</span><span class='fn'>ToString</span><span class='pn'>();<br />
}</span></code></div></div><br />
Best regards, burque505<br />
<br />
<!-- start: postbit_attachments_attachment -->
<br /><!-- start: attachment_icon -->
<img src="https://www.libreautomate.com/forum/images/attachtypes/image.png" title="GIF Image" border="0" alt=".gif" />
<!-- end: attachment_icon -->&nbsp;&nbsp;<a href="attachment.php?aid=1306" target="_blank" title="">dynamic_controls.gif</a> (Size: 310.7 KB / Downloads: 280)
<!-- end: postbit_attachments_attachment -->]]></content:encoded>
		</item>
	</channel>
</rss>