Porting winform application to run on raspberry PI - c#

I have a c# winform application that I want to port to run on my new Raspberry PI 3. I'm in moaning mode because I thought that my app would just run. That is not the case at all. My winform app uses quart. net, the aforge library and common .net libraries such as system.configuration.
I though I would start with my logging class as someone had mentioned that non UI code should be easy to convert if anything needed changing at all.
This looks like I'm going to have to reinvent the wheel. To be specific for startes have a look at the function below. Any code that uses system.configuration will not work.
Is there any easier way of getting my app to work or do I have to literally convert almost all my code. Is the aforge library even going to work on the PI?
Is quart.net going to work?
Right now I feel like giving up and buying a small windows PC that runs "proper" windows.
C# Winform Code
class Logging
{
public void Write_To_Log_File(String Message, String Procedure, String Error_Code, String Error_String)
{
try
{
// If the log file is bigger than allowed size then archive
if (File.Exists(#ConfigurationManager.AppSettings["LogSavePath"]))
{
FileInfo file = new FileInfo(#ConfigurationManager.AppSettings["LogSavePath"]);
if (file.Length > Convert.ToInt32(ConfigurationManager.AppSettings["FileLogSizeLimit"]))
{
// Rename the file
File.Move(#ConfigurationManager.AppSettings["LogSavePath"], #ConfigurationManager.AppSettings["LogSavePath"] + string.Format("{0:yyyy-MM-dd_hh-mm-ss-tt}", DateTime.Now) + ".csv");
}
}
// If log file does not exist then create it and add the headers
if (File.Exists(#ConfigurationManager.AppSettings["LogSavePath"]))
{
}
else
{
// Create the file
System.IO.File.Create("LogSavePath");
// Add data
string[] Headers = { "Time" + "," + "_Message" + "," + "Procedure" + "," + "Error_Code" + "," + "Error_String" };
System.IO.File.AppendAllLines(#ConfigurationManager.AppSettings["LogSavePath"], Headers);
}
if (File.Exists(#ConfigurationManager.AppSettings["LogSavePath"]))
{
string[] Log = { DateTime.Now.ToString() + "," + Message + "," + Procedure + "," + Error_Code + "," + Error_String };
System.IO.File.AppendAllLines(#ConfigurationManager.AppSettings["LogSavePath"], Log);
}
}
catch
{
}
}
}

Microsoft launched a Windows 10 IoT Core Porting Tool for this purpose. This can assist you in migrating from Win32 apps and libraries to Windows 10 IoT Core apps. More details here: https://developer.microsoft.com/en-us/windows/iot/win10/tools/iotapiportingtool

Related

C# File.copy and Directory.CreateDirectory working on Win10 but in Win7 it appends folder to parent folder

The same code, one on windows 10, the other on windows 7.
The idea is to have a directory from a network drive replicate over to a local drive.
On windows 10, the machine I am writing it on, it works perfectly fine as intended.
On windows 7, the target machine, it 'works' but the sub folder structure is messed up.
Example,
C:\target -> the target location
C:\targetNewFolderName1 -> What its being copied to
C:\targetNewFolderName2
C:\targetNewFolderNameN
When it should be doing this below,(which it is, on windows 10, not on windows 7)
C:\target -> the target location
C:\target\NewFolderName1 -> What its being copied to
C:\target\NewFolderName2
C:\target\NewFolderNameN
Master is a network directory, #"\\server\fu\bar\target"
Slave is a local directory, #"C:\target"
These are passed to the function.
Function header, private void CheckMasterToSlave(string MasterPath, string SlavePath, string BackupPath, string[] MasterFilesList, string[] SlaveFilesList)
The below code snipit is within a foreach; foreach (string master in MasterFilesList).
log.Info(master + " doesnt exist, copying");
string directoryCheck = (SlavePath + master.Substring(MasterPath.Length)).Substring(0,
(SlavePath + master.Substring(MasterPath.Length)).LastIndexOf("\\"));
if (!Directory.Exists(directoryCheck))
{
log.Debug(directoryCheck + " Directory not present, touching.");
try
{
Directory.CreateDirectory((SlavePath +
master.Substring(MasterPath.Length)).Substring(0, (SlavePath +
master.Substring(MasterPath.Length)).LastIndexOf("\\")));
}
catch
{
log.Error(master + " directory failed to be created in slave environment.");
}
}
try
{
File.Copy(master, SlavePath + master.Substring(MasterPath.Length));
log.Info(SlavePath + master.Substring(MasterPath.Length) + " Successfully created.");
BackupFile(master.Replace(MasterPath, SlavePath), BackupPath, SlavePath);
}
catch
{
log.Error(master + " failed to copy, backup has been halted for this file.");
}
I do not understand why this works as intended on windows 10 but moving it to windows 7 causes this issue.
What would be causing this and how can I stop the new folder from appending to the parent folder in windows 7?
Use Path.Combine to build a path name from different path components instead of just using string concatenation.
Alright, I am stupid and forgot to change to release. When changes that NineBerry mentioned were made. It did work.
I still do not understand why the original did work on windows 10 but not on windows 7. Especially since the BackupFile portion does the same thing as the old 'wrong' way. But both work now.
Regardless, here is the updated bit.
log.Info(master + " doesnt exist, copying");
string[] EndDirectoryFile = master.Substring(MasterPath.Length).Split('\\');
string[] EndDirectory = new string[EndDirectoryFile.Length-1];
for (int i = 0; i < EndDirectoryFile.Length - 1; i++)
{
EndDirectory[i] = EndDirectoryFile[i];
}
string directoryCheck = Path.Combine(SlavePath, Path.Combine(EndDirectory));
if (!Directory.Exists(directoryCheck))
{
log.Debug(directoryCheck + " Directory not present, touching.");
try
{
Directory.CreateDirectory(directoryCheck);
}
catch
{
log.Error(master + " directory failed to be created in slave environment.");
}
}
try
{
File.Copy(master, SlavePath + master.Substring(MasterPath.Length));
log.Info(SlavePath + master.Substring(MasterPath.Length) + " Successfully created.");
BackupFile(master.Replace(MasterPath, SlavePath), BackupPath, SlavePath);
}
catch
{
log.Error(master + " failed to copy, backup has been halted for this file.");
}

Ghostscript.NET - no output file when run as Windows service

I'm writing a Windows Service to scan a set of directories for new PDF files and convert them to TIFF with Ghostscript.NET. When I'd compiled and ran the code as a normal program it functioned perfectly, but when I used the same code as a Service the output TIFF never appears. I've set the destination directory to allow writing for Everyone, and the original PDF is being removed as it's supposed to, so it shouldn't be a permissions issue for the "Local System" user. Auditing the directory for access Failures and Successes just shows a list of Successes.
There is a function that reads the color population of the PDF to determine if it's a color document, or B&W scanned as color. That part works, so there isn't an issue accessing and reading the PDF.
I've also tried removing '-q' from the Ghostscript switches and I don't have any errors reported, and "-dDEBUG" outputs so much garbage I don't know what it's saying - but nothing is tagged as an error.
public static void ConvertPDF(string file, GSvalues gsVals)
{
gsProc = new Ghostscript.NET.Processor.GhostscriptProcessor();
System.Collections.Generic.List<string> switches = new System.Collections.Generic.List<string>();
switches.Add("-empty"); // GS.NET ignores the first switch
switches.Add("-r" + gsVals.Resolution); // dpi
switches.Add("-dDownScaleFactor=" + gsVals.ScaleFactor); // Scale the image back down
switches.Add("-sCompression=lzw"); // Compression
switches.Add("-dNumRenderingThreads=" + Environment.ProcessorCount);
switches.Add("-c \"30000000 setvmthreshold\"");
switches.Add("-dNOGC");
string device;
if (_checkPdf(file, gsVals.InkColorLevels, gsVals))
{
gsVals.WriteLog("Color PDF");
device = "-sDEVICE=tiffscaled24"; // 24bit Color TIFF
}
else
{
gsVals.WriteLog("Grayscale PDF");
device = "-sDEVICE=tiffgray"; // grayscale TIFF
}
switches.Add(device);
// Strip the filename out of the full path to the file
string filename = System.IO.Path.GetFileNameWithoutExtension(file);
// Set the output file tag
string oFileName = _setFileName(oPath + "\\" + filename.Trim(), GSvalues.Extension);
string oFileTag = "-sOutputFile=" + oFileName;
switches.Add(oFileTag);
switches.Add(file);
// Process the PDF file
try
{
string s = string.Empty;
foreach (string sw in switches) s += sw + ' ';
gsVals.DebugLog("Switches:\n\t" + s);
gsProc.StartProcessing(switches.ToArray(), new GsStdio());
while (gsProc.IsRunning) System.Threading.Thread.Sleep(1000);
}
catch (Exception e)
{
gsVals.WriteLog("Exception caught: " + e.Message);
Console.Read();
}
gsVals.DebugLog("Archiving PDF");
try
{
System.IO.File.Move(file, _setFileName(gsVals.ArchiveDir + "\\" + filename, ".pdf"));
}
catch (Exception e)
{
gsVals.WriteLog("Error moving PDF: " + e.Message);
}
}
private static string _setFileName(string path, string tifExt)
{
if (System.IO.File.Exists(path + tifExt)) return _setFileName(path, 1, tifExt);
else return path + tifExt;
}
private static string _setFileName(string path, int ctr, string tifExt)
{
// Test the proposed altered filename. It it exists, move to the next iteration
if(System.IO.File.Exists(path + '(' + ctr.ToString() + ')' + tifExt)) return _setFileName(path, ++ctr, tifExt);
else return path + '(' + ctr.ToString() + ')' + tifExt;
}
This is a sample output of the generated switches (pulled from the output log):
Switches: -empty -r220 -dDownScaleFactor=1 -sCompression=lzw -dNumRenderingThreads=4 -c "30000000 setvmthreshold" -dNOGC -sDEVICE=tiffscaled24 -sOutputFile=\\[servername]\amb_ops_scanning$\Test.tiff \\[servername]\amb_ops_scanning$\Test.pdf
Settings are read in an XML file and stored in a class, GSVals. The class also handles writing to the System log for output, or to a text file in the normal Program version. GSSTDIO is a class for handling GS input and output, which just redirects all the output to the same logs as GSVals. The only code changes between the Program version and the Service version is the Service handling code, and the output is changed from a text file to the system logs. Nothing about the Ghostscript processing was changed.
This is being compiled as x86 for portability, but is being run on x64. GS 9.15 is installed, both x86 and x64 versions. GS.NET is version 4.0.30319 installed via NuGet into VS 2012. ILMerge 2.13.0307 is being used to package the GS.NET dll into the exe, also for portability. None of these things changed between the normal EXE and the Windows Service versions, and as I said the normal EXE works without any issues.
I got it working by using CreateProcessAsUser() from advapi32.dll, using code from this article.
I also had to restructure the order of the switches:
switches.Add("-c 30000000 setvmthreshold -f\"" + file + "\"")
The original source I'd used for speeding up the conversion left out the '-f' part, and the fact that the -f was the tag marking the file. I don't know why this worked in GS.NET, but with normal gswin32c.exe I got an error saying that it was an invalid file, until I set the switch this way.
Oddly, the processes this method creates are still Session 0, but it actually works. I'll keep tinkering, but for now it's working.

Setting an application on start-up not working

I'm developing an windows application and i want to set this application as windows start-up application for that i use this code:-
Code
public static void SetStartup(string AppName,
bool enable)
{
try
{
string runKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
Microsoft.Win32.RegistryKey startupKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(runKey);
if (enable)
{
if (startupKey.GetValue(AppName) == null)
{
startupKey.Close();
startupKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(runKey, true);
startupKey.SetValue(AppName, Assembly.GetExecutingAssembly().Location + " /StartMinimized");
startupKey.Close();
}
}
else
{
startupKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(runKey, true);
startupKey.DeleteValue(AppName, false);
startupKey.Close();
}
}
catch
{
}
}
Calling code on application load
SetStartup(Application.ExecutablePath, true);
And this code works fine.It sets application as a start-up application.
I check that executing msconfig command in run window.It shows this application checked in start-up tab.But when i restarts the system it doesn't start application.
Can any one tell me what is the problem and how can i solve that problem.
If everything points to it being in startup then I can only assume that that part of it is correct, but the application is failing to start for some reason.
When you start an application on run, it's working directory is set to C:\Windows\System32
I have had issues with applications that may be looking for files in its home directory such as config files but are unable to find them.
Normally files referenced the normal way will be found anyway, but if you are manually specifying a path in your code you can use:
string pathToDLL = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LibraryFile.dll");
Using AppDomain.CurrentDomain.BaseDirectory should give the path of the your application exe, rather than the working directory.
Could this be the cause of the problem?
Also, I'm going to assume Vista upwards is the OS, and if that's the case then your application would have to be running as elevated to write to that registry. So, if UAC is off and the machine is restarted then your application, if it's set in the manifest to run as requireAdministrator, would fail silently.
Martyn
Finally i got a answer for this problem.Use StreamWriter for creating a URL link of application instead of creating a LNK into start-up folder.
Create shortcut
private void appShortcutToStartup()
{
string linkName ="MytestLink";
string startDir = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
if (!System.IO.File.Exists(startDir + "\\" + linkName + ".url"))
{
using (StreamWriter writer = new StreamWriter(startDir + "\\" + linkName + ".url"))
{
string app = System.Reflection.Assembly.GetExecutingAssembly().Location;
writer.WriteLine("[InternetShortcut]");
writer.WriteLine("URL=file:///" + app);
writer.WriteLine("IconIndex=0");
string icon = Application.StartupPath + "\\backup (3).ico";
writer.WriteLine("IconFile=" + icon);
writer.Flush();
}
}
}
Delete Shortcut
private void delappShortcutFromStartup()
{
string linkName ="MytestLink";
string startDir = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
if (System.IO.File.Exists(startDir + "\\" + linkName + ".url"))
{
System.IO.File.Delete(startDir + "\\" + linkName + ".url");
}
}
This code works very fine.
I believe the most simplest way would be by following the below steps
1.) Build your application
2.) Navigate to your debug folder
3) Copy the exe and place it at your Startup location
**C:\Documents and Settings\user\Start Menu\Programs\Startup**
OR
Simply drag your exe over start menu--> Program-->Startup and Paste it there (i.e
releasing the mouse button)
I guess that would do your work
Hope it helps

Porting a C# application to Mono, need to use FirewallAPI. Is there a Mono equivalent?

I have written a Windows service that I am needing to port over to Mono so it can be used on Mac / Linux platforms.
It makes use of the FirewallAPI.dll (I think that is the actual name...). The other names are NetFwTypeLb, NATUPNPLib and NETCONLib.
I have been Googling, trying to find a way to implement this on Mac / Linux platforms but I cannot find what I could use to do this.
Is this possible? And combining another question with this one: do Mac / Linux platforms allow services (I think otherwise called 'daemons') to be installed and ran easily?
Thanks,
Madeline
Just for note, this is the current code I am using, I got it off of another StackOverflow question:
public class Firewall
{
public static INetFwMgr WinFirewallManager()
{
Type type = Type.GetTypeFromCLSID(
new Guid("{304CE942-6E39-40D8-943A-B913C40C9CD4}"));
return Activator.CreateInstance(type) as INetFwMgr;
}
public bool AuthorizeProgram(string title, string path,
NET_FW_SCOPE_ scope, NET_FW_IP_VERSION_ ipver)
{
Type type = Type.GetTypeFromProgID("HNetCfg.FwAuthorizedApplication");
INetFwAuthorizedApplication authapp = Activator.CreateInstance(type)
as INetFwAuthorizedApplication;
authapp.Name = title;
authapp.ProcessImageFileName = path;
authapp.Scope = scope;
authapp.IpVersion = ipver;
authapp.Enabled = true;
EventLog.WriteEntry("MachineVerification", authapp.Name + " " + authapp.Scope + " " + authapp.IpVersion);
INetFwMgr mgr = WinFirewallManager();
try
{
mgr.LocalPolicy.CurrentProfile.AuthorizedApplications.Add(authapp);
EventLog.WriteEntry("MachineVerification", authapp.Name + " " + authapp.Scope + " " + authapp.IpVersion);
}
catch (Exception ex)
{
EventLog.WriteEntry("MachineVerification", "MROW!" + ex.Message);
return false;
}
return true;
}
}
I forgot to answer this when I figured it all out!
On OS X, there is no need for making a firewall exception. OS X will ask the user to give your application permission to access the internet.
I am not sure about Linux though, but Mono coverage is a lot higher on Linux so I am sure someone has answered this question for Linux before.

C# / Webservice app on server throwing strange exception

We use ADP for employee information. I had to create a small app that called some web services that ADP has to pull employee information. The app is fairly procedural..not really object orientated in a sense. Basically I go through some web services to pull general information, work information, employee status, etc.
I have most of this data writing out to a text file as a log so I can ensure that everything is working correctly. Finally got it all done, and it works perfect on my local machine. Thought I'd just copy the entire structure onto a server and use windows scheduler to schedule the exe to run nightly (once a day). When it tries to run the app it looks like it is dying when it calls the first web service. The task scheduler log says:
""ADP.job" (ADP.exe)
Started 2/11/2010 2:14:34 PM
"ADP.job" (ADP.exe)
Finished 2/11/2010 2:14:38 PM
Result: The task completed with an exit code of (e0434f4d)."
So I checked the event viewer and it says this:
EventType clr20r3, P1 adp.exe, P2 1.0.0.0, P3 4b745bb9, P4 adp, P5 1.0.0.0, P6 4b745bb9, P7 289, P8 2d, P9 system.io.filenotfoundexception, P10 NIL.
For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.
I put in some console.writelines to see where it is failing...
Here is a simple example of main:
static void Main(string[] args)
{
OpenTextFile();
Console.WriteLine("About to process employee work information...");
tw.WriteLine("About to process employee work information...");
//work info service
EmpWorkInfo();
}
And inside of opentextfile:
public static void OpenTextFile()
{
//used to log data
String sLogName;
Console.WriteLine("Inside of opentextfile");
if (Directory.Exists(logPath))
{
//directory exists
}
else
{
Directory.CreateDirectory(logPath);
}
Console.WriteLine("Inside of opentextfile2");
sLogName = "log_" + DateTime.Today.ToString("MM_dd_yyyy") + ".txt";
tw = new StreamWriter(logPath + sLogName);
}
I see all the console.writelines on the server but as soon as it hits this line from main:
EmpWorkInfo();
Thats when all hell breaks lose (basically it doesn't work). The EmpWorkInfo() is simply a function to get work related information from a web service (as I said this works locally).
static void EmpWorkInfo()
{
Console.Writeline("THIS NEVER PRINTS!!!");
SQLClass s=null;
// Create the web service proxy client.
GetEmployeeWorkInfoService oService = new GetEmployeeWorkInfoService();
oService.Timeout = Int32.MaxValue;
// Serialize the UsernameToken into XML.
// Create the UsernameToken as defined in the WS-I secure profile.
UsernameToken oUsernameToken = new UsernameToken(USERNAME, SECRET);
System.Xml.XmlElement oSecurityHeaderXml =
oUsernameToken.GetXml(new System.Xml.XmlDocument());
ADP.GetEmployeeWorkInfoWebService.SecurityHeaderType oSecurityHeader = new ADP.GetEmployeeWorkInfoWebService.SecurityHeaderType();
oSecurityHeader.Any = new System.Xml.XmlElement[] { oSecurityHeaderXml };
oService.Security = oSecurityHeader;
GetEmployeeWorkInfoRequestFilter oFilter = new GetEmployeeWorkInfoRequestFilter();
//filter by thyssenkrupp company
oFilter.Companies = new String[] { COMPANY_IDENTIFIER };
GetEmployeeWorkInfoRequest oRequest = new GetEmployeeWorkInfoRequest();
oRequest.Filter = oFilter;
try
{
EmployeeWorkInfoType[] arPersonalInfo = oService.GetEmployeeWorkInfo(oRequest);
try
{
s = new SQLClass();
}
catch (Exception e)
{
throw new System.Exception(e.Message.ToString());
}
for (int i = 0; i < arPersonalInfo.Length; i++)
{
String stID = arPersonalInfo[i].EmployeeKey.Identifier.EmployeeId; //employee number
String stEmailAddress = arPersonalInfo[i].WorkInfo.EmailAddress; //employee email address (work)
String stFax = arPersonalInfo[i].WorkInfo.Fax; //employee fax number
DateTime dtHireDate = arPersonalInfo[i].WorkInfo.OriginalHireDate;
String stPhone = arPersonalInfo[i].WorkInfo.Phone; //employee phone number
String stWireless = arPersonalInfo[i].WorkInfo.Wireless; //employee wireless number
tw.WriteLine("Processing ID:" + stID + " Email Work: " + stEmailAddress + " Fax Work: " + stFax + " Hire Date: " + dtHireDate + " Phone Work: " + stPhone + " Wireless Work: " + stWireless + ".");
Console.WriteLine("Processing ID:" + stID + " Email Work: " + stEmailAddress + " Fax Work: " + stFax + " Hire Date: " + dtHireDate + " Phone Work: " + stPhone + " Wireless Work: " + stWireless + ".");
s.SetSQLCommand("dbo.ADP_uiEmployeeWorkInfo");
s.AddSQLCmdParameter("#EmployeeNumber", System.Data.SqlDbType.VarChar, stID);
s.AddSQLCmdParameter("#EmailAddress", System.Data.SqlDbType.VarChar, stEmailAddress);
s.AddSQLCmdParameter("#Fax", System.Data.SqlDbType.VarChar, stFax);
s.AddSQLCmdParameter("#HireDate", System.Data.SqlDbType.DateTime, dtHireDate);
s.AddSQLCmdParameter("#Telephone", System.Data.SqlDbType.VarChar, stPhone);
s.AddSQLCmdParameter("#Mobile", System.Data.SqlDbType.VarChar, stWireless);
s.SQLExecuteNonQuery();
Console.WriteLine("Processed ID:" + stID + " Email Work: " + stEmailAddress + " Fax Work: " + stFax + " Hire Date: " + dtHireDate + " Phone Work: " + stPhone + " Wireless Work: " + stWireless + ".");
Console.WriteLine(Environment.NewLine);
}
s.CloseSQLDB();
s.Dispose();
}
//catch any exception from adp side.
catch (Exception e)
{
throw new System.Exception(e.Message.ToString());
}
}
This functions code is irrelevant (its ugly but do not let that bother you, the code works...). My issue is I cannot even get to the first console.writeline of that function. Is there anything special I need to do when it comes to working with webservices?
Edit
Logpath is defined as simply a static string outside of main:
private static string logPath = Environment.CurrentDirectory + "\\log\\";
I suspect that your application is not able to load the types referenced in that function - EmpWorkInfo.
1) Can you run this application on the target server in a commannd window (cmd.exe) ?
2) Are you using any assemblies from ADP that are installed in the global assembly cache (GAC)? Run "gacutil -l" on your localmachine to see if you are using any assemblies from ADP that are installed in thr gac. If they are, you will need to install these into the machine on which you are running the app.
Does logPath have a trailing backslash? Either way, you ought to use Path.Combine, rather than the string catenation operator (+).
What happens if you comment out all the code in EmpWorkInfo() apart from the first Console.Writeline? Does it still not get written out?
Found out I need the Microsoft.Web.Services3 dll installed on the server.
Continuation on "feroze" answer;
If you want to figure out if the 'loading of dependencies' is causing you grief here, i suggest using the "FUSLOGVW.EXE" tool *(part of .Net). When you run this it will give you a little dialog window with a few options. Create a directory somewhere (like "c:\temp\fusion_logs"), set the mode of FUSLOGVW to "log bind failures only", "custom location->c:\temp\fusion_logs".
Now restart your application and check that it failed. Now look into your fusion_logs directory. This should give you sub directories with different (maybe only 1 for now) application names. Inside each directory you will find the log files. These log files contain the "failed assembly loads" and who (which calling assembly) caused them.
They might help your hunt for a working application,
Hope this helps,
Edit: Posted this after you found the cause. The fuslogvw.exe would have shown you the missing assembly.

Categories

Resources