I'm working on a custom dialog in my WPF application that will allow the user to select a folder anywhere in the network that the user can see. I'm using code that I adapted from this CodeProject article on enumerating network resources.
The code in the original article immediately enumerates all network resources as soon as the EnumerateServers object is instantiated, recursively calling itself whenever it finds a container, and adds each node found to an ArrayList. This is inefficient as:
You can't start enumerating anything until after the network tree has been fully traversed.
The enumeration can take a significant amount of time depending on network traffic & the number of nodes that the current user can see.
I'm displaying the network resources in a WPF TreeView control, so I only want to traverse the nodes that are the direct children of the node that the user has expanded.
With that in mind, I made some changes to the code. Specifically, I modified it so it throws exceptions when errors occur and removed the recursion. The code now looks like this:
[StructLayout( LayoutKind.Sequential )]
internal class NetResource {
public ResourceScope Scope = 0;
public ResourceType Type = 0;
public ResourceDisplayType DisplayType = 0;
public ResourceUsage Usage = 0;
public string LocalName = null;
public string RemoteName = null;
public string Comment = null;
public string Provider = null;
};
public enum ResourceDisplayType {
Generic,
Domain,
Server,
Share,
File,
Group,
Network,
Root,
ShareAdmin,
Directory,
Tree,
NdsContainer
};
public enum ResourceScope {
Connected = 1,
GlobalNet,
Remembered,
Recent,
Context
};
public enum ResourceType {
Any,
Disk,
Print,
Reserved
};
[Flags]
public enum ResourceUsage {
Connectible = 0x00000001,
Container = 0x00000002,
NoLocalDevice = 0x00000004,
Sibling = 0x00000008,
Attached = 0x00000010,
All = Connectible | Container | Attached,
};
public class Share {
public string Comment { get; private set; }
public ResourceDisplayType DisplayType { get; private set; }
public string Name { get; private set; }
public string Provider { get; private set; }
public ResourceScope Scope { get; private set; }
public ResourceType ShareType { get; private set; }
public ResourceUsage Usage { get; private set; }
public string UNCPath { get; private set; }
internal Share( NetResource netResource ) {
DisplayType = netResource.DisplayType;
Scope = netResource.Scope;
ShareType = netResource.Type;
Usage = netResource.Usage;
if ( !string.IsNullOrWhiteSpace( netResource.Comment ) ) {
char[] commentChars = new char[ netResource.Comment.Length ];
netResource.Comment.CopyTo( 0, commentChars, 0, netResource.Comment.Length );
Comment = new string( commentChars );
}
if ( !string.IsNullOrWhiteSpace( netResource.LocalName ) ) {
char[] localNameChars = new char[ netResource.LocalName.Length ];
netResource.LocalName.CopyTo( 0, localNameChars, 0, netResource.LocalName.Length );
Name = new string( localNameChars );
}
if ( !string.IsNullOrWhiteSpace( netResource.Provider ) ) {
char[] providerChars = new char[ netResource.Provider.Length ];
netResource.Provider.CopyTo( 0, providerChars, 0, netResource.Provider.Length );
Provider = new string( providerChars );
}
if ( !string.IsNullOrWhiteSpace( netResource.RemoteName ) ) {
char[] remoteNameChars = new char[ netResource.RemoteName.Length ];
netResource.RemoteName.CopyTo( 0, remoteNameChars, 0, netResource.RemoteName.Length );
UNCPath = new string( remoteNameChars );
}
}
}
public class ShareEnumerator : IEnumerable<Share> {
public string Comment { get; set; }
public ResourceDisplayType DisplayType { get; set; }
public string Provider { get; set; }
public string ResourceName { get; set; }
public string ResourcePath { get; set; }
public ResourceScope Scope { get; set; }
public ResourceType ShareType { get; set; }
public ResourceUsage Usage { get; set; }
public ShareEnumerator() { }
public ShareEnumerator( Share aShare ) {
Comment = aShare.Comment;
DisplayType = aShare.DisplayType;
Provider = aShare.Provider;
ResourceName = aShare.Name;
ResourcePath = aShare.UNCPath;
Scope = aShare.Scope;
ShareType = aShare.ShareType;
Usage = aShare.Usage;
}
public IEnumerator<Share> GetEnumerator() {
NetResource netResource = new NetResource {
Comment = this.Comment,
DisplayType = this.DisplayType,
LocalName = this.ResourceName,
Provider = this.Provider,
RemoteName = this.ResourcePath,
Scope = this.Scope,
Type = this.ShareType,
Usage = this.Usage
};
uint bufferSize = 16384;
IntPtr buffer = IntPtr.Zero;
uint cEntries = 1;
IntPtr handle = IntPtr.Zero;
ErrorCodes result;
try {
buffer = Marshal.AllocHGlobal( (int) bufferSize );
result = WNetOpenEnum( Scope, ShareType, Usage, netResource, out handle );
if ( result != ErrorCodes.NO_ERROR ) {
throw new InvalidOperationException( string.Format( "The call to WNetOpenEnum failed: the result code was {0:x}", (int) result ) );
}
try {
do {
result = WNetEnumResource( handle, ref cEntries, buffer, ref bufferSize );
if ( result == ErrorCodes.NO_ERROR ) {
// It was. Marshal the buffer into the NetResource object.
Marshal.PtrToStructure( buffer, netResource );
if ( netResource.DisplayType == DisplayType || netResource.DisplayType == ResourceDisplayType.Domain ) {
// We do. Convert it into a Share & enumerate it.
yield return new Share( netResource );
}
} else if ( result == ErrorCodes.ERROR_NO_MORE_ITEMS ) {
break;
} else {
throw new InvalidOperationException( string.Format( "The call to WNetEnumResource failed: the result code was {0:x}", (int) result ) );
}
} while ( result == ErrorCodes.NO_ERROR );
} finally {
WNetCloseEnum( (IntPtr) buffer );
}
} finally {
if ( buffer != IntPtr.Zero ) {
// VERY IMPORTANT! Deallocate the buffer to prevent memory leaks!!
Marshal.FreeHGlobal( buffer );
}
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return GetEnumerator();
}
private enum ErrorCodes {
NO_ERROR = 0,
ERROR_NO_MORE_ITEMS = 259
};
[DllImport( "Mpr.dll", EntryPoint = "WNetOpenEnumA", CallingConvention = CallingConvention.Winapi )]
private static extern ErrorCodes WNetOpenEnum( ResourceScope dwScope, ResourceType dwType, ResourceUsage dwUsage, NetResource p, out IntPtr lphEnum );
[DllImport( "Mpr.dll", EntryPoint = "WNetCloseEnum", CallingConvention = CallingConvention.Winapi )]
private static extern ErrorCodes WNetCloseEnum( IntPtr hEnum );
[DllImport( "Mpr.dll", EntryPoint = "WNetEnumResourceA", CallingConvention = CallingConvention.Winapi )]
private static extern ErrorCodes WNetEnumResource( IntPtr hEnum, ref uint lpcCount, IntPtr buffer, ref uint lpBufferSize );
}
}
When the user clicks on the "Entire Network" node in the TreeView for a network node, this code runs:
private void NetworkExpanded( object sender, RoutedEventArgs e ) {
if ( !ShareScanner.IsBusy ) {
OriginalCursor = LayoutRoot.Cursor;
LayoutRoot.Cursor = Mouse.OverrideCursor = Cursors.Wait;
TreeViewItem networkItem = sender as TreeViewItem;
if ( networkItem.Items.Count == 1 && networkItem.Items[ 0 ] == dummyNode ) {
networkItem.Items.Clear();
ShareScanner.RunWorkerAsync( new ShareScannerArgs( networkItem, networkItem.Tag as Share, ShareScannerTypes.Computers ) );
}
}
e.Handled = true;
}
Underneath the Entire Network node will be nodes for specific computers that the user can see. When they expand one of these nodes, this code is run:
private void NetworkComputerExpanded( object sender, RoutedEventArgs e ) {
if ( !ShareScanner.IsBusy ) {
OriginalCursor = LayoutRoot.Cursor;
LayoutRoot.Cursor = Mouse.OverrideCursor = Cursors.Wait;
TreeViewItem computerItem = sender as TreeViewItem;
if ( computerItem.Items.Count == 1 && computerItem.Items[ 0 ] == dummyNode ) {
computerItem.Items.Clear();
ShareScanner.RunWorkerAsync( new ShareScannerArgs( computerItem, computerItem.Tag as Share, ShareScannerTypes.Shares ) );
}
}
e.Handled = true;
}
Below the computer nodes there will be "Share" nodes. When they click on a "Share" node in the TreeView, this code runs:
private void NetworkShareExpanded( object sender, RoutedEventArgs e ) {
if ( !ShareScanner.IsBusy ) {
OriginalCursor = LayoutRoot.Cursor;
LayoutRoot.Cursor = Mouse.OverrideCursor = Cursors.Wait;
TreeViewItem shareItem = sender as TreeViewItem;
if ( shareItem.Items.Count == 1 && shareItem.Items[ 0 ] == dummyNode ) {
shareItem.Items.Clear();
ShareScanner.RunWorkerAsync( new ShareScannerArgs( shareItem, shareItem.Tag as Share, ShareScannerTypes.Folders ) );
}
}
e.Handled = true;
}
There will be nodes for individual folders below the shares, but I haven't written any code for that level yet as I'm trying to get the other levels to work.
As you can see from the code I've posted, I'm using a BackgroundWorker to actually do the work. The UI becomes unresponsive while the calls to WNetOpenEnum and WNetEnumResource execute. My UI has to remain responsive, so I use the BackgroundWorker to do the waiting & keep the UI responsive.
Here's the BackgroundWorker's DoWork event handler:
private void ShareScanner_DoWork( object sender, DoWorkEventArgs e ) {
BackgroundWorker worker = sender as BackgroundWorker;
ShareScannerArgs info = e.Argument as ShareScannerArgs;
ShareEnumerator enumerator = null;
switch ( info.WhatToScanFor ) {
case ShareScannerTypes.Computers:
enumerator = new ShareEnumerator {
DisplayType = ResourceDisplayType.Network, // Retrieve Servers only
Scope = ResourceScope.GlobalNet, // Retrieve only objects the user can see
ShareType = ResourceType.Disk, // Retrieve only Disk shares
Usage = ResourceUsage.All // Retrieve all Connectible, Container & Attached nodes.
};
break;
case ShareScannerTypes.Shares:
case ShareScannerTypes.Folders:
enumerator = new ShareEnumerator( info.ParentShare );
break;
default:
// Should never get here!
throw new InvalidOperationException( string.Format( "Unknown ShareScannerType: {0}", info.WhatToScanFor ) );
}
try {
foreach ( Share share in enumerator ) {
if ( worker.CancellationPending ) {
e.Cancel = true;
return;
}
worker.ReportProgress( 0, new NodeArgs( info.Parent, share, info.WhatToScanFor ) );
}
} catch ( Exception ) { }
}
Here's the code for the ProgressChanged event handler:
private void ShareScanner_ProgressChanged( object sender, ProgressChangedEventArgs e ) {
NodeArgs nodeArgs = e.UserState as NodeArgs;
Share parentShare = nodeArgs.Tag as Share;
TreeViewItem item = new TreeViewItem {
Header = parentShare.UNCPath,
Tag = nodeArgs.Tag
};
switch ( nodeArgs.NodeToBuild ) {
case ShareScannerTypes.Computers:
item.Items.Add( dummyNode );
item.Expanded += new RoutedEventHandler( NetworkComputerExpanded );
break;
case ShareScannerTypes.Shares:
item.Items.Add( dummyNode );
item.Expanded += new RoutedEventHandler( NetworkShareExpanded );
break;
case ShareScannerTypes.Folders:
break;
default:
// Should never get here!
throw new InvalidOperationException( string.Format( "Unknown ShareScannerType: : {0}", nodeArgs.NodeToBuild ) );
}
nodeArgs.Parent.Items.Add( item );
}
Finally, the code for the RunWorkerCompleted event:
private void ShareScanner_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e ) {
Mouse.OverrideCursor = null;
LayoutRoot.Cursor = OriginalCursor;
}
The code produces a tree with the 'Entire Network' node. When you expand it, I get entries for "Microsoft Terminal Services", "Microsoft Windows Network", and "Web Client Network". When I expand any of these, WNetOpenEnum fails, returning 57 as the result.
What am I doing wrong? The code looked pretty straight forward but obviously I've missed something.
I finally got my dialog working as I wanted, but not using any of the code posted in the original question. Instead, I replaced all of that code with code taken from two other articles on CodeProject. Here are the comments from the code in my application with the information about where the code came from:
The `ComputerEnumerator` class originated as the `NetworkBrowser.cs` class from the
"Retrieving a List of Network Computer Names Using C#" article, which can be found at:
http://www.codeproject.com/Articles/16113/Retreiving-a-list-of-network-computer-names-using
The ShareEnumerator class originated as the ServerEnum.cs class from the
"Network Shares and UNC paths" article, which can be found at:
http://www.codeproject.com/Articles/2939/Network-Shares-and-UNC-paths
The first class makes use of the NetServerEnum function to retrieve all of the computers available on the network, while the second class uses the NetShareEnum method to retrieve the shares available on a particular server. The code from the first article worked correctly as it was. There were a couple of issues with the code in the second article, though.
The biggest problem was in the DllImport for the NetApiBufferFree function. When my code called it in the debugger when I stepped over the call, the function must have errored or something because the debugger never regained control and the code in the method that restored the mouse cursor never ran. When I replaced the definition of that API with the one from the first article, everything worked.
The less serious problem is that the second article created an enum type called ShareType that was decorated with the [Flags] attribute. The problem with this is that the values for the enum are taken from the LMShares.h file and are sequential values, not powers of 2. Such an enum will not work as a set of flags. There should be no enum identifier with the value 3, for example, because that value should be formed by bit-wise ORing the enum values for 1 & 2 together. But the enum identifier for the IPC type, sure enough, is 3.
This problem would (and did) cause problems if you try to use the values in this enum to filter the shares returned. To fix this latter problem, I removed the [Flags] attribute from that type & do not use is with any bit-wise operations.
It is amazing how quickly these functions return values. I'm sure they're cached somewhere and they may take longer to return values in certain circumstances. But in any case I have the code working as I'd like it to work.
Related
What causes the Reply to be null? I am building a game via Unity3D.
The code works on my PC - ping successfully completes, and I can access the reply.RoundtripTime. But when building and running on iOS (iPhone), the reply remains null, even though the _respose.Cancelled is false and Error is null? The phone is connected to internet.
class MyPing{
public SNNI.Ping ping{ get; private set;} = null;
public Ip_Array ip{ get; private set;} = null;
public SNNI.PingCompletedEventArgs _response{ get; private set;} = null;
public bool _isDone{
get{ return _response != null; }
}
public MyPing( Ip_Array ip, int ms_timeout=1000 ){
this.ip = ip;
ping = new SNNI.Ping();
ping.PingCompleted += (object sender, SNNI.PingCompletedEventArgs r)=>{ _response = r; };
System.Net.IPAddress addr = new System.Net.IPAddress( ip.ip );
ping.SendAsync(addr, ms_timeout, null);
}
}
.
//somewhere else:
p = new MyPing( new Ip_Array("0.0.0.0") );//<--actual ip instead of this placeholder
.
//later, checking every frame of the game:
if(p._isDone == false){ continue; }
if(p._response.Cancelled || p._response.Error != null){ continue; }
else{
Debug.Log(p._response.reply.Status);//throws exception because .reply is somewhow 'null'
}
Interestingly there is an example on using PingCompletedEventArgs, specifically the DisplayReply() method. In there they check if reply==null but never explain why it might be null...
I am in my first steps towards creating a very basic structural analysis software using Visual C#.
I decided to make it console-based (no user interface). Therefore the only way to get user's input is through chars and strings.
Imagine the user wants to create a 2D bar element. She would need to specify an initial point, a final point and a name for that bar. I want the syntax to be like follows:
"CREATE bar NAMED (bar_name) FIRST (first_point) LAST (last_point)"
Where:
(bar_name) is the name of the bar, up to the user. Let (object_name)="bar_A" (string type).
(first_point) would be the initial point of the bar. Since we are creating a 2D bar, (first_point) should be a 1x2 vector that the user should enter between parenthesis. For example, (first_point)=(0,0)
(last_point) would be the final point of the bar. Same type and syntax as (first_point).
I am just wondering if there is any easy way to achieve the string comparison task, something like comparing the user's input against a prefabricated command.
Of course without forgetting about user's input cleaning task.
I know there is a huge amount of possible solutions here. Maybe using LINQ. Maybe just using the String object. I just want to know the most efficient way, where efficient means:
The fastest the user's query gets processed, the better;
the less the lines of codes, the better; and
where thorough query sanitizing tasks are made.
This last point is really important since some user's input like this:
"CREATE bar NAMED bar_a FISRT (0,0) LAST (0,1)"
Note that the user commited a typo (FISRT instead of FIRST), and the query shouldn't run.
Thanks
Okay, I created a simple parser that should work good for you and, if the need arises, you can easily expand.
Start off by creating a new Console Application. Add a new class file called Tokenizer.cs. This file was auto generated by my TokenIcer project that I linked to you in the comments above. Make Tokenizer.cs look like this:
public class TokenParser
{
private readonly Dictionary<Tokens, string> _tokens;
private readonly Dictionary<Tokens, MatchCollection> _regExMatchCollection;
private string _inputString;
private int _index;
public enum Tokens
{
UNDEFINED = 0,
CREATE = 1,
FIRST = 2,
LAST = 3,
BAR = 4,
NAMED = 5,
BAR_NAME = 6,
WHITESPACE = 7,
LPAREN = 8,
RPAREN = 9,
COMMA = 10,
NUMBER = 11
}
public string InputString
{
set
{
_inputString = value;
PrepareRegex();
}
}
public TokenParser()
{
_tokens = new Dictionary<Tokens, string>();
_regExMatchCollection = new Dictionary<Tokens, MatchCollection>();
_index = 0;
_inputString = string.Empty;
_tokens.Add(Tokens.CREATE, "[Cc][Rr][Ee][Aa][Tt][Ee]");
_tokens.Add(Tokens.FIRST, "[Ff][Ii][Rr][Ss][Tt]");
_tokens.Add(Tokens.LAST, "[Ll][Aa][Ss][Tt]");
_tokens.Add(Tokens.BAR, "[Bb][Aa][Rr][ \\t]");
_tokens.Add(Tokens.NAMED, "[Nn][Aa][Mm][Ee][Dd]");
_tokens.Add(Tokens.BAR_NAME, "[A-Za-z_][a-zA-Z0-9_]*");
_tokens.Add(Tokens.WHITESPACE, "[ \\t]+");
_tokens.Add(Tokens.LPAREN, "\\(");
_tokens.Add(Tokens.RPAREN, "\\)");
_tokens.Add(Tokens.COMMA, "\\,");
_tokens.Add(Tokens.NUMBER, "[0-9]+");
}
private void PrepareRegex()
{
_regExMatchCollection.Clear();
foreach (KeyValuePair<Tokens, string> pair in _tokens)
{
_regExMatchCollection.Add(pair.Key, Regex.Matches(_inputString, pair.Value));
}
}
public void ResetParser()
{
_index = 0;
_inputString = string.Empty;
_regExMatchCollection.Clear();
}
public Token GetToken()
{
if (_index >= _inputString.Length)
return null;
foreach (KeyValuePair<Tokens, MatchCollection> pair in _regExMatchCollection)
{
foreach (Match match in pair.Value)
{
if (match.Index == _index)
{
_index += match.Length;
return new Token(pair.Key, match.Value);
}
if (match.Index > _index)
{
break;
}
}
}
_index++;
return new Token(Tokens.UNDEFINED, string.Empty);
}
public PeekToken Peek()
{
return Peek(new PeekToken(_index, new Token(Tokens.UNDEFINED, string.Empty)));
}
public PeekToken Peek(PeekToken peekToken)
{
int oldIndex = _index;
_index = peekToken.TokenIndex;
if (_index >= _inputString.Length)
{
_index = oldIndex;
return null;
}
foreach (KeyValuePair<Tokens, string> pair in _tokens)
{
var r = new Regex(pair.Value);
Match m = r.Match(_inputString, _index);
if (m.Success && m.Index == _index)
{
_index += m.Length;
var pt = new PeekToken(_index, new Token(pair.Key, m.Value));
_index = oldIndex;
return pt;
}
}
var pt2 = new PeekToken(_index + 1, new Token(Tokens.UNDEFINED, string.Empty));
_index = oldIndex;
return pt2;
}
}
public class PeekToken
{
public int TokenIndex { get; set; }
public Token TokenPeek { get; set; }
public PeekToken(int index, Token value)
{
TokenIndex = index;
TokenPeek = value;
}
}
public class Token
{
public TokenParser.Tokens TokenName { get; set; }
public string TokenValue { get; set; }
public Token(TokenParser.Tokens name, string value)
{
TokenName = name;
TokenValue = value;
}
}
In Program.cs, make it look like this:
class Program
{
private class Bar
{
public string Name { get; set; }
public int FirstX { get; set; }
public int FirstY { get; set; }
public int LastX { get; set; }
public int LastY { get; set; }
}
static void Main(string[] args)
{
const string commandCreateBar1 = "CREATE bar NAMED bar_a FIRST(5,10) LAST (15,20)";
const string commandCreateBar2 = "CREATE bar NAMED MyFooBar FIRST(25 , 31) LAST (153 ,210)";
const string commandCreateBar3 = "CREATE bar NAMED MySpaceyFooBar FIRST(0,0) LAST (12,39)";
Bar bar1 = ParseCreateBar(commandCreateBar1);
PrintBar(bar1);
Bar bar2 = ParseCreateBar(commandCreateBar2);
PrintBar(bar2);
Bar bar3 = ParseCreateBar(commandCreateBar3);
PrintBar(bar3);
}
private static void PrintBar(Bar bar)
{
Console.WriteLine("A new bar was Created! \"{0}\" ({1}, {2}) ({3}, {4})", bar.Name, bar.FirstX, bar.FirstY, bar.LastX, bar.LastY);
}
private static Bar ParseCreateBar(string commandLine)
{
var bar = new Bar();
var parser = new TokenParser { InputString = commandLine };
Expect(parser, TokenParser.Tokens.CREATE);
Expect(parser, TokenParser.Tokens.BAR);
Expect(parser, TokenParser.Tokens.NAMED);
Token token = Expect(parser, TokenParser.Tokens.BAR_NAME);
bar.Name = token.TokenValue;
Expect(parser, TokenParser.Tokens.FIRST);
Expect(parser, TokenParser.Tokens.LPAREN);
token = Expect(parser, TokenParser.Tokens.NUMBER);
bar.FirstX = int.Parse(token.TokenValue);
Expect(parser, TokenParser.Tokens.COMMA);
token = Expect(parser, TokenParser.Tokens.NUMBER);
bar.FirstY = int.Parse(token.TokenValue);
Expect(parser, TokenParser.Tokens.RPAREN);
Expect(parser, TokenParser.Tokens.LAST);
Expect(parser, TokenParser.Tokens.LPAREN);
token = Expect(parser, TokenParser.Tokens.NUMBER);
bar.LastX = int.Parse(token.TokenValue);
Expect(parser, TokenParser.Tokens.COMMA);
token = Expect(parser, TokenParser.Tokens.NUMBER);
bar.LastY = int.Parse(token.TokenValue);
Expect(parser, TokenParser.Tokens.RPAREN);
return bar;
}
private static Token Expect(TokenParser parser, TokenParser.Tokens expectedToken)
{
EatWhiteSpace(parser);
Token token = parser.GetToken();
if (token != null && token.TokenName != expectedToken)
{
Console.WriteLine("Expected Token " + expectedToken);
Environment.Exit(0);
}
if (token == null)
{
Console.WriteLine("Unexpected end of input!");
Environment.Exit(0);
}
return token;
}
private static void EatWhiteSpace(TokenParser parser)
{
while (parser.Peek() != null && parser.Peek().TokenPeek != null &&
parser.Peek().TokenPeek.TokenName == TokenParser.Tokens.WHITESPACE)
{
parser.GetToken();
}
}
}
As you can see, I created 3 test scenarios. Notice all white space is ignored. If you want to be strict about the white space, you can modify the EatWhiteSpace function to be strict.
If you want, I have a simple expression parser I could throw into this code too, that way you could have commands such as CREATE bar NAMED bar_a FIRST(3+2, 7*8 + 12) LAST (150-100, 12-3*2). I've got a simple expression parser I made a while back using TokenIcer that I can throw in. It can parse any math expression and supports parenthesis, add, subtract, multiply, and divide.
Tokenization is one way to go, but if you aren't planning on supporting way too many commands and parameters, you should look at Regexes.
Regex regex = new Regex(#"^CREATE bar NAMED (?<BarName>[A-Za-z0-9-_]*) FIRST (?<FirstPoint>\([0-9]+\|[0-9]+\)) LAST (?<LastPoint>\([0-9]+\|[0-9]+\)$");
Match match = regex.Match("create bar named bar_a first (0,0) last (0,1)", RegexOptions.IgnoreCase);
if (match.Success)
{
var name = match.Groups["BarName"].Value;
// and so on for other matches
}
When I run my c# console app the windows firewall pops up requesting access for vshost32 (my app listens for incomming messages over port 1234 using TCP and UDP). I accept the offered suggestion (private network). The console app then works fine.
I dont want the user to deal with this, so I have added the code below. However when I investigate what this has done in Control Panel > Firewall, it seems to have enabled it for the 'public network' rather than the private network. This is no use as far as allowing my app to work.
Is there an adjustment in the code below to force it to the private network?
INetFwOpenPorts ports3;
INetFwOpenPort port3 = (INetFwOpenPort)Activator.CreateInstance(
Type.GetTypeFromProgID("HNetCfg.FWOpenPort"));
port3.Port = 1234;
port3.Name = "vshost32.exe";
port3.Enabled = true;
//**UPDATE** added for suggestion in answer below - still doesnt change anything though
port3.Scope = NetFwTypeLib.NET_FW_SCOPE_.NET_FW_SCOPE_LOCAL_SUBNET;
Type NetFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false);
INetFwMgr mgr3 = (INetFwMgr)Activator.CreateInstance(NetFwMgrType);
ports3 = (INetFwOpenPorts)mgr3.LocalPolicy.CurrentProfile.GloballyOpenPorts;
ports3.Add(port3);
INetFwRule firewallRule = (INetFwRule)Activator.CreateInstance(
Type.GetTypeFromProgID("HNetCfg.FWRule"));
INetFwPolicy2 firewallPolicy = (INetFwPolicy2)Activator.CreateInstance(
Type.GetTypeFromProgID("HNetCfg.FwPolicy2"));
firewallRule.ApplicationName = "<path to your app>";
firewallRule.Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW;
firewallRule.Description = " My Windows Firewall Rule";
firewallRule.Enabled = true;
firewallRule.InterfaceTypes = "All";
firewallRule.Name = "<your rule name>";
// Should really check that rule is not already present before add in
firewallPolicy.Rules.Add(firewallRule);
Refer to my answer to your previous question.
Have a look at the following lines:
private static int Main (string [] args)
{
var application = new NetFwAuthorizedApplication()
{
Name = "MyService",
Enabled = true,
RemoteAddresses = "*",
Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL,
IpVersion = NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY,
ProcessImageFileName = "ServiceAssemblyName.dll",
};
return (FirewallUtilities.AddApplication(application, out exception) ? 0 : -1);
}
The NET_FW_SCOPE_ enumeration has the following values:
NET_FW_SCOPE_ALL = 0,
NET_FW_SCOPE_LOCAL_SUBNET = 1,
NET_FW_SCOPE_CUSTOM = 2,
NET_FW_SCOPE_MAX = 3,
You can further limit the ports, protocol as well as remote addresses to the rule.
UPDATE:
Here is the missing ReleaseComObject function. Place it whatever namespace and remove the reference to ComUtilities.
public static void ReleaseComObject (object o)
{
try
{
if (o != null)
{
if (Marshal.IsComObject(o))
{
Marshal.ReleaseComObject(o);
}
}
}
finally
{
o = null;
}
}
Here is the NetFwAuthorizedApplication class:
namespace MySolution.Configurator.Firewall
{
using System;
using System.Linq;
using NetFwTypeLib;
public sealed class NetFwAuthorizedApplication:
INetFwAuthorizedApplication
{
public string Name { get; set; }
public bool Enabled { get; set; }
public NET_FW_SCOPE_ Scope { get; set; }
public string RemoteAddresses { get; set; }
public string ProcessImageFileName { get; set; }
public NET_FW_IP_VERSION_ IpVersion { get; set; }
public NetFwAuthorizedApplication ()
{
this.Name = "";
this.Enabled = false;
this.RemoteAddresses = "";
this.ProcessImageFileName = "";
this.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL;
this.IpVersion = NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY;
}
public NetFwAuthorizedApplication (string name, bool enabled, string remoteAddresses, NET_FW_SCOPE_ scope, NET_FW_IP_VERSION_ ipVersion, string processImageFileName)
{
this.Name = name;
this.Scope = scope;
this.Enabled = enabled;
this.IpVersion = ipVersion;
this.RemoteAddresses = remoteAddresses;
this.ProcessImageFileName = processImageFileName;
}
public static NetFwAuthorizedApplication FromINetFwAuthorizedApplication (INetFwAuthorizedApplication application)
{
return (new NetFwAuthorizedApplication(application.Name, application.Enabled, application.RemoteAddresses, application.Scope, application.IpVersion, application.ProcessImageFileName));
}
}
}
i want write some setting to ini file with this code that search to find key and update it and if can't find the key add it to file . but it show this error :
"Object reference not set to an instance of an object."
i try this code :
internal class IniData
{
public string Key;
public string Value;
}
internal class IniSection : Dictionary<string, IniData>
{
public string Name { get; set; }
}
internal class IniFile : Dictionary<string, IniSection>
{
public string Path { get; set; }
}
public sealed class IniManager
{
private static readonly Dictionary<string, IniFile> IniFiles;
static IniManager()
{
IniFiles = new Dictionary<string, IniFile>();
}
public static void WriteIni(string fileName, string section, string key, string value)
{
/* Check if ini file exists in the ini collection */
var fileKey = fileName.ToLower();
if (!IniFiles.ContainsKey(fileKey))
{
if (!ImportIni(fileKey))
{
/* Add a new blank file */
var ini = new IniFile { Path = fileName };
IniFiles.Add(fileKey, ini);
}
}
/* Find section */
if (IniFiles[fileKey].ContainsKey(section.ToLower()))
{
/* Find key, if exists replace it */
if (IniFiles[fileKey][section.ToLower()].ContainsKey(key.ToLower()))
{
IniFiles[fileKey][section.ToLower()][key.ToLower()].Value = value;
return;
}
var data = new IniData { Key = key, Value = value };
IniFiles[fileKey][section.ToLower()].Add(key.ToLower(), data);
}
else
{
/* Create new ini section */
var sec = new IniSection { Name = section };
var data = new IniData { Key = key, Value = value };
sec.Add(key.ToLower(), data);
IniFiles[fileKey].Add(section.ToLower(), sec);
}
}
private static bool ImportIni(string fileName)
{
if (!File.Exists(fileName)) { return false; }
string[] data;
try
{
using (var stream = new FileStream(fileName, FileMode.Open))
{
using (var reader = new StreamReader(stream))
{
data = reader.ReadToEnd().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
reader.Close();
}
stream.Close();
}
}
catch (Exception) { return false; }
if (data.Length == 0) { return false; }
var file = new IniFile { Path = fileName };
var section = new IniSection();
foreach (var s in data)
{
if (s.StartsWith("[") && s.EndsWith("]"))
{
/* Section header */
if (section.Count > 0)
{
/* Add current section */
file.Add(section.Name.ToLower(), section);
}
section = new IniSection { Name = s.Replace("[", null).Replace("]", null) };
continue;
}
/* Using current section, parse ini keys/values */
var iniData = ParseIni(s);
section.Add(iniData.Key.ToLower(), iniData);
}
if (section.Count > 0)
{
/* Add current section */
//##################Erorr : Object reference not set to an instance of an object.
file.Add(section.Name.ToLower(), section);
}
IniFiles.Add(fileName, file);
return true;
}
private static IniData ParseIni(string s)
{
var parts = s.Split('=');
return new IniData { Key = parts[0].Trim(), Value = parts.Length > 1 ? parts[1].Trim() : string.Empty };
}
}
private void button9_Click(object sender, EventArgs e)
{
IniManager.WriteIni("seting.ini", "Sec", "key", "value");
}
Instead of implementing this yourself you should just use the API functions that Windows provide. Of course, if you need to run this on Mono or other platforms than Windows, you need to go back to a pure .NET implementation, but even so I would probably go look for an existing implementation instead of creating that wheel yourself.
Anywhere, here's the API functions:
GetPrivateProfileString
WritePrivateProfileString
Here's an example LINQPad program that uses them:
(hit F4 and paste the following two lines into the additional namespace tab):
System.Runtime.InteropServices
System.ComponentModel
Then try this program:
void Main()
{
var ini = new IniFile(#"d:\temp\test.ini");
ini.WriteValue("Section", "Key", "Value");
ini.ReadValue("Section", "Key").Dump();
ini["Main", "Key2"] = "Test";
ini["Main", "Key2"].Dump();
}
[DllImport("kernel32.dll", CharSet=CharSet.Unicode)]
static extern uint GetPrivateProfileString(string lpAppName, string lpKeyName,string lpDefault, StringBuilder lpReturnedString, uint nSize,string lpFileName);
[DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool WritePrivateProfileString(string lpAppName, string lpKeyName, string lpString, string lpFileName);
public class IniFile
{
const int MAX_SIZE = 1024;
private readonly string _FilePath;
public IniFile(string filePath)
{
if (filePath == null)
throw new ArgumentNullException("filePath");
_FilePath = filePath;
}
public string this[string section, string key]
{
get
{
return ReadValue(section, key);
}
set
{
WriteValue(section, key, value);
}
}
public string ReadValue(string section, string key, string defaultValue = null)
{
var result = new StringBuilder(MAX_SIZE);
if (GetPrivateProfileString(section, key, defaultValue ?? string.Empty, result, (uint)result.Capacity, _FilePath) > 0)
return result.ToString();
throw new Win32Exception();
}
public void WriteValue(string section, string key, string value)
{
if (!WritePrivateProfileString(section, key, value, _FilePath))
throw new Win32Exception();
}
}
The problem here is that if the file starts with a key and not with a section, the foreach doesn't match the if (s.StartsWith("[") && s.EndsWith("]")) at all and so the Section.Name is never set, thus it is null when called in file.Add(section.Name.ToLower(), section);
BTW: your code seems quite buggy, try to redesign it at least in the main foreach of ImportIni
I'll explain but codes can explain it way better than me.
I'm trying to get value of 2 variables
I create a new object at it's class and use its members which I set their values from packet.
Whenever I try to access them I find them zeros which kind of doesn't make sense to me.
Here is the important part of the code that is related to the problem
public class PacketHandler
{
.
. dots means unrelated long code
.
public PacketHandler()
{
}
public ushort actualX { get; set; }
public ushort actualY { get; set; }
.
.
.
case 10010:
{
if (BitConverter.ToUInt16(data, 8) == 1002)
{
actualX = BitConverter.ToUInt16(data, 24);
actualY = BitConverter.ToUInt16(data, 26);
}
break;
and here is the other class I'm trying to get the value at
public class ClientBase
{
GameUser role2;
PacketHandler ph = new PacketHandler(); <<<<<<<<
public ClientBase(GameUser role)
{
role2 = role;
Thread T = new Thread(HuntThread) { Name = "Hunt Thread" };
T.Start(this);
Console.WriteLine("with name : " +T.Name + " with execution context : "+T.ExecutionContext +" with state : "+ T.IsAlive + " with id : " + T.ManagedThreadId);
T.Join();
}
Monster GetNextkill()
{
Monster CurrentTarget = null;
foreach (KeyValuePair<uint, Monster> Pair in Dictionary.Clone(Dictionary.Monsters))
{
CurrentTarget = Pair.Value;
}
return CurrentTarget;
}
public void HuntThread(object Sender)
{
try
{
ClientBase Client = Sender as ClientBase;
while (true)
{
Monster Target = GetNextkill();
if (Target != null)
{
Thread.Sleep(1000);
ProxyParadise.Network.Packets.PacketStructure ps = new ProxyParadise.Network.Packets.PacketStructure();
ps.__Packet10010(Target.X, Target.Y, role2, "normal", "server", ph.actualX, ph.actualY); <<<<< i get zeros at the ph.actualX/Y
Thread.Sleep(1000);
ps.__Packet10010(Target.X, Target.Y, role2, "sdasdsa", "client", role2.X, role2.Y);
Thread.Sleep(1000);
ps.__Packet1022NONEXP(Target.UID, Target.X , Target.Y, role2);
Dictionary.Monsters.SafeRemove(Target.UID);
please forgive me if I sound stupid, and to make it more clear ,I want a proper way of transferring data through classes at a new thread.
Please try to apply it to this example for better understanding.
have a wonderful day