Using a Winforms App to Configure a Windows Service - winforms

I have a single project made up of a Winform and a Windows Service. When installed, they both exist in the same, common folder, c:\program files\
I would like for the Winform to be able to set settings such as connection strings, passwords, logfile paths, etc. in a common xml file that both applications share. i will handle all of the encryption and decryption so that is not my issue.
The issue seems to be that when the application is installed, the Winform is unable to update the file and I get an access denied error although I am a COMPLETE domain admin with absolute rights over the desktop.
The question is, how does one go about allowing a Winform to define a common connection string between both the Winform application and the Windows service it wishes to configure?
I do NOT want to rely on the machine.config and want my own XML file that will hold all of the configuration options. There must be something simple I'm missing, a simple solution?

The registry has caused nothing but headaches in the past since Microsoft seems to have moved to the application.config route and have really started to lock down the registry. Add to that the entire WOW6432 branching and it has become more difficult to use.
My current solution, which seems to be working well, it to have the following function to point to the same location whether called from the service or the Winform as long as both have the same company name and product name. I then place my own XML config file in that location and all is well in the world.
' **********************************************************************************************************
' Returns Commong Data Folder
' If folder does not exist, it attempts to create it
' **********************************************************************************************************
Public Function GetCommonDataFolder(ByRef ErrorMessage As String) As String
Dim Result As String = ""
Result = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) & "\" & My.Application.Info.CompanyName & "\" & My.Application.Info.ProductName
Try
Directory.CreateDirectory(Result)
Catch ex As Exception
ErrorMessage = "An exception occurred creating directory: " & Result & ": " & ex.Message
End Try
Return Result
End Function

Related

WPF install : creating and saving database location

I have a WPF C# app which I have created which uses an Access Database (accdb).
I am using Inno for an installer.
I currently have a static path the database in my app.config connectionString section, however what I need is to:
run INNO
ask if this is the Master
YES: install the DB file in C:/MyApp
NO: do not install the DB file, but messageBox asking for the database location (BROWSE and verify Name) - as they will have to look on network.
This browse location answer would then replace the C:/MyApp with //192.168.1.2/C/MyApp for example in the AppConfig connection string section...
Does that sound reasonable or possible (or even 'correct' to do?)
I'm new to C# and this is my first install/deploy I've done so I am very fresh on this.
The other option I was looking at would be to ask to install the database.
If they say NO then just carry on with install.
When the wpf starts up and cant locate the database then I can code in a browse feature in the app which then writes to the appconfig file.
Which is the more Correct way to proceed or is there another option which I am oblivious to which is how it should be done?
Aside from correctness, one advantage to picking the DB location in the app is if the location ever changes. If you do it in the installer, they would have to reinstall.
I would probably go with installing the app as "master" by default, but also provide an option for the user to change this. Then the installer is simplified and doesn't really have to contain any application logic.

Programatically retrieve the full data directory path in VB.NET

I am working on a VB.NET / WPF application which will use SQL Compact Edition databases.
The application should allow the user to save and load different versions of the database.
To do this, my intention was to have a standard database name (e.g. myDatabase.sdf) which would be saved in the DataDirectory.
Then the user would have an option to save a version of the current data (calling it what they want e.g. savedDatabase1.sdf) and the application would then take a copy of the database from DataDirectory and save it to another location (e.g. a SavedDatabase folder created in the Windows app data area)
and to load a different version of the database, the application could copy the database from the SavedDatabase folder and overwrite the database in the DataDirectory location.
I can see solutions for overriding the data directory location, but I can't find any code which allows you to retrieve the path of the current data directory folder so it can be used in any file copy activities as described above.
So my question is - How do I programmatically retrieve the full path currently being used as the data directory?
You could save all the data in the same folder as the application:
System.AppDomain.CurrentDomain.BaseDirectory
However if you are not sure you have access to that folder you can always write to the Application Data which is made for this:
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
Then in this location you can create your own application directory and then your sub directories.
simple example would be:
Dim DataPath as String = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) & "/MyApplication/Data/"
connectionString="Data source=|" & DataPath &"myDatabase.sdf;"

Setting Report Source in Crystal Report for .Net throws a NullReferenceException in crdb_adoplus.dll

