API to resolve %% variables - c#

I am working with the C# Event Log API in Windows (essentially everything in System.Diagnostics.Eventing.Reader).
I have an EventMetadata object and pull its Description property to retrieve the message template for an event.
Generally, these templates look similar to the following:
Network interface reports message %1
These variables are easily replaceable with actual data whenever I receive an event.
(EventLogRecord.Properties match up to the placeholders)
Here is where my problem comes in. The EventLogRecord.Properties sometimes contain different kinds of placeholders. These always begin in %% and I cannot find a way of resolving them.
As an example:
// This method is triggered when a new event comes in
async public static void ListenerEvent(object s, EventRecordWrittenEventArgs args) {
var evt = (EventLogRecord)args.EventRecord;
// This method retrieves the template from a ProviderMetadata object
// And replaces all %n with {n}
// So that we can string.Format on it
var tmp = TemplateCache.TemplateFor(evt);
// Need this since the indices start with 1, not 0
var props = new List<object> {string.Empty};
props.AddRange(evt.Properties.Select(prop => prop.Value));
// Now the message should be human-readable
var msg = string.Format(tmp, props);
}
Using the above example template, the Properties might be ["%%16411"] and now I end up with the following message
Network interface reports message %%16411
I figure my question now is, how do I replace this %%16411?
I have looked into ProviderMetadata and the rest of its properties but none seem to match up.
Any help figuring out how to resolve these placeholders (or even what they are/where they come from) is appreciated.
An event that shows this behaviour is 5152, as found here: http://community.spiceworks.com/windows_event/show/452-microsoft-windows-security-auditing-5152
Thank you.

Related

Message read by OnEntryWritten are coming with parameters not set

I'm using the OnEntryWritten event in order to get events from the event log when they are fired.
The problem I started to see today is that some messages come with parameters unset.
For example:
The Windows Filtering Platform has permitted a bind to a local port. Application Information: Process ID:9852 Application Name:\device\harddiskvolume7\program files (x86)\google\chrome\application\chrome.exe Network Information: Source Address::: Source Port:51714 Protocol:17 Filter Information: Filter Run-Time ID:0 Layer Name:%%14608 Layer Run-Time ID:38
You can see the %%14608 parameter. This comes with a value if I see the same log in the Event Viewer.
I'm running a windows service as LocalSystem, so I don't know if this is a permission issue or this technology is not useful at all.
I have tried the rest offered on C# and they also don't meet my requirements.
UPDATE: this is the code I'm using to read the events.
1) First I subscribe to the corresponding Event log:
private void Subscribe()
{
_eventLog.EnableRaisingEvents = true;
var callbackFunction = new EntryWrittenEventHandler(OnEntryWritten);
_eventLog.EntryWritten += callbackFunction;
// Save a reference for callbackFunction
_eventHandler = callbackFunction;
}
2) Then on the callback method, I read data from the message:
public void OnEntryWritten(Object source, EntryWrittenEventArgs entryArgs)
{
// When overwrite policy is enabled, this will trigger for all elements when it starts writing new ones
try
{
var entry = entryArgs.Entry;
var timeWritten = entry.TimeWritten;
// This comes with %% values depending on the log
string message = entry.Message;
}
catch(Exception ex)
{
...
}
}
3) The event log variable is simply initialized as:
var eventLog = EventLog.GetEventLogs().FirstOrDefault(el => el.Log.Equals(logName, StringComparison.OrdinalIgnoreCase));
I need some help on this, honestly I don't know what else to try.
UPDATE
I'm adding some images here so everybody can understand the situation a little bit better. To be honest, it looks like there's no solution but to implement a dictionary and replace manually the required values, which appear to be always constants.
This is what I see on the Event Viewer for a given Event ID:
This is what I see on my program when I read that entry:
You can clearly see that the following values:
"Machine key." (Key type)
"Read persisted key from file." (Operation)
Are coming unmapped in the ReplacementStrings and the Message properties as: %%2499 and %%2458
This is the message value I get on the program:
"Key file operation.\r\n\r\nSubject:\r\n\tSecurity ID:\t\tS-1-5-18\r\n\tAccount Name:\t\tMyAccount$\r\n\tAccount Domain:\t\tWORKGROUP\r\n\tLogon ID:\t\t0x3e7\r\n\r\nProcess Information:\r\n\tProcess ID:\t\t6644\r\n\tProcess Creation Time:\t2019-04-03T12:17:24.587994400Z\r\n\r\nCryptographic Parameters:\r\n\tProvider Name:\tMicrosoft Software Key Storage Provider\r\n\tAlgorithm Name:\tUNKNOWN\r\n\tKey Name:\t816339d2-c476-4f1e-bc40-954f0aa0f851\r\n\tKey Type:\t%%2499\r\n\r\nKey File Operation Information:\r\n\tFile Path:\tC:\ProgramData\Microsoft\Crypto\Keys\6d55a45fd69327293e9ed3e7930f4565_5663a8bb-2d1d-4c0d-90c1-624beddabe9c\r\n\tOperation:\t%%2458\r\n\tReturn Code:\t0x0"
What can be done here? There also nothing in entry.Data that might help me out to obtain both values.
No, I believe you're mistaken, sorry that this answer is too late. I found a similar event that was raised by chrome and evaluated if there's anything missed by the event handler. There wasn't anything missed. The message I got in my console output exactly matched what I saw in my Event Viewer.
A better solution would be to
Use entry.Data to get the complete data instead of entry.Message
entry.Data will return a byte[] which you can convert to a string. Here's the link to all properties that an entry will have.

