gsapi_init_with_args is made: -100 - c#

I'm trying to build a PostScript to PDF Converter using Ghostscript.Net.
The Args that GetArgs return, are the ones I usually use to call gswin32c.exe and they work fine.
But every time i call Process, i get an error Saying "An error occured when call to 'gsapi_init_with_args' is made: -100". Googling that error didn't bring anything up so I thought I might ask here.
Are there differnet arguments to consider when calling the .dll directly with Ghostscript.net? Or did I made a mistake somewhere else?
Here's my class:
public class PdfConverter
{
#region Private Fields
private List<GhostscriptVersionInfo> _Versions = GhostscriptVersionInfo.GetInstalledVersions(GhostscriptLicense.GPL | GhostscriptLicense.AFPL | GhostscriptLicense.Artifex);
#endregion
#region Private Properties
private GhostscriptVersionInfo Version { get; set; }
#endregion
#region Construction
public PdfConverter()
{
Version = GhostscriptVersionInfo.GetLastInstalledVersion();
}
#endregion
#region Public Members
public bool ConvertToPdf(DirectoryInfo dir)
{
var d = dir;
if(!d.Exists)
return false;
var postScriptFiles = d.GetFiles("*.ps");
var pdfFiles = postScriptFiles.Select(psf => new FileInfo(Path.ChangeExtension(psf.FullName, ".pdf")));
foreach(var file in postScriptFiles) {
//ThreadPool.QueueUserWorkItem(new WaitCallback((o) => {
Process(file, new FileInfo(Path.ChangeExtension(file.FullName, ".pdf")));
//}));
}
pdfFiles.ForEach(pdf => pdf?.Refresh());
return pdfFiles.All(pdf => pdf.Exists);
}
#endregion
#region Private Helpers
private void Process(FileInfo inputFile, FileInfo outputFile)
{
Console.WriteLine($"Converting {inputFile} to {outputFile}");
var proc = new GhostscriptProcessor(Version, true);
proc.Process(GetArgs(inputFile, outputFile).ToArray(), new ConsoleStdIO(true, true, true));
}
private IEnumerable<string> GetArgs(FileInfo inputFile, FileInfo outputFile)
{
return new [] {
$"-q ",
$"-sDEVICE=pdfwrite",
$"-dSAFER",
$"-dNOPAUSE",
$"-dBATCH",
$"-sPAPERSIZE=a4",
$"-dEmbedAllFonts=true",
$"-dAutoRotatePages=/None",
$"-sOutputFile=\"{outputFile.FullName}\"",
$"-dCompatibilityLevel#1.4",
$"-c .setpdfwrite",
$"-f \"{inputFile.FullName}\""
};
}
#endregion
}
Edit:
I forgot to mention: To implement it i had to make my own GhostsdcriptStdIO class. I admit that I'm not entirely sure if I did this right. Although it does get instanciated without exceptions, and override StdOut(...) get's called, and the output is written to the console as expected. override void StdError(...) get's called as well. And also written to the console as expeted.
The Output of the error btw is:
"**** Could not open the file "c:\temp\test.pdf""
"**** Unable to open the initial device, quitting."
Here's my ConsoleStdIO class:
public class ConsoleStdIO : Ghostscript.NET.GhostscriptStdIO
{
#region Construction
public ConsoleStdIO(bool handleStdIn, bool handleStdOut, bool handleStdError) : base(handleStdIn, handleStdOut, handleStdError) { }
#endregion
#region Overrides
public override void StdError(string error)
{
var foo = Encoding.Default.GetBytes(error);
var lenght = foo.Length;
using (var err = Console.OpenStandardError()) {
if(err.CanWrite)
err.Write(foo, 0, lenght);
}
}
public override void StdIn(out string input, int count)
{
byte[] bytes = new byte[0];
using(var stdInput = Console.OpenStandardInput()) {
stdInput.Read(bytes, 0, count);
}
input = Encoding.Default.GetString(bytes);
}
public override void StdOut(string output)
{
var foo = Encoding.Default.GetBytes(output);
var lenght = foo.Length;
using (var err = Console.OpenStandardError()) {
if(err.CanWrite)
err.Write(foo, 0, lenght);
}
}
#endregion
}
Again: doing the same operation with the exact same files and arguments using gswin32c.exe works fine.
Happy Hacking

