How to Create a classes for 2 different target platforms - c#

I have 2 projects- one that is a web service and the other is a windows mobile application that queries that webservice.
Say for instance I have a class called 'Animal'. I want to use the webservice to return an instance of an Animal to the mobile device. The problem being that the mobile device obviously doesnt support the full .net framework, and the class Animal has some features that require the full framework.
What are my best options here? The class Animal will really only contain properties that are just text. Am I best parsing the data into an XML message and sending this back to the mobile device (so not actually using the Animal object on the mobile device?) or do I create 2 classes, one for each platform?
Thanks

You can share a code file between two projects. Right click your project, choose Add -> Existing item, and then click the down arrow next to the Add button, you will see a "Add as Link" option there, if you know you class will compile for both projects even if they target different platforms, you can use this to share the class for both projects

I run Windows and Mobile using the same code.
Mobile devices have the word PocketPC defined in the Project's Properties, therefore, all you have to do (since you are coding in C#) is:
public static bool CreateDirectoryWithPermission(string path) {
bool ok = false;
DirectoryInfo dir = new DirectoryInfo(path);
#if !PocketPC
try {
DirectorySecurity ds;
if (dir.Exists) {
ds = dir.GetAccessControl();
} else {
ds = dir.Parent.GetAccessControl();
}
string user = Environment.UserDomainName + #"\" + Environment.UserName;
FileSystemAccessRule rule = new FileSystemAccessRule(user, FileSystemRights.FullControl, AccessControlType.Allow);
ds.AddAccessRule(rule);
dir.Create(ds);
ok = true;
} catch (Exception) { }
#endif
if (!ok) {
try {
dir.Create();
ok = true;
} catch (Exception) { }
}
return ok;
}
If I remember correctly, System.Security.AccessControl is not defined under Windows Mobile, so the DirectorySecurity is undefined.
UPDATE:
Here is another way to do what you are interested in: Create a Serializable class in a completely separate namespace, use that namespace in both projects, and pass the serialized data from the Webservice to the Mobile device. I do that, as well, but there is more code.
namespace LocksAnimal {
[Serializable()]
public class Animal {
private string name;
public Animal() {
name = "Lock";
}
public string GetName() {
#ifdef PocketPC
return name + " (Mobile Version)";
#else
return name + " (Webservice Version)";
#endif
}
}
}
The Webservice version, of course, can access more detailed information (like GetAccessControl() shown in the first code segment).
I hope this gives you some ideas.

Related

c# service: how to get user profile folder path

I need to get the user directory from within a C# windows service...
...like C:\Users\myusername\
Ideally, I'd like to have the roaming path...
...like C:\Users\myusername\AppData\Roaming\
When I used the following in a console program I got the correct user directory...
System.Environment.GetEnvironmentVariable("USERPROFILE");
...but when I use that same variable in a service, I get...
C:\WINDOWS\system32\config\systemprofile
How can I get the user folder and maybe even the roaming folder location from a service?
Thanks in advance.
I have searched for getting the profile path of user from Windows service. I have found this question, which does not include a way to do it. As I have found the solution, partly based on a comment by Xavier J on his answer, I have decided to post it here for others.
Following is a piece of code to do that. I have tested it on few systems, and it should work on different OSes ranging from Windows XP to Windows 10 1903.
//You can either provide User name or SID
public string GetUserProfilePath(string userName, string userSID = null)
{
try
{
if (userSID == null)
{
userSID = GetUserSID(userName);
}
var keyPath = #"SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\" + userSID;
var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(keyPath);
if (key == null)
{
//handle error
return null;
}
var profilePath = key.GetValue("ProfileImagePath") as string;
return profilePath;
}
catch
{
//handle exception
return null;
}
}
public string GetUserSID(string userName)
{
try
{
NTAccount f = new NTAccount(userName);
SecurityIdentifier s = (SecurityIdentifier)f.Translate(typeof(SecurityIdentifier));
return s.ToString();
}
catch
{
return null;
}
}
First, you'll want to use Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
Environment.SpecialFolder.ApplicationData is for roaming profiles.
Find all SpecialFolder enumeration values here: https://msdn.microsoft.com/en-us/library/system.environment.specialfolder(v=vs.110).aspx
As others have noted, the Service will run under the account LocalSystem/LocalService/NetworkService, depending on configuration: https://msdn.microsoft.com/en-us/library/windows/desktop/ms686005(v=vs.85).aspx
A service doesn't log on like a user, unless the service is configured to use a specific user's profile. So it's not going to point to "user" folders.

How to locate the correct Service Endpoint Uri

