Partial Classes (CF & Full FX)

Fri, September 17, 2004, 02:26 AM under Whidbey | VisualStudio
If you create a new Windows Application project in VS2005 you'll notice that viewing the code for the Form1 class shows very little. All the designer generated code is missing. If you then "Show All Files" in the solution explorer, you can see under the Form1.cs file a Form1.Designer.cs file which upon viewing reveals the designer generated code in yet another Form1 class. If you build the project and look into the assembly with ILDASM, you notice exactly one Form1 class and no indication about this split. The clue to the whole mystery is the keyword "partial" before each class declaration (in fact VB allows one class not to be declared as partial so you'll only see "Partial" in the Form1.Designer.vb file).

So this is a tool thing. You can split a class over multiple files in the same project, as long as you declare them as partial. With forms, note that adding event handler methods or any other methods that you write, appear in the main code file.

To complete the story of how the solution explorer links the two in the manner that it does, we simply look at the .csproj/vbproj file in Notepad or your favorite XML editor. Under the ItemGroup tag you find something like this:

    <Compile Include="Form1.cs">

         <SubType>Form</SubType>
    </Compile>
    <Compile Include="Form1.Designer.cs">
        <DependentUpon>Form1.cs</DependentUpon>
    </Compile>

It is the DependentUpon tag that makes a file appear under another in VS. It is being put to very good use with Partial classes.

So here is a money making thought (and you don't even have to turn adsense on). Write a tool/addin that will create these entries. MSFT is not offering anything in the IDE apart for forms, so it is down to tedious manual work at the moment. If I were using it, I'd wish for a menu item when I right click on a code file that offers: "Add Partial". So if I used it on a SomeClass.cs, it would ask me for a name XXX and then create a partial class SomeClass.XXX.cs under SomeClass.cs. The greatest use of this tool would be for upgrading existing projects. When upgrading, VS2005 does not split Form classes, so you have to do all the work manually if you want to fit in with the new environment. Oh, and if you need Beta testers, count me in :-)

Retargetable (=256)

Thu, September 16, 2004, 03:07 PM under MobileAndEmbedded
One of the FAQs on the CF newsgroup is about running assemblies on both desktop and CE device. The answer is that on a CE device you cannot run assemblies built against the desktop/full framework. The reverse is possible, due to the System.Reflection.AssemblyNameFlags enumeration and specifically its Retargetable value. Basically, almost all of the CF assemblies are retargetable to the desktop v1.1 assemblies, which means that your CF app will run on the desktop against the full framework. There is a caveat, which is obvious once you understand the previous sentence:

* You must only use functionality that is common on both platforms *

At first this may sound superfluous, given that the CF is a subset of the full framework, but a closer look reveals functionality specific to the CF in the Microsoft.WindowsCE namespace (such as MessageWindow and InputPanel), the infrared classes and of course the SQL stuff is different. In addition, no real CF 1.0 application survives without pinvoking, so you'll have to detect what platform you are on (System.Environment.OSVersion.Platform) in order to call the right dllimport (i.e. not pointing to coredll.dll). So, break these rules and look forward to TypeLoadExceptions and the like; follow them and you are good to go...for class libraries projects (dlls).

Do expect to encounter differences in functionality. On the desktop your app is running against a different implementation of the .NET library. Although interface-compatible and in most cases functionality compatible, there are differences due to bugs or design decisions based on platform applicability.

For GUI apps (exes), I don't think there is much point in attempting the above. Technically it will work (just run your exe from the build folder) and it also makes a nice demo for your boss, but how will a WinForms app look good on two platforms that are so different to each other? The difference in screen size should put the argument to rest but, in addition, the user input is typically very different too (keyboard vs soft input panel, mouse vs stylus), not to mention non-resizable windows etc.

It is worth mentioning that VS2005 with CF 2.0 makes this approach even easier. All the same issues apply, but there is help for debugging this scenario. When you choose Debug->Run, you are presented with a Deploy window that includes a bunch of emulators (so far the same as VS2003) but also includes the 'My Computer' option! Choosing this actually runs your app on the desktop, providing you with the same experience you'd have if it were a normal Windows app.

To sum it up, my attitude towards the "retargetable" approach is not to use it. I prefer the alternative of sharing the same code base between desktop and CF apps and I use that successfully everyday. More on that approach next time.

Contact

Thu, September 16, 2004, 02:06 PM under Personal
Today I got yet another email account (you would have thought 5 were enough but I enjoy reading the same spam over and over again) courtesy of Neil. The difference with this one (apart from its Beta status) is the 1GB storage! So, if you are too shy to use the comments facility (I know it doesn’t show up in aggregators), you can now email me about anything. It's on the sidebar under Links: Email Me. You can't miss it :-)