Acumatica - Another process has added/updated record - Creating Inventory Items with DAC and Graph Extensions

I am trying to create a process that will run daily that will import records from another database as Inventory Items. In order to do this, I need to create an extension of the InventoryItemMaint graph (to give me my custom action), as well as an extension of the InventoryItem DAC (to give me a custom field). I have tried to follow the guidelines laid out specifically in the T-300 manual to do this.
Here is the code for my InventoryItemMaint extension:
namespace PX.Objects.IN
{
public class InventoryItemMaint_Extension:PXGraphExtension<InventoryItemMaint>
{
public PXAction<PX.Objects.IN.InventoryItem> DailyOnixImport;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Daily Onix Import")]
protected void dailyOnixImport()
{
var invItemMaintExtInstance = Base.GetExtension<InventoryItemMaint_Extension>();
string todaysDate = DateTime.Today.ToString("MM/dd/yyyy");
foreach (STOnixItem currentOnixItem in PXSelect<STOnixItem,
Where<STOnixItem.addedDate, Equal<Required<STOnixItem.addedDate>>>>
.Select(this.Base, todaysDate))
{
InventoryItem currentInventoryItem = invItemMaintExtInstance.Base.Item.Current;
PXCache inventoryItemCache = invItemMaintExtInstance.Base.Item.Cache;
InventoryItemExt inventoryItemExtension = inventoryItemCache.GetExtension<InventoryItemExt>(currentInventoryItem);
inventoryItemCache.Clear();
currentInventoryItem.InventoryCD = currentOnixItem.ISBN13;
currentInventoryItem.Descr = currentOnixItem.Title;
currentInventoryItem.ItemClassID = currentOnixItem.ItemClass;
currentInventoryItem.RecPrice = decimal.Parse(currentOnixItem.MSRP);
currentInventoryItem.BasePrice = decimal.Parse(currentOnixItem.DefaultPrice);
currentInventoryItem.BaseItemWeight = decimal.Parse(currentOnixItem.Weight);
currentInventoryItem.WeightUOM = "POUND";
currentInventoryItem.ImageUrl = currentOnixItem.ImageLink;
//Assigning to the custom DAC Extension
inventoryItemExtension.UsrFromOnixFile = currentOnixItem.FromFile;
inventoryItemCache.Update(currentInventoryItem);
Base.Actions.PressSave();
}
}
}
}
I am currently getting an error that reads:
Error: Another process has updated the 'InventoryItem' record. Your
changes will be lost.
And here is the error trace text:
9/20/2018 3:26:05 PM Error: Error: Another process has added the
'InventoryItem' record. Your changes will be lost.
at PX.Data.PXCache1.PersistInserted(Object row) at
PX.Data.PXCache1.Persist(PXDBOperation operation) at
PX.Data.PXGraph.Persist(Type cacheType, PXDBOperation operation)
at PX.Data.PXGraph.Persist() at
PX.Objects.IN.InventoryItemMaint.Persist() at
PX.Data.PXSave1.d__2.MoveNext() at
PX.Data.PXAction1.d__31.MoveNext() at
PX.Data.PXAction1.d__31.MoveNext() at
PX.Data.PXActionCollection.PressSave(PXAction caller) at
PX.Objects.IN.InventoryItemMaint_Extension.dailyOnixImport() at
PX.Data.PXAction1.<>c__DisplayClass3_0.<.ctor>b__0(PXAdapter adapter)
at PX.Data.PXAction1.a(PXAdapter A_0) at
PX.Data.PXAction1.d__31.MoveNext() at
PX.Data.PXAction`1.d__31.MoveNext() at
PX.Web.UI.PXBaseDataSource.tryExecutePendingCommand(String viewName,
String[] sortcolumns, Boolean[] descendings, Object[] searches,
Object[] parameters, PXFilterRow[] filters, DataSourceSelectArguments
arguments, Boolean& closeWindowRequired, Int32& adapterStartRow,
Int32& adapterTotalRows) at
PX.Web.UI.PXBaseDataSource.ExecuteSelect(String viewName,
DataSourceSelectArguments arguments, PXDSSelectArguments pxarguments)
I have done a lot of searching around StackOverflow and other places, but haven't found any answers that seem to address my issue exactly. Tweaks I've made have resulted in other errors like variations on what I'm getting now (another process added vs another process updated) and MoveNext errors.
If anyone is able to help me out, I would be very appreciative.
guys,
I faced the same exception in the EmployeeMaint when we tried to update one of the complex fields in the DAC.
baseAction() throws this exception even though no code runs before saving.
I followed your suggestions #Hugues Beauséjour and found out that reason may be:
Means, the cache is dirty.
So, to resolve my issue I just needed to clear the cache for the object that throws the exception.
IN my case the exception was:
Update employee class error - Another process has added the 'VendorPaymentMethodDetail' record. Your changes will be lost
So I needed to clear the cache for VendorPaymentMethodDetail:
Base.Caches<VendorPaymentMethodDetail>().Clear();
Be careful to clear the cache in the case you need to read from the cache during your code after you clear the cache. In that case, you need to copy objects from the cache before cleaning and used this copy afterwards.
Hope it will be helpful to someone.
There seem to be a logical flaw in the code. You are updating the same current object in a loop. This serve no purpose as it will always overwrite with the last item returned by the loop. Invoking the Save action in a loop can also lead to errors if you're not careful.
As I mentioned in the comment, clearing the cache seems wrong. You want to keep the current data there. When you call clear you're invalidating the main document of the graph, that will lead to errors.
Changing fields closely tied to the key like InventoryCD can lead to the document being cleared and invalidated. If you have to modify key fields considering inserting new record instead of updating the current one.
There are other changes I would recommend.
Code:
// Consider replacing the default namespace to avoid conflicts
namespace MyNamespace
{
public class InventoryItemMaint_Extension:PXGraphExtension<InventoryItemMaint>
{
public PXAction<PX.Objects.IN.InventoryItem> DailyOnixImport;
// '(CommitChanges = true)' is not necessary
[PXButton]
[PXUIField(DisplayName = "Daily Onix Import")]
protected void dailyOnixImport()
{
InventoryItemMaint_Extension invItemMaintExtInstance = Base.GetExtension<InventoryItemMaint_Extension>();
string todaysDate = DateTime.Today.ToString("MM/dd/yyyy");
// You need to rethink that 'foreach' logic
STOnixItem currentOnixItem in PXSelect<STOnixItem,
Where<STOnixItem.addedDate, Equal<Required<STOnixItem.addedDate>>>>.Select(Base, todaysDate);
// You can access Base directly, no need to fetch it from the extension
InventoryItem currentInventoryItem = Base.Item.Current;
// Consider using more null check
if (currentOnixItem != null && currentInventoryItem != null)
{
// Consider using similar names for similar variables
InventoryItemExt currentInventoryItemExt = currentInventoryItem.GetExtension<InventoryItemExt>();
// Avoid setting key related fields like InventoryCD when updating
currentInventoryItem.Descr = currentOnixItem.Title;
currentInventoryItem.ItemClassID = currentOnixItem.ItemClass;
currentInventoryItem.RecPrice = decimal.Parse(currentOnixItem.MSRP);
currentInventoryItem.BasePrice = decimal.Parse(currentOnixItem.DefaultPrice);
currentInventoryItem.BaseItemWeight = decimal.Parse(currentOnixItem.Weight);
currentInventoryItem.WeightUOM = "POUND";
currentInventoryItem.ImageUrl = currentOnixItem.ImageLink;
currentInventoryItemExt.UsrFromOnixFile = currentOnixItem.FromFile;
// You fetched the item from the DataView
// you can update it in the DataView too.
Base.Item.Update(currentInventoryItem);
// Is it really needed to save here?
// This coupled with cache clearing and the loop updating
// the same record triggers the error in your question.
Base.Actions.PressSave();
}
}
}
}
I found another reason that can cause the same exception:
That may happen when you select from the same table in different caches, use one for select and another for insert. for instance:
we have a view:
public PXSelect<MPEmployeeWorkSchedule, Where<MPEmployeeWorkSchedule.employeeID, Equal<Current<MPEmployeeTermination.employeeID>>>> EmployeeWorkSchedule;
and in event we have code's segment:
we use the same DAC as in the view above and then we insert into the view:
EmployeeWorkSchedule.Cache.Insert(workSchedule);
Afterwards Persist() throws exception.
Solution was again to create another view instead of query and clear the cache:
This may be helpful, I hope.
Want to share one more way of finding the same error message. In my case the problem was in structure of database.
In order to fix the error message, I've did those steps:
dropped my table
generated sql script from existing table
deleted not needed columns
copy/pasted existing columns, with replacing names
As outcome, I've got error message to disappear.

How can I capture process names using the TraceEvent library?

I'm using the TraceEvent library to capture ETW traces, but I'm not able to determine the name of the process that caused an event.
Here is what I have so far:
var session = new TraceEventSession(sessionName, null);
session.EnableProvider(MyEventSource.Log.Guid, TraceEventLevel.Informational,
options: TraceEventOptions.Stacks);
Task.Delay(1000).ContinueWith(t => session.Stop()); // for testing, deal with it (⌐■_■)
var src = new ETWTraceEventSource(sessionName, TraceEventSourceType.Session);
TraceLog.CreateFromSource(src, etlxFile, null);
var log = TraceLog.OpenOrConvert(etlxFile);
var process = log.Events.First().ProcessName;
// breakpoint
When the breakpoint at the end is hit, process is "". ProcessID is a proper PID, but that's the only useful information I could find from the processes in the log.
I expected process names to be captured by the log. Am I doing something wrong, or is this API just not available on my OS (Windows 7)?
I truly believe that process name is not being captured by the ETW log. Etw system event contains only process ID field. Although TraceEvent library declares this one as a part of TraceEvent, this one actually is being populated based on executable image filename and process ID, which is implemented differently for all 4 TraceEventSource implementations.
Another observation is that I was never able to have this one populated (my OS is Windows 8.1).
The simple repro is to use SimpleEventSourceMonitor sample from Microsoft TraceEvent Library Samples package.
If you suspect that this is an issue, then it is better ask its owners Vance Morrison and Cosmin Radu.
This can be done by enabling the kernel provider, and then maintaining a lookup of process id to process name. Here's a rough example - no error checking, but you get the idea.
// create a lookup collection for future use
var pidToProcessName = new Dictionary<int, string>();
var session = new TraceEventSession(...);
// enable the kernel provider - note! this most come first
session.EnableKernelProvider(KernelTraceEventParser.Keywords.Process);
...
session.Source.Kernel.ProcessStart += ProcessStart;
session.Source.Dynamic.All += TraceEvent;
...
session.Source.Procces();
void ProcessStart(ProcessTraceData obj)
{
if(obj.OpCode == TraceEventOpcode.Start)
{
pidToProcessName[obj.ProcessID] = obj.ProcessName;
}
}
void TraceEvent(TraceEvent obj)
{
// pull the process name from our lookup
var processNameOfEvent = pidToProcessName[obj.ProcessID];
}

BizTalk Dynamic Disassembler Problems - The body part is NULL

I started with the solution here http://social.technet.microsoft.com/wiki/contents/articles/20547.biztalk-server-dynamic-schema-resolver-real-scenario.aspx
which matches my scenario perfectly except for the send port, but that isn't necessary. I need the receive port to choose the file and apply a schema to disassemble. From their the orchestration does the mapping, some of it custom, etc.
I've done everything in the tutorial but I keep getting the following error.
"There was a failure executing the receive pipeline... The body part is NULL"
The things I don't get from the tutorial but don't believe they should be an issue are:
I created a new solution and project to make the custompipeline component (reference figure 19) and thus the dll file. Meaning it is on it's own namespace. However, it looks like from the tutorial they created the project within the main biztalk solution (ie the one with the pipeline and the orchestration) and thus the namespace has "TechNetWiki.SchemaResolver." in it. Should I make the custompipeline component have the namespace of my main solution? I'm assuming this shouldn't matter because I should be able to use this component in other solutions as it is meant to be generic to the business rules that are associated with the biztalk application.
The other piece I don't have is Figure 15 under the "THEN Action" they have it equal the destination schema they would like to disassemble to but then they put #Src1 at the end of "http://TechNetWiki.SchemaResolver.Schemas.SRC1_FF#Src1". What is the #Src1 for?
In the sample you've linked to, the probe method of the pipeline component is pushing the first 4 characters from the filename into a typed message that is then passed into the rules engine. Its those 4 characters that match the "SRC1" in the example.
string srcFileName = pInMsg.Context.Read("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties This link is external to TechNet Wiki. It will open in a new window. ").ToString();
srcFileName = Path.GetFileName(srcFileName);
//Substring the first four digits to take source code to use to call BRE API
string customerCode = srcFileName.Substring(0, 4);
//create an instance of the XML object
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(string.Format(#"<ns0:Root xmlns:ns0='http://TechNetWiki.SchemaResolver.Schemas.SchemaResolverBRE This link is external to TechNet Wiki. It will open in a new window. '>
<SrcCode>{0}</SrcCode>
<MessageType></MessageType>
</ns0:Root>", customerCode));
//retreive source code in case in our cache dictionary
if (cachedSources.ContainsKey(customerCode))
{
messageType = cachedSources[customerCode];
}
else
{
TypedXmlDocument typedXmlDocument = new TypedXmlDocument("TechNetWiki.SchemaResolver.Schemas.SchemaResolverBRE", xmlDoc);
Microsoft.RuleEngine.Policy policy = new Microsoft.RuleEngine.Policy("SchemaResolverPolicy");
policy.Execute(typedXmlDocument);
So the matching rule is based on the 1st 4 characters of the filename. If one isn't matched, the probe returns a false - i.e. unrecognised.
The final part is that the message type is pushed into the returned message - this is made up of the namespace and the root schema node with a # separator - so your #src1 is the root node.
You need to implement IProbeMessage near to class
I forgot to add IProbeMessage in the code of article. It is updated now.
but it is there in sample source code
Src1 is the the root node name of schema. I mentioned that in article that message type is TargetNamespace#Root
I recommend to download the sample code
I hope this will help you

Visual Studio Custom Language Service

I am attempting to implement a Language Service in a VSPackage using the MPF, and it's not working quite as I understand it should.
I have several implementations already, such as ParseSource parsing the input file with a ParseRequest. However, when it finds an error, it adds it with AuthoringSink.AddError. The documentation for this implies it adds it to the Error List for me; it doesn't.
I also have a simple MySource class, a subclass of Source. I return this new class with an overridden LanguageService.CreateSource method. The documentation for OnCommand says it's fired 'when a command is entered'. However, it's not.
There's obviously some intermediate step which I haven't done correctly. I've already rambled enough, so I'll be glad to give any additional details by request.
Any clarification is much appreciated.
For the AuthoringSink error list question, I use this behavior in my Language Service. In ParseSource, the ParseRequest class has an AuthoringSink. You can also create a new ErrorListProvider if you want to work outside of the parser's behavior. Here is some example code:
error_list = new ErrorListProvider(this.Site);
error_list.ProviderName = "MyLanguageService Errors";
error_list.ProviderGuid = new Guid(this.errorlistGUIDstring.);
}
ErrorTask task = new ErrorTask();
task.Document = filename;
task.CanDelete = true;
task.Category = TaskCategory.CodeSense;
task.Column = column;
task.Line = line;
task.Text = message;
task.ErrorCategory = TaskErrorCategory.Error;
task.Navigate += NavigateToParseError;
error_list.Tasks.Add(task);
I hope this was helpful.
OnCommand should be firing every time there is a command, in your MySource class you can do something like this (pulled from working code):
public override void OnCommand(IVsTextView textView, VsCommands2K command, char ch)
{
if (textView == null || this.LanguageService == null
|| !this.LanguageService.Preferences.EnableCodeSense)
return;
if (command == Microsoft.VisualStudio.VSConstants.VSStd2KCmdID.TYPECHAR)
{
if (char.IsLetterOrDigit(ch))
{
//do something cool
}
}
base.OnCommand(textView, command, ch);
}
If that doesn't work double check that CodeSense = true in your ProvideLanguageService attribute when you setup your LanguageService package. A whole lot of what is cool to do in the LanguageService requires these attributes to be correctly turned on. Some even give cool behaviors for free!
Another thing to be careful of is that some behaviors like colorizer don't function correctly in the hive in my experience. I don't think these were ones that gave me trouble, but I implemented these a couple of years ago so I'm mostly just looking back at old code.
AuthoringSink.AddError only adds errors to the error list if ParseRequest.Reason is ParseReason.Check. When your ParseSource function attempts to add errors while parsing for any other ParseReason, nothing will happen.
It's possible that your language service is never calling ParseSource with this ParseReason. As far as I know, the only way to get a ParseReason of Check (outside of manually calling BeginParse or ParseSource yourself) is to proffer your service with an idle timer.

Categories

Resources