I am working on an application which is deployed to a TEST and then a LIVE webserver.
I want the class library I am working on to use the correct service endpoint when it is deployed.
Currently the code is as follows;
var data = new SettingsViewModel()
{
ServiceURI = Constants.LIVE_ENDPOINT_SERVICE_ADDRESS,
AutoSync = Constants.DEFAULT_AUTO_SYNC,
AppDataFolder = Path.Combine(ApplicationData.Current.LocalFolder.Path, Constants.ROOT_FOLDER, Constants.DATA_FOLDER),
MapKey = Constants.BASIC_MAP_KEY,
Logging = false
};
#if DEBUG
data.ServiceURI = Constants.DEV_ENDPOINT_SERVICE_ADDRESS;
#endif
As you can see, this can only pick up the DEV or the LIVE endpoints. This code cannot distinguish whether the webserver is LIVE or TEST
I thought about setting up an App.Config file and get the correct Endpoint from there. But when I create a new item, the Config template is not listed. So how do I do this?
For now I could propose this solution :
public static class Constants
{
public static string GetEndPoint()
{
// Debugging purpose
if (System.Diagnose.Debug.IsAttached)
{
return DEV_ENDPOINT_SERVICE_ADDRESS;
}
else if ( Environment.MachineName == "Test Server" ) // You need to know your test server machine name at hand.
{
return "Return test Server endpoint"
}
else
{
return "Return live server endpoint";
}
}
}
You can used it in your SettingsViewModel like this:
var data = new SettingsViewModel()
{
ServiceURI = Constants.GetEndPoint(),
AutoSync = Constants.DEFAULT_AUTO_SYNC,
AppDataFolder = Path.Combine(ApplicationData.Current.LocalFolder.Path, Constants.ROOT_FOLDER, Constants.DATA_FOLDER),
MapKey = Constants.BASIC_MAP_KEY,
Logging = false
};
The drawback for this solution is, if you change your test server you need to change is manually in your code.
Having done some research I realise I need to clarify something. The application I am working on is a Windows RT application and this does not allow config files. The solution I am meant to use is to use local settings, but these do not reference an external file like an App.Config. If I want to change the location of an EndPoint then I am going to have to specify where that is in the code.

What need I do to get this code to work in a Portable Class Library?

