Web Server
WinWrap® Basic is an embedded language control available for .NET/COM 32/64 bit Windows applications. The WinWrap® Basic Component is compatible with VBA, Sax Basic, VB.NET and Visual Basic 6.0 style scripts.
Edit, debug and execute scripts in a web server from a BasicIdeCtl control.
Web Server
- UI process requires a WWAC02X Application Certificate.
- Collaboratively edit, debug and execute from a BasicIdeCtl control.
- Scripts managed in a web server (requires a WWAC12 Application Certificate).
-
Collaborative editing requires the
Collaborative Editing
option on the WWAC12 Application Certificate.
Web Server

WinWrap® Basic scripting functionality on a server can be configured to limit
user capabilities:
-
No limitations: Requires application level user authentication.
(WinWrap® Basic does not provide this.)
-
Safe scripting: See the Secure Scripting best practices solution.
-
Script source: See the Virtual File System best practices solution.
-
Edit only: See the EditOnly property.
WinWrap® Basic inter-process communication requires that the
synchronize messages
be passed between to the two processes.
This example shows how to implement a server using a WCF service containing a
BasicNoUIObj
object and a client using a
BasicIdeCtl
control.
Server: WinWrap® Basic in a Web Server
This sample shows how to edit, debug and execute scripts in an embedded
Kestrel
web server.
Two urls are used for communicating WinWrap® Basic
synchronize messages
with the web server:
namespace winwrap_edit_server.Controllers
{
[Route("[controller]")] // winwrap
public class WinWrapController : Controller
{
[HttpPost("requests")]
public IActionResult Requests([FromBody]WinWrapMessage postdata)
{
string request = postdata.ToString();
WinWrapBasicService.Singleton.SendRequests(request);
return Ok(new WinWrapMessage("[]"));
}
[HttpPost("responses/{ids}")]
public IActionResult Responses(string ids, [FromBody]WinWrapMessage postdata)
{
SortedSet<int> idset = new SortedSet<int>();
foreach (string idx in ids.Split('-'))
if (int.TryParse(idx, out int id))
idset.Add(id);
string responses = WinWrapBasicService.Singleton.GetResponses(idset);
return Ok(new WinWrapMessage(responses));
}
}
}
The requests url sends the request messages to the BasicNoUIObj object and the
responses url gets the queued synchronizing messages from the BasicNoUIObj object.
namespace winwrap_edit_server
{
public class WinWrapBasicService
{
BasicThread basic_thread_ = new BasicThread();
WinWrap.Basic.IVirtualFileSystem filesystem_;
string log_file_;
static object lock_ = new object();
static WinWrapBasicService singleton_;
WWB.SynchronizingQueues responses_sqs_ = new WWB.SynchronizingQueues();
public static WinWrapBasicService Singleton
{
get
{
lock (lock_)
{
if (singleton_ == null)
singleton_ = new WinWrapBasicService();
return singleton_;
}
}
}
private WinWrapBasicService()
{
}
public static void Shutdown()
{
if (singleton_ != null)
{
singleton_.basic_thread_.Kill();
singleton_ = null;
}
}
public void Initialize(IDictionary<string, object< parameters, CancellationToken cancellationToken)
{
log_file_ = (string)parameters[".log_file"];
if (log_file_ != null)
File.WriteAllText(log_file_, "");
string scriptroot = (string)parameters["scriptroot"];
filesystem_ = new WWB.MyFileSystem(scriptroot);
string root = filesystem_.Combine(null, null);
bool reset = (bool)parameters["reset"];
if (!Directory.Exists(root))
{
reset = true;
Directory.CreateDirectory(root);
}
if (reset)
{
// copy samples to the virtual file system
foreach (string res_name in WWB.Util.GetResourceFileNames("Samples"))
{
string sample = WWB.Util.ReadResourceTextFile("Samples." + res_name, false);
string file_name = root + "\\" + res_name;
File.WriteAllText(file_name, sample);
}
}
bool debug = (bool)parameters["debug"];
bool sandboxed = (bool)parameters["sandboxed"];
SynchronizationContext sc = new SynchronizationContext();
basic_thread_.SendAction(basic =>
{
// configure basic
basic.Synchronizing += (sender, e) =>
{
// response/notification from the remote BasicNoUIObj
sc.Post(state => {
Log(e.Param);
lock (lock_)
responses_sqs_.Enqueue(e.Param, e.Id);
}, null);
};
basic.ReceivedAppSyncMessage += (sender, e) =>
{
basic.SendAppSyncMessage(e.Data, -1);
};
Util.IgnoreDialogs = true;
basic.Secret = new Guid(Secret.MySecret);
basic.Initialize();
basic.EditOnly = !debug;
basic.Sandboxed = sandboxed;
basic.BlockedKeywords = "AboutWinWrapBasic Beep Dialog GetFilePath InputBox MsgBox ShowPopupMenu";
basic.VirtualFileSystem = filesystem_;
basic.SynchronizedEdit = true; // synchronized editing
});
}
public string LogFile
{
get
{
return log_file_;
}
set
{
log_file_ = value;
if (log_file_ != null)
File.WriteAllText(log_file_, "");
}
}
public string GetResponses(SortedSet<int> idset)
{
// keep ids alive
basic_thread_?.PostAction(basic =>
{
foreach (int id in idset)
basic.Synchronize("[]", id);
});
WWB.SynchronizingQueue sq = new WWB.SynchronizingQueue(0);
lock (lock_)
foreach (int id in idset)
sq.Enqueue(responses_sqs_.Dequeue(id));
return sq.DequeueAll();
}
public void SendRequests(string requests)
{
Log(requests);
basic_thread_?.PostAction(basic => basic.Synchronize(requests, 0));
}
private void Log(string text)
{
if (log_file_ != null && text != "[]")
{
if (text.StartsWith("[") && text.EndsWith("]"))
text = text.Substring(1, text.Length - 2);
text = text.Replace("\r\n{", "_{");
text = text.Replace("\r\n", "") + "\r\n";
text = text.Replace("_{", "\r\n{");
text = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss\r\n") + text;
lock (lock_)
File.AppendAllText(log_file_, text + "\r\n");
if (text.IndexOf("\"response\":\"!attach\"") >= 0 || text.IndexOf("\"response\":\"!detached\"") >= 0)
Console.WriteLine(text);
}
}
}
}
Sample code for the
web server
is available.
Client: WinWrap® Basic in a Windows Application
The user can edit, debug and scripts using a client process which
contains a WinWrap® Basic IDE communicating with the web server.
The client process uses the web server urls to communicate with the
remote BasicNoUIObj object:
namespace SyncWebClient
{
class Transport
{
HttpClient http_client_;
string base_request_uri_ = "http://localhost:5000/winwrap/requests/";
string base_response_uri_ = "http://localhost:5000/winwrap/responses/";
int sync_id_;
public Transport()
{
// create and initialize the HTTP client
http_client_ = new HttpClient();
http_client_.DefaultRequestHeaders.Accept.Clear();
http_client_.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/winwrap"));
}
public void SetSyncId(int sync_id)
{
sync_id_ = sync_id;
}
async public Task PollingLoop(BasicIdeCtl basic)
{
// get responses task
while (http_client_ != null)
{
string responses = null;
try
{
// get responses
responses = await GetResponses(sync_id_.ToString());
}
catch (Exception ex)
{
Debug.Print(ex.ToString());
}
if (responses != null)
{
// send the response to the IDE
basic.Synchronize(responses, 0);
}
}
}
async public Task<string> GetResponses(string ids)
{
// get responses
string uri = base_response_uri_ + ids;
StringContent content = new StringContent("", Encoding.UTF8, "application/winwrap");
HttpResponseMessage response = await http_client_.PostAsync(uri, content);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
async public Task SendRequests(string requests)
{
// send requests
string uri = base_request_uri_;
StringContent content = new StringContent(requests, Encoding.UTF8, "application/winwrap");
HttpResponseMessage response = await http_client_.PostAsync(uri, content);
response.EnsureSuccessStatusCode();
}
public void Dispose()
{
if (http_client_ != null)
{
HttpClient http_client = http_client_;
http_client_ = null;
http_client.CancelPendingRequests();
http_client.Dispose();
}
}
}
}
namespace SyncWebClient
{
public partial class Form1 : Form
{
Transport transport_ = new Transport();
bool polling_loop_active_;
public Form1()
{
InitializeComponent();
}
private void Shutdown()
{
if (transport_ != null)
{
// dispose the transport
transport_.Dispose();
transport_ = null;
// wait for polling loop to end
while (!polling_loop_active_)
{
WinWrap.Basic.Util.DoEvents();
Thread.Sleep(50);
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
// start the polling task
transport_.PollingLoop(basicIdeCtl1).ContinueWith(task => polling_loop_active_ = true);
// attach
basicIdeCtl1.SynchronizedEdit = true;
if (!basicIdeCtl1.Synchronized)
{
// attach failed
Close();
return;
}
// set the transports synchronized id
transport_.SetSyncId(basicIdeCtl1.SynchronizedId);
// open Sample1.bas
basicIdeCtl1.FileName = "\\Sample1.bas";
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
Shutdown();
}
private void basicIdeCtl1_Disconnected(object sender, EventArgs e)
{
Shutdown();
}
async private void basicIdeCtl1_Synchronizing(object sender, WinWrap.Basic.Classic.SynchronizingEventArgs e)
{
if (e.Param == "[]")
return;
// disable Synchronizing event
basicIdeCtl1.SynchronizingInterval = -1;
try
{
// send the request and get the response (non-blocking "wait" for task to complete)
await transport_.SendRequests(e.Param);
}
catch (AggregateException aggregateException)
{
int n = 0;
string messages = null;
foreach (Exception ex in aggregateException.InnerExceptions)
messages += $"{++n}) {ex.Message}\r\n";
Debug.Print(messages);
}
catch (Exception ex)
{
Debug.Print(ex.Message);
}
finally
{
// reactivate the Synchronizing event (50 milliseconds)
basicIdeCtl1.SynchronizingInterval = 50;
}
}
}
}
Sample code for the
Windows Application web client
is available.
- Run the web server from Visual Studio.
- Run a http client from Visual Studio.
- All editing, debugging and execution controlled from the client occurs in the web server.
- Multiple clients can collaboratively edit the same script.
Client/Server Solutions:
Copyright Polar Engineering, Inc.