Error -100 is gs_error_Fatal, which means 'something catastrophic went wrong'. Its an indication that the program failed to start up properly and we can't tell why. The back channel may contain more information.
And indeed, the back channel tells you what's wrong:
**** Could not open the file "c:\temp\test.pdf
**** Unable to open the initial device, quitting.
Ghostscript is unable to open the output file, which means it can't open the pdfwrite device (because that requires an output file) so it aborts the operation.
There could be a number of reasons why Ghostscript can't open the output file. The first thing I'd do is trim down the number of arguments;
You don't want -q (quiet) when you are trying to debug a problem, you want all the information you can get.
I'd remove -dSAFER at least to start with, because that prevents Ghostscript accessing directories outside the current working directory and certain 'special' ones. It may well prevent you accessing the temp directory.
You don't need to set EmbedAllFonts when its the same value as the default.
You could drop the CompatibilityLevel (and note that you've used a # there instead of an =) switch, and the AutoRotatePages while getting this to work.
The "-c .setpdfwrite -f" string has been pointless for years but people still keep using it. All that does these days is slow down the start of processing, ditch it.
Finally you can try changing the backslash ('\') characters to forward slash ('/') in case your string handling is messing that up, or use double backslashes (I'd use the forward slash myself).
You should also check that c:\test\temp.pdf doesn't exist, or if it does exist is not read-only or already open in a different application.

So I solved the problem...
After taking KenS' advice I could run the application without Ghostscript (not Ghostscript.NET) giving me any errors. But it did not produce an actual PDF File.
So KenS's answer did not quite solve the problem, but since 'less is more' and since he took the time to talk to me on IRC to verify that my args in itself were correct, I'll give the answer points nonetheless.
What actually solved my was the following:
Here my original GetArgs(...)
private IEnumerable<string> GetArgs(FileInfo inputFile, FileInfo outputFile)
{
return new [] {
$"-sDEVICE=pdfwrite",
$"-dNOPAUSE",
$"-dBATCH",
$"-sPAPERSIZE=a4",
#"-sFONTPATH=" + System.Environment.GetFolderPath(System.Environment.SpecialFolder.Fonts),
$"-sOutputFile={outputFile.FullName}",
$"{inputFile.FullName}",
};
}
Someone in #csharp pointed out to me, that in c, the first argument is always the name of the command. So he suggested to just put "gs" as the first argument (as a dummy) and try... And that's what actually solved my problem.
So this is how the working GetArgs(...) looks:
private IEnumerable<string> GetArgs(FileInfo inputFile, FileInfo outputFile)
{
return new [] {
$"gs",
$"-sDEVICE=pdfwrite",
$"-dNOPAUSE",
$"-dBATCH",
$"-sPAPERSIZE=a4",
#"-sFONTPATH=" + System.Environment.GetFolderPath(System.Environment.SpecialFolder.Fonts),
$"-sOutputFile={outputFile.FullName}",
$"{inputFile.FullName}",
};
}

Related

Why are my strings not being validated by double equals and characters are not being appended as they should to the end

So I am a Software Packager and I am working on a little project for my company to automate the packaging of some of the free software available via Ninite and Chocolatey. I have managed to get the Ninite stuff working fine but Chocolatey is proving to be a pain.
This section of the code is supposed to compare the app versions from my .csv to data returned from querying chocolatey via cmd.exe.
If the versions don't match I have some other classes that deal with updating the .csv with the latest available version and the rest of the code creates some config files to be used by the other software I am dealing with. This bit is irrelevant as I have managed to get it to work. It's simply just the fact that for some unknown reason the strings returned from querying chocolatey are not behaving as they should.
I initially thought the issue may have been caused by the way I was using .Replace() to filter unwanted data but at this point if I remove all instances of .Replace() and just simply use the data stored in line I get the same results.
The issue seems to lay somewhere in the GetChocApps() method and the print outs are from the CompareVersions() method.
(Sorry if I have provided a tad more code than I should have but I personally thought it was all relevant)
using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
namespace XXXX_PackagingAutomation
{
class Choc
{
private Dictionary<string, string> chocApps = new Dictionary<string, string>();
private Dictionary<string, string> csvApps = new Dictionary<string, string>();
// For each app a new entry is to be added
// Key: Name in Chocolatey
// Value: Name to appear in CSV
private Dictionary<string, string> chocAppsToCheck = new Dictionary<string, string>() {
{"adobereader", "Adobe_Acrobat_Reader_DC"}
};
private List<Data> csvData; // Used to hold csv data passed in from Main method
#region Run
public void Run(List<Data> csvDataIn)
{
csvData = csvDataIn;
GetChocApps();
CsvAppsToDic(); // Place all the CSV apps along with corresponding versions to csvApps dictionary
CompareVersions();
}
#endregion
#region Get Choc Apps
private void GetChocApps()
{
foreach (var appName in chocAppsToCheck)
{
List<string> chocoTempList = new List<string>();
ProcessStartInfo procStartInfo = new ProcessStartInfo()
{
FileName = "cmd.exe",
Arguments = $"/c choco search {appName.Key} --exact",
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
};
//Process.Start(procStartInfo);
Process p = new Process();
p.StartInfo = procStartInfo;
p.Start();
StreamReader outputWriter = p.StandardOutput;
string errorReader = p.StandardError.ReadToEnd();
string lines = outputWriter.ReadToEnd();
string[] individualLines = lines.Split("\n"[0]);
// All lines returned from the command are then added to the chocoTempList
foreach (string line in individualLines)
chocoTempList.Add(line);
p.WaitForExit();
foreach (string line in chocoTempList)
{
// If the line contains the information we are looking for then we add it to the choco dictionaries
if (!line.Contains("Chocolatey v") && !line.Contains("packages found") && line.Contains(appName.Key))
{
#region Adobe Acrobat Reader DC
if (appName.Key == "adobereader")
{
// Filter the line to exclude anything else other than the version which is what we want
// and then assign it to the version variable.
string version = line.Replace($"{appName.Key} ", "").Replace(" [Approved]", "").Replace(".", "-").Replace("/n", "");
// Add the Name and version to the chocoApps dictionary from the chocoTempList
chocApps.Add(appName.Value, version);
}
#endregion
}
}
}
}
#endregion
#region CSV apps to Dictionary
private void CsvAppsToDic()
{
foreach (var app in csvData)
csvApps.Add(app.name, app.version);
}
#endregion
#region Compare Versions
private void CompareVersions()
{
// Run this for each entry in the chocApps Dictionary
foreach (var cApp in chocApps)
{
// If the
foreach (var app in csvApps)
{
// Find the matching name
if (app.Key == cApp.Key)
{
// Check if versions match
if (app.Value == cApp.Value)
{
// If the versions match
//Global.WriteVersion()
Console.WriteLine("Versions match :D");
Console.WriteLine($"{app.Value}a");
Console.WriteLine($"{cApp.Value}a");
}
else
{
// If they don't match
Console.WriteLine("This Version should have matched but it doesn't match :(\nRaw print outs:");
Console.WriteLine($"{cApp.Value}"); // chocoApps Dict
Console.WriteLine($"{app.Value}"); // csvApps Dict
// Heres where the funny bit happens :D
// Note how I have added some characters to the end of the line *abc
Console.WriteLine("\n\n");
Console.WriteLine($"{cApp.Value}*abc"); // chocoApps Dict
Console.WriteLine($"{app.Value}*abc"); // csvApps Dict
}
}
}
}
}
#endregion
}
}
This else statement should not have been called since the two strings should have matched.
Here's a screenshot which includes the console output of the running code.
it might just be case of white space, try:
if (app.Value.Trim() == cApp.Value.Trim())
{
//etc
}
edit: Trim() removes all white space at the start and end of the string including \r and \n also, so will also fix your issue above due to /n i.e. replace .Replace("/n","") with Trim() where you assign the string. Covers a number of issues with simpler code.

C#: Controlling a power supply using VISA libraries and the device USB-TP PIA4850. Not able to read the power supply output properly

I am tring to control a power supply (from Kikusui), using the Kikusui device, USB-TP PIA4850. I use the library, Ivi.Visa.Interop. Following is the code where I am succesfully able to control the power supply, by setting the desired voltage.
(Link to programming guide: https://www.kikusui.co.jp/kiku_manuals/P/PIA4800/english/index.html)
(Link to operation manual: https://www.kikusui.co.jp/kiku_manuals/P/PIA4850_E5.pdf)
Using Ivi.Visa.Interop;
namespace USBTPTest
{
class USBTPController
{
private IResourceManager3 rm = new ResourceManager();
private IMessage iMessage;
public USBTPController() //to get the address of the device connected and open the address if found
{
string[] adrsList = rm.FindRsrc("?*");
if (adrsList.Count() != 0)
{
Open(adrsList[0]);
}
}
public bool Open(string str)
{
string addr = str;
iMessage = (IMessage)rm.Open(addr, AccessMode.NO_LOCK, 0, "");
//iMessage.WriteString("TRM 2");
iMessage.WriteString("NODE 5");
iMessage.WriteString("CH 1");
iMessage.WriteString("REM 1");
bOpened = true;
return true;
}
public bool SetVoltage(float vol) {} //method to set the desired voltage to the power supply, when called with an appropriate argument
The set voltage is checked by a multimeter on the power supply terminals. Similarly current is also set and checked. Until here the code runs and gives the expected results.
public string ReadVOut()
{
iMessage.WriteString("VOUT?"); //Query OUT(ON/OFF) measurement value
string VOutStatus;
VOutStatus = iMessage.ReadString(1024); //Read from PIA
return VOutStatus;
}
public string ReadIOut()
{
iMessage.WriteString("IOUT?"); //Query OUT(ON/OFF) measurement value
string COutStatus;
COutStatus = iMessage.ReadString(1024); //Read from PIA
return COutStatus;
}
}
}
The problem occurs when I read the voltage/current values being outputted. Although I am able to read the values correctly, sometimes reading Voltage using ReadVOut() gives current and vice versa. I am not able to figure out if its a fault in the code or in the power supply I am connected to, or something else. And kind of help/ideas/leads would be much appreciated.

How to use Rx to monitor a project file and files for external changes?

I would like to reproduce the behavior of Visual Studio which informs you when a project file is touched externally and proposes to reload it!
Due to the requirements, I believe reactive is a great match to solve that problem.
I am using a modified reactive FileSystemWatcher described in this post: http://www.jaylee.org/post/2012/08/26/An-update-to-matthieumezil-Rx-and-the-FileSystemWatcher.aspx
public class FileWatcher
{
private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static IObservable<FileChanged> ObserveFolderChanges(string path, string filter, TimeSpan throttle, Predicate<string> isPartOfProject)
{
return Observable.Create<FileChanged>(
observer =>
{
var fileSystemWatcher = new FileSystemWatcher(path, filter) { EnableRaisingEvents = true, IncludeSubdirectories = true };
var sources = new[]
{
Observable.FromEventPattern<FileSystemEventArgs>(fileSystemWatcher, "Created")
.Where(IsMaybeAProjectFile)
.Select(ev => new FileChanged(ev.EventArgs.FullPath, FileChangeTypes.Added, SourceChangeTypes.FileSystem)),
Observable.FromEventPattern<FileSystemEventArgs>(fileSystemWatcher, "Deleted")
.Where(IsMaybeAProjectFile)
.Select(ev => new FileChanged(ev.EventArgs.FullPath, FileChangeTypes.Deleted, SourceChangeTypes.FileSystem))
};
return sources.Merge()
.Throttle(throttle)
.Do(changed =>
{
if (Logger.IsDebugEnabled)
{
Logger.Debug($"FileWatcher event [{changed.FileChangeType}] {changed.FullPath}");
}
})
.Finally(() => fileSystemWatcher.Dispose())
.Subscribe(observer);
}
);
}
private static bool IsMaybeAProjectFile(EventPattern<FileSystemEventArgs> ev)
{
return ev.EventArgs.FullPath.EndsWith(".zip") || ev.EventArgs.FullPath.EndsWith(".skye");
}
}
public class FileChanged
{
public string FullPath { get; }
public FileChangeTypes FileChangeType { get; }
public SourceChangeTypes SourceChangeType { get; }
public FileChanged(string fullPath, FileChangeTypes fileChangeType, SourceChangeTypes sourceChangeType)
{
FullPath = fullPath;
FileChangeType = fileChangeType;
SourceChangeType = sourceChangeType;
}
}
[Flags]
public enum FileChangeTypes
{
Added = 1,
Deleted = 2
}
[Flags]
public enum SourceChangeTypes
{
FileSystem = 1,
Project = 2
}
Now in my application I created an event
private ProjectChangedEventHandler ProjectChanged { get; set; }
private void OnProjectChanged(FileChanged fileChanged)
{
ProjectChanged?.Invoke(this, fileChanged);
}
public delegate void ProjectChangedEventHandler(object sender, FileChanged fileChanged);
Which is used like this when I delete or a add a file from the project
OnProjectChanged(new FileChanged(archive.Filename, FileChangeTypes.Deleted, SourceChangeTypes.Project));
OnProjectChanged(new FileChanged(archive.Filename, FileChangeTypes.Added, SourceChangeTypes.Project));
Now I can start to leverage those two streams and with a join (which needs fine tuning for the left and right duration selector) I am able to detect which file was modified by my application:
private void ObserveProjectModifications(string projectFilePath)
{
_observeFolderChanges = FileWatcher.ObserveFolderChanges(Path.GetDirectoryName(projectFilePath), "*.*", TimeSpan.FromMilliseconds(500), IsPartOfProject);
_observeProjectChanges = Observable.FromEventPattern<ProjectChangedEventHandler, FileChanged>(h => ProjectChanged += h, h => ProjectChanged -= h).Select(pattern => pattern.EventArgs);
_changes = _observeProjectChanges.Join(_observeFolderChanges, _ => Observable.Never<Unit>(), _ => Observable.Never<Unit>(), ResultSelector).Where(changed => IsPartOfProject(changed.FullPath));
}
private FileChanged ResultSelector(FileChanged fileChanged, FileChanged projectChanged)
{
if (Logger.IsDebugEnabled)
{
Logger.Debug($"ResultSelector File [{fileChanged.FileChangeType}] {fileChanged.FullPath} # Project [{projectChanged.FileChangeType}] {projectChanged.FullPath}");
}
if (fileChanged.FullPath == projectChanged.FullPath)
{
if (fileChanged.FileChangeType == projectChanged.FileChangeType)
{
if (fileChanged.SourceChangeType != projectChanged.SourceChangeType)
{
return projectChanged;
}
return fileChanged;
}
return fileChanged;
}
return fileChanged;
}
private bool IsPartOfProject(string fullPath)
{
if (_projectFileManager.ProjectFilePath.Equals(fullPath)) return true;
return _archives.Values.Any(a => a.Filename.Equals(fullPath));
}
My issue is that I also want to know that a file was modified externally! Any idea would be really helpful! Thanks
Unfortunatelly the FileSystemWatcher doesn't provide information which process has modified the file, so you are bit out of luck there. There are few possibilities that I can think of:
Ignore flag - When your application is doing a change you can set a flag and ignore the events when the flag is set. This is the simplest way, but you might miss some external change if it happens concurrently when the flag is set and also it gets even more complicated due to throttling you have.
Tagging the file - whenever you do a change to the file you generate a guid (or similar) which you will use to tag the file. And then whenever the file change is fired, you check the file property (can be stored either as real filesystem file property - similar for example to jpeg metadata you see in details in file explorer, there are more ways to set such file property) and then if the tag is different from what you have or is missing then you know it is external - there you need to also take care due to throttling and the tag being outdated etc
Minifilter file system driver - This would be the cleanest solution and probably is very close to what Visual studio is using - just a guess though. It is basically a universal windows driver that monitors any I/O change. Microsoft has created reference implementation called minispy, which is small tool to monitor and log any I/O and transaction activity that occurs in the system. You don't have to implement the driver yourself as there is already a 3rd party FileSystemWatcher implemented using this approach on github. That file system watcher provides information which process has modified the file. The only problem here is that the driver itself needs to be installed, before it can be used, so you need admin privileged installer of sort.
At the moment that's all I can think of.

Files,strings and save

I been having trouble trying to figure this out. When I think I have it I get told no. Here is a picture of it.
I am working on the save button. Now after the user adds the first name, last name and job title they can save it. If a user loads the file and it comes up in the listbox, that person should be able to click on the name and then hit the edit button and they should be able to edit it. I have code, but I did get inform it looked wackey and the string should have the first name, last name and job title.
It is getting me really confused as I am learning C#. I know how to use savefiledialog but I am not allowed to use it on this one. Here is what I am suppose to be doing:
When the user clicks the “Save” button, write the selected record to
the file specified in txtFilePath (absolute path not relative) without
truncating the values currently inside.
I am still working on my code since I got told that it will be better file writes records in a group of three strings. But this is the code I have right now.
private void Save_Click(object sender, EventArgs e)
{
string path = txtFilePath.Text;
if (File.Exists(path))
{
using (StreamWriter sw = File.CreateText(path))
{
foreach (Employee employee in employeeList.Items)
sw.WriteLine(employee);
}
}
else
try
{
StreamWriter sw = File.AppendText(path);
foreach (var item in employeeList.Items)
sw.WriteLine(item.ToString());
}
catch
{
MessageBox.Show("Please enter something in");
}
Now I can not use save or open file dialog. The user should be able to open any file on the C,E,F drive or where it is. I was also told it should be obj.Also the program should handle and exceptions that arise.
I know this might be a noobie question but my mind is stuck as I am still learning how to code with C#. Now I have been searching and reading. But I am not finding something to help me understand how to have all this into 1 code. If someone might be able to help or even point to a better web site I would appreciate it.
There are many, many ways to store data in a file. This code demonstrates 4 methods that are pretty easy to use. But the point is that you should probably be splitting up your data into separate pieces rather than storing them as one long string.
public class MyPublicData
{
public int id;
public string value;
}
[Serializable()]
class MyEncapsulatedData
{
private DateTime created;
private int length;
public MyEncapsulatedData(int length)
{
created = DateTime.Now;
this.length = length;
}
public DateTime ExpirationDate
{
get { return created.AddDays(length); }
}
}
class Program
{
static void Main(string[] args)
{
string testpath = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "TestFile");
// Method 1: Automatic XML serialization
// Requires that the type being serialized and all its serializable members are public
System.Xml.Serialization.XmlSerializer xs =
new System.Xml.Serialization.XmlSerializer(typeof(MyPublicData));
MyPublicData o1 = new MyPublicData() {id = 3141, value = "a test object"};
MyEncapsulatedData o2 = new MyEncapsulatedData(7);
using (System.IO.StreamWriter w = new System.IO.StreamWriter(testpath + ".xml"))
{
xs.Serialize(w, o1);
}
// Method 2: Manual XML serialization
System.Xml.XmlWriter xw = System.Xml.XmlWriter.Create(testpath + "1.xml");
xw.WriteStartElement("MyPublicData");
xw.WriteStartAttribute("id");
xw.WriteValue(o1.id);
xw.WriteEndAttribute();
xw.WriteAttributeString("value", o1.value);
xw.WriteEndElement();
xw.Close();
// Method 3: Automatic binary serialization
// Requires that the type being serialized be marked with the "Serializable" attribute
using (System.IO.FileStream f = new System.IO.FileStream(testpath + ".bin", System.IO.FileMode.Create))
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
bf.Serialize(f, o2);
}
// Demonstrate how automatic binary deserialization works
// and prove that it handles objects with private members
using (System.IO.FileStream f = new System.IO.FileStream(testpath + ".bin", System.IO.FileMode.Open))
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf =
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
MyEncapsulatedData o3 = (MyEncapsulatedData)bf.Deserialize(f);
Console.WriteLine(o3.ExpirationDate.ToString());
}
// Method 4: Manual binary serialization
using (System.IO.FileStream f = new System.IO.FileStream(testpath + "1.bin", System.IO.FileMode.Create))
{
using (System.IO.BinaryWriter w = new System.IO.BinaryWriter(f))
{
w.Write(o1.id);
w.Write(o1.value);
}
}
// Demonstrate how manual binary deserialization works
using (System.IO.FileStream f = new System.IO.FileStream(testpath + "1.bin", System.IO.FileMode.Open))
{
using (System.IO.BinaryReader r = new System.IO.BinaryReader(f))
{
MyPublicData o4 = new MyPublicData() { id = r.ReadInt32(), value = r.ReadString() };
Console.WriteLine("{0}: {1}", o4.id, o4.value);
}
}
}
}
As you are writing the employee objects with WriteLine, the underlying ToString() is being invoked. What you have to do first is to customize that ToString() methods to fit your needs, in this way:
public class Employee
{
public string FirstName;
public string LastName;
public string JobTitle;
// all other declarations here
...........
// Override ToString()
public override string ToString()
{
return string.Format("'{0}', '{1}', '{2}'", this.FirstName, this.LastName, this.JobTitle);
}
}
This way, your writing code still keeps clean and readable.
By the way, there is not a reverse equivalent of ToSTring, but to follow .Net standards, I suggest you to implement an Employee's method like:
public static Employee Parse(string)
{
// your code here, return a new Employee object
}
You have to determine a way of saving that suits your needs. A simple way to store this info could be CSV:
"Firstname1","Lastname 1", "Jobtitle1"
" Firstname2", "Lastname2","Jobtitle2 "
As you can see, data won't be truncated, since the delimiter " is used to determine string boundaries.
As shown in this question, using CsvHelper might be an option. But given this is homework and the constraints therein, you might have to create this method yourself. You could put this in Employee (or make it override ToString()) that does something along those lines:
public String GetAsCSV(String firstName, String lastName, String jobTitle)
{
return String.Format("\"{0}\",\"{1}\",\"{2}\"", firstName, lastName, jobTitle);
}
I'll leave the way how to read the data back in as an exercise to you. ;-)

c# identifier expected?

I am trying to create a program to copy all the files from one directory to another. But I am running in a basic issue. It says indentifier expected when I try to compile on line 52.
public bool RecursiveCopy()
{
string origDir = #"D:\Documents and Settings\Dub\My Documents\HoN Updates\test";
string destDir = #"C:\Games\HoN";
bool status = false;
//get all the info about the original directory
var dirInfo = new DirectoryInfo(origDir);
//retrieve all the _fileNames in the original directory
var files = dirInfo.GetFiles(origDir);
//always use a try...catch to deal
//with any exceptions that may occur
try
{
//loop through all the file names and copy them
foreach (string file in Directory.GetFiles(origDir))
{
var origFile = new FileInfo(file);
var destFile = new FileInfo(file.Replace(origDir, destDir));
//copy the file, use the OverWrite overload to overwrite
//destination file if it exists
System.IO.File.Copy(origFile.FullName, destFile.FullName, true);
//TODO: If you dont want to remove the original
//_fileNames comment this line out
File.Delete(origFile.FullName);
status = true;
}
Console.WriteLine("All files in " + origDir + " copied successfully!");
}
catch (Exception ex)
{
status = false;
//handle any errors that may have occurred
Console.WriteLine(ex.Message);
}
return status;
}
public string origDir = #"D:\Documents and Settings\Dub\My Documents\HoN Updates\test"; // ERROR HERE
public string destDir = #"C:\Games\HoN"; // ERROR HERE
private static void RecursiveCopy(origDir, destDir)
{
Console.WriteLine("done");
Console.ReadLine();
}
You did not give type identifiers to your argument list here
static void RecursiveCopy(origDir, destDir)
should be
static void RecursiveCopy(string origDir, string destDir)
Your method RecursiveCopy has two parameters listed without their types. It should be this:
static void RecursiveCopy(string origDir, string destDir)
Here is your problem:
static void RecursiveCopy(origDir, destDir)
You don't specify the types for the parameters, perhaps you intended the following:
static void RecursiveCopy(string origDir, string destDir)
There are more issues however that I've noticed. It's possible you're still working on these, but from what you've posted:
You never call your RecursiveCopy method. Perhaps you meant to call it from Main() instead of declaring an overload with two parameters?
You declare two public fields origDir and destDir but then never use them. Instead you create two local variables in RecursiveCopy() and use these instead. Did you intend to create parameters or use the public fields instead?
Your copy is not actually true to its name of "recursive".
cYou are missing the parameter types in the RecursiveCopy method declaration. Just Change
static void RecursiveCopy(origDir, destDir)
to
static void RecursiveCopy(String origDir, String destDir)
and all is fine.

Categories

Resources