How do I programmatically locate my Dropbox folder using C#? - c#

How do I programmatically locate my Dropbox folder using C#?
* Registry?
* Environment Variable?
* Etc...

UPDATED SOLUTION
Dropbox now provides an info.json file as stated here: https://www.dropbox.com/en/help/4584
If you don't want to deal with parsing the JSON, you can simply use the following solution:
var infoPath = #"Dropbox\info.json";
var jsonPath = Path.Combine(Environment.GetEnvironmentVariable("LocalAppData"), infoPath);
if (!File.Exists(jsonPath)) jsonPath = Path.Combine(Environment.GetEnvironmentVariable("AppData"), infoPath);
if (!File.Exists(jsonPath)) throw new Exception("Dropbox could not be found!");
var dropboxPath = File.ReadAllText(jsonPath).Split('\"')[5].Replace(#"\\", #"\");
If you'd like to parse the JSON, you can use the JavaScripSerializer as follows:
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var dictionary = (Dictionary < string, object>) serializer.DeserializeObject(File.ReadAllText(jsonPath));
var dropboxPath = (string) ((Dictionary < string, object> )dictionary["personal"])["path"];
DEPRECATED SOLUTION:
You can read the the dropbox\host.db file. It's a Base64 file located in your AppData\Roaming path. Use this:
var dbPath = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Dropbox\\host.db");
var dbBase64Text = Convert.FromBase64String(System.IO.File.ReadAllText(dbPath));
var folderPath = System.Text.ASCIIEncoding.ASCII.GetString(dbBase64Text);
Hope it helps!

UPDATE JULY 2016: THE CODE BELOW NO LONGER WORKS DUE TO CHANGES IN THE DROPBOX CLIENT, SEE ACCEPTED ANSWER ABOVE FOR UP-TO-DATE SOLUTION
Reinaldo's answer is essentially correct but it gives some junk output before the path because there seem to be two lines in the host.db file and in this case you only want to read the second one. The following will get you just the path.
string appDataPath = Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData);
string dbPath = System.IO.Path.Combine(appDataPath, "Dropbox\\host.db");
string[] lines = System.IO.File.ReadAllLines(dbPath);
byte[] dbBase64Text = Convert.FromBase64String(lines[1]);
string folderPath = System.Text.ASCIIEncoding.ASCII.GetString(dbBase64Text);
Console.WriteLine(folderPath);

Cleaner version based on previous answers (use var, added exists check, remove warnings):
private static string GetDropBoxPath()
{
var appDataPath = Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData);
var dbPath = Path.Combine(appDataPath, "Dropbox\\host.db");
if (!File.Exists(dbPath))
return null;
var lines = File.ReadAllLines(dbPath);
var dbBase64Text = Convert.FromBase64String(lines[1]);
var folderPath = Encoding.UTF8.GetString(dbBase64Text);
return folderPath;
}

This seems to be the suggested solution from Dropbox: https://www.dropbox.com/help/4584?path=desktop_client_and_web_app

Dropbox has added a new helper, there is a JSON file in either %APPDATA%\Dropbox\info.json or %LOCALAPPDATA%\Dropbox\info.json.
See https://www.dropbox.com/help/4584 for more information.

public static string getDropBoxPath()
{
try
{
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var dbPath = Path.Combine(appDataPath, "Dropbox\\host.db");
if (!File.Exists(dbPath))
{
return null;
}
else
{
var lines = File.ReadAllLines(dbPath);
var dbBase64Text = Convert.FromBase64String(lines[1]);
var folderPath = Encoding.UTF8.GetString(dbBase64Text);
return folderPath;
}
}
catch (Exception ex)
{
throw ex;
}
}

It's not stored in the registry (at least it isn't in plain text). I believe it's stored in the following location.
C:\Users\userprofile\AppData\Roaming\Dropbox
I would say it resides in the host.db or unlink.db file.
The config.db is a sqlite file. The other two are unknown (encrypted). The config.db contains a blob field only with the schema version.

