Execute a method on an existing process instance. Vb.Net - vb.net

I have a windows service that I have been writing in Vb.Net. As part of this service it calls a class that has a long running Process.
I can execute commands to this process when I want to via the ServerCommands() class within the service, however I want to call these remotely. Possibly from a website or click once WPF application.
For this I have used a simple Tcp.Ip WCF example, and have verified it as working correctly.
This called OnStart()
Private _serverCommands As ServerCommands
Protected Overrides Sub OnStart(ByVal args() As String)
' Add code here to start your service. This method should set things
' in motion so your service can do its work.
Debugger.Launch()
' Action a new implementaion of the WCF Service on localhost
_host.AddServiceEndpoint(GetType(ICommunicationService), New NetTcpBinding(), String.Format("net.tcp://127.0.0.1:{0}", AppSettings.TcpServicePort))
_host.Open()
' Start the server command
_serverCommands = New ServerCommands()
_serverCommands.StartServer()
End Sub
However... when I'm calling the service through WCF its starting a new instance of the ServerCommands() Class rather than attaching to the already running thread.
The following call
Public Function DoWork() As String Implements ICommunicationService.DoWork
Dim command As String = "say hello world"
Dim service As IMinecraftService = New MinecraftService()
service.ExecuteServerSideCommand(command)
Return "Command Executed"
End Function
Implements this on the main service.
Public Sub ExecuteServerSideCommand(command As String) Implements IMinecraftService.ExecuteServerSideCommand
If (_serverCommands IsNot Nothing) Then
_serverCommands.SendCommand(command)
End If
End Sub
It appears that in debug _serverCommands is Nothing when it should be running.
How might I go about ensuring any command I execute through WCF communicates with the running instance instead of creating a new ServerCommand() instance??
I haven't tried WCF before, so I might be hitting a dead end... however I'm sure its possible.
Thanks in advance.

I found that I was calling a new instance of the MinecraftService each time I sent a command via WCF.
As Jeff rightly said, I was not making the object shared, I was only accessing a new instance of this class.
I changed it from
From
MyMainClass
Private _serverCommands As ServerCommands
My WcfService
Dim command As String = "say hello world"
MinecraftService.ServerCommands.SendCommand(command)
To
MyMainClass
Public Shared ServerCommands As ServerCommands
My WcfService
MinecraftService.ServerCommands.SendCommand(command)

Related

Custom Post function to upload a file in Self Hosted REST API

I have written a VB.Net Visual basic console application for Self hosting a custom file upload service to be consumed by an application. Concept being the end user uses the application to generate data, when completed the file is uploaded to our server without user intervention. I have complete control over both applications. The problem is I can't figure out the POST Upload signature that can accept several params, including the file or how to actually receive the file. The User application is in beta now, testing all other functionality excluding the "Send File" sub's. I've never seen a file larger then 180 KB; I plan on accepting files sizes up to 1 MB. This lets me place some limitations (and filters) to help avoid abuse of the service.
I'm using NuGet packages webapi.client (4.0.30506), webapi.selfhost (4.0.3056) (and their associated required packages) and newtonsoft.json (4.5.11) and PostMan to test/debug the process. I'm using Visual Studio 2019 (Fully patched and up to date). All of the examples and google research point only to C# (not my language of choice), or are for hosted solutions like IIS.
In Postman, the only place where filenames are accepted are in the body, form-data. So, there is where I set up my key/value pairs with matching (including case and order) the params as defined in the FileULRequest class.
Everything that I've tried returns either
'500 internal server error'
or
"Message": "No HTTP resource was found that matches the request URI 'http://10.0.1.102:21212/file/upload/'."
The class object of the request looks like this:
Public Class FileULRequest
Public Property EncToken As String 'Holds an encrypted token for authorization
Public Property Filename As String 'Holds a recommended file name
Public Property AppID As String 'Holds the client/app ID for simpler server actions
Public Property File As Byte() 'Not sure if this is the right type/ should be the encrypted file contents.
End Class
The POST function signature currently looks like this:
Imports System.Web.Http
Namespace Controllers
Public Class FileController
Inherits ApiController
Public Function PostUpload(<FromBody()> ByVal ObjRequest As FileULRequest) As String
Return ""
End Function
End Class
End Namespace
In the Sub Main I have: (note, this is cleaned out)
Sub Main()
API_URL = Dns.GetHostByName(Dns.GetHostName()).AddressList(0).ToString()
Dim ThisConfig As New HttpSelfHostConfiguration("HTTP://" & API_URL & ":" & API_PORT)
ThisConfig.Routes.MapHttpRoute(name:="FileUpload", routeTemplate:="{controller}/{ObjRequest}", defaults:=New With {.id = RouteParameter.Optional})
ThisConfig.MaxConcurrentRequests = API_MaxCon
Dim Config As HttpSelfHostConfiguration = ThisConfig
Using Webserver As New HttpSelfHostServer(Config)
Try
Webserver.OpenAsync().Wait() 'Start the web server
Console.WriteLine("Listening at: " & API_URL & ":" & API_PORT) 'use the URL & port defined
Console.WriteLine("Enter to end")
Catch ex As Exception
Console.WriteLine("Error:{0}", ex.Message.ToString)
Console.WriteLine("Enter to end")
Console.ReadLine()
End
End Try
Dim Cmd As String = UCase(Console.ReadLine())
End
End Using
End Sub
API_Port and API_MaxCon are properties stored in the Appsettings.
What I'm trying to do is set the FileULRequest object params, post this to the service, confirm & validate the data and, if successful, save the file onto a network share. I've tried a large number of different combinations and nothing seems to get close; I cant get inside the Post event in the debugger to figure out or test anything.