I'm wondering if the Portable Class Library is even more restricted in functionality than the Compact Framework.
I'm trying to port a CF/Windows CE app (runs on a handheld device) to a Xamarin solution that will target Android, iOS, Windows Phone, and perhaps other things.
One of the problems I run into, though, is that this legacy code (which works under CF):
public static List<string> GetXMLFiles(string fileType, string startingDir)
{
const string EXTENSION = ".XML";
string dirName = startingDir;
// call it like so: GetXMLFiles("ABC", "\\"); <= I think the double-whack is what I need for Windows CE device...am I right?
var fileNames = new List<String>();
try
{
foreach (string f in Directory.GetFiles(dirName))
{
string extension = Path.GetExtension(f);
if (extension != null)
{
string ext = extension.ToUpper();
string fileNameOnly = Path.GetFileNameWithoutExtension(f);
if (fileNameOnly != null &&
((ext.Equals(EXTENSION, StringComparison.OrdinalIgnoreCase)) &&
(fileNameOnly.Contains(fileType))))
{
fileNames.Add(f);
}
}
}
foreach (string d in Directory.GetDirectories(dirName))
{
fileNames.AddRange(GetXMLFiles(fileType, d));
// from Brad Rem's answer here: http://stackoverflow.com/questions/22186198/why-is-this-function-returning-nothing-although-there-is-a-match/22186351?noredirect=1#22186351
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return fileNames;
}
...won't compile in the Xamarin/CPL solution. I get, "The name 'Directory' does not exist in the current context" and right-clicking that word does not afford a "resolve" option.
Is there a way to get PCL to recognize "Directory" or must I completely rewrite the code? If the latter, does anybody have any suggestions on what to do/use in its stead?
Relatedly, is there an URL that will show me what is [not] available in PCL and/or a site that will show how much of a provided block of code is "PCL-ready"?
UPDATE
The first image in this article is very illuminating. Later on, it specifically talks about "Directory" not being available in the PCL scenario.
UPDATE 2
I downloaded the PCLStorage package referenced by Daniel Plaisted below to allow me to access the file system within a PCL project.
Using the sample code at the start of the download page [http://pclstorage.codeplex.com/] as a starting point, I've gotten this far:
public async Task<List<string>> GetXMLFiles(string fileType, string startingDir)
{
const string EXTENSION = ".XML";
IFolder rootFolder = FileSystem.Current.LocalStorage;
IFolder folder = await rootFolder.GetFolderAsync(startingDir, CreationCollisionOption.OpenIfExists); //CreateFolderAsync(startingDir, CreationCollisionOption.OpenIfExists);
List<string> fileNames = await folder.GetFilesAsync(EXTENSION);
return fileNames;
}
...but "EXTENSION" as the arg to GetFilesAsync() is not right. I get with this, "Argument 1: cannot convert from 'string' to 'System.Threading.CancellationToken'"
So what need I do to get all the *.XML files the folder?
UPDATE 3
This compiles, but I'm not at all sure it's the right way to do it, besides the fact that it simply gets all the files from the folder, rather than just those that match "*.XML":
public async Task<List<IFile>> GetXMLFiles(string fileType, string startingDir)
{
const string EXTENSION = ".XML";
IFolder rootFolder = FileSystem.Current.LocalStorage;
IFolder folder = await rootFolder.GetFolderAsync(startingDir, System.Threading.CancellationToken.None);
IList<PCLStorage.IFile> fileNames = await folder.GetFilesAsync(System.Threading.CancellationToken.None);
return fileNames.ToList();
}
Since in a PCL I was unable to get a StreamWriter from a string (it required a stream), I created a simple interface to get some of the data from the platform implementation. You can also do this with DirectoryInfo and FileInfo.
https://github.com/sami1971/SimplyMobile/blob/master/Core/SimplyMobile.Text/IStreamLocator.cs
The implementation is really simple as well, only needs one single compiler flag for WP8:
https://github.com/sami1971/SimplyMobile/blob/master/WP8/SimplyMobile.Text.Platform/StreamLocator.cs
Recursively search for *.XML files:
private static void PrintDirectory(IStreamLocator locator, string dir)
{
foreach (var file in locator.GetFileNames(dir, "*.XML"))
{
System.Diagnostics.Debug.WriteLine(file);
}
foreach (var di in locator.GetFolderNames(dir, "*"))
{
PrintDirectory(locator, di);
}
}
Windows Phone applications do not use the file system of the operating
system and are restricted to using isolated storage to persist and
access files, so this namespace does not provide any additional
functionality.
http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.io%28v=vs.105%29.aspx
Xamarin has a scanner which will give you a rough idea of the portability of your code: http://scan.xamarin.com/
For some guidance on how to deal with non-portable APIs from PCLs, see my blog post: How to Make Portable Class Libraries Work for You
For file IO in particular, you can try my PCL Storage library.
Another option is to use Shim if all your platforms are supported by it.
API coverage for file operations isn't exhaustive, but it gets you a long way. As a bonus, it also gives you access to a bunch of other stuff.

ResourceMap not found error when referencing a resource file within a portable class library

The problem I am facing has as follows:
I have developed a portable class library to encapsulate a service connection. Inside this class library there is a Resources.resw file containing strings. These strings are called only by methods of the class library (for example to override ToString() methods).
As I said this is a portable class library. If I reference it as a dll, or even as a project inside another solution, it gets built and compiles correctly. Then I make a call using a method of this library within my application, say
ClientFacadeConnector connector = new ClientFacadeConnector();
ICollection<SearchResult> results = null;
string message = string.Empty;
if (maxResults != -1) //Search with max Results
{
try
{
if (!contextQuery.Trim().Equals(string.Empty))
{
results = await connector.GetConnected().SearchAsync(contextQuery, query, maxResults);
message = "Search with ContextQuery " + contextQuery + ", Query " + query + ", max results " + maxResults.ToString();
}
else
{
results = await connector.GetConnected().SearchAsync(query, maxResults, true);
message = "...using normal Query search, Query " + query + ", max results " + maxResults.ToString();
}
}
catch (IQserException ex)
{
message = ex.Message;
}
}
if (results != null)
{
ICollection<LocalSearchResult> contentResults = new List<LocalSearchResult>();
foreach (SearchResult s in results)
{
var q = s.ToString();
var contentItem = await connector.GetConnected().GetContentAsync(s.ContentId);
LocalSearchResult lContent = new LocalSearchResult(contentItem);
lContent.Score = s.Score;
lContent.Relevance = s.Relevance;
lContent.MarkFullText(query);
contentResults.Add(lContent);
}
At the point where I call s.ToString() method, I get an error "Resource Map not found".
To explain where this comes from:
public static class AppResources
{
private static ResourceLoader resourceLoader;
static AppResources()
{
// Load local file Resources.resw by default
resourceLoader = new ResourceLoader();
}
public static string GetResources(string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
return resourceLoader.GetString(key);
}
}
and inside the overridden ToString() method there is code that looks as follows:
public override string ToString()
{
StringBuilder buf = new StringBuilder(AppResources.GetResources("InstrSearchResultContent"));
if (ContentId != -1)
{
buf.Append(AppResources.GetResources("StringContent") + " ID:" + ContentId.ToString() + " | ");
}
else
{
buf.Append(AppResources.GetResources("StringNo") + AppResources.GetResources("StringContent") + "ID" + " | ");
}
...
The resource file is called resources.resw and is the default resw file that ResourceLoader calls if no other is called.
Strangely enough, if I copy the resource file inside the client application locally, it is referenced correctly by all calls to the class library resource file and everything works.
This class library is supposed to be an SDK when finished. Do I need to distribute the resource file separately?
Such a problem I have never experienced with normal Class libraries and resx files. Resw is giving me the creeps..
It looks like you have to specify the name of the resource map when you create the ResourceLoader, like this:
resourceLoader = new ResourceLoader("Assembly/ResourceFile");
For example, if your class library was called "Company.Lib.dll", and your resource file was "Resources.resw", you would use:
resourceLoader = new ResourceLoader("Company.Lib/Resources");
This doesn't seem to be documented fully on MSDN - it suggests that you can just specify the name of your resource file, but it might be that that only works for resource files that are in the Windows Store application project. It was this page that showed me that, for libraries, you need to specify the assembly name as well.
I also had similar issue even with repeating all steps from How to load string resources.
The problem was that my Resources.resw file was empty. When I added some fake string to it all started working as expected.
I had a similar issue which i resolved by changing the Build Action of the resw file to PRIResource in the properties. I had renamed an existing resx to resw but the documentation doesn't mention that you also have to change the build action.
Accepted answer posted by #Rory MacLeod may no longer be true. I tried and VS warned that ResourceLoader(String) is deprecated. The following worked in my case:
var loader = ResourceLoader.GetForCurrentView();
string localName = loader.GetString("someKey");
I faced a similar issue when developing a UWP app with a class library.
So I have a /Strings/en-Us/Resource.resw file in my library.
ResourceLoader.GetForCurrentView().GetString("someKey");
gives an exception
new ResourceLoader("Company.Lib/Resources").GetString("someKey");
gives me a deprecation warning but works.
My solution which does not give a warning is this:
ResourceLoader.GetForViewIndependentUse("AssemblyNamespace/Resources").GetString("someKey");

Change some windows username programmatically (Rename windows user)

How to change windows user name programmatically (using some API or command line tool)
Or how to rename a windows user?
I have written some small method to rename a windows user using System.DirectoryServices.DirectoryEntry class.
public bool RenameUser(string oldLoginName, string newLoginName)
{
bool renamed = false;
try
{
using (DirectoryEntry AD = new
DirectoryEntry("WinNT://" + Environment.MachineName + ",computer"))
{
try
{
using (DirectoryEntry NewUser = AD.Children.Find(oldLoginName, "user"))
{
if (NewUser != null)
{
NewUser.Rename(newLoginName);
renamed = true;
}
}
}
catch (Exception ex)
{
//TODO: Log
}
}
}
catch (Exception ex)
{
//TODO: Log
}
return renamed;
}
You can change the username of a user account with the NetUserSetInfo function.
If you only want to change the username set the level argument to 0 and pass a USER_INFO_0 structure. You can use a different level if you want to change several things at once.
This is a simple bit of code I've used successfully to change usernames:
#include <Windows.h>
#include <LM.h>
#include <stdio.h>
#pragma comment(lib, "netapi32.lib")
int main(int argc, char ** argv)
{
USER_INFO_0 ui0;
NET_API_STATUS result;
LPWSTR command = GetCommandLineW();
wchar_t newname[21];
while (*command != L'*') command++;
command++;
ui0.usri0_name = newname;
wcscpy_s(newname, _countof(newname), L"decommiss-");
wcscat_s(newname, _countof(newname), command);
result = NetUserSetInfo(NULL, command, 0, (LPBYTE)&ui0, NULL);
printf("%u\n", result);
return result;
}
You can not change obviously the name of the user on Windows system, as it kind of key for a lot of internal resources, but you can change DisplayName of it, which, by the way, will not affect on internal File structure, so kind of cosmetic change. Which most probably will create confusion for you, or for other users on the same machine along years of use, so I would suggest do not do that. But if you want, here is powershell script example, that should work for you :
$CurrentUserName = "Your_Domain_Name/Current_User_Name"
Get-QADUser -SearchRoot $CurrentUserName `
| Set-QADUser -DisplayName "New_User_Name" `
| FT FirstName, LastName, DisplayName, company
For more detailed description look on this good example:
Change user DisplayName from powershell
Note that here they use extraplugin for PowerShell.
EDIT
another link on subject to clarify what I mean :
Change user name on Windows7 Professional
Hope this helps.

Categories

Resources