IPC with Remoting in .NET 2.0

Mon, September 13, 2004, 07:29 PM under dotNET | Whidbey
If you have .NET apps talking to each other on the same machine today, chances are you are using remoting. Furthermore, you are probably using binary over tcp and have dealt with the idiosyncrancies of handling events over remoting.

So what is new with .NET 2.0 for this scenario? Well, there is now an Ipc namespace, and using it offers performance improvements over the TCP one. To use it you need to change your config files (and of course you are using config files and not setting up the channel programmatically :-).

Assuming your server.exe.config file looks like this, you simply have to change it to look like this:
<?xml version="1.0" encoding="utf-8" ?>

<configuration>
<system.runtime.remoting>
<application name="ServerHost">
<service>
<activated type="SomeNamespace.SomeClass, SomeDllName" />
</service>
<channels>
<channel ref="ipc" portName="server">
<serverProviders>
<formatter ref="binary" typeFilterLevel="Full" />
</serverProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>

Assuming your corresponding client.exe.config looks like this, you have to change it to look like this:
<?xml version="1.0" encoding="utf-8" ?>

<configuration>
<system.runtime.remoting>
<application>
<client url="ipc://server/ServerHost">
<activated type="SomeNamespace.SomeClass, SomeDllName" />
</client>
<channels>
<channel ref="ipc" portName="client">
<serverProviders>
<formatter ref="binary" typeFilterLevel="Full" />
</serverProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
</configuration>