windows mobile .net CF 3.5 Multiple Async Web Service Calls = Timeout

I'm developing an App for Windows Mobile 6.5, Compact Framework 3.5. The application starts by loading a simple login form. While the login form is created and opening, a new thread is created for retrieving some setup data from a Web Service. 3 Separate calls are made to the same Web Service asynchronously within this thread.
The first call retrieves a list of Companies
The second retrieves a list of Locations that belong to a Company
The third retrieves a list of Users and their Login ID's that belong to a Location & Company
Each Web Service OnGet***Completed event then saves the returned data to a local SQLite Database
When I compile and run that logic through the Visual Studio debugger, everything runs great. The app starts, the form is shown, the three web service calls are created and finish. I can login as a user and carry on. Ok all is good!
EXCEPT, when I build my CAB file and install the application on the exact same device I use for debugging... the application sits for 60 seconds and I get 3 WebException "The operation has timed-out" errors and the application is forced closed.
I've been playing and researching this issue for a couple of days now. I read a lot about setting the Max Connection in the config of the Web Service and I have also set the bindings to be large values as shown below:
<add address="*" maxconnection="324"/>
and
<binding name="BasicHttpBinding_IGenericContract" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647"
maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
</binding>
I have also set the Default Connection Limit to 22 before I start the Web Service calls in the Windows Mobile application.
Here is my code that starts all three asynchronous Web Service calls by creating new instances of each class (class structure shown below)
'Currently Inside of Initial Synchronize Thread
System.Net.ServicePointManager.DefaultConnectionLimit = 22
Dim syncCompanies As New SyncCompanies(True)
Dim syncLocations As New SyncLocations(True)
Dim syncUsers As New SyncUsers(True)
Here is the SyncCompanies class The New method accepts a "Asynchronous" boolean parameter which tells the class to Execute the Web Service call asynchronously or synchronously. I would like to run all three web services asynchronously.
Public Class SyncCompanies
Private service As New RDScanService.BasicHttpBinding_IGenericContract
'Method that Retrieves the Companies data from the server
Public Sub New(ByVal Asynchronous As Boolean)
DoAsynchronously = Asynchronous
NewCompanies = New Companies(False, False)
Try
IsBusy = True
If DoAsynchronously Then
'Begin the Asynchronous Call
Dim cb As New AsyncCallback(AddressOf onGetCompaniesComplete)
service.BeginGetCompanies(currentSetup.LastCompaniesSync, True, cb, Nothing)
Else
'Perform the Synchronous Call, create a list of Companies and pass to the Execute function for saving
For Each company In service.GetCompanies(currentSetup.LastCompaniesSync, True)
Dim newCompany As New Company(False, False, "")
newCompany.Code = company.Code
newCompany.ReportName = company.ReportName
NewCompanies.Companies.Add(newCompany)
Next
Execute()
IsBusy = False
End If
Catch ex As Exception
service.Abort()
IsBusy = False
Success = False
ErrorVerb = "retrieving companies from server"
ErrorMessage = ex.Message
Finally
Dispose()
End Try
End Sub
Private Sub onGetCompaniesComplete(ByVal ar As IAsyncResult)
'End the Asynchronous Web Service Call and start the Asynchronous Processing
For Each company In service.EndGetCompanies(ar)
Dim newCompany As New Company(False, False, "")
newCompany.Code = company.Code
newCompany.ReportName = company.ReportName
NewCompanies.Companies.Add(newCompany)
Next
ExecuteAsync()
End Sub
Public Sub ExecuteAsync()
'Run the Synchronous method on a new ThreadPool thread
ThreadPool.QueueUserWorkItem(AddressOf DoExecute)
End Sub
Public Sub Execute()
'First determine if the Web Service returned any Companies to save...
If NewCompanies.Companies.Count > 0 Then
NewCompanies.SubmitToDB(True)
If NewCompanies.Success Then
Success = True
Else
Success = False
ErrorVerb = "syncing companies"
ErrorMessage = NewCompanies.Message
End If
End If
IsBusy = False
End Sub
Private Sub DoExecute(ByVal stateInfo As Object)
'Call the Synchronous method on this ThreadPool thread
Execute()
End Sub
Private _newCompanies As Companies
Public Property NewCompanies() As Companies
Get
Return (_newCompanies)
End Get
Set(ByVal value As Companies)
_newCompanies = value
End Set
End Property
Private _success As Boolean
Public Property Success() As Boolean
Get
Return (_success)
End Get
Set(ByVal value As Boolean)
_success = value
End Set
End Property
Private _errVerb As String
Public Property ErrorVerb() As String
Get
Return (_errVerb)
End Get
Set(ByVal value As String)
_errVerb = value
End Set
End Property
Private _errMessage As String
Public Property ErrorMessage() As String
Get
Return (_errMessage)
End Get
Set(ByVal value As String)
_errMessage = value
End Set
End Property
Private _isBusy As Boolean
Public Property IsBusy() As Boolean
Get
Return (_isBusy)
End Get
Set(ByVal value As Boolean)
_isBusy = value
End Set
End Property
Private _doAsynchronously As Boolean
Public Property DoAsynchronously() As Boolean
Get
Return (_doAsynchronously)
End Get
Set(ByVal value As Boolean)
_doAsynchronously = value
End Set
End Property
Protected Sub Dispose()
service.Dispose()
End Sub
End Class
For the life of me, can't figure out why my application runs smoothly through the Visual Studio Debugger, but when installed on the MC55A Motorola device, it crashes. The application also runs fine in the emulator.
I have tried removing 1 of the Asynchronous Web Service calls and it worked perfectly fine installed, but when I try 3 or more, it fails. It is almost like when the application is installed on the device, the Default Connection Limit is ignored and set back to 2.
Sorry for the novel, just wanted to make sure I gave enough information since I have seen similar issues on Stackoverflow, but nothing exactly like this one. I do realize that I could perform these calls Synchronously with only 1 Web Service connection open at a time, but we are in a crunch to make this application the fastest for our customers. Even though I can perform this Initial Sync operation synchronously, I know there will be places down the road where I need more than 2 web service connections running at once.
I see you are catching and storing the Error Message. Did your novel happen to say what that error message was?
I also notice that your ErrorMessage string value is only 1 item, so it could be getting overwritten. You may try List(Of String) and add error messages... at least until you have had a chance to catch some of those errors.
Before going too far into this, can your device browse to the web service? If your device can not browse there, then the code in your app will not be able to get to it either.
I aggree with jp2code, before trying to adopt the code you should ensure that the device can connect to the web service.
So what is the diff between running in debugger/emulator and stand-alone on the device: the device is connected to your PC's network.
Can your device connect to the server or the web service? If not, how should your code ever success?
Check the devices network settings. Is it connected to work (windows sharing) or internet (web browsing, web services)? Do you use WLAN or GSM? What address is your web service? Is your network blocking access off WLAN or public internet (GSM)?
As said, first ensure the connection is OK before changing your working code.
BWT: If device is WLAN connected to same network as development PC you may use remote debugging via TCP/IP: VS2008 remotely connect to Win Mobile 6.1 Device
Using this you have the same environment when you disconnect the ActiveSync/WMDC connection to the device.
I ended up creating a new Smart Device Application and tried calling the web service 3 times in a row asynchronously. Surprisingly it worked when I deployed the new app to the device. I slowly brought in the code from my previous app into the new one and so far I have not been able to find why the other app wasn't working. :s They are now the exact same applications except the new one works as expected.
Thanks for the responses, those were definitely good starting points to see if the device was sharing the same connection when not connected to active sync. I will post up further information if I find out why the other App isn't working, but as of now the new one is working fine.

