So this weekend, my family and I spent 4 nights down at Diamond Lake with my wife’s two sisters, their families and their parents.
(not pictured, my brother-in-law, my wife’s parents and me)
For Kenny and Nathan, it was their second trip down to Diamond Lake in 2 weeks. During the first trip down, we went camping with my Mom and Dad. It was the first time that Kenny or Nathan had been camping (not counting the backyard) and it was so successful, we were looking forward to doing it again.
This trip was much different than the first. To start, there were 7 kids counting my two boys, with two of my nephews being Kenny and Nathan’s ages (6 and 3). This obviously was a lot of fun for the boys, as we had the 4 amigos running between 4 camps playing, digging and chasing each other.
Since we had 4 days down at Diamond Lake, we tried to pack quite a bit of stuff into it. The boys didn’t come to Crater Lake on our last visit (since I was riding the rim), so we made sure that we took them this time. We went to the lodge, drove the rim, and then hiked down to the water’s edge. For those that haven’t been to Crater Lake before, the hike down to the water is about a 0.7 miles (I think) covering 700 vertical feet. You start walking down obviously — and everyone finds the walk down to be a blast. We got down to the bottom, took a few pictures:
Once the kids (and I) had a chance to play in the water :), we started the hike back up. My brother-in-law, Rob, and I had the pleasure of packing the two youngest back up the rim on our shoulders. It’s times like those that I wish Nathan was growing quite so fast. 🙂 However, everyone made it to the top, and we got lots of nice pictures of the rim from the many lookouts around the lake. Here are a few:
Alyce and the boys
After doing the hike up and down the rim, my brother-in-law and Alyce’s parents decided that we would hike Mount Scott the next day. Mount Scott is the highest point at the Crater Lake National Park at 8900+ feet. The hike from the trail head is 2.5 miles and is rated for intermediate hikers because it gets steep over the last 1/3 to 1/4 of the hike. The hike is suppose to take 3 1/2 hours to go up and down, but the views from the top of the mountain are suppose to be worth it. So we decided to give it a go. Since we had things that we wanted to do as well, Rob and I hiked it up the mountain at a brisk pace getting to the top in about 45 minutes, and I can definitely say it was worth it. Rather than post the pictures, I’ll post the 260 degree video short that I shot while at the top. It’s a 16 mb QuickTime file, but from the top of the mountain, you can see Diamond Lake, Klamath Lake and Mount Shasta. Here’s the clip: View from Mount Scott. Going down was a blast as well. We jogged done the trail, getting off the mountain in about 12 minutes.
We didn’t spend all our time at Crater Lake though. We went swimming, fishing, boat riding, etc. All lots of fun. Fishing for me was especially fun. I love to fish — it’s something I don’t have nearly enough time for, but I got to go out for two hours in the morning and two hours in the evening and caught 2 Rainbow Trout (14+ inches each) which will be tomorrow’s dinner. The boys got to go out as well, though they don’t have the patience for fishing, so it was mostly riding the boat.
All and all, it was a great time. We got to spend time just with my family and time with our extended family making it a great second camping trip for the boys. Of course, next time we do this, hopefully it will be somewhere a little warmer. I’d warned my wife before I went that the nights this time of year would be a bit chilly, and boy were they. Each night was in the low thirties, which was a bit of a shock for everyone coming from the nice warm Valley. 🙂 Fun time, but I guess it’s time to get back online and back to work.
I’m posting this in hopes that it will save someone else a lot of time or someone that knows .NET a bit better than I can provide a better solution.
Last week, I had someone ping me regarding MarcEdit and a problem that they were running into with the Editor running it on a 64-bit version of Windows 2003 Server. MarcEdit is compiled for any processor, so in theory, the framework should adjust the variable types to the current CPU type and go on it’s merry way. And was it not that I have to work with some unmanaged code within my application, I’m sure that this would be the case. However, when opening the MarcEditor, the user was getting the following error message:
This is odd because I test MarcEdit on every version of Windows from 98 to Vista. The problem however, is I’ve never ran the program in a 64-bit version of Windows.
I did a little bit of research, and found what I thought to be the problem. The 64-bit version of windows shares many of the same signatures as its 32-bit counter-part, but one place where the signatures differ is in the Messaging Queue. SendMessage, for example, which uses integers to pass values between processes had been updated to 64 bit integers and would crash if the wrong data type is sent into the function. No problem, I fixed the signature issue, but the error message remained. What I didn’t realize is that this wasn’t the actual problem (though it was a problem). The real problem seemed to be related to simply accessing the RichTextbox Handle and passing it the callback. Anytime the Handle was touched and passed, this error would be generated.
So, Microsoft does make the Enterprise version of Windows 2003 Server available on a trial basis for developers wanting to test their software. So, I dug up a box with an AMD-64 bit processor and set to installing the software. Next, I installed SharpDevelop, an Open Source IDE for .NET. I created a small sample program to isolate the code that was causing me problems. In my case, the code that was causing the problem is necessary because of MARC being a UTF8 encoded data format. Microsoft’s Richtext library supports the loading of plaintext (ASCII), Unicode text, text with OLE objects and text in just about any character format, including UTF8. Unfortunately, the .NET framework only exposes plaintext and Unicode text as supported formats. This means that in order to load UTF8 data and utilize the components streaming nature to minimize the memory footprint during loading, we need to essentially write our own EditStreamCallback function, create the delegates, the EDITSTREAM struct, etc. And in that, there is the rub. When compiling the code in SharpDevelop, I specified that the code should be targeted specifically for a 64-bit processor. During compile, I got two warning messages that two core .NET components are compiled specifically for 32-bit processors. Since the signatures on the 64 and 32 bit machines are identical, one can generally ignore these compilation warnings, as the framework does it’s magic. However, the fact that I’m utilizing functionality from one of these two components within an unmanaged code block causes the problem. Within the .NET (and 64-bit environment in general), an 64-bit process cannot load a library compiled for a 32-bit process. A 32-bit process can run within a 64-bit environment, they just cannot share processes between themselves. My best guess is that this is what was happening. Since these two .NET components were compiled specifically for the 32-bit processors, my attempts to load them into a 64-bit process and utilize them within an unmanaged code block caused issues. The solution is a simply one — for the GUI application of MarcEdit (which doesn’t do much anyway), the program simply needs to be complied to target 32-bit processors. Now it runs just fine within a 64-bit environment, and will remain so until Microsoft cleans up these two core libraries. With that said, if anyone has a better way of dealing with this problem (code is attached, so if you can make it work, I’d love to here from you), I’d love to hear about it.
Finally, it’s pretty difficult to find example code dealing with the Richtext components in C#. I think this is primarily because most folks that use high level languages like C# either don’t have a need for it or don’t have the background in C++ to understand what is actually happening at the Proc level. Anyway, to that end, I’m posting the source to my small sample program (get it here) that I used to diagnosis this problem. The trick to doing this type of interaction is to avoid the use of integer class variables. In .NET, you have to remember that you are dealing with managed code, so when you make the call to a API like SendMessage, you should be Marshalling all your data, and passing it into the function via the IntPtr structure. The only exception to that with the SendMessage API is the message argument, which microsoft defines and an unsigned 32-bit integer on all platforms, though for practical purposes, the message argument should be classed as a 32-bit integer.
1: private const int SF_USECODEPAGE = 0x020;
2: private const int SF_TEXT = 0x001;
3: private const int SF_RTF = 0x002;
4: private const int CP_UTF8 = 65001;
6: private const int WM_SETREDRAW = 0x000B;
8: private const int WM_USER = 0x400;
9: private const int EM_STREAMIN = WM_USER + 73;
10: private const int EM_GETEVENTMASK = (WM_USER + 59);
11: private const int EM_SETEVENTMASK = (WM_USER + 69);
12: private const int EM_STREAMOUT = WM_USER + 74;
13: private const int ENM_NONE = 0;
14: private const int EM_SETTEXTMODE = WM_USER + 89;
16: private const int TM_PLAINTEXT = 1;
18: private const int ECO_AUTOWORDSELECTION = 0x00000001;
19: private const int ECO_AUTOVSCROLL = 0x00000040;
20: private const int ECO_AUTOHSCROLL = 0x00000080;
21: private const int ECO_NOHIDESEL = 0x00000100;
22: private const int ECO_READONLY = 0x00000800;
23: private const int ECO_WANTRETURN = 0x00001000;
24: private const int ECO_SAVESEL = 0x00008000;
25: private const int ECO_SELECTIONBAR = 0x01000000;
26: private const int ECO_VERTICAL = 0x00400000;
27: private const int ECOOP_SET = 0x0001;
28: private const int ECOOP_OR = 0x0002;
29: private const int ECOOP_AND = 0x0003;
30: private const int ECOOP_XOR = 0x0004;
32: private const int EM_SETOPTIONS = (WM_USER + 77);
33: private const int EM_GETOPTIONS = (WM_USER + 78);
36: delegate IntPtr EditStreamCallback(IntPtr dwCookie, IntPtr pbBuff, IntPtr
37: cb, out IntPtr pcb);
40: struct EDITSTREAM
42: public IntPtr dwCookie;
43: public IntPtr dwError;
44: public EditStreamCallback pfnCallback;
49: [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
50: static extern IntPtr SendMessage(HandleRef hWnd, Int32 Msg,
51: IntPtr wParam, IntPtr lParam);
53: [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
54: static extern IntPtr SendMessage(HandleRef hwnd, Int32 msg, IntPtr
55: wParam, ref EDITSTREAM lParam);
In the declarations, you will see that two forms of SendMessage have been defined. One where the lParam references the EDITSTREAM structure and on where it references an IntPtr structure. The former is used when streaming data into the RichText window, the latter is used when sending regular messages between controls. It should be noted, the later could be removed in .NET 2.0 by making use of the System.Windows.Forms.Message class, which essentially allows you to send messages to controls so long as all arguments can be sent as IntPtrs.
After the declarations, the remainder of the code is setting up the actual streaming, and creating the function that the delegate prototypes. In this example, I’ve called the streaming function, ReadRichTextStream and the actual streaming function, StreamIn. These functions would look like the following:
ReadRichTextStream: Accepts a RichTextBox Object and the filename of the file to load.
1: private void ReadRichTextStream(System.Windows.Forms.RichTextBox objRich,
2: string sfilename)
5: string filename = sfilename.ToLower();
6: objRich.Text = "";
7: int eType = SF_TEXT;
8: if (filename.EndsWith(".mrk")|filename.EndsWith(".mrk8")|filename.EndsWith(".tmp")|filename.EndsWith(".xml"))
10: eType = (((CP_UTF8)<<16)|SF_USECODEPAGE|SF_TEXT);
12: else if (filename.EndsWith(".bmrk"))
14: eType = SF_TEXT;
16: else if (filename.EndsWith(".rtf"))
18: eType = SF_RTF;
20: else if (filename.EndsWith(".txt"))
22: eType = SF_TEXT;
26: eType = (((CP_UTF8)<<16)|SF_USECODEPAGE|SF_TEXT);
29: //this.Redraw = false;
30: long b_length = 0;
31: System.IO.FileStream fs = new System.IO.FileStream(sfilename, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read);
32: b_length = fs.Length;
34: System.Runtime.InteropServices.GCHandle gch = System.Runtime.InteropServices.GCHandle.Alloc(fs, System.Runtime.InteropServices.GCHandleType.Normal);
35: EDITSTREAM es = new EDITSTREAM();
36: es.dwCookie = (IntPtr)gch;
37: EditStreamCallback callback = new EditStreamCallback(StreamIn);
38: es.pfnCallback = callback
40: SendMessage(new HandleRef(objRich, objRich.Handle), (Int32)EM_STREAMIN, (IntPtr)eType, ref es);
42: //Remember to free allocated memory to avoid leaks.
StreamIn: StreamIn is the function that actually reads the data from the file and pushs the data into the RichTextBox callback to print into the control.
1: public IntPtr StreamIn(IntPtr dwCookie, IntPtr pbBuff, IntPtr
2: cb, out IntPtr pcb)
4: byte buffer = new byte[cb.ToInt32()];
5: uint result = 0;
10: System.IO.FileStream fs = (System.IO.FileStream)((GCHandle)dwCookie).Target;
11: //pcb = cb;
14: pcb = (IntPtr)fs.Read(buffer, 0, cb.ToInt32());
16: if (pcb.ToInt32()<=0)
18: pcb = IntPtr.Zero;
19: result = 1;
20: return (IntPtr)result;
25: System.Runtime.InteropServices.Marshal.Copy(buffer, 0, pbBuff, pcb.ToInt32());
30: pcb = IntPtr.Zero;
31: result = 1;
32: return (IntPtr)result;
35: return (IntPtr)result;
Anyway, the gist of all this, is that by setting the compile option to target 32-bit processors in the MarcEdit gui, I’ve been able to solve this issue. I’m having the user that found the problem verify that I’ve indeed hunted this bug down and squashed it — so as soon as that’s confirmed, I’ll be pushing this fix out with MarcEdit.