|
Hi javier,
thanks for the info.
I can call any method I want of my remote object through its transparent proxy without any problems.
The problem is that I want to localize in one piece of code all the exception handling of the SocketException that gets thrown when the server where the remote object lives is not accessible to the client app that calls one if its methods.
There r two ways around it that I could find right now:
1) Implement the exception handling on all remote calls throughout my code.
2) Create a SingleCallManager object with a generic RunMethod method that will dinamycally link any method calls to the remoteobject (loss of performance is not an issue) dynamically invoking the call to its proxy like you would with any other object. The problem is, that the same piece of code will link perfectly to a dummy non remote class but it will not link to the remote object throwing the exception i've included in the reply to Heath's post. Through this RunMethod method I would be able to localize all excpetion handling of any method call due to a connection failure to the server in one place.
|
|
|
|
|
Hi Skynyrd,
I think I solved your problem or at least came up with an example for you to see. I created a simple remote object with a client and server to host it. Then, I created a delegate so I could call the remote object's method via that delegate. This is what I came up with:
WARNING THIS IS A LONG POST
RemoteObject.cs
using System;
namespace RemoteNamespace
{
public class RemoteObject : MarshalByRefObject
{
public string MyTypeString()
{
return this.GetType().ToString();
}
}
}
Server.cs
using System;
using System.Runtime.Remoting;
namespace RemoteServer
{
class Server
{
[STAThread]
static void Main(string[] args)
{
RemotingConfiguration.Configure("RemoteServer.exe.config");
Console.WriteLine("Hit <ENTER> to exit.");
Console.ReadLine();
}
}
}
Client.cs
using System;
using System.Runtime.Remoting;
using System.Reflection;
using RemoteNamespace;
namespace RemoteTest
{
public delegate string del_void();
class Client
{
[STAThread]
static void Main(string[] args)
{
RemotingConfiguration.Configure("RemoteTest.exe.config");
RemoteObject ro = new RemoteObject();
MethodInfo m = ro.GetType().GetMethod("MyTypeString",BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod);
try
{
Delegate d = Delegate.CreateDelegate(typeof(del_void),ro,m.Name);
object o = d.DynamicInvoke(null);
Console.WriteLine(o);
}
catch(TargetInvocationException tie)
{
Console.WriteLine(tie.StackTrace);
}
catch(Exception e)
{
Console.WriteLine(e.StackTrace);
}
Console.ReadLine();
}
}
}
Server.exe.config -- For remoting object config
<configuration>
<system.runtime.remoting>
<application>
<service>
<wellknown
mode="SingleCall" objectUri="RemoteObject.rem"
type="RemoteNamespace.RemoteObject, RemoteLibrary"/>
</service>
<channels>
<channel ref="tcp" port="8080"/>
</channels>
</application>
</system.runtime.remoting>
</configuration>
Client.exe.config -- For remote object config
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="tcp" port="0">
<clientProviders>
<formatter
ref="binary"
/>
</clientProviders>
</channel>
</channels>
<client>
<wellknown
url="tcp://localhost:8080/RemoteObject.rem"
type="RemoteNamespace.RemoteObject, RemoteLibrary"
/>
</client>
</application>
</system.runtime.remoting>
</configuration>
The key for this to work was to use the following line:
MethodInfo m = ro.GetType().GetMethod("MyTypeString",BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod);
For some reason when you passed the full name of the method, the object couldn't bind. However, I just tried this, if you pass the name of the method MyTypeString instead of m.Name into the CreateDelegate method, it also works.
Take a look at this code and tell me what you think.
~javier lozano
(blog)
|
|
|
|
|
Skynyrd wrote:
The problem is that I want to localize in one piece of code all the exception handling of the SocketException that gets thrown when the server where the remote object lives is not accessible to the client app that calls one if its methods.
Could you elaborate a little more? No offense, but the grammar is getting in the way of the problem, specifically "thrown when the server where the remote object lives". If I'm correct, you're trying to say that you want to localize the exception message that the server throws, and you want to localize it on the client?
If so, just catch that exception type and display a localized error message to the user, or if this is for a library (never lets your users see exceptions - there's simply no excuse for that in an application, but in a library it's common since your library gets used by applications) localize the text and throw a new exception with the original exception as the InnerException .
Sorry if I'm wrong, but like I said I have problems parsing and understanding your sentance. Again, no offense meant. It's usually not a problem but in this case - in order to hopefully help you with the true problem - I need to be sure I understand you correctly.
This posting is provided "AS IS" with no warranties, and confers no rights.
Software Design Engineer
Developer Division Sustained Engineering
Microsoft
[My Articles] [My Blog]
|
|
|
|
|
Hi Skynyrd,
I think I solved your problem or at least came up with an example for you to see. I created a simple remote object with a client and server to host it. Then, I created a delegate so I could call the remote object's method via that delegate. This is what I came up with:
WARNING THIS IS A LONG POST
RemoteObject.cs
using System;
namespace RemoteNamespace
{
public class RemoteObject : MarshalByRefObject
{
public string MyTypeString()
{
return this.GetType().ToString();
}
}
}
Server.cs
using System;
using System.Runtime.Remoting;
namespace RemoteServer
{
class Server
{
[STAThread]
static void Main(string[] args)
{
RemotingConfiguration.Configure("RemoteServer.exe.config");
Console.WriteLine("Hit <ENTER> to exit.");
Console.ReadLine();
}
}
}
Client.cs
using System;
using System.Runtime.Remoting;
using System.Reflection;
using RemoteNamespace;
namespace RemoteTest
{
public delegate string del_void();
class Client
{
[STAThread]
static void Main(string[] args)
{
RemotingConfiguration.Configure("RemoteTest.exe.config");
RemoteObject ro = new RemoteObject();
MethodInfo m = ro.GetType().GetMethod("MyTypeString",BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod);
try
{
Delegate d = Delegate.CreateDelegate(typeof(del_void),ro,m.Name);
object o = d.DynamicInvoke(null);
Console.WriteLine(o);
}
catch(TargetInvocationException tie)
{
Console.WriteLine(tie.StackTrace);
}
catch(Exception e)
{
Console.WriteLine(e.StackTrace);
}
Console.ReadLine();
}
}
}
Server.exe.config -- For remoting object config
<configuration>
<system.runtime.remoting>
<application>
<service>
<wellknown
mode="SingleCall" objectUri="RemoteObject.rem"
type="RemoteNamespace.RemoteObject, RemoteLibrary"/>
</service>
<channels>
<channel ref="tcp" port="8080"/>
</channels>
</application>
</system.runtime.remoting>
</configuration>
Client.exe.config -- For remote object config
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="tcp" port="0">
<clientProviders>
<formatter
ref="binary"
/>
</clientProviders>
</channel>
</channels>
<client>
<wellknown
url="tcp://localhost:8080/RemoteObject.rem"
type="RemoteNamespace.RemoteObject, RemoteLibrary"
/>
</client>
</application>
</system.runtime.remoting>
</configuration>
The key for this to work was to use the following line:
MethodInfo m = ro.GetType().GetMethod("MyTypeString",BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod);
For some reason when you passed the full name of the method, the object couldn't bind. However, I just tried this, if you pass the name of the method MyTypeString instead of m.Name into the CreateDelegate method, it also works.
Take a look at this code and tell me what you think.
~javier lozano
(blog)
|
|
|
|
|
Javier, thanks a lot for your help. I'm gonna give it a try right now and will let you know how it turns out.
Funny that u can succesfully link if using the mehtodinfo but not through the method's name.
Trying it right away.
Heath, I'm sorry if I'm not explaining myself too well, english isnt my native language :p.
What I'm trying to do is localize all the exception handling of a failed connection to the remote server in one place inside my client app (the singlecallmanager class), and not in all the calls to remote object's different methods throughout my client app code.
Still Javier seems to have solved the problem. Thanks for your time.
|
|
|
|
|
Sorry to say but ur code did not work Javier
Still, I've figured out what the problem is. Maybe it was obvious and I've been wasting ur time:
I can't create the proxy using the public interface of my remote object:
object proxy=RemotingServices.Connect(typeof(IDataToolLayer),myUrl.AbsoluteUri);
DynamicInvoke fails.
However, if I create the proxy using the remote object class, everything works ok:
object proxy=RemotingServices.Connect(typeof(DataToolLayer),myUrl.AbsoluteUri);
The question is, is there a way around this? I dont want to have to include the remote library in my client app, it shouldn't be necessary.
Another interesting thing, is that if I try to get the MethodInfo as Javier does in his example, I get a null return if I'm using the IDataLayerTool interface.
|
|
|
|
|
|
Ok Javier, got it!
I've discoverd one hell of a class in the Reflexion namespace that has solved all my problems: TypeDelegator
public object RunMethod(string methodName, object[] arguments)
{
object proxy=GetProxy();
TypeDelegator delegator=new TypeDelegator(typeof(IDataLayerTool));
return delegator.GetMethod(methodName).Invoke(proxy,arguments);
}
It seems that the TypeDelegator can map correctly through interfaces and even makes the job a lot easier as u dont have to create delegates as u would with a DynamicInvoke.
Nice little class, yessir
Again, thanks for your time Javier.
|
|
|
|
|
Does anybody know of a control that behaves like the RichTextBox control of .NET but uses HTML formating instead?
Thanks in advance,
Eric
|
|
|
|
|
And I should add that it needs to be Forms based not Web based.
Thanks again
Eric
|
|
|
|
|
The WebBrowser control (what actually is "Internet Explorer") itself does. Customize your toolbox in VS.NET to include the "Microsoft WebBrowser Control" and click OK (this is under the COM tab, not the .NET tab).
Drag and drop that onto your form. That'll create a couple of RCWs (runtime-callable wrappers) that encapsulate the COM control for marshaling to and from managed code. Then add a reference to the Microsoft.mshtml.dll assembly that should've been installed with VS.NET, IIRC.
Cast the AxWebBrowser.Document property to IHTMLDocument2 and set the designMode property to "On" (case-sensitive).
If you google for "WebBrowser designMode" you should find many articles that discuss more advanced interaction and persisting the document.
There's also a lot of commercial controls that extend the capabilities for managed code, like the NetRix HtmlEditor[^] that I evaluated (along with others) and choose for my former employer. It's very good and priced pretty decently. It extends the basic authoring that the WebBrowser control provides with advanced features like tables and more. You can accomplish this all yourself if you read about the hosting interfaces and more for Internet Explorer. Read Programming and Reusing the Browser[^] on MSDN. Knowledge of OLE/COM is very helpful, especially about Interoperating with Unmanaged Code[^].
There are also editors that don't use the WebBrowser control and rely entirely on their own HTML generation and user interaction, like the Writer for .NET[^], from the same guy that wrote .NET Reflector.
This posting is provided "AS IS" with no warranties, and confers no rights.
Software Design Engineer
Developer Division Sustained Engineering
Microsoft
[My Articles] [My Blog]
|
|
|
|
|
I've found a PING utility online for C#. Looks fine at first glance, and i disected it a bit to see how it worked and tried to run it. However, i get stuck at line 5 of the PingHost method.
When trying to create a socket, it throws an error in the following line:
Socket socket =
new Socket(AddressFamily.AfINet, SocketType.SockRaw, ProtocolType.ProtICMP);
This doesn't work as none of the enumeration members are correct, but I've tried the following, which look to match properly, along with a few dozen other configurations.
socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
Every time I try and declare the socket though, it throws an error from the windows native API code.
Is this an issue with Win2k?
When I get home, i'll try it with XP and see if that works.
Cheers for the input
Tris
|
|
|
|
|
Whether or not code compiles has nothing to do with the platform on which you're compiling - only with the version of the base class library (BCL) or any other assemblies that you're compiling against.
The AddressFamily.AfiNet member has never been defined for .NET 1.x and is not currently defined for 2.0.
Without the example type and message, however, it's difficult to help you diagnose your problem. Please be specific and verbose about the exception that was thrown.
This posting is provided "AS IS" with no warranties, and confers no rights.
Software Design Engineer
Developer Division Sustained Engineering
Microsoft
[My Articles] [My Blog]
|
|
|
|
|
It's a run time error.
It returns a value of 2 from the native windows API.
Works fine on my XP machine from home.
At work, i'm running Win2k Professional SP3 (Can't upgrade to 4)
As I said, none of the origional enumeration members exist in any of the .net builds. So I tested others.
I even changed the socket type from RAW. Win2k just refuses to construct the socket.
Kinda stumpped on this one.
Cheers
Tris
|
|
|
|
|
|
It still throws a runtime error, even if I don't use RAW sockets.
I'm using SP3 though, and i'm unable to upgrade.
If you could offer any advice on what to do, I would be most greatful.
Cheers
Tris
|
|
|
|
|
And as I asked for before, what is the exception type and what does the exception message read?
This posting is provided "AS IS" with no warranties, and confers no rights.
Software Design Engineer
Developer Division Sustained Engineering
Microsoft
[My Articles] [My Blog]
|
|
|
|
|
I'm really bored at work.... and thought i'd download and run the .NET enviroment. Unfortunately, I have no SDK to work in.
I can't really install VS.net, as it's a big company I work for, and the computer we have is capped at an 8 gig HD which is almost 60% full.
I've done a few google searches and such, but i've had no joy finding any SDE's.
Could someone recomend one please.
I would be eternaly greatful.
Cheers
Tris
|
|
|
|
|
|
What a great opportunity to really test your skills: no IDE. You can download the SDK from http://msdn.microsoft.com/netframework/[^]. Add both the Framework version directory (ex: %WINDIR%\Microsoft.NET\Framework\v1.1.4322) and the SDK bin directory (ex: %ProgramFiles%\Microsoft.NET\SDK\v1.1\bin) to your PATH environment variable (right-click on "My Computer", select Properties, then click the "Advanced" tab to find the "Environment Variables" button, or just use set PATH=<those two directories above>;%PATH% on the command-line for only that console window).
Type your source code files in your favorite text editor and compile with csc.exe that is installed with the Framework (not the SDK; actually, you don't even need the SDK but that contains the local documentation which is faster to find things, as well as handle tools like ildasm.exe and gacutil.exe).
Almost all code I've ever posted to the forums here on CodeProject where written in ViM (a vanilla text editor ported from UNIX) and compiled on the command-line to check for typos before posting.
This posting is provided "AS IS" with no warranties, and confers no rights.
Software Design Engineer
Developer Division Sustained Engineering
Microsoft
[My Articles] [My Blog]
|
|
|
|
|
|
The Catalyst wrote:
Could someone recomend one please.
I completely agree with Heath on this one, following suit, I just use TextPad[^] as my editor to post all my code examples here, and I use a simple batch file that uses csc.exe to compile everything. Two additional options that you have for a free IDE are #develop [^] or you could write you own forms designer following this[^] article. This would still require you to download the SDK though.
- Nick Parker My Blog | My Articles
|
|
|
|
|
See link below, works well with .NET runtime, no SDK required. No frills, basic project support, 100% .NET written C# syntax highlighting editor, buggy in places... but mite be wat you need, o and its less than 600kb Try it, you have nothing to lose
top secret Download xacc-ide 0.0.3 now! See some screenshots
|
|
|
|
|
I was having an idea of a program I want to make. I was just sitting and thinking about it and came up with an idea to a solution where I would use lots of small panels on top of large panel (canvas). The problem is that these small panels arent necessarily completely used. Maybe only parts of them are used. They can also be on top of each other. When they are on top of each other it wont look good if the top panel overdraws panels belowin parts that arent actually used. So is there a way to say that a part of a panel is supposed to be transparent so that things belo are shown instead of the empy panel?
|
|
|
|
|
Using multiple panels contained within another panel has another problem: unnecessary overhead. Each is a control with its own window handle, window procedure, and more - all consuming memory and ticking away at the number of possible window handles allocated by the OS.
There are a couple ways you could do this. The easiest way is - for Windows 2000 and newer - to use layered Windows. This is what the Opacity property of a Form uses. In this case, however, you'll need to code something yourself, which I'll get to in a moment.
The other way is supported on any Windows platforms (perhaps other platforms, too) that supports the .NET Framework. You exclude a region of your control from painting. This is known as clipping. Anything below it is sent a WM_PAINT message to redraw that portion of itself. This can be expensive (in terms of resources), which is why layered Windows are nice (but only supported on Windows 2000 and newer).
The first way requires that you P/Invoke SetLayeredWindowAttributes and - upon creation of your control (override OnHandleCreated , and be sure to call base.OnHandleCreated ) - you pass it the Handle property, the color of your transparent color, and LWA_COLORKEY (0x1) to tell the function to use your color, not an alpa value (which is what the Form.Opacity property actually uses). Then you assign an image as your BackgroundImage for your Panel that contains the color you want to mask out.
See the example below, where Example.jpg is just an image with white that I mask out:
using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
class Example : Form
{
Example()
{
SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.ResizeRedraw, true);
using (Image bg = Image.FromStream(
GetType().Assembly.GetManifestResourceStream("Example.jpg")))
{
BackgroundImage = (Image)bg.Clone();
Size = bg.Size;
}
FormBorderStyle = FormBorderStyle.None;
}
void SetTransparentColor(Color c)
{
int key = ColorTranslator.ToWin32(c);
SetLayeredWindowAttributes(Handle, key, 0, LWA_COLORKEY);
}
protected override System.Windows.Forms.CreateParams CreateParams
{
get
{
System.Windows.Forms.CreateParams cp = base.CreateParams;
cp.ExStyle = WS_EX_LAYERED;
return cp;
}
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
SetTransparentColor(Color.White);
}
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
Close();
}
const int LWA_COLORKEY = 0x1;
const int WS_EX_LAYERED = 0x00080000;
[DllImport("user32.dll")]
static extern bool SetLayeredWindowAttributes(IntPtr hwnd,
[MarshalAs(UnmanagedType.U4)] int crKey, byte bAlpha,
[MarshalAs(UnmanagedType.U4)] int dwFlags);
static void Main()
{
Application.Run(new Example());
}
} The other way you override OnPaint and set the Graphics.Clip to a Region . You can easily construct this Region from a GraphicsPath (see the .NET Framework SDK documentation for detials) that you can add shapes to - rectangles, polygons, circles, etc. This is the region that is not painted.
Note that you shouldn't calculate this region in OnPaint - it's very expensive. You really only need to calculate it again if your control is resized (and your shape resizes along with it) or if your shape changes. Below I posted an example I posted a while back that discusses some other topics, but you'll see your question answered in there, too:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using AS = System.Windows.Forms.AnchorStyles;
class Test : Form
{
static void Main()
{
Application.Run(new Test());
}
Test()
{
Text = "Triangle Example";
Triangle t = new Triangle();
Controls.Add(t);
t.Dock = DockStyle.Fill;
BackColor = Color.White;
}
}
class Triangle : Control
{
public Triangle()
{
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint
| ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw, true);
UpdatePath();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (!init)
{
UpdateBrush();
init = true;
}
Graphics g = e.Graphics;
g.Clip = region;
g.CompositingQuality = CompositingQuality.AssumeLinear;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.FillPath(brush, path);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
UpdatePath();
}
bool drag;
protected override void OnMouseDown(MouseEventArgs e)
{
drag = true;
SetCenter(e.X, e.Y);
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (drag)
SetCenter(e.X, e.Y);
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
drag = false;
base.OnMouseUp(e);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (path != null) path.Dispose();
if (region != null) region.Dispose();
if (brush != null) brush.Dispose();
}
}
void SetCenter(int x, int y)
{
if (brush != null)
{
brush.CenterPoint = new PointF(x, y);
Invalidate();
}
}
GraphicsPath path;
Region region;
void UpdatePath()
{
if (Width == 0 || Bottom == 0) return;
PointF p1 = new PointF(Width / 2, Top);
PointF p2 = new PointF(0, Bottom);
PointF p3 = new PointF(Width, Bottom);
if (path != null)
{
path.Dispose();
path = null;
}
if (region != null)
{
region.Dispose();
region = null;
}
path = new GraphicsPath();
path.AddPolygon(new PointF[] {p1, p2, p3});
region = new Region(path);
if (init) UpdateBrush();
}
PathGradientBrush brush;
bool init = false;
void UpdateBrush()
{
PointF point = PointF.Empty;
if (brush != null)
{
point = brush.CenterPoint;
brush.Dispose();
}
brush = new PathGradientBrush(path);
brush.CenterColor = Color.Gray;
if (point != PointF.Empty)
brush.CenterPoint = point;
brush.SurroundColors = new Color[] {c1, c2, c3};
}
Color c1 = Color.Red, c2 = Color.Green, c3 = Color.Blue;
public Color C1
{
get { return c1; }
set { c1 = value; UpdateBrush(); }
}
public Color C2
{
get { return c2; }
set { c2 = value; UpdateBrush(); }
}
public Color C3
{
get { return c3; }
set { c3 = value; UpdateBrush(); }
}
} This draws a triangular control (instead of rectangular) and sets the clipping region so that mouse messages outside of the rectangle are translated to the control behind the triangular control. Both layered windows and clipping regions have this affect. The control is seemingly not there (in the masked region).
If you want a really cool design-time method for assigning such regions, see the RegionMaster Controls[^] on WindowsForms.net.
This posting is provided "AS IS" with no warranties, and confers no rights.
Software Design Engineer
Developer Division Sustained Engineering
Microsoft
[My Articles] [My Blog]
|
|
|
|
|