WCF Service Method - Refactoring for Unit Test and Mocking

I've a WCF service with the following method:
Public Function ScheduleEmail(ByVal request As ScheduleEmailRequest) As ScheduleEmailResponse _
Implements EmailProtocol.ISchedulingService.ScheduleEmail
Try
If Not Email.IsValidEmailAddress(request.EmailAddress) Then
EmailSchedulerTrace.Source.WriteError(String.Format("Email with template '{0}' was not sent to '{1}' because it the address is invalid.", request.EmailName, request.EmailAddress))
Else
Dim mgr As New JobManager
Dim job As New EmailJob
Dim suppression As New SuppressionManager
Dim emailItem As Email = Email.GetEmailByName(request.EmailName)
If suppression.CheckSuppresion(emailItem, request.EmailAddress) Then
job.JobGuid = Guid.NewGuid
job.EmailAddress = request.EmailAddress
job.EmailGuid = emailItem.ID
job.ScheduledSendTime = request.ScheduledTime
job.CustomAttributes = request.CustomAttributes
job.ConsumerID = Email.GetConsumerId(request.CustomAttributes)
mgr.ScheduleJob(job)
Else
EmailSchedulerTrace.Source.WriteWarning(String.Format("Email with template '{0}' was not sent to '{1}' because it was suppressed.", request.EmailName, request.EmailAddress))
End If
End If
Catch ex As Exception
EmailSchedulerTrace.Source.WriteError(ex)
Throw
End Try
Return New ScheduleEmailResponse
End Function
I need to write Unit Test for this Method. Please help me out with
Do i need to change anything in my method?
What should I mock?
Your help is greatly appreciated. Thanks in advance.
Regards,
Sachin
You need to be able to swap out any 'services' (classes that you new up in a method or fields in the class) which connect to other systems (database, email server etc) so you need to create interfaces for the classes and inject the correct implementation at runtime and in your unit test, you can create mock or fake implementations for testing purposes.
A good start would be to define an interface for:
JobManager
EmailSchedulerTrace
SuppressionManager
You also might need to move the functionality of your static methods on Email
GetEmailByName
GetConsumerId
if they encapsulate database access or any other service which you cannot isolate.