I have come across a very strange exception when using Crystal Reports XI for dot net. I'm currently developing a reporting service in VB.Net for an enterprise application. The service is hosted in the main application as a plugin/add-in using System.AddIn (aka Managed Add-in Framework or MAF). The main application utilizes Win Forms, and will be hosting controls provided by my add-in in a WPFElementHost.
The add-in resides in it's own app domain and passes WPF controls using NativeHandleContracts from the add-in app domain, to the host app domain where they are bound at runtime to the WPFElementHost.
The WPF control the add-in provides has a Crystal Report Viewer Control in it. Up to this point, everything works fine. I can create nearly any WPF control in my Add-in and it works flawlessly in the host application. This falls apart as soon as I try and attach a report to the viewer.
First I tried using and ADO.Net DataSet as my data schema to build my report against. Whenever this would try and load into the report viewer, crystal reports would throw an exception that the schema didn't exist. This was caused because for some reason crystal report want's to look in the host applications app domain, as well as namespace for the schema. However The schema exists in an entirely different app domain and namespace. I tried embedding the schema as a resource, and copying local. With no success I moved on to using .Net Objects.
With .Net Objects I created an XML document that the report is designed against. This worked fine and allowed me to pass my report and view it in the host application. This is where I am stuck at the moment. Whenever I try and supply a DataSource for the report Crystal Reports throws a null reference exception in the crdb_adoplus.dll which is SAP's DLL and gives me no indication of what caused the exception. All of my objects are properly instantiated, the report, viewer, wpf element host, and the wpf control. My dataset, which is served using entity framework, is being converted to a DataSet so there are no nullable types in it, only dbnull values. There is no reason this exception should be throwing at this point. No additional output is provided other than the exception being thrown.
Additionally, the report object that is created will have portions of it time out during this loading process. This will happen without any exceptions or errors of any kind being thrown.
Here is the code that is retrieving the data, attempting to bind data to the report, and binding the report to the viewer.
Try
Dim messages As String = Nothing
If report Is Nothing Then
report = New BOMPartsListWithStandard
End If
Using conn = BOMReportingService.BOMReportingServiceClient.CreateConnection()
Dim dataSet As DataSet = conn.Proxy.GetBOMTreePartsListElements(5339, messages).ToDataSet
report.SetDataSource(dataSet)
End Using
reportViewer.ViewerCore.ReportSource = report
reportViewer.ViewerCore.RefreshReport()
Debug.WriteLine($"Created and attached report succesfully. With {IIf(String.IsNullOrEmpty(messages), "no messages.", messages)}")
Catch ex As Exception
Debug.WriteLine(ex.ToString)
End Try
The report.SetDataSource(dataSet) bit of code is where the exception is being thrown.
Am I overlooking somthing obvious? Is there a way to convince Crystal Reports to use an ADO.Net DataSet in the correct app domain so I can get away from the XML issue?
This add-in needs to exist in a separate app domain, or process, so that it can be unloaded and reloaded dynamically at runtime. This is a requirement for the system. It also needs to be self contained, so I can't do anything on the host side of the application, it all must operate within the add-in.
Thanks in advance for the help.
After digging around I figured out an answer to my own question. So i'll post the answer here just in case someone else comes across a similar problem.
The problem seems to lie in how the report interprets the DataSet object. For some reason it get's lost when looking for data. So you have to point it to the actual first table entry.
So I changed this bit of code:
Using conn = BOMReportingService.BOMReportingServiceClient.CreateConnection()
Dim dataSet As DataSet = conn.Proxy.GetBOMTreePartsListElements(5339, messages).ToDataSet
report.SetDataSource(dataSet)
End Using
I replaced report.SetDataSource(dataSet) with report.SetDatSource(dataSet.Tables(0)). So now it looks like this
Using conn = BOMReportingService.BOMReportingServiceClient.CreateConnection()
Dim dataSet As DataSet = conn.Proxy.GetBOMTreePartsListElements(5339, messages).ToDataSet
report.SetDataSource(dataSet.Tables(0))
End Using
Hopefully this will help someone else out in the future.

Winforms ConnectionString and TeamCity

