Math world, simple mental calculations or what’s going on with education?
Today, I want to write blog post which is absolutely not related to programming. It related to math and education in general those days. During work interviews, I see a lot of people, who was absolutely unable to calculate mentally. They just can’t understand, that it’s possible to do without calculators. When my kids (2nd, 6th and 7th grade) were small I taught them to play with numbers, and until 4th grade (bigger kids) they were able do it. but then school teachers “killed” this ability. Why people should use calculator for simple math operations, if he can do it mentally? Shame you, the modern educational system. Let’s go back and try to understand how people were able to live without devil devices, such as calculators…
Following the paint of Nikolai Bogdanov-Belsky “Counting in their heads”. This painting is dated 1895.
As you can see at the painting, peasant kids trying to solve following exercise mentally:
(102 + 112 + 122 + 132 + 142) / 365
This is not very simple exercise, especially when should be solved without your favorite calculator. However, when I was 4th grade I learned to square two-digit numbers mentally (my, and I think, yours too): First, find the nearest multiple of ten, by raising or lowering your number, then add and remove the rest to each of numbers and add the square of oddment. For example
45 * 45 = (45+5) * (45-5) + (5 * 5) = 50 * 40 + 25 = (5 * 4) * 100 + 25 = 20 * 100 + 25 = 2000 + 25 = 2025
14 * 14 = (14+4) * (14-4) + (4 * 4) = 18 * 10 + 16 = 180 + 16 = 196
So, now it can be solved easily:
102 = 100
112= (11+1) * (11-1) + 1 = 12 * 10 + 1 = 121
122= (12+2) * (12-2) + 4 = 14 * 10 + 4 = 144
132= (13+3) * (13-3) + 9 = 16 * 10 + 9 = 169
142= (14+4) * (14-4) + 16 = 18 * 10 + 16 = 196
And so on… but wait, 100+121+144 already equals 365, which is our denominator. Next sequence will bring us 169+196, which is also 365. So the answer to this black board brain teaser is 2.
However, it can be rather complicated to calculate 862 for instance:
862 = (86 + 4) * (86 – 4) + (4 * 4) = 90 * 82 + 16…
Let’s try another way – multiple the difference between the number and 25 by 100, then add the square of the difference or excess of the number and 50. For example
862 = (86 – 25) * 100 + (86 – 50)2 = 61 * 100 + 362 = 6100 + (36 – 25) * 100 + (50 – 36)2 = 6100 + 1100 + 142 = 7200 + 196 = 7396
Isn’t it really simple and fun to calculate squares of numbers?
Bonus: how to calculate multiple of two digit numbers with the sum of its unity digits equals to 10?
- Multiply first digit of the first number by 10
- Add 1 to first digit of second number and multiply the result by 10
- Multiply results of step 1 and step 2
- Deduct second number and the result of step 1
- Multiply second digit of the first number by the result of step 4
- Add results of steps 3 and 5
Looks complicated? Let’s make it easier. Assuming that first number is X = 10x + z and second number is Y = 10y + (10 – z), the formula for quick multiplication calculation is: 100 * x * (y + 1) + z * (Y – 10 * x). For example:
96 * 84 = 100 * 9 * (8+1) + 6 * (84-10 * 9) = 100 * 9 * 9 + 6 * (84 – 90) = 8100 – 6 * 6 = 8100 – 36 = 8064
37 * 93 = 100 * 3 * (9+1) + 7 * (93 – 10 * 3) = 3000 + 7 * 63 = 3000 + (100 * 6 * 1 + 3 * (7 – 60)) = 3000 + 600 – 3 * 53 = 3600 – 159 = 3441
Have a nice day and be good people. Also, throw out all hardware calculators and uninstall all software
February 8th, 2009 · Comments (5)
Bootstrapper for .NET framework version detector
You wrote your .NET program, that can be used as stand alone portable application (such as it should be for Smart Client Apps), however you have to be sure, that necessary prerequisites (such as .NET framework) are installed on client’s machine. What to do? How to detect .NET framework version installed on target machine before running .NET application. The answer is – to use unmanaged C++ bootstrapper, that invoke your application if correct version of framework is installed.
Until now there are 15 possible .NET frameworks can be installed on client’s machine. Here the table of possible and official supported versions, as appears in Q318785
| .NET version | Actual version |
| 3.5 SP1 | 3.5.30729.1 |
| 3.5 | 3.5.21022.8 |
| 3.0 SP2 | 3.0.4506.2152 |
| 3.0 SP1 | 3.0.4506.648 |
| 3.0 | 3.0.4506.30 |
| 2.0 SP2 | 2.0.50727.3053 |
| 2.0 SP1 | 2.0.50727.1433 |
| 2.0 | 2.0.50727.42 |
| 1.1 SP1 | 1.1.4322.2032 |
| 1.1 SP1 (in 32 bit version of Windows 2003) | 1.1.4322.2300 |
| 1.1 | 1.1.4322.573 |
| 1.0 SP3 | 1.0.3705.6018 |
| 1.0 SP2 | 1.0.3705.288 |
| 1.0 SP1 | 1.0.3705.209 |
| 1.0 | 1.0.3705.0 |
All of those versions are detectible by queering specific registry keys. However, in some cases, you need to load mscoree.dll and call “GETCOREVERSION” API to determine whether specific version of .NET is installed. You can read more about it in MSDN.
So it’s really simple to write small C++ application (or PowerShell applet), that queries registry and invoke your managed application. How to do this? You can either read about it in outstanding blog of Aaron Stebner, who is Project Manager in XNA platform deployment team or attend my session next week to learn do it yourself. We’ll speak about nifty ways to do it also.
Anyway, by now, you can use small stand alone program, I wrote a while ago, that will tell you all versions of .NET frameworks installed in target machine without any prerequisites. It can be run even from shared network location
See you next week.
PS: Do not forget to download and install the new version of Visual Studio Snippet Designer, which is extremely useful tool by MVP Bill McCarthy, you’ll need it later next week…
Have a nice day and be good people.
February 4th, 2009 · Comments (8)
Line-Of-Business vs. Beautifulness or two dogmas comparison as exemplified by two Twitter applications
Today I want to speak about two dogmas: design and functional driven programming. As the example of those two approaches, I want to introduce two Twitter clients: *Chirp by thirteen23 and TwitterFox by Naan Studio
As you can see, *Chirp is state of art application with outstanding user interface, and well-defined usability studies. While TwitterFox is wacky grey boring kind-of-grid only. However, you cannot judge app by only how it looks like. Let’s try to understand first what’s for you need twitter client?
Defining application goals by user story
I’m using twitter as quick and handy business tool to write my thought, feelings and everyday events. It is not my main (not even secondary) task during the day, so I want to be able to open, write and forget. Thus, I need an application, that can be invoked by one click and dismissed after writing. Also, I do not want background application to gasp valuable space in my screen, when not in use. Thus it should be background process with reduced workset and one textarea, to be focused when the main window become active. Also the application should hide itself when unfocus, yet be able to notify me about events without disturbing.
Let’s see how it done in *Chirp:
- 140MB workset
- No ability to hide
- Bouncing thingy at left upper corner to disturb you – it designed as you main desktop beautifier.
- No ability to know that new twittes arrived without showing main window
- Twit process required to click additional button (named “Update” for some reason)
- If you not finished typing, you can either dismiss all text of post it.
- Strange 140 characters countdown on background absolutely esthetical, yet very disturbing.
- You cannot type more, then 140 characters – this restricted by textbox. If pasted bigger text all additional characters truncated.
- You need mouse to operate an application
Now TwitterFox:
- 10MB workset
- You can hide it by hitting escape or clicking X button
- Small and portable without disturbing elements – it not designed as your main everyday app.
- New twits counter over small icon in browser tray, all other notifications can be disabled
- Once focused text are become active, expanded automatically and ready to write
- If you’re hiding it without clearing area, all un write text remains – you can clear it by one click
- Small 140 characters countdown which is visible only when typing
- You can type more, then 140 characters – counter becomes red, and you cannot post, however you’re able to fix, by dismissing unnecessary spaces or characters.
- Can be operated by only keyboard.
Bottom line: *Chirp designed to show how good it looks, while TwitterFox to twit only. Thus for my specific user story TwitterFox won!
Defining functional specifications
Next task defined for Twitter is read other twits. I used to read all my following and followers when I have free minute. Sometimes I retwit things, rather often reply followers and read replies and rarely send direct messages.
*Chirp provides twit area without scrollbar, yet not restricted to number of twits. Other words, you can scroll with mouse wheel only or by holding somewhere inside and dragging unlimited up and down. When the mouse is over specific twit, it fades and show three buttons: reply, direct and retwit. Also each twit contains the name of the client was used (just like in regular web interface). When clicking user avatar it brings to special internal screen with last twit of the user, information and statistics about him, three functional buttons: UnFollow, Fave and Block and huge button Get User’s Tweets. When clicking the line displays the time of the twit it puts twit url into clipboard.
Also *Chirp contains five main functional buttons: Faves, Home, Direct, Update and Refresh. When Home tab unfocused (for example you’re on other screen), it also displays a number of new twits.
Error screen of *Chirp is really odd. It contains everything you not really need to know and beautiful whales moving on screen.
TwitterFox is much simpler. It contains two buttons on mouse/keyboard over – reply and fave. When clicking on user’s avatar it opens it’s page in Twitter with all necessary information. Main TwitterFox window contains three buttons: Recent, Replies, Messages.
No doubt, that *Chirp provides much richer functional spec, but wait, am I really need all this? I told earlier, that I used to read twits and replies, while *Chirp has no such view at all. You can easy copy twit url into clipboard, but what for? Also, you can read bio and statistics of people you following whenever you want without opening browser window. But how often you’re doing that?
TwitterFox concentrated on functionality – twit, read, reply, read replies (and direct messages) – base tasks , Twitter designed for. It also marks replies with contrast color in public timeline, while *Chirp has inline reply functionality with threaded discussions support (which is very odd for Twitter)
Bottom line: *Chirp is enriched with not useful features, while TwitterFox contains only things, you’re use. Thus for my specific functional requirements TwitterFox won again!
Developers vs. Designers final round
So, we already understand, that *Chirp is an application, designed to show how skilled thirteen23 designers are. And it achieved this goal. The application is state-of-art, looks and designed very well with taking into account even small details, however it huge, unusable for everyday twittering and extremely slow. This is a general example about Designers’ doctrine.
TwitterFox is very ugly, but concentrated on functionality, tiny and reactive. It includes only features are necessary for twittering and has no other goals. So, this is a general example about Developers’ doctrine.
Is it possible to messmate those doctrines? Probably it is. And it is really simple. Each one of actors should do his own work. Designers should design and Developers – develop. I spoke about it a lot during my lectures, I’ll speak about it also at 11th February in user group meeting. By now, when you know how I see Twitter, you can start following me. Also, I’m interesting to hear your ideas about Designer-Developer intercommunication. It is not just about Microsoft way
Have a nice day and be good people.
January 30th, 2009 · Comments (9)
Making TFS better or what is TITS?
Those days me and my team work very hard toward new version of “The System”. This includes massive refactoring of all solutions, hard work with TFS (which not restricted to only adding files, but also deleting, moving, etc. other words, all stuff, which TFS is not really love). Because of this, we need a bunch of handy tools to make our dreams come true and to decrease unnecessary number of clicks inside Team System Explorer and Visual Studio. You do not really think, that we have no tools to make our everyday job easier. We have. However, we never package and release it. Let me introduce “TITS” – Tools, Invaluable for Team System. This suite I’m planning to release as another open source project within couple of months.
What “TITS” includes? First of all –
“QOF” – Quick Open File
This tools is absolutely invaluable if you have big solutions. While all it knows to do is to search. But, wait, what’s wrong with build-in search of Visual Studio? First of all, it does not search Solution items and files, are in solution directory, but not in project. Also it cannot fix your typos and errors. Also it does not know to move you quickly to found solution item in Solution Explorer or in Source Editor.
Basic set of QOF features:
- No mouse – open any file
- No mouse – locate any file in solution explorer
- Highlighting found items
- Multiple files open
- Filter by source files only, resources, owner or any other kind of filters
- Search inside TFS, including history, changesets, shelves (either private and public)
- …and much much more
Next tool is:
“WIBREW” – Who Is Breaking What
Absolutely invaluable tool to know who actually breaking what file inside TFS. For example, I do not want to lock files, while I still want to know who holds what file. TFS provides such feature out-of-the-box, however from command prompt only. You can add it even as macro. Like this:
However it not user friendly and impossible for use, ‘cos it looks as following:
You do not know what actually developer doing, where and why. With “WIBREW”, you can know:
- When developer started to break files
- What exactly he’s doing
- Is the breaking file locked or now
- Where the developer breaks it (workspace and computer name of the user)
- …and much much more
Another tool is:
“WITCH” – What I have To Check-in
If you ever worked with Team Force, you know what this tool is doing. It shows you a preview of all changed files, you’ll check-in. For some reason, TFS has no such feature. Let’s imagine, that your work method is to check out everything, change something and check-in only changed files. Until here TFS does everything, however if you want to preview changeset (for example in order to compare with “WIBREW” output), you can not. Here “WITCH” comes to help.
[Here should be a screenshot of “WITCH”, but it looks exactly the same as “WIBREW” with shameless blurring]
Another invaluable tool is:
“VOCUS” – VOid CUstom Settings for check in
This tool is absolutely UI-less. It allows developers to work with their own custom settings in Visual Studio, while for check-in and check-out it format all documents, according predefined custom settings (for example indentation). How many times, you tried to merge files, when all the difference is indentation it tab size? Well, this tool solves this problem.
It stores custom settings for each user (BTW, it also makes able for each developer to restore his settings fluently in any computer) and reformat documents on check-in action toward corporate settings, when on check-out toward custom developer’s setting.
“SHMOC” – SHow MOre Code
This is not actually tool, works with TFS. It rather works with your Visual Studio Development Environment. It’s UI-less as well and makes able to hide and restore all docking windows in VS. It makes you able to write in “Dark Room” mode (which is full screen, distraction free environment) and return to Visual Studio within one button press. It can also change VS color scheme, if required.
There are some other tools should be inside this suite, however, I still have no names for them
Also, if you have something interesting, and you want to contribute it to this suite, you’re highly welcome.
PS: This blog is about code, but this post is 6th in row without even one line of code, so I have to fix it as soon as possible. Thus, I’ll example how WIBREW works under the hood. Other words, small example of how to work with TFS API from Visual Studio plugin.
First of all, as in any VS plugin, you need to acquire DTE2 application object:
_applicationObject = (DTE2)application;
_addInInstance = (AddIn)addInInst;
When you have it, you need to detect what TFS server you’re working with and what are user credentials for this session. The common problem of WIBREW for poor men, was how to work with this tool over VPN (when your connected session is only inside VS). So each time, you tried to run it, you had to enter your domain credentials – very inconvenience way of work.
In order to prevent it, let’s ask your environment about Team Foundation information:
private TeamFoundationServerExt _tfsExt;
…
_tfsExt = (TeamFoundationServerExt)_applicationObject.GetObject("Microsoft.VisualStudio.TeamFoundation.TeamFoundationServerExt");
Also, you can be notified when your work project context was changed. To do this, just subscribe to ProjectContextChanged event and handle it inside:
_tfsExt.ProjectContextChanged += OnProjectContextChanged;
…
public void OnProjectContextChanged(object sender, EventArgs e) {
if (!string.IsNullOrEmpty(_tfsExt.ActiveProjectContext.ProjectName)) {
Now when we know, that we have out active project context, all we have to do is to ask about changes
private VersionControlExt _vcExt;
…
_vcExt = (VersionControlExt)_applicationObject.GetObject("Microsoft.VisualStudio.TeamFoundation.VersionControl.VersionControlExt");
Inside VersionControlExt object you have following self-descriptive properties and methods: FindChangeSet, History, PendingChanges, SolutionWorkspace etc. however it works only with TFS solution explorer. To handle pending changes for the project without tickling TFS, we can use it internal methods. All the difference is with references. To work with Visual Studio TFS explorer methods, you should reference:
Microsoft.VisualStudio.TeamFoundation.dll, Microsoft.VisualStudio.TeamFoundation.Client.dll and Microsoft.VisualStudio.TeamFoundation.VersionControl.dll, while working with TFS API directly, use Microsoft.TeamFoundation.dll, Microsoft.TeamFoundation.Client.dll and Microsoft.TeamFoundation.VersionControl.dll from [PROGRAM FILES]\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\. Just like this:
VersionControlServer _vcs…_vcs = (VersionControlServer)_server.GetService(typeof(VersionControlServer));…var _sets = _vcs.QueryPendingSets( new[] { new ItemSpec(serverPath, RecursionType.Full) }, null, null);…foreach (PendingSet set in sets) {…
//Get everything you need here
We done. It’s very easy to work with Team System from inside Visual Studio. Also it’s very easy to build useful tools, not built by Microsoft for some reason
Have a nice day, be good people and wait for me to beatify sources before releasing as another Open Source application.
January 27th, 2009 · Comments (11)
Nifty time savers for WPF development
I just published an article on Code Project, that explains how to use my latest FM USB library for building real world software radio receiver with WPF. There I referenced to some nifty WPF time savers, I’m using for everyday development. So, today I want to share those code pieces with you.
Binding time savers
Want to use following syntax for set binding in code?
Presets.SetBinding(ListBox.ItemsSourceProperty, _device, "Presets");
This piece of code sets binding to Preset DependencyObject, which is Listbox and connects ListBox.ItemsSource dependency property with “Presets” property of CLR object _device. How it done? Simple, as usual
[DebuggerStepThrough]
public static BindingExpressionBase SetBinding(this DependencyObject target, DependencyProperty dp, object source, string path) {
Binding b = new Binding(path);
b.Source = source;
return BindingOperations.SetBinding(target, dp, b);
}
But what to do when we need a converter? Simple:
[DebuggerStepThrough]
public static BindingExpressionBase SetBinding(this DependencyObject target, DependencyProperty dp, object source, string path, IValueConverter converter) {
Binding b = new Binding(path);
b.Source = source;
b.Converter = converter;
return BindingOperations.SetBinding(target, dp, b);
}
However to use this method, we need to create special object, which implements IValueConverter. Whey not to do it generically? Like this:
SignalTransform.SetBinding(ScaleTransform.ScaleYProperty, _device.RDS,"SignalStrength", new ValueConverter<byte, double>(b => { return 1-(b / 36d); }));
But we need this special handy ValueConverter class. What’s the problem? Here come the king:
public class ValueConverter<TIN, TOUT> : IValueConverter {
public ValueConverter(Func<TIN, TOUT> forwardConversion) {
ForwardConversion = forwardConversion;
}public ValueConverter(Func<TIN, TOUT> forwardConversion, Func<TOUT, TIN> reverseConversion) {
ForwardConversion = forwardConversion;
ReverseConversion = reverseConversion;
}public Func<TIN, TOUT> ForwardConversion { get; set; }
public Func<TOUT, TIN> ReverseConversion { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
try {
var in1 = Object.ReferenceEquals(value, DependencyProperty.UnsetValue) ? default(TIN) : (TIN)value;
return ForwardConversion(in1);
} catch {
return Binding.DoNothing;
}
}public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
try {
var out1 = Object.ReferenceEquals(value, DependencyProperty.UnsetValue) ? default(TOUT) : (TOUT)value;
return ReverseConversion(out1);
} catch {
return Binding.DoNothing;
}
}}
Isn’t it really simple? But what to do with ugly App.Current.Dispatcher.BeginInvoke((SendOrPostCallback)delegate(object o)…? Use dispatcher time savers.
Dispatcher time savers
Don’t you ever want to do this in order to make context switching between UI thread and other application thread in WPF?
this.Dispatch(() => {… DO SOMETHING IN UI THREAD …};
Now you can (with default and preset DispatcherPriority:
public static DispatcherOperation Dispatch(this DispatcherObject sender, Action callback) { return sender.Dispatch(DispatcherPriority.Normal, callback); }
public static DispatcherOperation Dispatch(this DispatcherObject sender, DispatcherPriority priority, Action callback) {
if (sender.Dispatcher == null) return null;
if (sender.Dispatcher.CheckAccess()) {
callback();
return null;
} else {
return sender.Dispatcher.BeginInvoke(priority, callback);
}
}
Nice, isn’t it? But what to do if we do not want to set binding, but we do want to be notified about property change of dependency objects?
Bindingless handlers time saver
Let’s assume, that we have “Tune” UIelement, which has Angle property, but not exposes PropertyChanged event (like it done with Rotary custom control by Expression Blend team… Designers, you know…
However I want to be able to add handler for Angle dependency property changed event and do something when it changed. Like this:
Tune.AddValueChanged(RotaryControl.RotaryControl.AngleProperty, (s, ex) => {
_device.Tune(Tune.Angle > _prevTune);
_prevTune = Tune.Angle;
});
Here comes our time saver for this purpose:
public static void AddValueChanged(this DependencyObject sender, DependencyProperty property, EventHandler handler) {
DependencyPropertyDescriptor.FromProperty(property, sender.GetType()).AddValueChanged(sender, handler);
}
But if we add handler we should be able to remove it too.
public static void RemoveValueChanged(this DependencyObject sender, DependencyProperty property, EventHandler handler) {
DependencyPropertyDescriptor.FromProperty(property, sender.GetType()).RemoveValueChanged(sender, handler);
}
Now we done with some of my nifty helpers. And last, but not the least:
All times question: how to scale ranges
Here is how
public static double ToRange(this double value, double minSource, double maxSource, double minTarget, double maxTarget) {
var sr = maxSource – minSource;
var tr = maxTarget – minTarget;
var ratio = sr / tr;
return minTarget+(value / ratio);
}
Now we can connect them and get something like this:
Volume.AddValueChanged(RotaryControl.RotaryControl.AngleProperty, (s, ex) => {
DirectSoundMethods.Volume = (int)Volume.Angle.ToRange(Volume.CounterClockwiseMostAngle, Volume.ClockwiseMostAngle, -4000, 0);
});
Isn’t it brilliant?
Have a good day and be sure to read and rate my last article on Code Project
Be good people.
January 8th, 2009 · Comments (2)
Audio CD operation including CD-Text reading in pure C#
Recently we spoke about reading radio data in C#, however as in any vehicle we have also CD players. So what can be better, than to have an ability to play CDs while being notified about track name, gathered from CD-Text?
So, let’s start. First of all, I want to express my pain with MSDN documentation about CD-ROM structure. Documentation team, please, please, please update it. First of all it is no accurate, then there are a ton of things missing. However, “À la guerre comme à la guerre”, thus I invested three days in deep DDK research.
Before we can do anything with CD-ROM, we have to find it. I took the same approach as I used for HID devices. Let’s create a device
[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
public class CDDADevice : SafeHandleZeroOrMinusOneIsInvalid, IDisposable, INotifyPropertyChanged {
Internal constructor for security reasons
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
internal CDDADevice(char drive) : base(true) {
findDevice(drive);
}
And a find method itself
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
private void findDevice(char drive) {
if (Drive == drive) return;
if (Native.GetDriveType(string.Concat(drive, ":\")) == Native.DRIVE.CDROM) {
this.handle = Native.CreateFile(string.Concat("\\.\", drive, ‘:’), Native.GENERIC_READ, Native.FILE_SHARE_READ, IntPtr.Zero, Native.OPEN_EXISTING, Native.FILE_ATTRIBUTE_READONLY | Native.FILE_FLAG_SEQUENTIAL_SCAN, IntPtr.Zero);
if (this.handle.ToInt32() != -1 && this.handle.ToInt32() != 0) this.Drive = drive;
}
}
Where GetDriveType and CreateFile are win32 methods with following signatures
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern DRIVE GetDriveType(string drive);
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr SecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern bool CloseHandle(IntPtr hHandle);
Also, we need some constants
internal enum DRIVE : byte {
UNKNOWN = 0,
NO_ROOT_DIR,
REMOVABLE,
FIXED,
REMOTE,
CDROM,
RAMDISK
}internal const uint GENERIC_READ = 0×80000000;
internal const uint FILE_SHARE_READ = 0×00000001;
internal const uint OPEN_EXISTING = 3;
internal const uint FILE_ATTRIBUTE_READONLY = 0×00000001;
internal const uint FILE_FLAG_SEQUENTIAL_SCAN = 0×08000000;
Now, when we have our cdrom handle in hands, we can read it’s Table Of Content. Now, thing become harder because of the fact, that we have to use very complicated platform method:
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
[DllImport("kernel32.dll", EntryPoint = "DeviceIoControl", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeviceIoControl(
[In] IntPtr hDevice,
IOCTL dwIoControl,
[In] IntPtr lpInBuffer,
uint nInBufferSize,
IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped);
When thing are generic it’s good, however this one is, probably, most generic method in Win32 API. You can do anything with this method and you never know what to expect in lpOutBuffer
However, as I told earlier, I invested three days in investigations and researches (tnx to DDK documentation team) and now things become to be clearer. We need to get CDROM_TOC. It done by invoking IOCTL_CDROM_READ_TOC call
uint bytesRead = 0;
TOC = new Native.CDROM_TOC();
TOC.Length = (ushort)Marshal.SizeOf(TOC);
var hTOC = Marshal.AllocHGlobal(TOC.Length);
Marshal.StructureToPtr(TOC, hTOC, true);
if (Native.DeviceIoControl(this.handle, Native.IOCTL.CDROM_READ_TOC, IntPtr.Zero, 0, hTOC, TOC.Length, out bytesRead, IntPtr.Zero)) Marshal.PtrToStructure(hTOC, TOC);
Marshal.FreeHGlobal(hTOC);
But, not too fast. CDROM_TOC contains array of TRACK_DATA with unknown size.
typedef struct _CDROM_TOC { UCHAR Length[2]; UCHAR FirstTrack; UCHAR LastTrack; TRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS];} CDROM_TOC, *PCDROM_TOC;typedef struct _TRACK_DATA { UCHAR Reserved; UCHAR Control : 4; UCHAR Adr : 4; UCHAR TrackNumber; UCHAR Reserved1; UCHAR Address[4];} TRACK_DATA, *PTRACK_DATA;
P/Invoke it! But how to marshal unknown array? We should create wrapper object. Also there is very fun BitVector, used in this structure! What’s the problem? Pin it with some Math!
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class CDROM_TOC {public ushort Length;
public byte FirstTrack;
public byte LastTrack;
public TRACK_DATA_ARRAY TrackData;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TRACK_DATA {public byte Reserved;
public byte bitVector;
public byte Control {
get { return ((byte)((this.bitVector & 15u))); }
set { this.bitVector = ((byte)((value | this.bitVector))); }
}
public byte Adr {
get { return ((byte)(((this.bitVector & 240u) / 16))); }
set { this.bitVector = ((byte)(((value * 16) | this.bitVector))); }
}
public byte TrackNumber;
public byte Reserved1;
public uint Address;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal sealed class TRACK_DATA_ARRAY {internal TRACK_DATA_ARRAY() { data = new byte[MAXIMUM_NUMBER_TRACKS * Marshal.SizeOf(typeof(TRACK_DATA))]; }
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAXIMUM_NUMBER_TRACKS * 8)]
private byte[] data;public TRACK_DATA this[int idx] {
get {
if ((idx < 0) | (idx >= MAXIMUM_NUMBER_TRACKS)) throw new IndexOutOfRangeException();
TRACK_DATA res;
var hData = GCHandle.Alloc(data, GCHandleType.Pinned);
try {
var buffer = hData.AddrOfPinnedObject();
buffer = (IntPtr)(buffer.ToInt32() + (idx * Marshal.SizeOf(typeof(TRACK_DATA))));
res = (TRACK_DATA)Marshal.PtrToStructure(buffer, typeof(TRACK_DATA));
} finally {
hData.Free();
}
return res;
}
}
}
Fuf, done. The code is rather self explaining, we just “tell” marshaler, that we have byte array, while calculating pointers to pinned object to get actual value and marshal it back. So, now we have TOC. So, we know how many tracks we have and addresses to data chunks inside the CD.
But it now enough to understand where our tracks. CD-ROM structure is very tricky. There we have blocks or sectors (which is the smallest chunks of data), so we have to convert bytes into sector addresses. Each block is 2352 bytes in RAW mode, while address value inside TRACK_DATA points us to layout address with is sync, sector id, error detection etc… So, in order to convert TRACK object into actual track number on disk, we have to stick to following method
public static int SectorAddress(this TRACK_DATA data) {
var addr = BitConverter.GetBytes(data.Address);return (addr[1] * 60 * 75 + addr[2] * 75 + addr[3]) – 150;
}
Now, when we know numbers of tracks, we also know start and end sector, disk type and other useful information we are ready to twist it a bit and read CD-Text (if there are and your CD reader supports it).
So, coming back to our favorite method DeviceIoControl, but this time with IOCTL_CDROM_READ_TOC_EX control.
bytesRead = 0;
TOCex = new Native.CDROM_READ_TOC_EX {Format = Native.CDROM_READ_TOC_EX_FORMAT.CDTEXT
};
var sTOCex = Marshal.SizeOf(TOCex);
var hTOCex = Marshal.AllocHGlobal(sTOCex);
Marshal.StructureToPtr(TOCex, hTOCex, true);
var Data = new Native.CDROM_TOC_CD_TEXT_DATA();
Data.Length = (ushort)Marshal.SizeOf(Data);var hData = Marshal.AllocHGlobal(Data.Length);
Marshal.StructureToPtr(Data, hData, true);
if (Native.DeviceIoControl(this.handle, Native.IOCTL.CDROM_READ_TOC_EX, hTOCex, (ushort)sTOCex, hData, Data.Length, out bytesRead, IntPtr.Zero)) Marshal.PtrToStructure(hData, Data);
Marshal.FreeHGlobal(hData);
Marshal.FreeHGlobal(hTOCex);
Looks too simple? Let’s see inside CDROM_READ_TOC_EX structure. It is very similar to _CDROM_TOC.
typedef struct _CDROM_READ_TOC_EX { UCHAR Format : 4; UCHAR Reserved1 : 3; UCHAR Msf : 1; UCHAR SessionTrack; UCHAR Reserved2; UCHAR Reserved3;} CDROM_READ_TOC_EX, *PCDROM_READ_TOC_EX;
Simple. Isn’t it?
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CDROM_READ_TOC_EX {public uint bitVector;
public CDROM_READ_TOC_EX_FORMAT Format {
get { return ((CDROM_READ_TOC_EX_FORMAT)((this.bitVector & 15u))); }
set { this.bitVector = (uint)((byte)value | this.bitVector); }
}
public uint Reserved1 {
get { return ((uint)(((this.bitVector & 112u) / 16))); }set { this.bitVector = ((uint)(((value * 16) | this.bitVector))); }
}
public uint Msf {
get { return ((uint)(((this.bitVector & 128u) / 128))); }set { this.bitVector = ((uint)(((value * 128) | this.bitVector))); }
}
public byte SessionTrack;
public byte Reserved2;
public byte Reserved3;
}
But what will come inside lpOutBuffer? Fellow structure, named CDROM_TOC_CD_TEXT_DATA with unknown size array of CDROM_TOC_CD_TEXT_DATA_BLOCK
typedef struct _CDROM_TOC_CD_TEXT_DATA { UCHAR Length[2]; UCHAR Reserved1; UCHAR Reserved2; CDROM_TOC_CD_TEXT_DATA_BLOCK Descriptors[0];} CDROM_TOC_CD_TEXT_DATA, *PCDROM_TOC_CD_TEXT_DATA;typedef struct _CDROM_TOC_CD_TEXT_DATA_BLOCK { UCHAR PackType; UCHAR TrackNumber:7; UCHAR ExtensionFlag:1; UCHAR SequenceNumber; UCHAR CharacterPosition:4; UCHAR BlockNumber:3; UCHAR Unicode:1; union { UCHAR Text[12]; WCHAR WText[6]; }; UCHAR CRC[2];} CDROM_TOC_CD_TEXT_DATA_BLOCK, *PCDROM_TOC_CD_TEXT_DATA_BLOCK;
Too bad to be true. Isn’t it? Let’s try to marshal it my hands (with the trick used for TRACK_DATA
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class CDROM_TOC_CD_TEXT_DATA {public ushort Length;
public byte Reserved1;
public byte Reserved2;
public CDROM_TOC_CD_TEXT_DATA_BLOCK_ARRAY Descriptors;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal sealed class CDROM_TOC_CD_TEXT_DATA_BLOCK_ARRAY {internal CDROM_TOC_CD_TEXT_DATA_BLOCK_ARRAY() { data = new byte[MINIMUM_CDROM_READ_TOC_EX_SIZE * MAXIMUM_NUMBER_TRACKS * Marshal.SizeOf(typeof(CDROM_TOC_CD_TEXT_DATA_BLOCK))]; }
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MINIMUM_CDROM_READ_TOC_EX_SIZE * MAXIMUM_NUMBER_TRACKS * 18)]
private byte[] data;public CDROM_TOC_CD_TEXT_DATA_BLOCK this[int idx] {
get {
if ((idx < 0) | (idx >= MINIMUM_CDROM_READ_TOC_EX_SIZE * MAXIMUM_NUMBER_TRACKS)) throw new IndexOutOfRangeException();
CDROM_TOC_CD_TEXT_DATA_BLOCK res;
var hData = GCHandle.Alloc(data, GCHandleType.Pinned);
try {
var buffer = hData.AddrOfPinnedObject();
buffer = (IntPtr)(buffer.ToInt32() + (idx * Marshal.SizeOf(typeof(CDROM_TOC_CD_TEXT_DATA_BLOCK))));
res = (CDROM_TOC_CD_TEXT_DATA_BLOCK)Marshal.PtrToStructure(buffer, typeof(CDROM_TOC_CD_TEXT_DATA_BLOCK));
} finally {
hData.Free();
}
return res;
}
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CDROM_TOC_CD_TEXT_DATA_BLOCK {public CDROM_CD_TEXT_PACK PackType;
public byte bitVector1;
public byte TrackNumber {
get { return ((byte)((this.bitVector1 & 127u))); }
set { this.bitVector1 = ((byte)((value | this.bitVector1))); }
}
public byte ExtensionFlag {
get { return ((byte)(((this.bitVector1 & 128u) / 128))); }set { this.bitVector1 = ((byte)(((value * 128) | this.bitVector1))); }
}
public byte SequenceNumber;
public byte bitVector2;public byte CharacterPosition {
get { return ((byte)((this.bitVector2 & 15u))); }set { this.bitVector2 = ((byte)((value | this.bitVector2))); }
}
public byte BlockNumber {
get { return ((byte)(((this.bitVector2 & 112u) / 16))); }set { this.bitVector2 = ((byte)(((value * 16) | this.bitVector2))); }
}
public byte Unicode {
get { return ((byte)(((this.bitVector2 & 128u) / 128))); }set { this.bitVector2 = ((byte)(((value * 128) | this.bitVector2))); }
}
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12, ArraySubType = UnmanagedType.I1)]
public byte[] TextBuffer;
public string Text {
get { return (Unicode == 1) ? ASCIIEncoding.ASCII.GetString(TextBuffer) : UTF32Encoding.UTF8.GetString(TextBuffer); }}
public ushort CRC;
}
Can’t you see a small problem here? Yes, we do not know the actual/maximum size of CDROM_TOC_CD_TEXT_DATA_BLOCK array. Until, I’ll find a nice way to marshal smart pointers, we’ll stick to MAX_TRACKS (100) * MIN_DATA_BLOCK (2).
We almost finished and the worst things are behind us. Now you should enumerate thru CDROM_TOC_CD_TEXT_DATA_BLOCK and look for Text, TrackNumber and SequenceNumber (which is continuation of text reported). For example, slot for ALBUM_NAME “Satisfaction” will looks as following
| BlockNumber | 0×00 |
| CharacterPosition | 0×00 |
| CRC | 0x3EAB |
| SequenceNumber | 0×00 |
| Text |
