Hey everyone,
I just finished up an application I've been working on for a while now. Probably the most complex one I've made to date. Due to this, I figured I'd go and make a help document to provide users with some info on it.
I've created a CHM file, and set up a helpProvider, however now my problem is how to include this and the HHC (Table of contents) file with my application. I feel like it'd be a pain to require the user to copy the two files themselves, so I'm trying to store them as embedded resources, then have the application write these out in the current directory.
Currently, this is the code I'm using:
var data = Properties.Resources.RERHelp;
using (var stream = new FileStream("RERHelp", FileMode.Create))
{
stream.Write(data, 0, data.Count() - 1);
stream.Flush();
}
helpProvider1.HelpNamespace = Directory.GetCurrentDirectory() + "\\RERHelp\\RERHelp.chm";
This works just fine, but it means I'd have to run through this twice, once with data set to Properties.Resources.RERHelp, and once for the Table of Contents file. Is there a better way to do this? Perhaps some way to embed the CHM and HHC files in the application, and access them without writing them to disk? If that isn't possible, which I'm thinking it isn't, is there a better way to go about it than how I am currently?
Thanks for any help!
Best Regards,
Ian
Apps usually use an installer, or zip archive of some sort. Both methods would allow a user to receive the application and the help files, without having to provide them separately.
Under your project properties - Resources, add a file resource ie:textmag.chm. I use filetype Text for the chm's
private void HelpToolStripMenuItem_Click(object sender, EventArgs e)
{
string helpFileName = "";
try
{
helpFileName = System.IO.Path.Combine(System.Windows.Forms.Application.StartupPath, "Resources") + #"\TextMag.chm";
Help.ShowHelp(this, helpFileName);
}
catch (Exception ex)
{
string xxx = ex.Message;
}
}
Important: in the properties of the chm file under resources, the Build Action must be Content.
Oh, wow. Turns out I didn't need the HHC file as well. I assumed I did because when I'd open the help dialog, it would say that it couldn't find the table of contents.hhc file. I assumed for some reason it needed that in addition to the CHM. I originally just made a method to pass the resources to so as to prevent redundancy, and called that once for the CHM, and once for the HHC, but then I noticed this bit:
data.Count() - 1
I'm not sure why that - 1 was there, the solution I found had it, so I just left it there. When I removed that, the program ran, wrote out that file, and could then read it for the help documentation without the complaint of the missing HHC. All is well. Thanks for the suggestions, everyone!
So a solution is:
1) Copy your chm file to the required project folder
2) In your Visual C# solution explorer add existing item to the project (your chm file).
3) Select the Project menu then project properties.
4) Add existing resource.
5) Add the below code and connect to your help menu item.
private void WORKING_HELP()
{
string filePath = Directory.GetCurrentDirectory() + "\\BlitzHelp.chm";
try
{
//Check if already exists before making
if (!File.Exists(filePath))
{
var data = Properties.Resources.BlitzHelp;
using (var stream = new FileStream("BlitzHelp.chm", FileMode.Create))
{
stream.Write(data, 0, data.Count());
stream.Flush();
}
MessageBox.Show("file made");
}
}
catch
{
//May already be opened
}
Help.ShowHelp(this, filePath);
}
Related
There are similar questions regarding this which were asked earlier. But I couldn't find a proper solution yet.
I have an application which uses a user defined dll library(Cplus_Function_Library.dll). Imagine that the application is launched already to customers. And if there's a new version of the dll available the application will automatically download it and replaces the old one. There's no problem there.
Now I want to create new dll libraries(a lot) and upload it in the sameplace where the Cplus_Function_Library.dll new version exsists(ex: http path/FTP server). And then I can add them by reference in the Cplus_Function_Library.dll. It's also clear. But my question is how can I download all the dll files in this path without giving the file names one by one inside my updater function? Because when I launch the app these files are not known.(updater function is included inside the application.)Is there an easy way to download all the dll files from a specified path without much hassle?
My current update function can be seen below.
Uri pathToNewVerNo = new Uri("//....../New_version.txt"); //Path to the new version number
Uri pathToCurrentVerNo = new Uri("...../Current_version.txt"); //Path to the current version number
Uri pathToDownload = new Uri(".....new_library.dll");
StreamReader readNewVer; //To read the new version number
StreamReader readCurVer; //To read the current version number
StreamWriter writeToCurVer;
WebClient verNew = new WebClient(); //will be used to download the New_version .txt file
WebClient verCur = new WebClient(); //will be used to download the Current_version .txt file
WebClient update = new WebClient(); //will be used to download the new dll file
verNew.DownloadFile(pathToNewVerNo, "New_version.txt"); //Download the New_version .txt file
readCurVer = new StreamReader("Current_version.txt"); //open Current_version.txt file to read
current_Version = readCurVer.ReadLine(); //assign the value to a string
readCurVer.Close(); //close the file
readNewVer = new StreamReader("New_version.txt"); //open New_version.txt file to read
new_Version = readNewVer.ReadLine(); //assign the value to a string
readNewVer.Close(); //close the file
current_ver_doub = Convert.ToDouble(current_Version); //convert the string value to a double
new_ver_doub = Convert.ToDouble(new_Version);
if (new_ver_doub > current_ver_doub) //check if the new version number is greater than the current version number
{
obj.SBO_Application.StatusBar.SetText("Please wait update in process", BoMessageTime.bmt_Medium, BoStatusBarMessageType.smt_Warning);
writeToCurVer = new StreamWriter("Current_version.txt"); //open the current_version.txt to write
writeToCurVer.Write(new_Version); //update with new version number
writeToCurVer.Close(); //close the file
update.DownloadFile(pathToDownload, "new_library.dll"); //download the new .dll file
//*************There will be a roll back functionality added in the future in case if the updated dll file is currupted.*****************
File.Replace("new_library.dll", "Cplus_Function_Library.dll", "Cplus_Function_Library.dll.bac", false); //make a back up file of the old .dll file and replace it
obj.SBO_Application.MessageBox("Update Successful. Please restart the AddOn", 1, "Ok");
try
{
foreach (Process proc in Process.GetProcessesByName("cplus_Global"))
{
proc.Kill();
proc.WaitForExit();
}
}
catch (Exception ex)
{
obj.SBO_Application.MessageBox(ex.Message, 1, "Ok");
}
}
else
{
// SBO_Application.MessageBox("No Update Available", 1, "Ok");
}
}//End of updater_cplus function */
First of all you need to ask yourself whether you absolutely want to reinvent the wheel, and are ready to tackle all problems you will encounter while doing that, such as the problem you now ran into. There are plenty of existing solutions to include installers and updaters with your software.
Anyway, to answer your question:
how can I download all the dll files in this path without giving the file names one by one inside my updater function?
You casually mention it should work over HTTP and FTP. The former has no formal "directory listing" command, so you'll have to invent that yourself. The latter does, but requires the directory to only contain relevant files, or you need to create a whitelist and/or blacklist to include or exclude certain files.
The easiest solution would be to define your version file format so as to include the list of files to download. Then you fetch the version file, interpret it and request the files mentioned in it.
Acording to CodeCaster's idea I put all my dll files inside one folder and the new_version text file in another folder. This way I made sure different file formats won't get mixed up.
The next step was to read the new_version.txt from the ftp server and compare the value with the current version. If it's greater than the later I took all the file names of dll files to a list. Then easily you can download the files one by one to your desired location.
how to find whether specific .txt file is opened in notepad?
I have tried solutions mentioned here
Is there a way to check if a file is in use?
But they work fine for Word and pdf file but not working for txt file opened in Notepad.
here is code I have wrote.
public bool IsFileOpen(string strFileName)
{
bool retVal = false;
try
{
if (File.Exists(pstrFileName))
{
using (FileStream stream = File.OpenWrite(pstrFileName))
{
try
{
}
catch (IOException)
{
retVal = true;
}
finally
{
stream.Close();
stream.Dispose();
}
}
}
}
catch (IOException)
{ //file is opened at another location
retVal = true;
}
catch (UnauthorizedAccessException)
{ //Bypass this exception since this is due to the file is being set to read-only
}
return retVal;
}
am i missing somthing here.??
My requirement:
I have application which works similar to VSS. When user checks out specific file and opens ,and try to check in the same, while it has opened. Application is suppose to throw a warning message.For that i have used the above functionality.Its working fine for word and pdf.
To expand on my comment. A file is only locked if a handle is kept open by an application. Word for example will open the file, read in the stream and maintain the handle so that other applications cannot delete that file while the user is working on it.
Notepad, and other applications, just open the file, read in the entire stream and then close the file releasing the lock they have. This means that the file is no longer locked and can be edited by another application or even deleted and Notepad will not care as it has its own copy in memory.
You could try and hack around with getting instances of Notepad and checking if a file is open but this is ultimately not a great idea. If the file is not locked then you should be free to do what you want with it.
This is a hack solution I just came up with, but it should work for you. This makes use of System.Diagnostics.
Process[] processes = Process.GetProcessesByName("notepad");
for (int i = 0; i < processes.Length; i++)
{
Console.WriteLine(processes[i].MainWindowTitle);
if (processes[i].MainWindowTitle.Equals("myFile.txt - Notepad"))
{
Console.WriteLine("The file myFile is Open!");
}
}
Console.ReadLine();
Hopefully that should do the trick. My example looks to see if an instance of notepad is open with the window title "myFile.txt - Notepad". The window name is always "filename.extension - Notepad" so you can handle that however you might need to.
I suppose you could make a call to System.IO.File.GetLastAccessTime(filePath). You could then poll the file every so often and when the access time changes you know the file has been opened, you can then fire an event that the file has been opened. See Jeffs post here:
Detect File Read in C#
You could also do this using the following tactic: It seems that notepad does hold some kind of lock on the hosting folder (try to delete the folder and you'll see you can't).
you could use the following code Using C#, how does one figure out what process locked a file? to check list of processes that lock the folder.
one of the processes will be your notepad.
you could them compare by Title as another answers mentioned.
if you're issuing the open of the file - you could save the PID and comapre it with one of the processes that returned.
How can I read content of a text file inside a zip archive?
For example I have an archive qwe.zip, and insite it there's a file asd.txt, so how can I read contents of that file?
Is it possible to do without extracting the whole archive? Because it need to be done quick, when user clicks a item in a list, to show description of the archive (it needed for plugin system for another program). So extracting a whole archive isn't the best solution... because it might be few Mb, which will take at least few seconds or even more to extract... while only that single file need to be read.
You could use a library such as SharpZipLib or DotNetZip to unzip the file and fetch the contents of individual files contained inside. This operation could be performed in-memory and you don't need to store the files into a temporary folder.
Unzip to a temp-folder take the file and delete the temp-data
public static void Decompress(string outputDirectory, string zipFile)
{
try
{
if (!File.Exists(zipFile))
throw new FileNotFoundException("Zip file not found.", zipFile);
Package zipPackage = ZipPackage.Open(zipFile, FileMode.Open, FileAccess.Read);
foreach (PackagePart part in zipPackage.GetParts())
{
string targetFile = outputDirectory + "\\" + part.Uri.ToString().TrimStart('/');
using (Stream streamSource = part.GetStream(FileMode.Open, FileAccess.Read))
{
using (Stream streamDestination = File.OpenWrite(targetFile))
{
Byte[] arrBuffer = new byte[10000];
int iRead = streamSource.Read(arrBuffer, 0, arrBuffer.Length);
while (iRead > 0)
{
streamDestination.Write(arrBuffer, 0, iRead);
iRead = streamSource.Read(arrBuffer, 0, arrBuffer.Length);
}
}
}
}
}
catch (Exception)
{
throw;
}
}
Although late in the game and the question is already answered, in hope that this still might be useful for others who find this thread, I would like to add another solution.
Just today I encountered a similar problem when I wanted to check the contents of a ZIP file with C#. Other than NewProger I cannot use a third party library and need to stay within the out-of-the-box .NET classes.
You can use the System.IO.Packaging namespace and use the ZipPackage class. If it is not already included in the assembly, you need to add a reference to WindowsBase.dll.
It seems, however, that this class does not always work with every Zip file. Calling GetParts() may return an empty list although in the QuickWatch window you can find a property called _zipArchive that contains the correct contents.
If this is the case for you, you can use Reflection to get the contents of it.
On geissingert.com you can find a blog article ("Getting a list of files from a ZipPackage") that gives a coding example for this.
SharpZipLib or DotNetZip may still need to get/read the whole .zip file to unzip a file. Actually, there is still method could make you just extract special file from the .zip file without reading the entire .zip file but just reading small segment.
I needed to have insights into Excel files, I did it like so:
using (var zip = ZipFile.Open("ExcelWorkbookWithMacros.xlsm", ZipArchiveMode.Update))
{
var entry = zip.GetEntry("xl/_rels/workbook.xml.rels");
if (entry != null)
{
var tempFile = Path.GetTempFileName();
entry.ExtractToFile(tempFile, true);
var content = File.ReadAllText(tempFile);
[...]
}
}
I am having an xml file like:
<CurrentProject>
// Elements like
// last opened project file to reopen it when app starts
// and more global project independend settings
</CurrentProject>
Now I asked myself wether I should deliver this xml file with above empty elements with the installer for my app or should I create this file on the fly on application start if it does not exist else read the values from it.
Consider also that the user could delete this file and that should my application not prevent from working anymore.
What is better and why?
UPDATE:
What I did felt ok for me so I post my code here :) It just creates the xml + structure on the fly with some security checks...
public ProjectService(IProjectDataProvider provider)
{
_provider = provider;
string applicationPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
_projectPath = Path.Combine(applicationPath,#"TBM\Settings.XML");
if (!File.Exists(_projectPath))
{
string dirPath = Path.Combine(applicationPath, #"TBM");
if (!Directory.Exists(dirPath))
Directory.CreateDirectory(dirPath);
using (var stream = File.Create(_projectPath))
{
XElement projectElement = new XElement("Project");
projectElement.Add(new XElement("DatabasePath"));
projectElement.Save(stream, SaveOptions.DisableFormatting);
}
}
}
In a similar scenario, I recently went for creating the initial file on the fly. The main reason I chose this was the fact that I wasn't depending on this file being there and being valid. As this was a file that's often read from/written to, there's a chance that it could get corrupted (e.g. if the power is lost while the file is being written).
In my code I attempted to open this file for reading and then read the data. If anywhere during these steps I encountered an error, I simply recreated the file with default values and displayed a corresponding message to the user.
I keep getting the error "Stream was not writable" whenever I try to execute the following code. I understand that there's still a reference to the stream in memory, but I don't know how to solve the problem. The two blocks of code are called in sequential order. I think the second one might be a function call or two deeper in the call stack, but I don't think this should matter, since I have "using" statements in the first block that should clean up the streams automatically. I'm sure this is a common task in C#, I just have no idea how to do it...
string s = "";
using (Stream manifestResourceStream =
Assembly.GetExecutingAssembly().GetManifestResourceStream("Datafile.txt"))
{
using (StreamReader sr = new StreamReader(manifestResourceStream))
{
s = sr.ReadToEnd();
}
}
...
string s2 = "some text";
using (Stream manifestResourceStream =
Assembly.GetExecutingAssembly().GetManifestResourceStream("Datafile.txt"))
{
using (StreamWriter sw = new StreamWriter(manifestResourceStream))
{
sw.Write(s2);
}
}
Any help will be very much appreciated. Thanks!
Andrew
Embedded resources are compiled into your assembly, you can't edit them.
As stated above, embedded resources are read only. My recommendation, should this be applicable, (say for example your embedded resource was a database file, XML, CSV etc.) would be to extract a blank resource to the same location as the program, and read/write to the extracted resource.
Example Pseudo Code:
if(!Exists(new PhysicalResource())) //Check to see if a physical resource exists.
{
PhysicalResource.Create(); //Extract embedded resource to disk.
}
PhysicalResource pr = new PhysicalResource(); //Create physical resource instance.
pr.Read(); //Read from physical resource.
pr.Write(); //Write to physical resource.
Hope this helps.
Additional:
Your embedded resource may be entirely blank, contain data structure and / or default values.
A bit late, but for descendants=)
About embedded .txt:
Yep, on runtime you couldnt edit embedded because its embedded. You could play a bit with disassembler, but only with outter assemblies, which you gonna load in current context.
There is a hack if you wanna to write to a resource some actual information, before programm starts, and to not keep the data in a separate file.
I used to worked a bit with winCE and compact .Net, where you couldnt allow to store strings at runtime with ResourceManager. I needed some dynamic information, in order to catch dllNotFoundException before it actually throws on start.
So I made embedded txt file, which I filled at the pre-build event.
like this:
cd $(ProjectDir)
dir ..\bin\Debug /a-d /b> assemblylist.txt
here i get files in debug folder
and the reading:
using (var f = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("Market_invent.assemblylist.txt")))
{
str = f.ReadToEnd();
}
So you could proceed all your actions in pre-build event run some exes.
Enjoy! Its very usefull to store some important information and helps avoid redundant actions.