The changes may seem obvious (isn't everything once you know it:-), but I would not have figured the above out if it wasn't for Manish G, whose help was invaluable.

Another improvement with .NET 2.0 – well with the IDE really – is the ability to add references to EXE assemblies, not just DLLs. You can do this today via the command line, but with VS2005 you can do it in the IDE as well. This brings us closer to the ActiveX EXE days (out-of-proc COM) where there were only two files. So you can now merge (if that is what your design wishes) SomDllName.dll into the Server.exe and offer the SomeNamespace.SomeClass from it directly.

That's it!

UPDATE: On Ohad's WebLog you can see how to use IPC programmatically

IPC with CF on CE part 2

Sun, September 12, 2004, 05:16 PM under MobileAndEmbedded
Continuing from previous entry on IPC options with Compact Framework there are only two left - they are very similar.

7. Named Events + registry
8. Directly Share memory

7. For simple data sharing, it is easy for an app to write data to predefined regisrty entries and signal a named event; at that point the other side can read from the registry. It looks something like this:

// SERVER PROCESS
//write data
private void cmdWrite_Click(object sender, System.EventArgs e) {
IntPtr hWnd = Win32Api.CreateEvent(IntPtr.Zero, true,
false, "YOUR_NAME_HERE");

// TODO write to reg
//e.g. with opennetcf Registry class

Win32Api.SetEvent(hWnd);
System.Threading.Thread.Sleep(500);
Win32Api.CloseHandle(hWnd);
}

// CLIENT PROCESS
private System.Threading.Thread mMonitorThread;
private bool mStayAlive;
private IntPtr mMonitorHwnd;

//read data
private void cmdRead_Click(object sender, System.EventArgs e) {
mStayAlive = true;

mMonitorHwnd = Win32Api.CreateEvent(IntPtr.Zero, true,
false, "YOUR_NAME_HERE");

mMonitorThread = new System.Threading.Thread(
new System.Threading.ThreadStart(
this.MonitorOtherProc));

mMonitorThread.Start();
}

// on background thread so make sure we don't
// touch GUI controls from here
private void MonitorOtherProc(){
while (mStayAlive){
Win32Api.WaitForSingleObject(mMonitorHwnd, -1);
if (mStayAlive == false) return;

MessageBox.Show("Got data "+
DateTime.Now.ToString(), "TODO read from reg");
// TODO read data from reg

Win32Api.ResetEvent(mMonitorHwnd);
}
}

// must call this before closing app - e.g. from Form_Closing
public void Shutdown(){
if (mMonitorThread == null) return;
mStayAlive = false;
Win32Api.SetEvent( mMonitorHwnd);
System.Threading.Thread.Sleep(500);
Win32Api.CloseHandle( mMonitorHwnd);
mMonitorThread = null;
}


8. Directly sharing memory is not advisable but we can do it. The logic is identical to case 7 with named events but instead of writing/reading from registry, we access memory directly. It looks something like this:


// BOTH CLIENT & SERVER PROCESS NEED THESE

// Returns pointer to shared memory
private IntPtr ObtainHandleToSharedMemory(){
const uint PHYS_ADDR =0x80230000;//Make sure this is not used
on your platform
const int MEM_SIZE = 10;

IntPtr hwnd =
Win32Api.VirtualAlloc(0, MEM_SIZE, Win32Api.MEM_RESERVE,
Win32Api.PAGE_READWRITE|Win32Api.PAGE_NOCACHE);
if (hwnd.ToInt32() != 0){
if (Win32Api.VirtualCopy(hwnd, PHYS_ADDR, MEM_SIZE,
(Win32Api.PAGE_READWRITE|Win32Api.PAGE_NOCACHE))
== true){
return hwnd;
}
}
MessageBox.Show(
Marshal.GetLastWin32Error().ToString(),"Failed");
return IntPtr.Zero;
}

// Define common structure/class in both client and server e.g.
private class SharedMemory{
public byte b1;
public byte b2;
public char c;
public bool flag;
public int i;

public SharedMemory(bool aFlag){
flag=aFlag;
if (aFlag){
b1=1;b2=2;c='!';i=3;
}else{
b1=0;b2=0;c=' ';i=0;
}
}

public override string ToString() {
return "b1=" + b1.ToString() + ", b2="+b2.ToString()
+ ", c=" + c + ", i=" + i.ToString();
}
}

// CLIENT
// As in previous example but instead of reading the registry
// read the following in MonitorOtherProc
IntPtr memHwnd=ObtainHandleToSharedMemory();
if (memHwnd.ToInt32() !=0 ){
SharedMemory sm=new SharedMemory(false);
Marshal.PtrToStructure(memHwnd,sm);
MessageBox.Show(sm.ToString(),sm.flag.ToString());
}

// SERVER
// As in previous example but instead of writing to registry
// do the following in cmdWrite_Click
IntPtr memHwnd=ObtainHandleToSharedMemory();
if (memHwnd.ToInt32() !=0 ){
SharedMemory sm=new SharedMemory(true);
Marshal.StructureToPtr(sm,memHwnd,false);
}


IPC with CF on CE part 1

Sun, September 12, 2004, 05:13 PM under MobileAndEmbedded
With no remoting in the Compact Framework or named pipes on Windows CE there are still other ways to achieve Inter-process communication. Here is a list (feel free to tell me if I have missed any):

1. Sockets
2. Memory Mapped Files
3. Windows Messages
4. Point to Point Message Queues
5. MSMQ
6 Out-of-proc COM
7. Named Events + registry
8. Directly Share memory

1. Sockets are accessible to .NET apps via the System.Net.Sockets namespace. You could use a TcpListener (on 127.0.0.1 and some port you fancy) and a TcpClient or even get down to the Socket class which the other two aggregate. The msdn links given are good and as always search the web and newsgroup.

2. CF code for memory mapped files can be found here and an example of their use here

3. Passing windows messages between apps requires a windows handle of course. CF has the Microsoft.WindowsCE.Forms namespace not available on the desktop that includes the MessageWindow class. [This class was necessary to overcome the CF's limitation of not being able to pass delegates to pinvoke and hence achieve windows callbacks - this limitation is no longer in CF 2.0]. Learn about the MessageWindow here, here, here and here.

4. Point-to-Point Message Queues with the .NET Compact Framework

5. Pinvoking MSMQ is not easy (apparently, I haven't tried it myself) and I am aware of no sample code for that. CF 2.0 will support it via the System.Messaging namespace. For further details and a bunch of links on this subject check out these blog entries here.

6. COM interop is not supported in CF. A commercial offer is available. CF 2.0 will have some support for COM Interop but I don't know if out-of-proc servers will be supported. If you know drop me a note.

This is getting long enough already so we look at options 7 and 8 next time.

[again] Blog link of the week 37

Sun, September 12, 2004, 11:27 AM under Links
This week Kenny Kerr started an Introduction to MSIL
Check out parts 1, 2, 3, 4
[if you are interested in MSIL, there is the good “Partition III CIL.doc” on your dev machine – on mine its under: C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Tool Developers Guide\docs]

If you are confused about the various different GCs (Server, Workstation, Concurrent) check out this entry by Chris Lyon as well as this one.

Serial (RS232) communications in .NET

Mon, September 6, 2004, 12:16 PM under dotNET
The .NET Framework has survived until now without offering a class for serial comms in its otherwise rich class library. I guess MS thought that rs232 was becoming obsolete - or I don't know what. The fact is that, especially on CE devices, rs232 is part of everyday life. The upshot is that .NET 2.0 comes with a SerialPort class.

So can you use your COMM ports today?

In VB6 one would use the MSComm control, which did a great job. You could still use that in your .NET apps (by hosting the ActiveX control on a Form - assuming the license for the control exists) just be aware of various threading scenarios. In particular, things become tricky if you are using the control in a remoted server (all threads are MTA and not STA as required, so it's pretty much a dead end situation - I ran into this a while back).

Another answer is PInvoke. In other words, use the traditional Win32 APIs in the .NET app. There are a number of good MSDN pages on the subject including:
1. Serial Communications in Win32
2. Programming Serial Connection on WinCE

The first rs232 code to appear for .net is here. This leads us to a 3rd solution, which is to use an existing library. Apart from commercial packages and a number of user samples, there is a very good article with code on the MSDN mag; so you could use that!

Now, if you are working with the Compact Framework, you have to be mindful of a few facts. WinCE does not support overlapped io. Even the non-overlapped io is implemented differently on the PC than on CE (e.g. Read/WriteFile do not block on CE). Finally, the threading support of the CF is not as rich as that of the full framework. What does all this mean? Well, basically rs232 libraries designed for the desktop/full framework will not just work on CE devices with the CF.

So what is the solution?

You can use a different library depending on which platform you are targeting, e.g. download a free assembly for the CF here. Alternatively, the shared source repository for CF has C# code that works on both platforms!

Finally, I recommend looking at the interfaces of the System.IO.Ports.SerialPort in Whidbey, so that when you upgrade your code it is easy to switch one implementation out for the other. I have put an example here.

[again] Blog link of the week 36

Sun, September 5, 2004, 11:46 AM under Links
Luca blogs about x+=x++; (via post from Ericgu) and then Paul Vick follows it up with The joys of ++ and --

Help improve the WinCE debugging tools by replying to Sue Loh here

C# with MyEvents functionality

Fri, September 3, 2004, 05:15 AM under Whidbey | VisualStudio
If you are not aware of the MyEvents functionality that VB2005 offers, check out a screenshot and short description here.

The question is how to get the same support in C#. Well it all becomes pretty straightforward if you understand how MyEvents and the new startup model work in VB. I provided an exploration here.

So, armed with that knowledge, in a new C# Windows Application do the following:
1. Add a reference to Microsoft.VisualBasic.dll
2. Replace all the code in Program.cs with the 35 lines of code given below
3. Form1.cs. This is the main form (you could put a button that throws an exception)
4. Add a form Form2.cs. This is the splash screen, no code needed.
5. Build in release mode and run from explorer.

Here is the code:
namespace WindowsApplicationCSusingVB{

public class MyApplication :
System.Windows.Forms.WindowsFormsApplicationBase{

public MyApplication():
base(System.Windows.Forms.AuthenticationMode.Windows){

base.NetworkAvailabilityChanged += new
Microsoft.VisualBasic.MyServices.NetworkAvailableEventHandler
(this.MyApplication_NetworkAvailabilityChanged);

base.Shutdown +=
new System.Windows.Forms.ShutdownEventHandler
(this.MyApplication_Shutdown);

base.UnhandledException += new
System.Windows.Forms.UnhandledExceptionEventHandler
(this.MyApplication_UnhandledException);

base.Startup += new
System.Windows.Forms.StartupEventHandler
(this.MyApplication_Startup);

this.IsSingleInstance = false;
this.EnableVisualStyles = true;
this.ShutdownStyle =
System.Windows.Forms.ShutdownMode.AfterMainFormCloses;
}

protected override void OnCreateMainForm(){
this.MainForm = new Form1();
}

protected override void OnCreateSplashScreen(){
this.SplashScreen = new Form2();
}

private void MyApplication_NetworkAvailabilityChanged(object sender,
Microsoft.VisualBasic.MyServices.NetworkAvailableEventArgs e){

System.Windows.Forms.MessageBox.Show("network");
}

private void MyApplication_Shutdown(object sender, System.EventArgs e){
System.Windows.Forms.MessageBox.Show("shutdown");
}

private void MyApplication_Startup(object sender,
System.Windows.Forms.StartupEventArgs e){
System.Windows.Forms.MessageBox.Show("startup");
}

private void MyApplication_UnhandledException(object sender,
System.Windows.Forms.UnhandledExceptionEventArgs e){
System.Windows.Forms.MessageBox.Show(
e.Exception.StackTrace, e.Exception.Message);
e.ExitApplication = false;
}

internal static void Main(string[] Args){
(new MyApplication()).Run();
}
} //end class
} //end namespace