The host.db method has stopped working in later versions of dropbox.
https://www.dropbox.com/en/help/4584 gives the recommended approach.
Here is the c# code I wrote to parse the json and get the dropbox folder.
// https://www.dropbox.com/en/help/4584 says info.json file is in one of two places
string filename = Environment.ExpandEnvironmentVariables( #"%LOCALAPPDATA%\Dropbox\info.json" );
if ( !File.Exists( filename ) ) filename = Environment.ExpandEnvironmentVariables( #"%APPDATA%\Dropbox\info.json" );
JavaScriptSerializer serializer = new JavaScriptSerializer();
// When deserializing a string without specifying a type you get a dictionary <string, object>
Dictionary<string, object> obj = serializer.DeserializeObject( File.ReadAllText( filename ) ) as Dictionary<string, object>;
obj = obj[ "personal" ] as Dictionary<string, object>;
string path = obj[ "path" ] as string;
return path;

I'm posting here a solution that does not use Dictionary; so many years after original answers, every time that I try to use answers from Reinaldo and Derek, I get a Could not load type 'System.Web.Util.Utf16StringValidator' from assembly 'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=... using both LinqPad 7 (.NET 6.0.9) and VS 2022 (Net Standard 2.0),
I do not know if this error is because I'm already referencing Newtonsoft.Json in Assembly as suggested in this unaccepted answer.
Anyway, here is 2022 piece of cake way to do it:
private static string GetDropBoxPath()
{
// https://www.dropbox.com/en/help/4584 says info.json file is in one of two places
string jsonPath = Environment.ExpandEnvironmentVariables(#"%LOCALAPPDATA%\Dropbox\info.json");
if (!File.Exists(jsonPath)) jsonPath = Environment.ExpandEnvironmentVariables(#"%APPDATA%\Dropbox\info.json");
var dropbox = JsonConvert.DeserializeObject<DropboxRoot>(File.ReadAllText(jsonPath));
return dropbox.personal.path;
}
And these are the auxiliary classes:
public class DropboxRoot
{
public Personal personal { get; set; }
}
public class Personal
{
public string path { get; set; }
public long host { get; set; }
public bool is_team { get; set; }
public string subscription_type { get; set; }
}

Related

How to complete aspx connection string from text file

I must use a text file "db.txt" which inherits the names of the Server and Database to make my connection string complete.
db.txt looks like this:
<Anfang>
SERVER==dbServer\SQLEXPRESS
DATABASE==studentweb
<Ende>
The connection string:
string constr = ConfigurationManager.ConnectionStrings["DRIVER={SQL Server}; SERVER=SERVER DATABASE=DB UID=;PWD=;LANGUAGE=Deutsch;Trusted_Connection=YES"].ConnectionString;
Unfortunatly we are only allowed to use Classic ASPX.net (C# 2.0) and not the web.config.
I've searched a lot, but found nothing close to help me.
Somebody got an Idea how to make it work?
Here is something to get you going.
In a nutshell, I put the DBInfo file through a method that reads the file line by line. When I see the line <anfang> I know the next line will be important, and when I see the line <ende> I know it's the end, so I need to grab everything in between. Hence why I came up with the booleans areWeThereYet and isItDoneYet which I use to start and stop gathering data from the file.
In this snippet I use a Dictionary<string, string> to store and return the values but, you could use something different. At first I was going to create a custom class that would hold all the DB information but, since this is a school assignment, we'll go step by step and start by using what's already available.
using System;
using System.Collections.Generic;
namespace _41167195
{
class Program
{
static void Main(string[] args)
{
string pathToDBINfoFile = #"M:\StackOverflowQuestionsAndAnswers\41167195\41167195\sample\DBInfo.txt";//the path to the file holding the info
Dictionary<string, string> connStringValues = DoIt(pathToDBINfoFile);//Get the values from the file using a method that returns a dictionary
string serverValue = connStringValues["SERVER"];//just for you to see what the results are
string dbValue = connStringValues["DATABASE"];//just for you to see what the results are
//Now you can adjust the line below using the stuff you got from above.
//string constr = ConfigurationManager.ConnectionStrings["DRIVER={SQL Server}; SERVER=SERVER DATABASE=DB UID=;PWD=;LANGUAGE=Deutsch;Trusted_Connection=YES"].ConnectionString;
}
private static Dictionary<string, string> DoIt(string incomingDBInfoPath)
{
Dictionary<string, string> retVal = new Dictionary<string, string>();//initialize a dictionary, this will be our return value
using (System.IO.StreamReader sr = new System.IO.StreamReader(incomingDBInfoPath))
{
string currentLine = string.Empty;
bool areWeThereYet = false;
bool isItDoneYet = false;
while ((currentLine = sr.ReadLine()) != null)//while there is something to read
{
if (currentLine.ToLower() == "<anfang>")
{
areWeThereYet = true;
continue;//force the while to go into the next iteration
}
else if (currentLine.ToLower() == "<ende>")
{
isItDoneYet = true;
}
if (areWeThereYet && !isItDoneYet)
{
string[] bleh = currentLine.Split(new string[] { "==" }, StringSplitOptions.RemoveEmptyEntries);
retVal.Add(bleh[0], bleh[1]);//add the value to the dictionary
}
else if (isItDoneYet)
{
break;//we are done, get out of here
}
else
{
continue;//we don't need this line
}
}
}
return retVal;
}
}
}

How to add additional files to an ad hoc Roslyn workspace to expose them to analyzers

I am creating some Roslyn analyzers, that use the AdditionFiles feature to access a settings file. I am trying to test the analyzers use this correctly.
I have a method that sets up an ad hoc test workspace, and I've tried adding additional documents via two routes:
private static Project CreateProject(IEnumerable<string> sources)
{
var projectId = ProjectId.CreateNewId(TestProjectName);
var solution = new AdhocWorkspace()
.CurrentSolution
.AddProject(projectId, TestProjectName, TestProjectName, LanguageNames.CSharp)
.AddMetadataReference(projectId, CorlibReference)
.AddMetadataReference(projectId, SystemCoreReference)
.AddAdditionalDocument(DocumentInfo.Create(DocumentId.CreateNewId(projectId),
"arnolyzer.yaml",
filePath: #"..\..\arnolyzer.yaml"));
var count = 0;
foreach (var source in sources)
{
var newFileName = $"{DefaultFilePathPrefix}{count++}.{CSharpDefaultFileExt}";
var documentId = DocumentId.CreateNewId(projectId, newFileName);
solution = solution.AddDocument(documentId, newFileName, SourceText.From(source));
}
var settingsFileId = DocumentId.CreateNewId(projectId, "arnolyzer.yaml");
solution = solution.AddAdditionalDocument(settingsFileId, "arnolyzer.yaml", SourceText.From(#"..\..\arnolyzer.yaml"));
return solution.GetProject(projectId);
}
Examining the Project instance, I can see that both additional documents have been added.
However, when inspecting CompilationStartAnalysisContext.Options.AdditionalFiles within a AnalysisContext.RegisterCompilationStartAction action, AdditionalFiles is empty.
Does anyone know whether this approach should work and thus whetehr I've gone wrong somewhere? Or are the additional documents added to a Project unrelated to the AditionalFiles feature?
This will not directly answer your question, but you mention that you are trying to test analyzers if they use the additional file or not. So, here is how we solve that:
var compilationWithAnalyzer = compilation.WithAnalyzers(
diagnosticAnalyzers,
new AnalyzerOptions(ImmutableArray.Create<AdditionalText>(new AnalyzerAdditionalFile(configuration.Path))),
tokenSource.Token);
var diagnostics = await compilationWithAnalyzer.GetAnalyzerDiagnosticsAsync();
where AnalyzerAdditionalFile just extends AdditionalText:
public sealed class AnalyzerAdditionalFile : AdditionalText
{
private readonly string path;
public AnalyzerAdditionalFile(string path)
{
this.path = path;
}
public override string Path => path;
public override SourceText GetText(CancellationToken cancellationToken)
{
return SourceText.From(File.ReadAllText(path));
}
}

Path to XML dosent work

Everytime i try to save something to my created XML document the path i use keeps being wrong.
Here is the code:
public string ToXml<T>(T obj, string path)
{
var saveToXmlPath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), path);
using (var stringWriter = new StreamWriter((saveToXmlPath)))
{
var xmlSerializer = new XmlSerializer(typeof(ObservableCollection<object>));
xmlSerializer.Serialize(stringWriter, obj);
return stringWriter.ToString();
}
}
public Constructor()
{
var temp = new ObservableCollection<Model> {
new Model { ID = 1, Name = "Name1" },
new Model { ID = 2, Name = "Name2" },
new Model { ID = 3, Name = "Name3" } };
ToXml(temp, #"Common\Assets\XML\XmlFile.xml");
}
It keep saying that the path is wrong, keeps adding /debug/big to the path.
Set a breakpoint on this line:
var saveToXmlPath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), path);
What does it say?
Assembly.GetEntryAssembly().Location is adding /debug to your path.
Firstly, your "problem" has nothing to do with XML. You're wanting to know why Assembly.GetEntryAssembly().Location is giving you ".../bin/Debug".
Secondly... from what little information you have actually provided, there is in fact no problem at all; just your misunderstanding. Assembly.GetEntryAssembly().Location will give you the location of the executing ".exe" file (your app). In this case, that would indeed be inside the "bin/Debug" folder by default. If you want the XML file written somewhere else, then it would be helpful if you would specify where you think that somewhere else should be.

How to get string resources from specific resource file in C#

In some cases in our project we have to ignore current culture of application
and get resources in English.
Basically, we do it like for all languages
<label>#ModelEntities.Properties.Resource.Gender</label>
How is it possible to get in English only?
I assume we have somehow created some reference to Resource.resx ?
I have used
ResourceManager rm = new ResourceManager("Resource.Strings",
Assembly.GetAssembly(typeof(ModelEntities.Properties.Resource)));
var s = rm.GetString("Age");
But it seems like not working.
Thank you!
I found the working solution.
First of all our resource files are ending like
Resource.resx
and not like Resources.resx
And in second we have resources in a separate assembly: ModelEntities
So the working code is following
var resourceManager = new ResourceManager(typeof(ModelEntities.Properties.Resource));
var genderString = resourceManager.GetString("Gender", ci);
You can use the resource manager class click here for more info about the class
System.Globalization.CultureInfo ci = new System.Globalization.CultureInfo("en-GB");
ResourceManager resourceManager = new ResourceManager("YourResource", Assembly.GetExecutingAssembly());
string bodyResource = resourceManager.GetString("yourText", ci);
A simple way:
YorResourceName.ResourceManager.GetString("ResourceKeyName", System.Globalization.CultureInfo.GetCultureInfo("en-US"))
Folder structure:
MyApplicationSolution
/Resources
/Resource.resx
public static string GetLocalisedRes(string resourceName = "", string resourceNameKey = "")
{
string translate = string.Empty;
string baseName = "YourNameSpace.Resources." + resourceName + "";
try
{
Type resType = Type.GetType(baseName);
ResourceManager rm = new ResourceManager(baseName, resType.Assembly);
translate = rm.GetString(resourceNameKey);
}
catch {
translate = string.Empty;
}
return translate;
}
Usage
var age = GetLocalisedRes("Resource", "Age");
This working for me

In C#, how do I compare the datemodified property of a local file with an Amazon S3 file?

I've got some local files that need to be updated from S3 occasionally. I'd prefer to update them only if the S3 version is newer. I can't quite understand how to use the
ModifiedSinceDate
property in S3 Objects. In fact, I'm not sure whether to use Metadata, or if there is something else I'm supposed to be checking.
I'm imagining something like this:
GetObjectRequest request = new GetObjectRequest().WithBucketName(my_bucketName).WithKey(s3filepath);
using (GetObjectResponse response = client.GetObject(request))
{
string s3DateModified = response.ModifiedSinceDate; // Complete psuedo code
DateTime s3_creationTime = DateTime.Convert(s3DateModified); //Complete psuedo code
DateTime local_creationTime = File.GetCreationTime(#"c:\file.txt");
if (s3_creationTime > local_CreationTime)
{
//download the S3 object;
}
}
Thanks so much!
EDIT---------------------------------------
I think I'm almost there... I'm writing the date modified as a meta-tag when I originally upload the object:
PutObjectRequest titledRequest = new PutObjectRequest();
.WithFilePath(savePath)
.WithBucketName(bucketName)
.WithKey(tempFileName)
.WithMetaData("Last-Modified", System.DateTime.Now.ToString());
Then using this for the check/download:
using (GetObjectResponse response = client.GetObject(request))
{
string lastModified = response.Metadata["x-amz-meta-last-modified"];
DateTime s3LastModified = Convert.ToDateTime(lastModified);
string dest = Path.Combine(#"c:\Temp\", localFileName);
DateTime localLastModified = File.GetLastWriteTime(dest);
if (!File.Exists(dest))
{
response.WriteResponseStreamToFile(dest);
}
if (s3LastModified > localLastModified)
{
response.WriteResponseStreamToFile(dest);
}
}
Something's breaking, I think perhaps the WriteResponseStream is asynchronous, and it's trying two respnse.WriteResponseStreamtToFile(dest) at the same time. Anyways, I'll update this if I can get it nailed down.
You should use GetObjectMetadata to retrieve information about an object without actually downloading the object itself.
The GetObjectMetadataResponse class has a LastModified property or you could use custom Metadata I guess.
Then you would use GetObject to actually download and save the file.
Something like:
using (GetObjectMetadataResponse response = client.GetObjectMetadata(request))
{
DateTime s3LastModified = response.LastModified;
string dest = Path.Combine(#"c:\Temp\", localFileName);
DateTime localLastModified = File.GetLastWriteTime(dest);
if (!File.Exists(dest) || s3LastModified > localLastModified)
{
// Use GetObject to download and save file
}
}

Categories