We are starting a new WinForms project and decided to use TeamCity to create builds and run unit and integration tests. The project deals with database. We have 3 databases (developDB (this is used by developers while developing =) ), testDB (this is used by teamcity to run tests) and productionDB(this is used by client)). TeamCity has 3 buildConfiguration. The first is triggered when commit happens. The second is triggered every night to run integration tests. And the third is triggered by developer when we what to make a release. So I want TeamCity to be able to change connectionString depending on what kind of build happens. Also I don't want to store connectionString in app.config (I don't want client to know the user and password). What options are available to perform the task?
Thanks in advance!
Updated
I use NHibernate and FluentNHibernate to connect to databases if it matters.
In this situation, I would use TeamCity to run a nant script to perform the build.
NAnt allows you to modify config file values (such as your connection string) at build time.
An example of using TeamCity/NAnt to deploy to different staging environments can be found at this blog post:
http://thecodedecanter.wordpress.com/2010/03/25/one-click-website-deployment-using-teamcity-nant-git-and-powershell/
As #surfen suggests, the connection string values for each environment should be encrypted to prevent credentials from being stored in plain text.
I have not used TeamCity, but I have written multiple applications with dynamically changing ConnectionStrings during logon process (ie. at runtime), and It's quite simple.
You didn't tell how do you connect to your Database. Since you mention app.config, I suppose it is ADO.NET DataSets or simmilar technology, which creates a read-only(getter) ConnectionString in your Settings.Designer.cs / app.config.
What I did, was to create a setter method in Settings.cs (not Settings.Designer.cs) for the ConnectionString property like this:
public void setNorthwindConnectionString(String value) {
this["NorthwindConnectionString"] = value;
}
My generated DataSet then uses this NorthwindConnectionString for accessing data.
You can use preprocessor directives for conditional setup of your ConnectionString:
#if DEBUG
Console.WriteLine("Mode=Debug");
Settings.Default.setNorthwindConnectionString("(DebugDBConnectionString)");
#else
Console.WriteLine("Mode=Release");
Settings.Default.setNorthwindConnectionString("(ReleaseDBConnectionString)");
#endif
You could also encrypt your connection strings, and copy the right app.config during post build event.
I am assuming you would be using msbuild to build your projects in Team city. If that is the case, then you can send the Conditional Compilation Symbol where in you can pass what ever symbols you need.
Once you have the symols, you can do things like:
#if DEVBUILD
//.... Your Connection String Code here
#endif
#if INTBUILD
.... Your Connection String Code here
#endif
That's the answer to your frst question.
Looking at the second part of your question, where in you do not want to store the user name & password in the app.config,
Options:
try intergrated security, it will use your domain account
if option cannot be used, try keeping your connection string as a Registry Key, so that its not obvious or an Environment variable.

How data sources are linked in compiled Visual Basic .NET applications?

When I try adding a new data source to my project I get a prompt saying "The connection you selected uses a local data file that is not part of the current project. Would you like to copy the file to your project and modify the connection?"
This sounds rather obscure to me, as I'm new to VB.NET and programming non-script apps in general. The data source in this case is a .sdf SQL CE file.
Question 1: If I say that I want to copy the database to the project, what will happen after I compile the app, where the database will be? (how will I edit it from another app?)
Question 2: If I do not include it, how the data source will still keep linked? Can I link using filesystem enviroiment variables like %ProgramFiles%\MyAppDir?
Question 3: Can I just tell it to use a read-only (Just needs to read it) data source on the web, like on an FTP?
Thanks for help in advance! =)
Visual Studio just tries to have all your files in one place, with the rest of them - in project folder.
Q1 - SQL Ce database can be anywhere you like, you use ConnectionStrings settings section in your app.settings. SQL Ce is just a file, nothing more, nothing less, therefore accessible from any application. Is is not compiled into your app, it just travels with it when you deploy etc.
To answer Q2 - you probably can, but I'm not sure that it will work automatically for you.
Q3 - to read from ftp, probably not, unless you work ftp client support into your app and download it in background. Another solution would be to map ftp to local drive and access it from there.
For example, storing connection string in app.config (excerpt from app.config):
<connectionStrings>
<add name="db" connectionString="Provider=Microsoft.SQLSERVER.OLEDB.CE.2.0; data source=c:SomePathToSDFfile" />
</connectionStrings>
Later in your application, you access this connection string like this:
(add reference to System.Configuration)
Dim c As Configuration.ConnectionStringSettingsCollection
Dim cn As SqlClient.SqlConnection
c = Configuration.ConfigurationManager.ConnectionStrings
cn = New SqlClient.SqlConnection
cn.ConnectionString = c.Item("db").ConnectionString
cn.Open()

Resources