gSOAP instance of .NET service

I have a linux c++ client (via gSOAP) to WCF c# server. The WCF c# service contains list of objects, on which some action is executed. Each time i call some function on service, the new object is created, action on that object is executed and that object lands into list in service. at the end i am calling another function on service, which loops over all objects in a list and executes another call on them. this works as intended on c#, with both client and service pure WCF.
it works different via gSOAP. each time i call a first function on service via gSOAP, that action is executed and list is updated. but it is each time new service. so basically i am dealing each time with new service. i do not wont serialize/deserialize object itself, to have it on inux side.
any ideas how to solve this?
on c# side i have something like (unnecessery details skipped)
class Service : IService
{
List list = new List();
void func1(int i)
{
Class1 c = new Class1(i);
c.create();
list.Add(c);
}
void func2()
{
foreach(Class1 c in list)
{
c.close();
}
}
}
on gSOAP side i have something like
Proxy service (endpoint);
service.func1(1);
service.func1(2);
//...
service.func2();
as i said problem is: when func2() is executed it operates on empty list, meaning gSOAP object of Proxy service does not contain c# object of service.
Help, help!
ps.
the solution is found: container made "static" does the trick.

How can I write to my own app.config using a strongly typed object?

The following code has two flaws, I can't figure out if they are bugs or by design. From what I have seen it should be possible to write back to the app.config file using the Configuration.Save and according to http://www.codeproject.com/KB/cs/SystemConfiguration.aspx the code should work.
The bugs are shown in the source below and appear when you try to set the property or save the config back out.
Imports System.Configuration
Public Class ConfigTest
Inherits ConfigurationSection
<ConfigurationProperty("JunkProperty", IsRequired:=True)> _
Public Property JunkProperty() As String
Get
Return CStr(Me("JunkProperty"))
End Get
Set(ByVal value As String)
' *** Bug 1, exception ConfigurationErrorsException with message "The configuration is read only." thrown on the following line.
Me("JunkProperty") = value
End Set
End Property
Public Sub Save()
Dim ConfigManager As Configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
' The add / remove is according to http://www.codeproject.com/KB/cs/SystemConfiguration.aspx
ConfigManager.Sections.Remove("ConfigTest")
' *** Bug 2, exception InvalidOperationException thrown with message "Cannot add a ConfigurationSection that already belongs to the Configuration."
ConfigManager.Sections.Add("ConfigTest", Me)
ConfigManager.Save(ConfigurationSaveMode.Full, True)
End Sub
Public Shared Sub Main()
Dim AppConfig As ConfigTest = TryCast(ConfigurationManager.GetSection("ConfigTest"), ConfigTest)
AppConfig.JunkProperty = "Some test data"
AppConfig.Save()
End Sub
' App.Config should be:
' <?xml version="1.0" encoding="utf-8" ?>
'<configuration>
' <configSections>
' <section name="ConfigTest" type="ConsoleApp.ConfigTest, ConsoleApp" />
' </configSections>
' <ConfigTest JunkProperty="" />
'</configuration>
End Class
I'd like to do it this way so that on the first run of the app I check for the properties and then tell the user to run as admin if they need to be set, where the UI would help them with the settings. I've already 'run as admin' to no effect.
Your code doesn't really make any sense. I took your example code and turned it into a simple example that works. Please note this is not best practise code, merely an example to aid you on your journey of learning the configuration API.
Public Class ConfigTest
Inherits ConfigurationSection
<ConfigurationProperty("JunkProperty", IsRequired:=True)> _
Public Property JunkProperty() As String
Get
Return CStr(Me("JunkProperty"))
End Get
Set(ByVal value As String)
' *** Bug 1, exception ConfigurationErrorsException with message "The configuration is read only." thrown on the following line.
Me("JunkProperty") = value
End Set
End Property
Public Overrides Function IsReadOnly() As Boolean
Return False
End Function
Public Shared Sub Main()
Dim config As Configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
Dim AppConfig As ConfigTest = config.GetSection("ConfigTest")
AppConfig.JunkProperty = "Some test data"
config.Save()
End Sub
End Class
This code will open the config file, modify the attribute JunkProperty and persist it back it the executable's configuration file. Hopefully this will get you started- it looks like you need to read about the configuration API a bit more.
I've used the API to create configuration sections for large scale enterprise apps, with several 1000 of lines of custom hierarchical config (my config was readonly though). The configuration API is very powerful once you've learnt it. One way I found out more about its capabilities was to use Reflector to see how the .NET framework uses the API internally.
Maybe you don't know Portuguese or c# but this is you want http://www.linhadecodigo.com.br/Artigo.aspx?id=1613
using BuildProvider from asp.net
After loading a configuration it is readonly by default, principally because you have not overriden the IsReadOnly property. Try to override it.
¿Is there something that prevents you from using a setting?
Looks like it is not possible by design. App.config is normally protected as it resides along with the app in the Program Files directory so must be amended at installation time by the installer.
Pity really, I'd like the app to have settings that an admin can set.
Sorry if I didn't understand your case, but yes, you can change App.config at runtime.
Actually, you will need to change YourApp.exe.config, because once your app is compiled, App.config contents are copied into YourApp.exe.config and your application never looks back at App.config.
So here's what I do (C# code - sorry, I still haven't learnt VB.Net)
public void UpdateAppSettings(string key, string value)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
foreach (XmlElement item in xmlDoc.DocumentElement)
{
foreach (XmlNode node in item.ChildNodes)
{
if (node.Name == key)
{
node.Attributes[0].Value = value;
break;
}
}
}
using (StreamWriter sw = new StreamWriter(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile))
{
xmlDoc.Save(sw);
}