Why this DataProtectionProvider Error Occurs - c#

I am testing the config data protection using DataProtectionProvider class. The test code works in one way, but failed in another way.
TEST ENVIRONMENT:
App type: ASP.NET 6 console app with c#
Package: Microsoft.AspNetCore.DataProtection.Extensions 6.0.3
IDE: VS 2022
Test Project: ProtectData
PROBLEM DESCRIPTION:
In DataProtector class has 3 methods (see test code #1).
The TestProtector method is for initial testing. It encrypt and decrypt data in the same method. It works without any problem
In methods EncryptData and DecryptData handle the process in 2 separate steps. When running tests with these methods, the exception occurs in the DecryptData method on the statement: decrypted = protector.Unprotect(encryptedData); The exception info is shown in following screen shot.
The tests were run using the program.main method (see test code $2)
QUESTION:
When comparing the code in "TestProtector" method with code in these 2 method, they all handle the process in the same way with same key. Why does one method works perfectly and the "two-step" always fail? It really puzzles me. I'll appreciate any help or suggestions for troubleshooting.
TEST CODE
---- test code #1 (console app using DataProtectionProvider)
using Microsoft.AspNetCore.DataProtection;
using System;
namespace ProtectData
{
public static class DataProtector
{
public static string EncryptData(string inputText)
{
string encrypted = string.Empty;
try
{
var dataProtectionProvider = DataProtectionProvider.Create($".\appconfig.txt");
var protector = dataProtectionProvider.CreateProtector("protect data");
//var protectedPayload = protector.Protect(inputText);
encrypted = protector.Protect(inputText);
}
catch (Exception ex)
{
Console.WriteLine("ERROR: " + ex.Message);
}
return encrypted;
}
public static string DecryptData(string encryptedData)
{
string decrypted = string.Empty;
try
{
var dataProtectionProvider = DataProtectionProvider.Create($".\appconfig.txt");
var protector = dataProtectionProvider.CreateProtector("protect conn string");
decrypted = protector.Unprotect(encryptedData);
}
catch(Exception ex)
{
Console.WriteLine("ERROR: " + ex.Message, ex);
}
return decrypted;
}
public static void TestProtector()
{
string inputText = "DataSource=localhost, database=testdb, userID=appuser, password=topsecret";
Console.WriteLine($"inputText:\n{inputText}\n");
string encrypted = string.Empty;
string decrypted = string.Empty;
try
{
// encrypt given string
var dataProtectionProvider = DataProtectionProvider.Create($".\appconfig.txt");
var protector = dataProtectionProvider.CreateProtector("protect data");
//generate protected payload for input text
encrypted = protector.Protect(inputText);
Console.WriteLine($"protectedPayload:\n{encrypted}\n");
//decrypt protected data
decrypted = protector.Unprotect(encrypted);
Console.WriteLine($"UnprotectPayload:\n{decrypted}\n");
//show verification result
Console.WriteLine($"Verify result:\n{(inputText == decrypted ? true : false)}");
}
catch(Exception ex)
{
Console.WriteLine("Error:", ex);
}
}
}
}
---- Test code #2 (program main)
namespace ProtectData
{
public class Program
{
static void Main()
{
string testType = "two_step";
RunTest(testType);
Console.WriteLine();
Console.WriteLine("Press any key...");
Console.ReadKey();
}
static void RunTest(string testType)
{
switch ( testType.ToLower())
{
case "simple":
DataProtector.TestProtector();
break;
case "two_step":
string inputData = "DataSource=localhost, database=testdb, userID=appuser, password=topsecret";
Console.WriteLine($"inputData:\n{inputData}\n");
string protectedData = DataProtector.EncryptData(inputData);
Console.WriteLine($"protectedData:\n{protectedData}\n");
string outputData = DataProtector.DecryptData(protectedData);
Console.WriteLine($"outputData:\n{outputData}\n");
bool verify = inputData == outputData;
Console.WriteLine($"verified: {verify}");
break;
}
}
}
}

I was able to fix the problem using examples provided in following online doc:
https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/non-di-scenarios?view=aspnetcore-6.0

Related

Operation returned an invalid status code 'unauthorized' on azure cognitive

I have download this code from official microsoft cognitive github repository:
https://github.com/Azure-Samples/cognitive-services-dotnet-sdk-samples/tree/master/samples/ComputerVision/OCR
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace Microsoft.Azure.CognitiveServices.Samples.ComputerVision.BatchReadFile
{
using Microsoft.Azure.CognitiveServices.Vision.ComputerVision;
using Microsoft.Azure.CognitiveServices.Vision.ComputerVision.Models;
class Program
{
static void Main(string[] args)
{
// Add your Computer Vision subscription key and endpoint to your environment variables
string subscriptionKey = "my key0001"; // Environment.GetEnvironmentVariable("my key0001");
string endpoint = "https://controllo.cognitiveservices.azure.com/"; // Environment.GetEnvironmentVariable("https://controllo.cognitiveservices.azure.com/");
try
{
BatchReadFileSample.RunAsync(endpoint, subscriptionKey).Wait(5000);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.WriteLine("\nPress ENTER to exit.");
Console.ReadLine();
}
}
public class BatchReadFileSample
{
public static async Task RunAsync(string endpoint, string key)
{
ComputerVisionClient computerVision = new ComputerVisionClient(new ApiKeyServiceClientCredentials(key))
{
Endpoint = endpoint
};
const int numberOfCharsInOperationId = 36;
// string localImagePath = #"Images\handwritten_text.jpg"; // See this repo's readme.md for info on how to get these images. Alternatively, you can just set the path to any appropriate image on your machine.
string localImagePath = #"C:\Users\marco.panza\OneDrive - Accenture\Desktop\Sorgenti\OCR C#\info.png";
// string remoteImageUrl = "https://github.com/Azure-Samples/cognitive-services-sample-data-files/raw/master/ComputerVision/Images/printed_text.jpg";
Console.WriteLine("Text being batch read ...");
await BatchReadFileFromStreamAsync(computerVision, localImagePath, numberOfCharsInOperationId);
// await BatchReadFileFromUrlAsync(computerVision, remoteImageUrl, numberOfCharsInOperationId);
}
// Read text from a remote image
private static async Task BatchReadFileFromUrlAsync(ComputerVisionClient computerVision, string imageUrl, int numberOfCharsInOperationId)
{
if (!Uri.IsWellFormedUriString(imageUrl, UriKind.Absolute))
{
Console.WriteLine("\nInvalid remote image url:\n{0} \n", imageUrl);
return;
}
// Start the async process to read the text
BatchReadFileHeaders textHeaders = await computerVision.BatchReadFileAsync(imageUrl);
await GetTextAsync(computerVision, textHeaders.OperationLocation, numberOfCharsInOperationId);
}
// Recognize text from a local image
private static async Task BatchReadFileFromStreamAsync(ComputerVisionClient computerVision, string imagePath, int numberOfCharsInOperationId)
{
if (!File.Exists(imagePath))
{
Console.WriteLine("\nUnable to open or read local image path:\n{0} \n", imagePath);
return;
}
using (Stream imageStream = File.OpenRead(imagePath))
{
// Start the async process to recognize the text
BatchReadFileInStreamHeaders textHeaders = await computerVision.BatchReadFileInStreamAsync(imageStream);
await GetTextAsync(computerVision, textHeaders.OperationLocation, numberOfCharsInOperationId);
}
}
// Retrieve the recognized text
private static async Task GetTextAsync(ComputerVisionClient computerVision, string operationLocation, int numberOfCharsInOperationId)
{
// Retrieve the URI where the recognized text will be
// stored from the Operation-Location header
string operationId = operationLocation.Substring(operationLocation.Length - numberOfCharsInOperationId);
ReadOperationResult result = await computerVision.GetReadOperationResultAsync(operationId);
// Wait for the operation to complete
int i = 0;
int maxRetries = 10;
while ((result.Status == TextOperationStatusCodes.Running ||
result.Status == TextOperationStatusCodes.NotStarted) && i++ < maxRetries)
{
Console.WriteLine("Server status: {0}, waiting {1} seconds...", result.Status, i);
await Task.Delay(1000);
result = await computerVision.GetReadOperationResultAsync(operationId);
}
// Display the results
Console.WriteLine();
var recResults = result.RecognitionResults;
foreach (TextRecognitionResult recResult in recResults)
{
foreach (Line line in recResult.Lines)
{
Console.WriteLine(line.Text);
}
}
Console.WriteLine();
}
}
}
but I get this error:
One or more errors occurred. (Operation returned an invalid status code 'Unauthorized')
the key and end point are correct (I have posted a pseudo key for security reasons).
The first time I have tried with this code:
string subscriptionKey = Environment.GetEnvironmentVariable("my key0001");
string endpoint = Environment.GetEnvironmentVariable("https://controllo.cognitiveservices.azure.com/");
but these string return null and after I have tried to assigh directly the value:
string subscriptionKey = "my key0001");
string endpoint = "https://controllo.cognitiveservices.azure.com/);
and I get this error:
"One or more errors occurred. (Operation returned an invalid status code 'Unauthorized')"
can someone help me please ?
Pls make sure that you have created a current type of cognitive service, I recommend you to create All Cognitive Services just as below:
You can follow this doc to create it(Multi-service resource).
I did some test on my side by this service and everything works for me as expected :
My local test image:
Result:

Calling a static method in C#

I implemented a plugin (using pGina software) to allow the user to authenticate the username/password in their computer by scanning a NFC tag.
I used a program I found called CSharp PC/SC Wrapper for .NET to read the tag ID. Every time a tag is scanned the program writes the ID to a text file and checks that the ID is the the same as the one set on the string.
if (userInfo.Username.Contains("hello") && userInfo.Password.Contains("pGina")
&& text.Equals("UID = 0x04 82 EC BA 7A 48 80"))
The plugin is set to find the .exe file that reads the ID (PC/SC Wrapper). Everything works fine. However, I don't one the reader program to be in a different file. I want everything to be in the plugin file.
I created a method and copied the code from the wrapper that performs the reading of the tag ID (runme()), but I'm not sure how to replace the line that calls the .exe file with the method I created
ProcessStartInfo ps = new ProcessStartInfo(#"C:\Users\Student\Desktop\CSharpPCSC\CSharpPCSC\ExamplePCSCReader\bin\Release\ExamplePCSCReader.exe");
Any suggestions? I'm new to C#
Below is my code for the plugin with the method containing the code that reads the ID
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using pGina.Shared.Types;
using log4net;
using System.IO;
using System.Diagnostics;
using GS.PCSC;
using GS.Apdu;
using GS.SCard;
using GS.Util.Hex;
using System.Threading;
namespace HelloPlugin
{
public class PluginImpl : pGina.Shared.Interfaces.IPluginAuthentication
{
private ILog m_logger;
private static readonly Guid m_uuid = new Guid("CED8D126-9121-4CD2-86DE-3D84E4A2625E");
public PluginImpl()
{
m_logger = LogManager.GetLogger("pGina.Plugin.HelloPlugin");
}
public string Name
{
get { return "Hello"; }
}
public string Description
{
get { return "Authenticates users with 'hello' in the username and 'pGina' in the password"; }
}
public Guid Uuid
{
get { return m_uuid; }
}
public string Version
{
get
{
return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
}
public void Starting()
{
}
public void Stopping() { }
public BooleanResult AuthenticateUser(SessionProperties properties)
{
UserInformation userInfo = properties.GetTrackedSingle<UserInformation>();
ProcessStartInfo ps = new ProcessStartInfo(#"C:\Users\Student\Desktop\CSharpPCSC\CSharpPCSC\ExamplePCSCReader\bin\Release\ExamplePCSCReader.exe");
Process.Start(ps);
Thread.Sleep(2000);
string text = File.ReadAllText(#"C:\Users\Student\Desktop\text.txt", Encoding.UTF8);
text = text.Trim();
if (userInfo.Username.Contains("hello") && userInfo.Password.Contains("pGina") && text.Equals("UID = 0x04 82 EC BA 7A 48 80"))
{
// Successful authentication
m_logger.InfoFormat("Successfully authenticated {0}", userInfo.Username);
return new BooleanResult() { Success = true };
}
// Authentication failure
m_logger.ErrorFormat("Authentication failed for {0}", userInfo.Username);
return new BooleanResult() { Success = false, Message = "Incorrect username or password." };
}
static void runme()
{
ConsoleTraceListener consoleTraceListener = new ConsoleTraceListener();
Trace.Listeners.Add(consoleTraceListener);
PCSCReader reader = new PCSCReader();
string cardid = "";
try
{
reader.Connect();
reader.ActivateCard();
RespApdu respApdu = reader.Exchange("FF CA 00 00 00"); // Get NFC Card UID ...
if (respApdu.SW1SW2 == 0x9000)
{
Console.WriteLine("UID = 0x" + HexFormatting.ToHexString(respApdu.Data, true));
cardid = "UID = 0x" + HexFormatting.ToHexString(respApdu.Data, true);
cardid = cardid.Trim();
}
}
catch (WinSCardException ex)
{
Console.WriteLine(ex.WinSCardFunctionName + " Error 0x" +
ex.Status.ToString("X08") + ": " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
string path = #"C:\Users\Student\Desktop\text.txt";
string text2write = cardid;
System.IO.StreamWriter writer = new System.IO.StreamWriter(path);
writer.Write(text2write);
writer.Close();
reader.Disconnect();
Environment.Exit(0);
Console.WriteLine("Please press any key...");
Console.ReadLine();
}
}
}
}
You've created a class called PluginImpl and in that class declared the method runme. To call that method from anywhere, you need to write PluginImpl.runme().
Since you've put your class in the namespace HelloPlugin - if the calling *.cs file is in a different namespace, you'll need a using HelloPlugin directive at the top.
That's all!
It's possible I have misunderstood your question, if so please re-word your question and send me a comment.
If you want to replace the line
ProcessStartInfo ps = new ProcessStartInfo(
#"C:\Users\Student\Desktop\CSharpPCSC\CSharpPCSC\"
+"ExamplePCSCReader\bin\Release\ExamplePCSCReader.exe");
with a method call instead, you want something like this
ProcessStartInfo ps = runme();
Since you are calling your static method from within the class, you don't need a PluginImpl. prefix.
Okay, so now it will complain that runme doesn't return ProcessStartInfo. You're going to need to change runme so that it does. Any subclass of ProcessStartInfo will do.
static ProcessStartInfo runme()
{
// ... Some code
ProcessStartInfo toReturn = new ProcessStartInfo( //...
);
// ... More code
return toReturn;
}

Hadoop task is cancelled due to task timeout

I am writing a C# mapreduce program that runs on the cluster. The system returns an error of
System.Threading.Tasks.Task
The task is cancelled after a timeout of 32 milliseconds. The data is pretty huge .I even changed the data size to check but the error still occurs, so I am assuming it is not due to the data size.
Is there a way to change the task timeout of 32 milliseconds.
I am using Azure, Visual studio 2013 and a cluster with 4 nodes.
The system aggregate exception occured in mscorlib.dll
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Threading;
using Microsoft.Hadoop.MapReduce;
using Microsoft.WindowsAzure.Management.HDInsight;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Hadoop.Client;
namespace StackExtractor
{
//Our Mapper that takes a line of XML input and spits out the {OwnerUserId,ParentId,Score}
//i.e, {User,Question,Weightage}
public class UserQuestionsMapper : MapperBase
{
public override void Map(string inputLine, MapperContext context)
{
try
{
var obj = XElement.Parse(inputLine);
var postType = obj.Attribute("PostTypeId");
if (postType != null && postType.Value == "2")
{
var owner = obj.Attribute("OwnerUserId");
var parent = obj.Attribute("ParentId");
// Write output data. Ignore records will null values if any
if (owner != null && parent != null)
{
context.EmitLine(string.Format("{0},{1}", owner.Value, parent.Value));
}
}
}
catch
{
//Ignore this line if we can't parse
}
}
}
//Our Extraction Job using our Mapper
public class UserQuestionsExtractionJob : HadoopJob<UserQuestionsMapper>
{
public override HadoopJobConfiguration Configure(ExecutorContext context)
{
var config = new HadoopJobConfiguration();
config.DeleteOutputFolder = true;
config.InputPath = "/input/recommender";
config.OutputFolder = "/output/recommender";
return config;
}
}
//Driver that submits this to the cluster in the cloud
//And will wait for the result. This will push your executables to the Azure storage
//and will execute the command line in the head node (HDFS for Hadoop on Azure uses Azure storage)
public class Driver
{
public static void Main()
{
try
{
var azureCluster = new Uri("https://name r.azurehdinsight.net:563");
const string clusterUserName = "****";
const string clusterPassword = "****";
// This is the name of the account under which Hadoop will execute jobs.
// Normally this is just "Hadoop".
const string hadoopUserName = "Hadoop";
// Azure Storage Information.
const string azureStorageAccount = "name.blob.core.windows.net";
const string azureStorageKey = "id;
const string azureStorageContainer = "namecontainer";
const bool createContainerIfNotExist = true;
Console.WriteLine("Connecting : {0} ", DateTime.Now);
var hadoop = Hadoop.Connect(azureCluster,
clusterUserName,
hadoopUserName,
clusterPassword,
azureStorageAccount,
azureStorageKey,
azureStorageContainer,
createContainerIfNotExist);
Console.WriteLine("Starting: {0} ", DateTime.Now);
var result = hadoop.MapReduceJob.ExecuteJob <UserQuestionsExtractionJob>();
var info = result.Info;
Console.WriteLine("Done: {0} ", DateTime.Now);
Console.WriteLine("\nInfo From Server\n----------------------");
Console.WriteLine("StandardError: " + info.StandardError);
Console.WriteLine("\n----------------------");
Console.WriteLine("StandardOut: " + info.StandardOut);
Console.WriteLine("\n----------------------");
Console.WriteLine("ExitCode: " + info.ExitCode);
}
catch (Exception ex)
{
Console.WriteLine("Error: {0} ", ex.StackTrace.ToString(CultureInfo.InvariantCulture));
}
Console.WriteLine("Press Any Key To Exit..");
Console.ReadLine();
}
}
}

What is the best way to work with PHP and MySQL in C#

I'm building a simple application that requires login. At the moment I'm connecting to my database directly from my C# application however, the college network on which this will be used doesn't allow direct connections to MySQL for some reason. I decided to take a look at how I would do this from PHP instead. I've build a simple login form and tested it and it seems to work. However I have some questions and issues that need sorting out.
How would I first of all stop just anyone typing in the address of the PHP file and getting the data back?
Second, how will I be able to get multiple results back? Let's say I make a PHP file that gets all of the user's files and stores them in the C# application, how do I actually parse this from the PHP file?
Here is an example of a login.php file I would have on the server:
<?php
include("connect.php");
$username = mysql_escape_string($_GET['username']);
$password = mysql_escape_string($_GET['password']);
$squery = mysql_query("SELECT * FROM users WHERE username='$username'");
$query = mysql_fetch_array($squery);
$rowcount = mysql_num_rows($squery);
if($rowcount == 1)
{
if($password != $query['password'])
echo'Password errata';
else
echo 'Login avvenuto';
}
else
echo 'Account non registrato';
?>
And here is the code I'd use on C# to access the PHP file:
string Reply = new WebClient().DownloadString("http://127.0.0.1/ClipCloud.Service/account_auth/login.php?username=" + textBox1.Text + "&password=" + textBox2.Text);
switch (Reply.ToLower())
{
case "account non registrato":
{
MessageBox.Show("Account not registered!");
break;
}
case "password errata":
{
MessageBox.Show("Password error!");
break;
}
case "login avvenuto":
{
MessageBox.Show("Login happened!");
break;
}
default:
{
MessageBox.Show("Error with the remote server, please let try again later!");
break;
}
}
Sorry if this question is a bit confusing, I basically just need to know how to correctly manipulate a database with PHP using C# with correct security in place.
You can get C# communicating with PHP by implementing a simple JSON API Server.
Conside the following : http://yoursite.com/api_server.php
api_server.php
<?php
// Load Request
$api_method = isset($_POST['api_method']) ? $_POST['api_method'] : '';
$api_data = isset($_POST['api_data']) ? $_POST['api_data'] : '';
// Validate Request
if (empty($api_method) || empty($api_data)) {
API_Response(true, 'Invalid Request');
}
if (!function_exists($api_method)) {
API_Response(true, 'API Method Not Implemented');
}
// Call API Method
call_user_func($api_method, $api_data);
/* Helper Function */
function API_Response($isError, $errorMessage, $responseData = '')
{
exit(json_encode(array(
'IsError' => $isError,
'ErrorMessage' => $errorMessage,
'ResponseData' => $responseData
)));
}
/* API Methods */
function loginUser($api_data)
{
// Decode Login Data
$login_data = json_decode($api_data);
// Dummy Check
if ($login_data->username == 'test' &&
$login_data->password == '1234')
{
// Success
API_Response(false, '', 'SUCCESS');
}
else
{
// Error
API_Response(true, 'Invalid username and/or password.');
}
}
?>
Then you communicate with it via C# like this, making POST Requests:
using (var wb = new WebClient())
{
var data = new NameValueCollection();
data["api_method"] = "loginUser";
data["api_data"] = "{ \"username\":\"test\", \"password\":\"1234\" }";
var responseBytes = wb.UploadValues(
"http://yoursite.com/api_server.php", "POST", data);
string responseString = Encoding.Default.GetString(responseBytes);
}
Here, the responseString from the API server will the json string. To decode this, you can use this: http://james.newtonking.com/json
Here's a fully working example of how everything is put together in the C# app using a simple console app:
Note how I am generating json string (for api_data) via the json library, instead of manually typing it.
using System;
using System.Text;
using System.Net;
using System.Collections.Specialized;
using Newtonsoft.Json;
namespace TemplateFive
{
public class API_Response
{
public bool IsError { get; set; }
public string ErrorMessage { get; set; }
public string ResponseData { get; set; }
}
public class Login_Request
{
public string username { get; set; }
public string password { get; set; }
}
class Program
{
static void Main(string[] args)
{
// request params
string apiUrl = "https://yoursite.com/api_server.php";
string apiMethod = "loginUser";
Login_Request myLogin_Request = new Login_Request()
{
username = "test",
password = "1234"
};
// make http post request
string response = Http.Post(apiUrl, new NameValueCollection()
{
{ "api_method", apiMethod },
{ "api_data", JsonConvert.SerializeObject(myLogin_Request) }
});
// decode json string to dto object
API_Response r =
JsonConvert.DeserializeObject<API_Response>(response);
// check response
if (!r.IsError && r.ResponseData == "SUCCESS")
{
Console.WriteLine("login success");
}
else
{
Console.WriteLine("login error, reason is: {0}",
r.ErrorMessage);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
public static class Http
{
public static String Post(string uri, NameValueCollection pairs)
{
byte[] response = null;
using (WebClient client = new WebClient())
{
response = client.UploadValues(uri, pairs);
}
return Encoding.Default.GetString(response);
}
}
}
finally, to secure the whole thing, run your site under SSL, so you'd access the api server via this URL: https://yoursite.com/api_server.php
Here's me testing the API server locally using a RESTClient pluggin on firefox.
Success Scenario: http://i.imgur.com/sw5yxvE.png
Error Scenario: http://i.imgur.com/HHmHlWX.png
The Solution is: remove the BOM :-)
static class Http
{
public static String Post(string uri, NameValueCollection pairs)
{
byte[] response = null;
using (WebClient client = new WebClient())
{
response = client.UploadValues(uri, pairs);
}
string ret = Encoding.UTF8.GetString(response);
ret = ret.Trim(new char[] { '\uFEFF', '\u200B' });//removes the BOM
return ret;
}
}

How do I access the CodeDomProvider from a class inheriting from Microsoft.VisualStudio.TextTemplating.VSHost.BaseCodeGeneratorWithSite?

Does anyone know how to get a CodeDomProvider in the new Microsoft.VisualStudio.TextTemplating.VSHost.BaseCodeGeneratorWithSite from the Visual Studio 2010 SDK? I used to get access to it just by in mere inheritance of the class Microsoft.CustomTool.BaseCodeGeneratorWithSite, but now with this new class it is not there. I see a GlobalServiceProvider and a SiteServiceProvider but I can't find any example on how to use them.
Microsoft.VisualStudio.TextTemplating.VSHost.BaseCodeGeneratorWithSite:
http://msdn.microsoft.com/en-us/library/bb932625.aspx
I was to do this:
public class Generator : Microsoft.VisualStudio.TextTemplating.VSHost.BaseCodeGeneratorWithSite {
public override string GetDefaultExtension() {
// GetDefaultExtension IS ALSO NOT ACCESSIBLE...
return this.InputFilePath.Substring(this.InputFilePath.LastIndexOf(".")) + ".designer" + base.GetDefaultExtension();
}
// This method is being called every time the attached xml is saved.
protected override byte[] GenerateCode(string inputFileName, string inputFileContent) {
try {
// Try to generate the wrapper file.
return GenerateSourceCode(inputFileName);
} catch (Exception ex) {
// In case of a faliure - print the exception
// as a comment in the source code.
return GenerateExceptionCode(ex);
}
}
public byte[] GenerateSourceCode(string inputFileName) {
Dictionary<string, CodeCompileUnit> oCodeUnits;
// THIS IS WHERE CodeProvider IS NOT ACCESSIBLE
CodeDomProvider oCodeDomProvider = this.CodeProvider;
string[] aCode = new MyCustomAPI.GenerateCode(inputFileName, ref oCodeDomProvider);
return Encoding.ASCII.GetBytes(String.Join(#"
", aCode));
}
private byte[] GenerateExceptionCode(Exception ex) {
CodeCompileUnit oCode = new CodeCompileUnit();
CodeNamespace oNamespace = new CodeNamespace("System");
oNamespace.Comments.Add(new CodeCommentStatement(MyCustomAPI.Print(ex)));
oCode.Namespaces.Add(oNamespace);
string sCode = null;
using (StringWriter oSW = new StringWriter()) {
using (IndentedTextWriter oITW = new IndentedTextWriter(oSW)) {
this.CodeProvider.GenerateCodeFromCompileUnit(oCode, oITW, null);
sCode = oSW.ToString();
}
}
return Encoding.ASCII.GetBytes(sCode );
}
}
Thanks for your help!
You can access the CodeDomProvider via the SiteServiceProvider by asking for the SVSMDCodeDomProvider service.
Something along the lines of:
IVSMDCodeDomProvider provider = SiteServiceProvider.
GetService(typeof(SVSMDCodeDomProvider)) as IVSMDCodeDomProvider;
if (provider != null)
{
codeDomProvider = provider.CodeDomProvider as CodeDomProvider;
}
The SiteServiceProvider is the limited scope service provider exposed by the site of a SingleFileGenerator, whereas the GlobalServiceProvider is VS' main service provider that you can ask for any globally-scoped interface.
Hope this helps.
Gareth

Categories