<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tamir Khason - Just code &#187; Interop</title>
	<atom:link href="http://khason.net/tag/interop/feed/" rel="self" type="application/rss+xml" />
	<link>http://khason.net</link>
	<description>Take care of the sense, and the sounds will take care of themselves.</description>
	<lastBuildDate>Thu, 15 Apr 2010 13:25:55 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Quick how to: Reduce number of colors programmatically</title>
		<link>http://khason.net/dev/quick-how-to-reduce-number-of-colors-programmatically/</link>
		<comments>http://khason.net/dev/quick-how-to-reduce-number-of-colors-programmatically/#comments</comments>
		<pubDate>Mon, 09 Feb 2009 18:46:50 +0000</pubDate>
		<dc:creator>Tamir</dc:creator>
				<category><![CDATA[DEV]]></category>
		<category><![CDATA[.NET 3.5]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[CodeProject]]></category>
		<category><![CDATA[DirectX]]></category>
		<category><![CDATA[Interop]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[Tips and Tricks]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[Work process]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[WPF crossbow]]></category>

		<guid isPermaLink="false">http://khason.net/dev/quick-how-to-reduce-number-of-colors-programmatically/</guid>
		<description><![CDATA[My colleague just asked me about how to reduce a number of colors in image programmatically. This is very simple task and contains of 43 steps: First of all, you have to read a source image using (var img = Image.FromFile(name)) { var bmpEncoder = ImageCodecInfo.GetImageDecoders().FirstOrDefault(e =&#62; e.FormatID == ImageFormat.Bmp.Guid); Then create your own encoder [...]


Related posts:<ol><li><a href='http://khason.net/dev/read-singleton-approach-in-wpf-application/' rel='bookmark' title='Permanent Link: Real singleton approach in WPF application'>Real singleton approach in WPF application</a></li>
<li><a href='http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code/' rel='bookmark' title='Permanent Link: INotifyPropertyChanged auto wiring or how to get rid of redundant code'>INotifyPropertyChanged auto wiring or how to get rid of redundant code</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>My colleague just asked me about how to reduce a number of colors in image programmatically. This is very simple task and contains of 43 <img src='http://khason.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  steps:</p>
<p><img style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" title="Simple color matrix" src="http://khason.net/images/2009/02/image2.png" border="0" alt="Simple color matrix" width="383" height="366" /></p>
<p>First of all, you have to read a source image</p>
<blockquote><p>using (var img = Image.FromFile(name)) {<br />
var bmpEncoder = ImageCodecInfo.GetImageDecoders().FirstOrDefault(e =&gt; e.FormatID == ImageFormat.Bmp.Guid);</p></blockquote>
<p>Then create your own encoder with certain color depth (32 bits in this case)</p>
<blockquote><p>var myEncoder = System.Drawing.Imaging.Encoder.ColorDepth;<br />
var myEncoderParameter = new EncoderParameter(myEncoder, 32L);<br />
var myEncoderParameters = new EncoderParameters(1) { Param = new EncoderParameter[] { myEncoderParameter } };</p></blockquote>
<p>Then save it</p>
<blockquote><p>img.Save(name.Replace(&#8220;.png&#8221;, &#8220;.bmp&#8221;), bmpEncoder, myEncoderParameters);</p></blockquote>
<p>It it enough? Not really, because if you’re going to loose colors (by reducing color depth), it makes sense to avoid letting default WIX decoder to do this, thus you have to find nearest base colors manually. How to do this? By using simple math</p>
<blockquote><p>Color GetNearestBaseColor(Color exactColor) {<br />
Color nearestColor = Colors.Black;<br />
int cnt = baseColors.Count;<br />
for (int i = 0; i &lt; cnt; i++) {<br />
int rRed = baseColors[i].R &#8211; exactColor.R;<br />
int rGreen = baseColors[i].G &#8211; exactColor.G;<br />
int rBlue = baseColors[i].B &#8211; exactColor.B;</p>
<p>int rDistance =<br />
(rRed * rRed) +<br />
(rGreen * rGreen) +<br />
(rBlue * rBlue);<br />
if (rDistance == 0.0) {<br />
return baseColors[i];<br />
} else if (rDistance &lt; maxDistance) {<br />
maxDistance = rDistance;<br />
nearestColor = baseColors[i];<br />
}<br />
}<br />
return nearestColor;<br />
}</p></blockquote>
<p>Now, you can either change colors on base image directly</p>
<blockquote><p>unsafe {<br />
uint* pBuffer = (uint*)hMap;<br />
for (int iy = 0; iy &lt; (int)ColorMapSource.PixelHeight; ++iy)<br />
{<br />
for (int ix = 0; ix &lt; nWidth; ++ix)<br />
{<br />
Color nc = GetNearestBaseColor(pBuffer[0].FromOle());</p>
<p>pBuffer[0] &amp;= (uint)((uint)nc.A &lt;&lt; 24) | //A<br />
(uint)(nc.R &lt;&lt; 16 ) | //R<br />
(uint)(nc.G &lt;&lt; 8 ) | //G<br />
(uint)(nc.B ); //B<br />
++pBuffer;<br />
}<br />
pBuffer += nOffset;<br />
}<br />
}</p></blockquote>
<p>Or, if you’re in WPF and .NET 3.5 <a title="HLSL (Pixel shader) effects tutorial" href="http://khason.net/blog/hlsl-pixel-shader-effects-tutorial/" target="_blank">create simple pixel shader effect</a> to do it for you in hardware. Now, my colleague can do it himself in about 5 minutes <img src='http://khason.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  . Have a nice day and be good people.</p>


<p>Related posts:<ol><li><a href='http://khason.net/dev/read-singleton-approach-in-wpf-application/' rel='bookmark' title='Permanent Link: Real singleton approach in WPF application'>Real singleton approach in WPF application</a></li>
<li><a href='http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code/' rel='bookmark' title='Permanent Link: INotifyPropertyChanged auto wiring or how to get rid of redundant code'>INotifyPropertyChanged auto wiring or how to get rid of redundant code</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://khason.net/dev/quick-how-to-reduce-number-of-colors-programmatically/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Bootstrapper for .NET framework version detector</title>
		<link>http://khason.net/tech/bootstrapper-for-net-framework-version-detector/</link>
		<comments>http://khason.net/tech/bootstrapper-for-net-framework-version-detector/#comments</comments>
		<pubDate>Wed, 04 Feb 2009 17:22:26 +0000</pubDate>
		<dc:creator>Tamir</dc:creator>
				<category><![CDATA[TECH]]></category>
		<category><![CDATA[.NET 3.5]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[DEV]]></category>
		<category><![CDATA[download]]></category>
		<category><![CDATA[Interop]]></category>
		<category><![CDATA[My tools]]></category>
		<category><![CDATA[promo]]></category>
		<category><![CDATA[soft]]></category>
		<category><![CDATA[Tips and Tricks]]></category>
		<category><![CDATA[Vista]]></category>
		<category><![CDATA[VSTS]]></category>
		<category><![CDATA[Work process]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[XNA]]></category>

		<guid isPermaLink="false">http://khason.net/tech/bootstrapper-for-net-framework-version-detector/</guid>
		<description><![CDATA[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 [...]

<p/>]]></description>
			<content:encoded><![CDATA[<p>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.</p>
<p><a title=".NET Framework Detector" href="http://khason.net/images/2009/02/whoooot.exe" target="_blank" rel="enclosure"><img title=".NET framework vrsion detector" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="212" alt=".NET framework vrsion detector" src="http://khason.net/images/2009/02/image.png" width="310" border="0" /></a> </p>
<p>Until now there are 15 possible .NET frameworks can be installed on client’s machine. Here the table of possible and official supported versions, <a href="http://support.microsoft.com/kb/318785" target="_blank">as appears in Q318785</a></p>
<table cellspacing="0" cellpadding="2" width="400" border="1">
<tbody>
<tr>
<td valign="top" width="200">.NET version</td>
<td valign="top" width="200">Actual version</td>
</tr>
<tr>
<td valign="top" width="200">3.5 SP1</td>
<td valign="top" width="200">3.5.30729.1</td>
</tr>
<tr>
<td valign="top" width="200">3.5</td>
<td valign="top" width="200">3.5.21022.8</td>
</tr>
<tr>
<td valign="top" width="200">3.0 SP2</td>
<td valign="top" width="200">3.0.4506.2152</td>
</tr>
<tr>
<td valign="top" width="200">3.0 SP1</td>
<td valign="top" width="200">3.0.4506.648</td>
</tr>
<tr>
<td valign="top" width="200">3.0</td>
<td valign="top" width="200">3.0.4506.30</td>
</tr>
<tr>
<td valign="top" width="200">2.0 SP2</td>
<td valign="top" width="200">2.0.50727.3053</td>
</tr>
<tr>
<td valign="top" width="200">2.0 SP1</td>
<td valign="top" width="200">2.0.50727.1433</td>
</tr>
<tr>
<td valign="top" width="200">2.0</td>
<td valign="top" width="200">2.0.50727.42</td>
</tr>
<tr>
<td valign="top" width="200">1.1 SP1</td>
<td valign="top" width="200">1.1.4322.2032</td>
</tr>
<tr>
<td valign="top" width="200">1.1 SP1 (in 32 bit version of Windows 2003)</td>
<td valign="top" width="200">1.1.4322.2300</td>
</tr>
<tr>
<td valign="top" width="200">1.1</td>
<td valign="top" width="200">1.1.4322.573</td>
</tr>
<tr>
<td valign="top" width="200">1.0 SP3</td>
<td valign="top" width="200">1.0.3705.6018</td>
</tr>
<tr>
<td valign="top" width="200">1.0 SP2</td>
<td valign="top" width="200">1.0.3705.288</td>
</tr>
<tr>
<td valign="top" width="200">1.0 SP1</td>
<td valign="top" width="200">1.0.3705.209</td>
</tr>
<tr>
<td valign="top" width="200">1.0</td>
<td valign="top" width="200">1.0.3705.0</td>
</tr>
</tbody>
</table>
<p>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 <a href="http://msdn2.microsoft.com/library/ydh6b3yb.aspx" target="_blank">in MSDN</a>.</p>
<p>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 <a href="http://blogs.msdn.com/astebner/archive/2009/01/31/9387659.aspx" target="_blank">Aaron Stebner</a>, who is Project Manager in XNA platform deployment team or <a href="http://khason.net/blog/action-required-smart-client-development-present-and-future/" target="_blank">attend my session next week</a> to learn do it yourself. We’ll speak about nifty ways to do it also. </p>
<p>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 <img src='http://khason.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><a href="http://khason.net/images/2009/02/whoooot.exe" target="_blank"><strong>Download whoooot.exe (13K) &gt;&gt;</strong></a></p>
<p>See you next week. </p>
<p>PS: Do not forget to <a href="http://www.codeplex.com/SnippetEditor/" target="_blank">download and install the new version</a> of <a href="http://khason.net/blog/visual-studio-snippet-designer/" target="_blank">Visual Studio Snippet Designer</a>, which is extremely useful tool by MVP <a href="http://msmvps.com/blogs/bill/" target="_blank">Bill McCarthy</a>, you’ll need it later next week…</p>
<p>Have a nice day and be good people.</p>


<p/>]]></content:encoded>
			<wfw:commentRss>http://khason.net/tech/bootstrapper-for-net-framework-version-detector/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Audio CD operation including CD-Text reading in pure C#</title>
		<link>http://khason.net/dev/audio-cd-operation-including-cd-text-reading-in-pure-c/</link>
		<comments>http://khason.net/dev/audio-cd-operation-including-cd-text-reading-in-pure-c/#comments</comments>
		<pubDate>Wed, 07 Jan 2009 18:46:26 +0000</pubDate>
		<dc:creator>Tamir</dc:creator>
				<category><![CDATA[DEV]]></category>
		<category><![CDATA[.NET 3.5]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[CodeProject]]></category>
		<category><![CDATA[Hardware]]></category>
		<category><![CDATA[Interop]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[source]]></category>
		<category><![CDATA[Tips and Tricks]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[Work process]]></category>

		<guid isPermaLink="false">http://khason.net/dev/audio-cd-operation-including-cd-text-reading-in-pure-c/</guid>
		<description><![CDATA[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 [...]


Related posts:<ol><li><a href='http://khason.net/dev/read-singleton-approach-in-wpf-application/' rel='bookmark' title='Permanent Link: Real singleton approach in WPF application'>Real singleton approach in WPF application</a></li>
<li><a href='http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code/' rel='bookmark' title='Permanent Link: INotifyPropertyChanged auto wiring or how to get rid of redundant code'>INotifyPropertyChanged auto wiring or how to get rid of redundant code</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>Recently we spoke about <a target="_blank" href="http://khason.net/dev/usb-fm-radio-library-was-published-on-codeplex/">reading radio data in C#</a>, 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 <a target="_blank" href="http://en.wikipedia.org/wiki/CD-Text">CD-Text</a>?</p>
<p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://khason.net/images/2009/01/image1.png" width="240" height="198" /> </p>
<p>So, let’s start. First of all, I want to express my pain with MSDN documentation <a target="_blank" href="http://msdn.microsoft.com/en-us/library/ms808417.aspx">about CD-ROM structure</a>. 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.</p>
<p>Before we can do anything with CD-ROM, we have to find it. I took the same approach as <a target="_blank" href="http://khason.net/blog/read-and-use-fm-radio-or-any-other-usb-hid-device-from-c/">I used for HID devices</a>. Let’s create a device</p>
<blockquote><p>[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]     <br />[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]      <br />public class CDDADevice : SafeHandleZeroOrMinusOneIsInvalid, IDisposable, INotifyPropertyChanged {</p>
</blockquote>
<p>Internal constructor for security reasons</p>
<blockquote><p>[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]     <br />internal CDDADevice(char drive) : base(true) {       <br />&#160;&#160; findDevice(drive);      <br />}</p>
</blockquote>
<p>And a find method itself</p>
<blockquote><p>[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]     <br />private void findDevice(char drive) {      <br />&#160;&#160; if (Drive == drive) return;      <br />&#160;&#160; if (Native.GetDriveType(string.Concat(drive, &quot;:\&quot;)) == Native.DRIVE.CDROM) {      <br />&#160;&#160;&#160;&#160;&#160; this.handle = Native.CreateFile(string.Concat(&quot;\\.\&quot;, drive, &#8216;:&#8217;), Native.GENERIC_READ, Native.FILE_SHARE_READ, IntPtr.Zero, Native.OPEN_EXISTING, Native.FILE_ATTRIBUTE_READONLY | Native.FILE_FLAG_SEQUENTIAL_SCAN, IntPtr.Zero);      <br />&#160;&#160;&#160;&#160;&#160; if (this.handle.ToInt32() != -1 &amp;&amp; this.handle.ToInt32() != 0) this.Drive = drive;      <br />&#160;&#160; }      <br />}</p>
</blockquote>
<p>Where GetDriveType and CreateFile are win32 methods with following signatures</p>
<blockquote><p>[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]     <br />[DllImport(&quot;kernel32.dll&quot;, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]      <br />internal static extern DRIVE GetDriveType(string drive);      <br />[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]      <br />[DllImport(&quot;kernel32.dll&quot;, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]      <br />internal static extern IntPtr CreateFile(      <br />&#160;&#160;&#160;&#160;&#160; string lpFileName,      <br />&#160;&#160;&#160;&#160;&#160; uint dwDesiredAccess,      <br />&#160;&#160;&#160;&#160;&#160; uint dwShareMode,      <br />&#160;&#160;&#160;&#160;&#160; IntPtr SecurityAttributes,      <br />&#160;&#160;&#160;&#160;&#160; uint dwCreationDisposition,      <br />&#160;&#160;&#160;&#160;&#160; uint dwFlagsAndAttributes,      <br />&#160;&#160;&#160;&#160;&#160; IntPtr hTemplateFile);      <br />[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]      <br />[DllImport(&quot;kernel32.dll&quot;, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]      <br />internal static extern bool CloseHandle(IntPtr hHandle);</p>
</blockquote>
<p>Also, we need some constants</p>
<blockquote><p>internal enum DRIVE : byte {     <br />&#160;&#160; UNKNOWN = 0,      <br />&#160;&#160; NO_ROOT_DIR,      <br />&#160;&#160; REMOVABLE,      <br />&#160;&#160; FIXED,      <br />&#160;&#160; REMOTE,      <br />&#160;&#160; CDROM,      <br />&#160;&#160; RAMDISK      <br />}</p>
<p>internal const uint GENERIC_READ = 0&#215;80000000;     <br />internal const uint FILE_SHARE_READ = 0&#215;00000001;      <br />internal const uint OPEN_EXISTING = 3;      <br />internal const uint FILE_ATTRIBUTE_READONLY = 0&#215;00000001;      <br />internal const uint FILE_FLAG_SEQUENTIAL_SCAN = 0&#215;08000000;</p>
</blockquote>
<p>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:</p>
<blockquote><p>[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]     <br />[DllImport(&quot;kernel32.dll&quot;, EntryPoint = &quot;DeviceIoControl&quot;, SetLastError=true)]      <br />[return: MarshalAs(UnmanagedType.Bool)]      <br />internal static extern bool DeviceIoControl(      <br />&#160;&#160; [In] IntPtr hDevice,      <br />&#160;&#160; IOCTL dwIoControl,       <br />&#160;&#160; [In] IntPtr lpInBuffer,       <br />&#160;&#160; uint nInBufferSize,       <br />&#160;&#160; IntPtr lpOutBuffer,       <br />&#160;&#160; uint nOutBufferSize,       <br />&#160;&#160; out uint lpBytesReturned,       <br />&#160;&#160; IntPtr lpOverlapped);</p>
</blockquote>
<p>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 <img src='http://khason.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  </p>
<p>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</p>
<blockquote><p>uint bytesRead = 0;     <br />TOC = new Native.CDROM_TOC();      <br />TOC.Length = (ushort)Marshal.SizeOf(TOC);      <br />var hTOC = Marshal.AllocHGlobal(TOC.Length);      <br />Marshal.StructureToPtr(TOC, hTOC, true);      <br />if (Native.DeviceIoControl(this.handle, Native.IOCTL.CDROM_READ_TOC, IntPtr.Zero, 0, hTOC, TOC.Length, out bytesRead, IntPtr.Zero)) Marshal.PtrToStructure(hTOC, TOC);      <br />Marshal.FreeHGlobal(hTOC);</p>
</blockquote>
<p>But, not too fast. CDROM_TOC contains array of TRACK_DATA with unknown size.</p>
<blockquote><pre>typedef struct _CDROM_TOC {&#160; UCHAR&#160; Length[2];&#160; UCHAR&#160; FirstTrack;&#160; UCHAR&#160; LastTrack;&#160; TRACK_DATA&#160; TrackData[MAXIMUM_NUMBER_TRACKS];} CDROM_TOC, *PCDROM_TOC;</pre>
<pre>typedef struct _TRACK_DATA {&#160; UCHAR&#160; Reserved;&#160; UCHAR&#160; Control : 4;&#160; UCHAR&#160; Adr : 4;&#160; UCHAR&#160; TrackNumber;&#160; UCHAR&#160; Reserved1;&#160; UCHAR&#160; Address[4];} TRACK_DATA, *PTRACK_DATA;</pre>
</blockquote>
<p><a target="_blank" href="http://khason.net/blog/pinvoke-cheat-sheet/">P/Invoke</a> 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!</p>
<blockquote>
<p>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]<br />
    <br />public class CDROM_TOC {</p>
<p>&#160;&#160; public ushort Length;</p>
<p>&#160;&#160; public byte FirstTrack;</p>
<p>&#160;&#160; public byte LastTrack;</p>
<p>&#160;&#160; public TRACK_DATA_ARRAY TrackData;</p>
<p>}</p>
<p>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]<br />
    <br />public struct TRACK_DATA {</p>
<p>&#160;&#160; public byte Reserved;</p>
<p>&#160;&#160; public byte bitVector;</p>
<p>&#160;&#160; public byte Control {</p>
<p>&#160;&#160;&#160;&#160;&#160; get { return ((byte)((this.bitVector &amp; 15u))); }</p>
<p>&#160;&#160;&#160;&#160;&#160; set { this.bitVector = ((byte)((value | this.bitVector))); }</p>
<p>&#160;&#160; }</p>
<p>&#160;&#160; public byte Adr {</p>
<p>&#160;&#160;&#160;&#160;&#160; get { return ((byte)(((this.bitVector &amp; 240u) / 16))); }</p>
<p>&#160;&#160;&#160;&#160;&#160; set { this.bitVector = ((byte)(((value * 16) | this.bitVector))); }</p>
<p>&#160;&#160; }</p>
<p>&#160;&#160; public byte TrackNumber;</p>
<p>&#160;&#160; public byte Reserved1;</p>
<p>&#160;&#160; public uint Address;</p>
<p>}</p>
<p>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]<br />
    <br />internal sealed class TRACK_DATA_ARRAY {</p>
<p>&#160;&#160; internal TRACK_DATA_ARRAY() { data = new byte[MAXIMUM_NUMBER_TRACKS * Marshal.SizeOf(typeof(TRACK_DATA))]; } </p>
<p>&#160;&#160; [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAXIMUM_NUMBER_TRACKS * 8)]<br />
    <br />&#160;&#160; private byte[] data;</p>
<p>&#160;&#160; public TRACK_DATA this[int idx] {</p>
<p>&#160;&#160;&#160;&#160;&#160; get {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ((idx &lt; 0) | (idx &gt;= MAXIMUM_NUMBER_TRACKS)) throw new IndexOutOfRangeException();</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; TRACK_DATA res;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var hData = GCHandle.Alloc(data, GCHandleType.Pinned);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; try {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var buffer = hData.AddrOfPinnedObject();</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; buffer = (IntPtr)(buffer.ToInt32() + (idx * Marshal.SizeOf(typeof(TRACK_DATA))));</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; res = (TRACK_DATA)Marshal.PtrToStructure(buffer, typeof(TRACK_DATA));</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } finally {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; hData.Free();</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return res;</p>
<p>&#160;&#160;&#160;&#160;&#160; }</p>
<p>&#160;&#160; }</p>
<p>}</p>
</blockquote>
<p>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. </p>
<p>But it now enough to understand where our tracks. <a target="_blank" href="http://en.wikipedia.org/wiki/CD-ROM">CD-ROM</a> 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</p>
<blockquote>
<p>public static int SectorAddress(this TRACK_DATA data) {<br />
    <br />&#160;&#160; var addr = BitConverter.GetBytes(data.Address);</p>
<p>&#160;&#160; return (addr[1] * 60 * 75 + addr[2] * 75 + addr[3]) &#8211; 150;</p>
<p>}</p>
</blockquote>
<p>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).</p>
<p>So, coming back to our favorite method DeviceIoControl, but this time with IOCTL_CDROM_READ_TOC_EX control. </p>
<blockquote>
<p>bytesRead = 0;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <br />TOCex = new Native.CDROM_READ_TOC_EX {</p>
<p>&#160;&#160; Format = Native.CDROM_READ_TOC_EX_FORMAT.CDTEXT</p>
<p>};</p>
<p>var sTOCex = Marshal.SizeOf(TOCex);</p>
<p>var hTOCex = Marshal.AllocHGlobal(sTOCex);</p>
<p>Marshal.StructureToPtr(TOCex, hTOCex, true); </p>
<p>var Data = new Native.CDROM_TOC_CD_TEXT_DATA();<br />
    <br />Data.Length = (ushort)Marshal.SizeOf(Data);</p>
<p>var hData = Marshal.AllocHGlobal(Data.Length);</p>
<p>Marshal.StructureToPtr(Data, hData, true);</p>
<p>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);</p>
<p>Marshal.FreeHGlobal(hData);</p>
<p>Marshal.FreeHGlobal(hTOCex);</p>
</blockquote>
<p>Looks too simple? Let’s see inside CDROM_READ_TOC_EX structure. It is very similar to _CDROM_TOC.</p>
<blockquote>
<pre>typedef struct _CDROM_READ_TOC_EX {&#160; UCHAR Format : 4;&#160; UCHAR Reserved1 : 3; &#160; UCHAR Msf : 1;&#160; UCHAR SessionTrack;&#160; UCHAR Reserved2;&#160; UCHAR Reserved3;} CDROM_READ_TOC_EX, *PCDROM_READ_TOC_EX;</pre>
</blockquote>
<p>Simple. Isn’t it?</p>
<blockquote>
<p>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]<br />
    <br />public struct CDROM_READ_TOC_EX {</p>
<p>&#160;&#160; public uint bitVector;</p>
<p>&#160;&#160; public CDROM_READ_TOC_EX_FORMAT Format {</p>
<p>&#160;&#160;&#160;&#160;&#160; get { return ((CDROM_READ_TOC_EX_FORMAT)((this.bitVector &amp; 15u))); }</p>
<p>&#160;&#160;&#160;&#160;&#160; set { this.bitVector = (uint)((byte)value | this.bitVector); }</p>
<p>&#160;&#160; } </p>
<p>&#160;&#160; public uint Reserved1 {<br />
    <br />&#160;&#160;&#160;&#160;&#160; get { return ((uint)(((this.bitVector &amp; 112u) / 16))); }</p>
<p>&#160;&#160;&#160;&#160;&#160; set { this.bitVector = ((uint)(((value * 16) | this.bitVector))); }</p>
<p>&#160;&#160; } </p>
<p>&#160;&#160; public uint Msf {<br />
    <br />&#160;&#160;&#160;&#160;&#160; get { return ((uint)(((this.bitVector &amp; 128u) / 128))); }</p>
<p>&#160;&#160;&#160;&#160;&#160; set { this.bitVector = ((uint)(((value * 128) | this.bitVector))); }</p>
<p>&#160;&#160; }</p>
<p>&#160;&#160; public byte SessionTrack;</p>
<p>&#160;&#160; public byte Reserved2;</p>
<p>&#160;&#160; public byte Reserved3;</p>
<p>}</p>
</blockquote>
<p>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</p>
<blockquote>
<pre>typedef struct _CDROM_TOC_CD_TEXT_DATA {&#160; UCHAR&#160; Length[2];&#160; UCHAR&#160; Reserved1;&#160; UCHAR&#160; Reserved2;&#160; CDROM_TOC_CD_TEXT_DATA_BLOCK&#160; Descriptors[0];} CDROM_TOC_CD_TEXT_DATA, *PCDROM_TOC_CD_TEXT_DATA;</pre>
<pre>typedef struct _CDROM_TOC_CD_TEXT_DATA_BLOCK {&#160; UCHAR&#160; PackType;&#160; UCHAR&#160; TrackNumber:7;&#160; UCHAR&#160; ExtensionFlag:1;&#160; UCHAR&#160; SequenceNumber;&#160; UCHAR&#160; CharacterPosition:4;&#160; UCHAR&#160; BlockNumber:3;&#160; UCHAR&#160; Unicode:1;&#160; union {&#160;&#160;&#160; UCHAR&#160; Text[12];&#160;&#160;&#160; WCHAR&#160; WText[6];&#160; };&#160; UCHAR&#160; CRC[2];} CDROM_TOC_CD_TEXT_DATA_BLOCK, *PCDROM_TOC_CD_TEXT_DATA_BLOCK;</pre>
</blockquote>
<p>Too bad to be true. Isn’t it? Let’s try to marshal it my hands (with the trick used for TRACK_DATA</p>
<blockquote>
<p>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]<br />
    <br />public class CDROM_TOC_CD_TEXT_DATA {</p>
<p>&#160;&#160; public ushort Length;</p>
<p>&#160;&#160; public byte Reserved1;</p>
<p>&#160;&#160; public byte Reserved2;</p>
<p>&#160;&#160; public CDROM_TOC_CD_TEXT_DATA_BLOCK_ARRAY Descriptors;</p>
<p>} </p>
<p>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]<br />
    <br />internal sealed class CDROM_TOC_CD_TEXT_DATA_BLOCK_ARRAY {</p>
<p>&#160;&#160; 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))]; } </p>
<p>&#160;&#160; [MarshalAs(UnmanagedType.ByValArray, SizeConst = MINIMUM_CDROM_READ_TOC_EX_SIZE * MAXIMUM_NUMBER_TRACKS * 18)]<br />
    <br />&#160;&#160; private byte[] data;</p>
<p>&#160;&#160; public CDROM_TOC_CD_TEXT_DATA_BLOCK this[int idx] {</p>
<p>&#160;&#160;&#160;&#160;&#160; get {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if ((idx &lt; 0) | (idx &gt;= MINIMUM_CDROM_READ_TOC_EX_SIZE * MAXIMUM_NUMBER_TRACKS)) throw new IndexOutOfRangeException();</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; CDROM_TOC_CD_TEXT_DATA_BLOCK res;</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var hData = GCHandle.Alloc(data, GCHandleType.Pinned);</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; try {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var buffer = hData.AddrOfPinnedObject();</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; buffer = (IntPtr)(buffer.ToInt32() + (idx * Marshal.SizeOf(typeof(CDROM_TOC_CD_TEXT_DATA_BLOCK))));</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; res = (CDROM_TOC_CD_TEXT_DATA_BLOCK)Marshal.PtrToStructure(buffer, typeof(CDROM_TOC_CD_TEXT_DATA_BLOCK));</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } finally {</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; hData.Free();</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return res;</p>
<p>&#160;&#160;&#160;&#160;&#160; }</p>
<p>&#160;&#160; }</p>
<p>} </p>
<p>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]<br />
    <br />public struct CDROM_TOC_CD_TEXT_DATA_BLOCK {</p>
<p>&#160;&#160; public CDROM_CD_TEXT_PACK PackType;</p>
<p>&#160;&#160; public byte bitVector1;</p>
<p>&#160;&#160; public byte TrackNumber {</p>
<p>&#160;&#160;&#160;&#160;&#160; get { return ((byte)((this.bitVector1 &amp; 127u))); }</p>
<p>&#160;&#160;&#160;&#160;&#160; set { this.bitVector1 = ((byte)((value | this.bitVector1))); }</p>
<p>&#160;&#160; } </p>
<p>&#160;&#160; public byte ExtensionFlag {<br />
    <br />&#160;&#160;&#160;&#160;&#160; get { return ((byte)(((this.bitVector1 &amp; 128u) / 128))); }</p>
<p>&#160;&#160;&#160;&#160;&#160; set { this.bitVector1 = ((byte)(((value * 128) | this.bitVector1))); }</p>
<p>&#160;&#160; } </p>
<p>&#160;&#160; public byte SequenceNumber;<br />
    <br />&#160;&#160; public byte bitVector2;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; </p>
<p>&#160;&#160; public byte CharacterPosition {<br />
    <br />&#160;&#160;&#160;&#160;&#160; get { return ((byte)((this.bitVector2 &amp; 15u))); }</p>
<p>&#160;&#160;&#160;&#160;&#160; set { this.bitVector2 = ((byte)((value | this.bitVector2))); }</p>
<p>&#160;&#160; } </p>
<p>&#160;&#160; public byte BlockNumber {<br />
    <br />&#160;&#160;&#160;&#160;&#160; get { return ((byte)(((this.bitVector2 &amp; 112u) / 16))); }</p>
<p>&#160;&#160;&#160;&#160;&#160; set { this.bitVector2 = ((byte)(((value * 16) | this.bitVector2))); }</p>
<p>&#160;&#160; } </p>
<p>&#160;&#160; public byte Unicode {<br />
    <br />&#160;&#160;&#160;&#160;&#160; get { return ((byte)(((this.bitVector2 &amp; 128u) / 128))); }</p>
<p>&#160;&#160;&#160;&#160;&#160; set { this.bitVector2 = ((byte)(((value * 128) | this.bitVector2))); }</p>
<p>&#160;&#160; }</p>
<p>&#160;&#160; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12, ArraySubType = UnmanagedType.I1)]</p>
<p>&#160;&#160; public byte[] TextBuffer; </p>
<p>&#160;&#160; public string Text {<br />
    <br />&#160;&#160;&#160;&#160;&#160; get { return (Unicode == 1) ? ASCIIEncoding.ASCII.GetString(TextBuffer) : UTF32Encoding.UTF8.GetString(TextBuffer); }</p>
<p>&#160;&#160; } </p>
<p>&#160;&#160; public ushort CRC;<br />
    <br />}</p>
</blockquote>
<p>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).</p>
<p>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 </p>
<table border="0" cellspacing="0" cellpadding="2" width="200">
<tbody>
<tr>
<td valign="top" width="100">BlockNumber</td>
<td valign="top" width="100">0&#215;00</td>
</tr>
<tr>
<td valign="top" width="100">CharacterPosition</td>
<td valign="top" width="100">0&#215;00</td>
</tr>
<tr>
<td valign="top" width="100">CRC</td>
<td valign="top" width="100">0x3EAB</td>
</tr>
<tr>
<td valign="top" width="100">SequenceNumber</td>
<td valign="top" width="100">0&#215;00</td>
</tr>
<tr>
<td valign="top" width="100">Text</td>
<td valign="top" width="100"> SATISFACTIO</td>
</tr>
</tbody>
</table>
<table border="0" cellspacing="0" cellpadding="2" width="219">
<tbody>
<tr>
<td valign="top" width="100">BlockNumber</td>
<td valign="top" width="117">0&#215;00</td>
</tr>
<tr>
<td valign="top" width="100">CharacterPosition</td>
<td valign="top" width="117">0x0B</td>
</tr>
<tr>
<td valign="top" width="100">CRC</td>
<td valign="top" width="117">0&#215;0564</td>
</tr>
<tr>
<td valign="top" width="100">SequenceNumber</td>
<td valign="top" width="117">0&#215;01</td>
</tr>
<tr>
<td valign="top" width="100">Text</td>
<td valign="top" width="117">N </td>
</tr>
</tbody>
</table>
<p>&#160;</p>
<p>And so on… What to do with all other data and how to use it to enhance listening (ripping/crunching/seeking) experience we’ll speak next time. Have a good day and be good people.</p>
<div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:5737277B-5D6D-4f48-ABFC-DD9C333F4C5D:eeb67b17-3e9f-4256-8b73-87096ef1d693" class="wlWriterEditableSmartContent">
<div><object width="425" height="355"><param name="movie" value="http://www.youtube.com/v/Y7T49_LPt-8&amp;hl=en"></param><embed src="http://www.youtube.com/v/Y7T49_LPt-8&amp;hl=en" type="application/x-shockwave-flash" width="425" height="355"></embed></object></div>
</div>


<p>Related posts:<ol><li><a href='http://khason.net/dev/read-singleton-approach-in-wpf-application/' rel='bookmark' title='Permanent Link: Real singleton approach in WPF application'>Real singleton approach in WPF application</a></li>
<li><a href='http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code/' rel='bookmark' title='Permanent Link: INotifyPropertyChanged auto wiring or how to get rid of redundant code'>INotifyPropertyChanged auto wiring or how to get rid of redundant code</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://khason.net/dev/audio-cd-operation-including-cd-text-reading-in-pure-c/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>USB FM radio library was published on CodePlex</title>
		<link>http://khason.net/dev/usb-fm-radio-library-was-published-on-codeplex/</link>
		<comments>http://khason.net/dev/usb-fm-radio-library-was-published-on-codeplex/#comments</comments>
		<pubDate>Fri, 02 Jan 2009 20:48:30 +0000</pubDate>
		<dc:creator>Tamir</dc:creator>
				<category><![CDATA[DEV]]></category>
		<category><![CDATA[.NET 3.5]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[DirectX]]></category>
		<category><![CDATA[download]]></category>
		<category><![CDATA[Hardware]]></category>
		<category><![CDATA[Interop]]></category>
		<category><![CDATA[My tools]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[promo]]></category>
		<category><![CDATA[soft]]></category>
		<category><![CDATA[source]]></category>

		<guid isPermaLink="false">http://khason.net/dev/usb-fm-radio-library-was-published-on-codeplex/</guid>
		<description><![CDATA[I just published a part of my latest project – dynamic library to work with FM receivers on CodePlex under MS-PL. So, feel free do download, test and use it. Note, that this release is preliminary and has a lot of bugs. Also, RDS is not fully implements as well as recording capabilities with Direct [...]


Related posts:<ol><li><a href='http://khason.net/itpro/quick-it-tip-how-to-build-bootable-usb-stick/' rel='bookmark' title='Permanent Link: Quick IT tip: How to build bootable USB stick'>Quick IT tip: How to build bootable USB stick</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><a target="_blank" href="http://www.codeplex.com/FM">I just published</a> a part of my latest project – dynamic library to work with <a target="_blank" href="http://www.codeplex.com/FM">FM receivers on CodePlex</a> under MS-PL. So, feel free do download, test and use it. </p>
<p>Note, that this release is preliminary and has a lot of bugs. Also, RDS is not fully implements as well as recording capabilities with Direct Sound. </p>
<p>I’m keep working to provide WPF UI for this library to “productize” it.</p>
<p>So, what are you waiting for? <a target="_blank" href="http://www.codeplex.com/FM/Release/ProjectReleases.aspx">Download</a> and Spear the word with this news! This is the first and only fully managed library (as far as I know) to work with RDS, TMC and FM data. Also, there are not a lot of information about HID usage as FM receiver in managed code.</p>
<p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://khason.net/images/2009/01/image.png" width="405" height="405" /> </p>
<p><a target="_blank" href="http://www.codeplex.com/FM/Release/ProjectReleases.aspx"><strong>Download latest release of USBFM.DLL &gt;&gt;</strong></a></p>


<p>Related posts:<ol><li><a href='http://khason.net/itpro/quick-it-tip-how-to-build-bootable-usb-stick/' rel='bookmark' title='Permanent Link: Quick IT tip: How to build bootable USB stick'>Quick IT tip: How to build bootable USB stick</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://khason.net/dev/usb-fm-radio-library-was-published-on-codeplex/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>How to migrate from CS2007 to WordPress, Movable Type  (or any other blog engine, supports XML-RPC) with C#</title>
		<link>http://khason.net/dev/how-to-migrate-from-cs2007-to-wordpress-movable-type-or-any-other-blog-engine-supports-xml-rpc-with-c/</link>
		<comments>http://khason.net/dev/how-to-migrate-from-cs2007-to-wordpress-movable-type-or-any-other-blog-engine-supports-xml-rpc-with-c/#comments</comments>
		<pubDate>Thu, 01 Jan 2009 19:31:51 +0000</pubDate>
		<dc:creator>Tamir</dc:creator>
				<category><![CDATA[DEV]]></category>
		<category><![CDATA[.NET 3.5]]></category>
		<category><![CDATA[blogging tools]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Interop]]></category>
		<category><![CDATA[LINQ]]></category>
		<category><![CDATA[source]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://khason.net/dev/how-to-migrate-from-cs2007-to-wordpress-movable-type-or-any-other-blog-engine-supports-xml-rpc-with-c/</guid>
		<description><![CDATA[Today we’ll speak about migration from community server 2007 to another blog engine, when you have no access to CS and/or other database. Let’s set targets first: You want to migrate all posts You want to migrate all comments You want to transfer all hosted images and media files You should update all internal links [...]


Related posts:<ol><li><a href='http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code/' rel='bookmark' title='Permanent Link: INotifyPropertyChanged auto wiring or how to get rid of redundant code'>INotifyPropertyChanged auto wiring or how to get rid of redundant code</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>Today we’ll speak about migration from community server 2007 to another blog engine, when you have no access to CS and/or other database. </p>
<p>Let’s set targets first:</p>
<ul>
<li>You want to migrate all posts</li>
<li>You want to migrate all comments</li>
<li>You want to transfer all hosted images and media files</li>
<li>You should update all internal links </li>
</ul>
<p>Looks complicated? not really. First of all, grab any <a target="_blank" href="http://www.xmlrpc.com/">XML-RPC</a> framework (for example <a target="_blank" href="http://www.xml-rpc.net/">xml-rcp.net</a>). Then create a proxy to CS2007 – it uses Metablog API. You can see all defined methods by accessing /blogs/metablog.ashx</p>
<blockquote><p>[XmlRpcUrl(&quot;<a href="http://blogs.microsoft.co.il/blogs/metablog.ashx&quot;)]&#8220;>http://blogs.microsoft.co.il/blogs/metablog.ashx&quot;)]</a>      <br />public interface ICommunityServer {      <br />&#160;&#160; [XmlRpcMethod(&quot;blogger.deletePost&quot;)]      <br />&#160;&#160; bool deletePost(string appKey, string postid, string username, string password, bool publish); </p>
<p>&#160;&#160;&#160; [XmlRpcMethod(&quot;blogger.getUsersBlogs&quot;)]     <br />&#160;&#160; BlogInfo[] getUsersBlogs(string appKey, string username, string password); </p>
<p>&#160;&#160; [XmlRpcMethod(&quot;metaWeblog.editPost&quot;)]     <br />&#160;&#160; bool editPost(string postid, string username, string password, Post post, bool publish); </p>
<p>&#160;&#160; [XmlRpcMethod(&quot;metaWeblog.getCategories&quot;)]     <br />&#160;&#160; CategoryInfo[] getCategories(string blogid, string username, string password); </p>
<p>&#160;&#160; [XmlRpcMethod(&quot;metaWeblog.getPost&quot;)]     <br />&#160;&#160; Post getPost(string postid, string username, string password); </p>
<p>&#160;&#160; [XmlRpcMethod(&quot;metaWeblog.getRecentPosts&quot;)]     <br />&#160;&#160; Post[] getRecentPosts(string blogid, string username, string password, int numberOfPosts); </p>
<p>&#160;&#160; [XmlRpcMethod(&quot;metaWeblog.newPost&quot;)]     <br />&#160;&#160; string newPost(string blogid, string username, string password, Post post, Boolean publish);      <br />}</p>
</blockquote>
<p>As you can see, you can read and update posts, but there is no methods for comments. What to do? Community Server supports comments rss syndication. So why not to use it? Also, if you want to fix links later, save all retrieved urls</p>
<blockquote><p>posts = csblog.getRecentPosts(csBlogid, csUsername, csPassword, toFetch);     <br />for (int i = 0; i &lt; posts.Length; i++) {      <br />if (!_postsIndex.ContainsKey(posts[i].link)) _postsIndex.Add(posts[i].link, string.Empty);      <br />…      <br />var commentsRSSURL = string.Concat(&quot;<a href="http://blogs.microsoft.co.il/blogs/tamir/rsscomments.aspx?PostID=&quot;">http://blogs.microsoft.co.il/blogs/tamir/rsscomments.aspx?PostID=&quot;</a>, posts[i].postid);      <br />_rssReader = new XmlTextReader(commentsRSSURL);      </p>
<p>while (_rssReader.Read()) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; _rssReader.MoveToContent();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (_rssReader.NodeType == XmlNodeType.Element) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (_rssReader.Name == &quot;pubDate&quot;) { date = DateTime.Parse(_rssReader.ReadElementContentAsString()); }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (_rssReader.Name == &quot;dc:creator&quot;) { author = _rssReader.ReadElementContentAsString(); }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (_rssReader.Name == &quot;description&quot;) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (!shouldSkip) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; content = _rssReader.ReadElementContentAsString();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; comments.Add(new Comment {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; author = author,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; date_created_gmt = date,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; status = true</p>
</blockquote>
<p>As you can see, now you have all comments. Next step is to detect and reupload all images to the new host.</p>
<blockquote><p>private const string imgRX = &quot;&lt;img[^&gt;]*src=\&quot;?([^\&quot;]*)\&quot;?([^&gt;]*alt=\&quot;?([^\&quot;]*)\&quot;?)?[^&gt;]*&gt;&quot;;     <br />var matches = Regex.Matches(posts[i].description, imgRX);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Console.WriteLine(&quot;Fixing {0} images&#8230;&quot;, matches.Count);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; for (int j = 0; j &lt; matches.Count; j++) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Console.WriteLine(&quot;Retriving image #{0}&quot;, j);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var url = matches[j].Groups[1].Value;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (url.Contains(baseURL)) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; try {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var data = wc.DownloadData(url);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Console.WriteLine(&quot;Uploading image #{0}&quot;, j);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var uf = newblog.uploadFile(newblogid, newUsername, newPassword, new MediaObject {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; bits = data,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; name = matches[j].Groups[1].Value.Substring(matches[j].Groups[1].Value.LastIndexOf(&#8216;/&#8217;) + 1)      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; });      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; posts[i].description = posts[i].description.Replace(url, uf.url);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } catch { }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
</blockquote>
<p>Now all images are stored in the new location and all image links are updated within stored posts. Next step is to upload all posts to the new location. CS stores tags as categories, which is wrong. Why? Because categories can be hierarchical, while tags cannot. So we have to convert all categories within retrieved posts into real tags. After it we can post everything</p>
<blockquote><p>for (int i = posts.Length &#8211; 1; i &gt;= 0; i&#8211;) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; posts[i].mt_keywords = string.Join(&quot;,&quot;, posts[i].categories);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var pid = newblog.newPost(newblogid, newUsername, newPassword, posts[i], true);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; foreach (var comment in posts[i].comments) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; try {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var cid = newblog.newComment(newblogid, newUsername, newPassword, pid, comment);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } catch { }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
</blockquote>
<p>Now we have to update all internal links within new locations. For this we should grab all posts back to learn new URLs.</p>
<blockquote><p>var newPosts = newblog.getRecentPosts(newblogid, newUsername, newPassword, toFetch);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; for (int i = 0; i &lt; newPosts.Length; i++) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; foreach (var pi in _postsIndex) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (newPosts[i].description.Contains(pi.Key)) newPosts[i].description = newPosts[i].description.Replace(string.Concat(baseURL,pi.Key), pi.Value);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; wpblog.editPost((string)newPosts[i].postid, newUsername, newPassword, newPosts[i], true);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (!refereces.ContainsKey(newPosts[i].link)) refereces.Add(newPosts[i].link, posts[i].link); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
</blockquote>
<p>We done. Last, but not the least, is to update old posts with new URL to make visitors able to forward into new location.</p>
<blockquote><p>csposts = csblog.getRecentPosts(csBlogid, csUsername, csPassword, toFetch);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; for(int i=0;i&lt; csposts.Length;i++) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (_postsIndex.ContainsKey(csposts[i].link)) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string write = string.Format(&quot;&lt;h3&gt;[This blog was migrated. You will not be able to comment here.&lt;br/&gt;The new URL of this post is &lt;a href=\&quot;{0}\&quot;&gt;{0}&lt;/a&gt;]&lt;/h3&gt;&lt;hr/&gt;&quot;, _postsIndex[csposts[i].link]);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; csposts[i].description = string.Concat(write, csposts[i].description);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; csblog.editPost((string)csposts[i].postid, csUsername, csPassword, csposts[i], true);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Console.WriteLine(&quot;Post {0} was updated&quot;,i);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
</blockquote>
<p>Have a nice day and be good people!</p>


<p>Related posts:<ol><li><a href='http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code/' rel='bookmark' title='Permanent Link: INotifyPropertyChanged auto wiring or how to get rid of redundant code'>INotifyPropertyChanged auto wiring or how to get rid of redundant code</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://khason.net/dev/how-to-migrate-from-cs2007-to-wordpress-movable-type-or-any-other-blog-engine-supports-xml-rpc-with-c/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Read and use FM radio (or any other USB HID device) from C#</title>
		<link>http://khason.net/blog/read-and-use-fm-radio-or-any-other-usb-hid-device-from-c/</link>
		<comments>http://khason.net/blog/read-and-use-fm-radio-or-any-other-usb-hid-device-from-c/#comments</comments>
		<pubDate>Tue, 30 Dec 2008 20:42:18 +0000</pubDate>
		<dc:creator>Tamir</dc:creator>
				<category><![CDATA[BLOG]]></category>
		<category><![CDATA[.NET 3.5]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[DEV]]></category>
		<category><![CDATA[Hardware]]></category>
		<category><![CDATA[Interop]]></category>
		<category><![CDATA[source]]></category>
		<category><![CDATA[Tips and Tricks]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[Vista]]></category>
		<category><![CDATA[Windows 7]]></category>
		<category><![CDATA[Work process]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[WPF crossbow]]></category>

		<guid isPermaLink="false">http://khason.net/blog/read-and-use-fm-radio-or-any-other-usb-hid-device-from-c/</guid>
		<description><![CDATA[Last time we spoke about reading and decoding RDS information from FM receivers. Also we already know how to stream sound from DirectSound compatible devices. However, before we can do it, we should be able to “speak” with such devices. So, today we’ll spoke about detection and reading information from Radio USB adapters (actually from [...]


Related posts:<ol><li><a href='http://khason.net/itpro/quick-it-tip-how-to-build-bootable-usb-stick/' rel='bookmark' title='Permanent Link: Quick IT tip: How to build bootable USB stick'>Quick IT tip: How to build bootable USB stick</a></li>
<li><a href='http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code/' rel='bookmark' title='Permanent Link: INotifyPropertyChanged auto wiring or how to get rid of redundant code'>INotifyPropertyChanged auto wiring or how to get rid of redundant code</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>Last time <a href="http://khason.net/blog/reading-and-decoding-rds-radio-data-system-in-c/">we spoke about reading and decoding RDS information from FM receivers</a>. Also <a href="http://khason.net/blog/capturing-and-streaming-sound-by-using-directsound-with-c/">we already know how to stream sound from DirectSound compatible devices</a>. However, before we can do it, we should be able to “speak” with such devices. So, today we’ll spoke about detection and reading information from Radio USB adapters (actually from any Human Input Devices). Let’s start.</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="USB FM HID" border="0" alt="USB FM HID" src="http://khason.net/images/2008/12/image-460dcf0b.png" width="240" height="211" /> </p>
<p>First, if you want to do it, go and buy such device. The are not a lot of alternatives, but if you’ll seek, you’ll find it very quickly.</p>
<p>So, let’s start. First of all, we’ll use platform invoke to get and set the information. Also, we have to preserve handle of the device from being collected by GC. After we’ll finish using the device, we’ll have to dispose it. Thus it makes sense to inherit from SafeHandle and IDisposable.</p>
<blockquote><p>[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]     <br />[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]      <br />public class USBDevice : SafeHandleZeroOrMinusOneIsInvalid, IDisposable {</p>
</blockquote>
<p>Next, we’ll set a number of arguments, that will be in use during the device lifetime.</p>
<blockquote><p>public uint ProductID { get; private set; }     <br />public uint VendorID { get; private set; }      <br />public uint VersionNumber { get; private set; }      <br />public string Name { get; private set; }      <br />public string SerialNumber { get; private set; }      <br />public override bool IsInvalid { get { return !isValid; } } </p>
<p>internal ushort FeatureReportLength { get; private set; }     <br />internal ushort[] Registers { get; set; }</p>
</blockquote>
<p>Now, we have to find it. The best way of detection human input devices is by product and vendor IDs. Those values are always unique for certain device type.</p>
<blockquote><p>[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]     <br />internal USBDevice(uint pid, uint vid) : base(true) { findDevice(pid, vid); }</p>
</blockquote>
<p>Next step is to find a device. To do this, we have to provide extern interfaces to methods of hid.dll and setupapi.dll. Here all methods we will use in our class</p>
<blockquote><p>[SuppressUnmanagedCodeSecurity()]     <br />internal static class Native {      <br />&#160;&#160; #region methods      <br />&#160;&#160; [DllImport(&quot;hid.dll&quot;, SetLastError = true)]      <br />&#160;&#160; internal static extern void HidD_GetHidGuid(      <br />&#160;&#160;&#160;&#160;&#160; ref Guid lpHidGuid); </p>
<p>&#160;&#160; [DllImport(&quot;hid.dll&quot;, SetLastError = true)]     <br />&#160;&#160; internal static extern bool HidD_GetAttributes(      <br />&#160;&#160;&#160;&#160;&#160; IntPtr hDevice,      <br />&#160;&#160;&#160;&#160;&#160; out HIDD_ATTRIBUTES Attributes); </p>
<p>&#160;&#160; [DllImport(&quot;hid.dll&quot;, SetLastError = true)]     <br />&#160;&#160; internal static extern bool HidD_GetPreparsedData(      <br />&#160;&#160;&#160;&#160;&#160; IntPtr hDevice,      <br />&#160;&#160;&#160;&#160;&#160; out IntPtr hData); </p>
<p>&#160;&#160; [DllImport(&quot;hid.dll&quot;, SetLastError = true)]     <br />&#160;&#160; internal static extern bool HidD_FreePreparsedData(      <br />&#160;&#160;&#160;&#160;&#160; IntPtr hData); </p>
<p>&#160;&#160; [DllImport(&quot;hid.dll&quot;, SetLastError = true)]     <br />&#160;&#160; internal static extern bool HidP_GetCaps(      <br />&#160;&#160;&#160;&#160;&#160; IntPtr hData,      <br />&#160;&#160;&#160;&#160;&#160; out HIDP_CAPS capabilities); </p>
<p>&#160;&#160; [DllImport(&quot;hid.dll&quot;, SetLastError = true, CallingConvention = CallingConvention.StdCall)]     <br />&#160;&#160; internal static extern bool HidD_GetFeature(      <br />&#160;&#160;&#160;&#160;&#160; IntPtr hDevice,      <br />&#160;&#160;&#160;&#160;&#160; IntPtr hReportBuffer,      <br />&#160;&#160;&#160;&#160;&#160; uint ReportBufferLength); </p>
<p>&#160;&#160; [DllImport(&quot;hid.dll&quot;, SetLastError = true, CallingConvention = CallingConvention.StdCall)]     <br />&#160;&#160; internal static extern bool HidD_SetFeature(      <br />&#160;&#160;&#160;&#160;&#160; IntPtr hDevice,      <br />&#160;&#160;&#160;&#160;&#160; IntPtr ReportBuffer,      <br />&#160;&#160;&#160;&#160;&#160; uint ReportBufferLength); </p>
<p>&#160;&#160; [DllImport(&quot;hid.dll&quot;, SetLastError = true, CallingConvention = CallingConvention.StdCall)]     <br />&#160;&#160; internal static extern bool HidD_GetProductString(      <br />&#160;&#160;&#160;&#160;&#160; IntPtr hDevice,       <br />&#160;&#160;&#160;&#160;&#160; IntPtr Buffer,       <br />&#160;&#160;&#160;&#160;&#160; uint BufferLength); </p>
<p>&#160;&#160; [DllImport(&quot;hid.dll&quot;, SetLastError = true, CallingConvention = CallingConvention.StdCall)]     <br />&#160;&#160; internal static extern bool HidD_GetSerialNumberString(      <br />&#160;&#160;&#160;&#160;&#160; IntPtr hDevice,      <br />&#160;&#160;&#160;&#160;&#160; IntPtr Buffer,      <br />&#160;&#160;&#160;&#160;&#160; uint BufferLength); </p>
<p>&#160;&#160; [DllImport(&quot;setupapi.dll&quot;, SetLastError = true)]     <br />&#160;&#160; internal static extern IntPtr SetupDiGetClassDevs(      <br />&#160;&#160;&#160;&#160;&#160; ref Guid ClassGuid,      <br />&#160;&#160;&#160;&#160;&#160; [MarshalAs(UnmanagedType.LPTStr)] string Enumerator,      <br />&#160;&#160;&#160;&#160;&#160; IntPtr hwndParent,      <br />&#160;&#160;&#160;&#160;&#160; UInt32 Flags); </p>
<p>&#160;&#160; [DllImport(&quot;setupapi.dll&quot;, SetLastError = true)]     <br />&#160;&#160; internal static extern bool SetupDiEnumDeviceInterfaces(      <br />&#160;&#160;&#160;&#160;&#160; IntPtr DeviceInfoSet,      <br />&#160;&#160;&#160;&#160;&#160; int DeviceInfoData,      <br />&#160;&#160;&#160;&#160;&#160; ref&#160; Guid lpHidGuid,      <br />&#160;&#160;&#160;&#160;&#160; uint MemberIndex,      <br />&#160;&#160;&#160;&#160;&#160; ref&#160; SP_DEVICE_INTERFACE_DATA lpDeviceInterfaceData); </p>
<p>&#160;&#160; [DllImport(&quot;setupapi.dll&quot;, SetLastError = true)]     <br />&#160;&#160; internal static extern bool SetupDiGetDeviceInterfaceDetail(      <br />&#160;&#160;&#160;&#160;&#160; IntPtr DeviceInfoSet,      <br />&#160;&#160;&#160;&#160;&#160; ref SP_DEVICE_INTERFACE_DATA lpDeviceInterfaceData,      <br />&#160;&#160;&#160;&#160;&#160; IntPtr hDeviceInterfaceDetailData,      <br />&#160;&#160;&#160;&#160;&#160; uint detailSize,      <br />&#160;&#160;&#160;&#160;&#160; out uint requiredSize,      <br />&#160;&#160;&#160;&#160;&#160; IntPtr hDeviceInfoData); </p>
<p>&#160;&#160; [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]     <br />&#160;&#160; [DllImport(&quot;kernel32.dll&quot;, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]      <br />&#160;&#160; internal static extern IntPtr CreateFile(      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; string lpFileName,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; uint dwDesiredAccess,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; uint dwShareMode,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IntPtr SecurityAttributes,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; uint dwCreationDisposition,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; uint dwFlagsAndAttributes,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IntPtr hTemplateFile); </p>
<p>&#160;&#160; [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]     <br />&#160;&#160; [DllImport(&quot;kernel32.dll&quot;, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]      <br />&#160;&#160; internal static extern bool CloseHandle(IntPtr hHandle);</p>
</blockquote>
<p>Also, we will need a number of structures, such as device attributes and capabilities.</p>
<blockquote><p>[StructLayout(LayoutKind.Sequential)]     <br />internal struct SP_DEVICE_INTERFACE_DATA {      <br />&#160;&#160; public int cbSize;      <br />&#160;&#160; public Guid InterfaceClassGuid;      <br />&#160;&#160; public int Flags;      <br />&#160;&#160; public int Reserved;      <br />} </p>
<p>[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]     <br />internal class PSP_DEVICE_INTERFACE_DETAIL_DATA {      <br />&#160;&#160; public int cbSize;      <br />&#160;&#160; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]      <br />&#160;&#160; public string DevicePath;      <br />} </p>
<p>[StructLayout(LayoutKind.Sequential)]     <br />internal struct HIDD_ATTRIBUTES {      <br />&#160;&#160; public int Size; // = sizeof (struct _HIDD_ATTRIBUTES) = 10      <br />&#160;&#160; public UInt16 VendorID;      <br />&#160;&#160; public UInt16 ProductID;      <br />&#160;&#160; public UInt16 VersionNumber;      <br />}      <br />[StructLayout(LayoutKind.Sequential)]      <br />internal struct HIDP_CAPS {      <br />&#160;&#160; public UInt16 Usage;      <br />&#160;&#160; public UInt16 UsagePage;      <br />&#160;&#160; public UInt16 InputReportByteLength;      <br />&#160;&#160; public UInt16 OutputReportByteLength;      <br />&#160;&#160; public UInt16 FeatureReportByteLength;      <br />&#160;&#160; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]      <br />&#160;&#160; public UInt16[] Reserved;      <br />&#160;&#160; public UInt16 NumberLinkCollectionNodes;      <br />&#160;&#160; public UInt16 NumberInputButtonCaps;      <br />&#160;&#160; public UInt16 NumberInputValueCaps;      <br />&#160;&#160; public UInt16 NumberInputDataIndices;      <br />&#160;&#160; public UInt16 NumberOutputButtonCaps;      <br />&#160;&#160; public UInt16 NumberOutputValueCaps;      <br />&#160;&#160; public UInt16 NumberOutputDataIndices;      <br />&#160;&#160; public UInt16 NumberFeatureButtonCaps;      <br />&#160;&#160; public UInt16 NumberFeatureValueCaps;      <br />&#160;&#160; public UInt16 NumberFeatureDataIndices;      <br />}</p>
</blockquote>
<p>And a number of system constants</p>
<blockquote><p>internal const uint DIGCF_PRESENT = 0&#215;00000002;     <br />internal const uint DIGCF_DEVICEINTERFACE = 0&#215;00000010;      <br />internal const uint GENERIC_READ = 0&#215;80000000;      <br />internal const uint GENERIC_WRITE = 0&#215;40000000;      <br />internal const uint FILE_SHARE_READ = 0&#215;00000001;      <br />internal const uint FILE_SHARE_WRITE = 0&#215;00000002;      <br />internal const int OPEN_EXISTING = 3;      <br />internal const int FILE_FLAG_OVERLAPPED = 0&#215;40000000;      <br />internal const uint MAX_USB_DEVICES = 16;</p>
</blockquote>
<p>Now, we are ready to start. So let’s find all devices and get its information</p>
<blockquote><p>Native.HidD_GetHidGuid(ref _hidGuid);     <br />hHidDeviceInfo = Native.SetupDiGetClassDevs(ref _hidGuid, null, IntPtr.Zero, Native.DIGCF_PRESENT | Native.DIGCF_DEVICEINTERFACE);</p>
</blockquote>
<p>Now, if a handle we get is valid, we should search our specific device. For this purpose, we have to read device interface information and then get details info about this device.</p>
<blockquote><p>if (hHidDeviceInfo.ToInt32() &gt; -1) {     <br />&#160;&#160; uint i = 0;      <br />&#160;&#160; while (!isValid &amp;&amp; i &lt; Native.MAX_USB_DEVICES) {      <br />&#160;&#160;&#160;&#160;&#160; var hidDeviceInterfaceData = new Native.SP_DEVICE_INTERFACE_DATA();      <br />&#160;&#160;&#160;&#160;&#160; hidDeviceInterfaceData.cbSize = Marshal.SizeOf(hidDeviceInterfaceData);      <br />&#160;&#160;&#160;&#160;&#160; if (Native.SetupDiEnumDeviceInterfaces(hHidDeviceInfo, 0, ref _hidGuid, i, ref hidDeviceInterfaceData)) {</p>
</blockquote>
<p>Once we have all this and information is valid, let’s detect its capabilities</p>
<blockquote><p>bool detailResult;     <br />uint length, required;      <br />Native.SetupDiGetDeviceInterfaceDetail(hHidDeviceInfo, ref hidDeviceInterfaceData, IntPtr.Zero, 0, out length, IntPtr.Zero);      <br />var hidDeviceInterfaceDetailData = new Native.PSP_DEVICE_INTERFACE_DETAIL_DATA();      <br />hidDeviceInterfaceDetailData.cbSize = 5; //DWORD cbSize (size 4) + Char[0] (size 1) for 32bit only!      <br />var hDeviceInterfaceDetailData = Marshal.AllocHGlobal(Marshal.SizeOf(hidDeviceInterfaceDetailData));      <br />Marshal.StructureToPtr(hidDeviceInterfaceDetailData, hDeviceInterfaceDetailData, true);      <br />detailResult = Native.SetupDiGetDeviceInterfaceDetail(hHidDeviceInfo, ref hidDeviceInterfaceData, hDeviceInterfaceDetailData, length, out required, IntPtr.Zero);      <br />Marshal.PtrToStructure(hDeviceInterfaceDetailData, hidDeviceInterfaceDetailData);      <br />if (detailResult) {</p>
</blockquote>
<p>To do this, we have to create memory file first and then share device attributes by using this file.</p>
<blockquote><p>base.handle = Native.CreateFile(hidDeviceInterfaceDetailData.DevicePath,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Native.GENERIC_READ |      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Native.GENERIC_WRITE,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Native.FILE_SHARE_READ |      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Native.FILE_SHARE_WRITE,       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IntPtr.Zero,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Native.OPEN_EXISTING,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Native.FILE_FLAG_OVERLAPPED,       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IntPtr.Zero);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (base.handle.ToInt32() &gt; -1) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Native.HIDD_ATTRIBUTES hidDeviceAttributes;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (Native.HidD_GetAttributes(base.handle, out hidDeviceAttributes)) {</p>
</blockquote>
<p>All the rest is straight forward. Just compare info retrieved with one we already have. And, of cause, release all resources were used (remember, we’re in win32 api world!)</p>
<blockquote><p>if ((hidDeviceAttributes.VendorID == vid) &amp;&amp; (hidDeviceAttributes.ProductID == pid)) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; isValid = true;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; ProductID = pid;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; VendorID = vid;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; VersionNumber = hidDeviceAttributes.VersionNumber;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IntPtr buffer = Marshal.AllocHGlobal(126);//max alloc for string;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (Native.HidD_GetProductString(this.handle, buffer, 126)) Name = Marshal.PtrToStringAuto(buffer);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (Native.HidD_GetSerialNumberString(this.handle, buffer, 126)) SerialNumber = Marshal.PtrToStringAuto(buffer);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Marshal.FreeHGlobal(buffer);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; var capabilities = new Native.HIDP_CAPS();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; IntPtr hPreparsedData;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (Native.HidD_GetPreparsedData(this.handle, out hPreparsedData)) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (Native.HidP_GetCaps(hPreparsedData, out capabilities)) FeatureReportLength = capabilities.FeatureReportByteLength;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Native.HidD_FreePreparsedData(hPreparsedData);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; break;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } else {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Native.CloseHandle(base.handle);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Marshal.FreeHGlobal(hDeviceInterfaceDetailData);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; i++; </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p>
</blockquote>
<p>Now we have a handle to our device and can manipulate it. Like this:</p>
<blockquote><p>using (var device = USBRadioDevice.FindDevice(0&#215;0000, 0&#215;1111)) {     <br />…      <br />}</p>
</blockquote>
<p>But we still have to provide methods for such usage. Here there are no very complicated code.</p>
<blockquote><p>public static USBDevice FindDevice(uint pid, uint vid) {     <br />&#160;&#160; var device = new USBDevice(pid,vid);      <br />&#160;&#160; var fillRegisters = device.InitRegisters();      <br />&#160;&#160; if (!device.IsInvalid &amp;&amp; fillRegisters) return device;      <br />&#160;&#160; else throw new ArgumentOutOfRangeException(string.Format(&quot;Human input device {0} was not found.&quot;, pid));      <br />} </p>
<p>public override string ToString() {     <br />&#160;&#160; return string.Format(&quot;{0} (Product:{1:x}, Vendor:{2:x}, Version:{3:x}, S/N:{4})&quot;, Name, ProductID, VendorID, VersionNumber, SerialNumber);      <br />} </p>
<p>[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]     <br />protected override bool ReleaseHandle() {      <br />&#160;&#160; return Native.CloseHandle(base.handle);      <br />} </p>
<p>#region IDisposable Members     <br />public void Dispose() {      <br />&#160;&#160; Dispose(true);      <br />&#160;&#160; GC.SuppressFinalize(this); </p>
<p>}     <br />[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]      <br />void IDisposable.Dispose() {      <br />&#160;&#160; if (base.handle != null &amp;&amp; !base.IsInvalid) {      <br />&#160;&#160;&#160;&#160;&#160; // Free the handle      <br />&#160;&#160;&#160;&#160;&#160; base.Dispose();      <br />&#160;&#160; }      <br />} </p>
<p>#endregion</p>
</blockquote>
<p>We done. Have a nice day and be good people.</p>


<p>Related posts:<ol><li><a href='http://khason.net/itpro/quick-it-tip-how-to-build-bootable-usb-stick/' rel='bookmark' title='Permanent Link: Quick IT tip: How to build bootable USB stick'>Quick IT tip: How to build bootable USB stick</a></li>
<li><a href='http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code/' rel='bookmark' title='Permanent Link: INotifyPropertyChanged auto wiring or how to get rid of redundant code'>INotifyPropertyChanged auto wiring or how to get rid of redundant code</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://khason.net/blog/read-and-use-fm-radio-or-any-other-usb-hid-device-from-c/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Capturing and streaming sound by using DirectSound with C#</title>
		<link>http://khason.net/blog/capturing-and-streaming-sound-by-using-directsound-with-c/</link>
		<comments>http://khason.net/blog/capturing-and-streaming-sound-by-using-directsound-with-c/#comments</comments>
		<pubDate>Thu, 25 Dec 2008 10:48:20 +0000</pubDate>
		<dc:creator>Tamir</dc:creator>
				<category><![CDATA[BLOG]]></category>
		<category><![CDATA[.NET 3.5]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[CodeProject]]></category>
		<category><![CDATA[DEV]]></category>
		<category><![CDATA[DirectX]]></category>
		<category><![CDATA[Hardware]]></category>
		<category><![CDATA[Interop]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[source]]></category>
		<category><![CDATA[Tips and Tricks]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[Work process]]></category>
		<category><![CDATA[WPF]]></category>

		<guid isPermaLink="false">http://khason.net/blog/capturing-and-streaming-sound-by-using-directsound-with-c/</guid>
		<description><![CDATA[I already wrote a little about managed way to use DirectX DirectSound. Today we’ll speak about how to get sound from your microphone or any other DirectSound capturing device (such as FM receiver) and stream it out to your PC speakers and any other DirectSound Output device. So, let’s start creating our first echo service [...]


Related posts:<ol><li><a href='http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code/' rel='bookmark' title='Permanent Link: INotifyPropertyChanged auto wiring or how to get rid of redundant code'>INotifyPropertyChanged auto wiring or how to get rid of redundant code</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>I already wrote a little about <a href="http://khason.net/blog/sound-tone-and-dtmf-generation-by-using-managed-directsound-and-c-and-sine-tone-detection-with-pure-managed-goertzel-algorithm-implementation/">managed way to use DirectX DirectSound</a>. Today we’ll speak about how to get sound from your microphone or any other DirectSound capturing device (such as <a href="http://khason.net/blog/reading-and-decoding-rds-radio-data-system-in-c/">FM receiver</a>) and stream it out to your PC speakers and any other DirectSound Output device. So, let’s start creating our first echo service by using managed DirectX.</p>
<p><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://khason.net/images/2008/12/image-13541f81.png" width="225" height="229" /> </p>
<p>First of all we should decide what Wave format we want to use for capturing and recording. So, let’s choose anything reasonable <img src='http://khason.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<blockquote><p>var format = new WaveFormat {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; SamplesPerSecond = 96000,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; BitsPerSample = 16,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Channels = 2,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; FormatTag = WaveFormatTag.Pcm      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; };</p>
</blockquote>
<p>Now, we should calculate block align and average byte per second value for this format. I’m wondering why it cannot be done automatically…</p>
<blockquote><p>format.BlockAlign = (short)(format.Channels * (format.BitsPerSample / 8));     <br />format.AverageBytesPerSecond = format.SamplesPerSecond * format.BlockAlign;</p>
</blockquote>
<p>Next step is to set the size of two buffers – one for input and other for output. Generally those buffers are circular, and capturing one should be twice bigger, then output. Why? Because we choose two channels to use. Also, we should decide about chunk size of the buffer, we want to signal when filled.</p>
<blockquote><p>_dwNotifySize = Math.Max(4096, format.AverageBytesPerSecond / 8);     <br />_dwNotifySize -= _dwNotifySize % format.BlockAlign;      <br />_dwCaptureBufferSize = NUM_BUFFERS * _dwNotifySize;      <br />_dwOutputBufferSize = NUM_BUFFERS * _dwNotifySize / 2;</p>
</blockquote>
<p>Next step is to create CaptureBufferDescriptor and actual capturing buffer. We’ll enumerate all devices and choose one, satisfies given string (captureDescriptor) – for example “Mic” <img src='http://khason.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<blockquote><p>var cap = default(Capture);     <br />var cdc = new CaptureDevicesCollection();      <br />for (int i = 0; i &lt; cdc.Count; i++) {      <br />&#160;&#160; if (cdc[i].Description.ToLower().Contains(captureDescriptor.ToLower())) {      <br />&#160;&#160;&#160;&#160;&#160; cap = new Capture(cdc[i].DriverGuid);      <br />&#160;&#160;&#160;&#160;&#160; break;      <br />&#160;&#160; }      <br />}      <br />var capDesc = new CaptureBufferDescription {      <br />&#160;&#160; Format = format,      <br />&#160;&#160; BufferBytes = _dwCaptureBufferSize      <br />};      <br />_dwCapBuffer = new CaptureBuffer(capDesc, cap);</p>
</blockquote>
<p>Then we’ll create output device and buffer. To simplify program, we will use default speakers to output, however, you can choose output device the same way we did for capturing. Also, because DirectSound uses any window as it’s message pump, we have to use SetCooperativeLevel method. In my case (windowless application), I’ll use desktop window as message broker. This why you will have to add Windows.Forms as reference for your project, even if it console application. Also, do not forget to set GlobalFocus value to True, if you want to play echo, even if desktop window is not focused.</p>
<blockquote><p>var dev = new Device();     <br />dev.SetCooperativeLevel(Native.GetDesktopWindow(), CooperativeLevel.Priority); </p>
<p>var devDesc = new BufferDescription {     <br />&#160;&#160; BufferBytes = _dwOutputBufferSize,      <br />&#160;&#160; Format = format,      <br />&#160;&#160; DeferLocation = true,      <br />&#160;&#160; GlobalFocus = true      <br />};      <br />_dwDevBuffer = new SecondaryBuffer(devDesc, dev);</p>
</blockquote>
<p>Now, we will subscribe to buffer notifications and set autoResetEvent to be fired when it filled up.</p>
<blockquote><p>var _resetEvent = new AutoResetEvent(false);     <br />var _notify = new Notify(_dwCapBuffer);      <br />//half&amp;half      <br />var bpn1 = new BufferPositionNotify();      <br />bpn1.Offset = _dwCapBuffer.Caps.BufferBytes / 2 &#8211; 1;      <br />bpn1.EventNotifyHandle = _resetEvent.SafeWaitHandle.DangerousGetHandle();      <br />var bpn2 = new BufferPositionNotify();      <br />bpn2.Offset = _dwCapBuffer.Caps.BufferBytes &#8211; 1;      <br />bpn2.EventNotifyHandle = _resetEvent.SafeWaitHandle.DangerousGetHandle(); </p>
<p>_notify.SetNotificationPositions(new BufferPositionNotify[] { bpn1, bpn2 });</p>
</blockquote>
<p>Almost done, the only thing we should do is to fire worker thread to take care on messages</p>
<blockquote><p>int offset = 0;     <br />_dwCaptureThread = new Thread((ThreadStart)delegate {      <br />&#160;&#160; _dwCapBuffer.Start(true); </p>
<p>&#160;&#160; while (IsReady) {     <br />&#160;&#160;&#160;&#160;&#160; _resetEvent.WaitOne();      <br />&#160;&#160;&#160;&#160;&#160; var read = _dwCapBuffer.Read(offset, typeof(byte), LockFlag.None, _dwOutputBufferSize);      <br />&#160;&#160;&#160;&#160;&#160; _dwDevBuffer.Write(0, read, LockFlag.EntireBuffer);      <br />&#160;&#160;&#160;&#160;&#160; offset = (offset + _dwOutputBufferSize) % _dwCaptureBufferSize;      <br />&#160;&#160;&#160;&#160;&#160; _dwDevBuffer.SetCurrentPosition(0);      <br />&#160;&#160;&#160;&#160;&#160; _dwDevBuffer.Play(0, BufferPlayFlags.Default);      <br />&#160;&#160; }      <br />&#160;&#160; _dwCapBuffer.Stop();      <br />});      <br />_dwCaptureThread.Start();</p>
</blockquote>
<p>That’s it. Compile and run. Now if you’ll speak, you can hear your echo from PC speakers. </p>
<p>Merry Christmas for whom concerns and be good people – do not scare your co-workers with strange sounds – be polite and make the volume lower <img src='http://khason.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  </p>


<p>Related posts:<ol><li><a href='http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code/' rel='bookmark' title='Permanent Link: INotifyPropertyChanged auto wiring or how to get rid of redundant code'>INotifyPropertyChanged auto wiring or how to get rid of redundant code</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://khason.net/blog/capturing-and-streaming-sound-by-using-directsound-with-c/feed/</wfw:commentRss>
		<slash:comments>36</slash:comments>
		</item>
		<item>
		<title>Reading and decoding RDS (Radio Data System) in C#</title>
		<link>http://khason.net/blog/reading-and-decoding-rds-radio-data-system-in-c/</link>
		<comments>http://khason.net/blog/reading-and-decoding-rds-radio-data-system-in-c/#comments</comments>
		<pubDate>Fri, 12 Dec 2008 13:44:24 +0000</pubDate>
		<dc:creator>Tamir</dc:creator>
				<category><![CDATA[BLOG]]></category>
		<category><![CDATA[.NET 3.5]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[DEV]]></category>
		<category><![CDATA[Hardware]]></category>
		<category><![CDATA[Interop]]></category>
		<category><![CDATA[Math]]></category>
		<category><![CDATA[Mobile]]></category>
		<category><![CDATA[source]]></category>
		<category><![CDATA[Tips and Tricks]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[Work process]]></category>
		<category><![CDATA[WPF]]></category>

		<guid isPermaLink="false">http://khason.net/blog/reading-and-decoding-rds-radio-data-system-in-c/</guid>
		<description><![CDATA[RDS or Radio Data System is very common in US and many European countries. It is communication protocol used to send small amount of digital information using regular FM radio broadcast. This protocol is used to &#8220;tell&#8221; your receiver about alternative frequencies, time, program notifications, program types, traffic information and regular text (such as singer [...]

<p/>]]></description>
			<content:encoded><![CDATA[<p><a href="http://en.wikipedia.org/wiki/Radio_Data_System">RDS or Radio Data System</a> is very common in US and many European countries. It is communication protocol used to send small amount of digital information using regular FM radio broadcast. This protocol is used to &#8220;tell&#8221; your receiver about alternative frequencies, time, program notifications, program types, traffic information and regular text (such as singer name or genre). Unfortunately in Israel RDS is not very common and there is very limited number of radio stations broadcasts RDS information. </p>
<p><img border="0" alt="image" src="http://khason.net/images/2008/12/image-01bbfbdd-e89d-4a4e-89cd-e209655b5664.png" width="207" height="54"/> </p>
<h3>How RDS works?</h3>
<p>As mentioned earlier, it uses FM subcarrier to broadcast digital information. It was designed to support 10 and 18 characters numeric and 80 characters alphanumeric displays. RDS operates at 1187.5 bps and based on 26-bit word consisting of 16 data and 10 error detection bits. Due to the fact, that FM carrier is not very reliable, error code allows correct information to be received even if an error of 3-5 bits exists within 26 bit block. Each four data blocks interpreted as 104-bit signal and named &#8220;group&#8221;. Depending of the type of information, contained within the group, as different group type code is defined and transmitted within the group as upper five bits code. Even if more, then 104 bits required to completely send the information, there is no requirement that the next segment of the transmission be sent in the next group. There are 32 known groups types, defined by <a href="ftp://ftp.rds.org.uk/pub/acrobat/rbds1998.pdf">RFC</a>:</p>
<blockquote><p>private enum groupType : byte {<br />&nbsp;&nbsp; RDS_TYPE_0A = (0 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_0B = (0 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_1A = (1 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_1B = (1 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_2A = (2 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_2B = (2 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_3A = (3 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_3B = (3 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_4A = (4 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_4B = (4 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_5A = (5 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_5B = (5 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_6A = (6 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_6B = (6 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_7A = (7 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_7B = (7 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_8A = (8 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_8B = (8 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_9A = (9 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_9B = (9 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_10A = (10 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_10B = (10 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_11A = (11 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_11B = (11 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_12A = (12 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_12B = (12 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_13A = (13 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_13B = (13 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_14A = (14 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_14B = (14 * 2 + 1),<br />&nbsp;&nbsp; RDS_TYPE_15A = (15 * 2 + 0),<br />&nbsp;&nbsp; RDS_TYPE_15B = (15 * 2 + 1)<br />}</p>
</blockquote>
<p>Not all groups are in use all the time. However, there are some commitments, defined by the protocol. For example, 1A have to be transmitted at least once a second. This group contains special information, required for receivers to be synchronized and locked into the transmitting channel. </p>
<p>Within the error correction information we also receive the direction to treat them.</p>
<blockquote><p>private enum correctedType : byte {<br />&nbsp;&nbsp; NONE = 0,<br />&nbsp;&nbsp; ONE_TO_TWO = 1,<br />&nbsp;&nbsp; THREE_TO_FIVE = 2,<br />&nbsp;&nbsp; UNCORRECTABLE = 3<br />}</p>
</blockquote>
<p>Also, each message type has it own limits. For example RT (Radio Text &#8211; 64 character text to display on your receiver) and PS (Programme Service &#8211; eight character station identification) message are limited to 2 groups, when PI (Programme Identification &#8211; unique code of the station) and PTY (Programme Type &#8211; one of 31 predefined program types &#8211; e.g. News, Drama, Music) are limited to 4.</p>
<p>In addition to those constraints, block types are also different. But in this case, there are only 4 kinds</p>
<blockquote><p>private enum blockType : byte {<br />&nbsp;&nbsp; A = 6,<br />&nbsp;&nbsp; B = 4,<br />&nbsp;&nbsp; C = 2,<br />&nbsp;&nbsp; D = 0<br />}</p>
</blockquote>
<p>So, what we&#8217;re waiting for? Let&#8217;s start working.</p>
<h3>Handling errors</h3>
<p>First of all we should take care on errors and fix them if possible. For this purpose, we should first count them and detect the way of fixing</p>
<blockquote><p>var errorCount = (byte)((registers[0xa] &amp; 0x0E00) &gt;&gt; 9);<br />var errorFlags = (byte)(registers[0x6] &amp; 0xFF);<br />if (errorCount &lt; 4) {<br />&nbsp;&nbsp; _blocksValid += (byte)(4 &#8211; errorCount);<br />} else { /*drop data on more errors*/ return; } </p>
</blockquote>
<p>Once it done, we can try to fix them</p>
<blockquote><p>//Also drop the data if more than two errors were corrected<br />if (_getErrorsCorrected(errorFlags, blockType.B) &gt; correctedType.ONE_TO_TWO) return;</p>
<p>private correctedType _getErrorsCorrected(byte data, blockType block) { return (correctedType)((data &gt;&gt; (byte)block) &amp; 0&#215;30); }</p>
</blockquote>
<p>Now, our registers should be fine and we can start the detection of group type</p>
<h3>Group Type Detection</h3>
<p>This is very simple task, all we have to do is to get five upper bites to get a type and version.</p>
<blockquote><p>var group_type = (groupType)(registers[0xD] &gt;&gt; 11);</p>
</blockquote>
<p>Then we can handle PI and PTY, which we always have in RDS.</p>
<h3>PI and PTY treatment</h3>
<p>Now, let&#8217;s update pi code, due to the fact, that B format always have PI in words A and C</p>
<blockquote><p>_updatePI(registers[0xC]);  </p>
<p>if (((byte)group_type &amp; 0&#215;01) != 0) {<br /> _updatePI(registers[0xE]);<br />}</p>
</blockquote>
<p>To update PI, we should check whether the new value is different from the previous and update it only in case it changed.<br />
<blockquote>
<p>private void _updatePI(byte pi) {<br />&nbsp;&nbsp; uint rds_pi_validate_count = 0;<br />&nbsp;&nbsp; uint rds_pi_nonvalidated = 0;  </p>
<p>&nbsp;&nbsp; // if the pi value is the same for a certain number of times, update a validated pi variable<br />&nbsp;&nbsp; if (rds_pi_nonvalidated != pi) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rds_pi_nonvalidated = pi;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rds_pi_validate_count = 1;<br />&nbsp;&nbsp; } else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rds_pi_validate_count++;<br />&nbsp;&nbsp; }  </p>
<p>&nbsp;&nbsp; if (rds_pi_validate_count &gt; PI_VALIDATE_LIMIT) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _piDisplay = rds_pi_nonvalidated;<br />&nbsp;&nbsp; }<br />}</p>
</blockquote>
<p>Then we will update PTY<br />
<blockquote>
<p>_updatePTY((byte)((registers[0xd] &gt;&gt; 5) &amp; 0x1f));</p>
</blockquote>
<p>PTY treatment is very similar to PI, however it can be multiplied.&nbsp;<br />
<blockquote>
<p>private void _updatePTY(byte pty) {<br />&nbsp;&nbsp; uint rds_pty_validate_count = 0;<br />&nbsp;&nbsp; uint rds_pty_nonvalidated = 0;  </p>
<p>&nbsp;&nbsp; // if the pty value is the same for a certain number of times, update a validated pty variable<br />&nbsp;&nbsp; if (rds_pty_nonvalidated != pty) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rds_pty_nonvalidated = pty;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rds_pty_validate_count = 1;<br />&nbsp;&nbsp; } else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rds_pty_validate_count++;<br />&nbsp;&nbsp; }  </p>
<p>&nbsp;&nbsp; if (rds_pty_validate_count &gt; PTY_VALIDATE_LIMIT) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _ptyDisplay = rds_pty_nonvalidated;<br />&nbsp;&nbsp; }<br />}</p>
</blockquote>
<p>When we done with those two groups, we can start handling another. Today, we&#8217;ll handle only 0B, 2A and 2B types (I have a good reason for it, due to the fact, that only those are supported in Israel by now <img src='http://khason.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  ) So,<br />
<h3>Handling PS and different RTs</h3>
</p>
<p>Simple switch on those groups</p>
<blockquote><p>switch (group_type) {<br />&nbsp;&nbsp; case groupType.RDS_TYPE_0B:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; addr = (byte)((registers[0xd] &amp; 0&#215;3) * 2);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _updatePS((byte)(addr + 0), (byte)(registers[0xf] &gt;&gt; 8));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _updatePS((byte)(addr + 1), (byte)(registers[0xf] &amp; 0xff));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />&nbsp;&nbsp; case groupType.RDS_TYPE_2A:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; addr = (byte)((registers[0xd] &amp; 0xf) * 4);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; abflag = (byte)((registers[0xb] &amp; 0&#215;0010) &gt;&gt; 4);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _updateRT(abflag, 4, addr, (byte[])registers.Skip(0xe), errorFlags);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />&nbsp;&nbsp; case groupType.RDS_TYPE_2B:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; addr = (byte)((registers[0xd] &amp; 0xf) * 2);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; abflag = (byte)((registers[0xb] &amp; 0&#215;0010) &gt;&gt; 4);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // The last 32 bytes are unused in this format<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtTmp0[32] = 0x0d;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtTmp1[32] = 0x0d;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtCnt[32] = RT_VALIDATE_LIMIT;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _updateRT(abflag, 2, addr, (byte[])registers.Skip(0xe), errorFlags);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />}</p>
</blockquote>
<p>and let&#8217;s dig into PS.</p>
<p>In PS, we have high and low probability bits. So, if new bit in sequence matches the high probability bite and we have recieved enough bytes to max out the counter, we&#8217;ll push it into the low probability array.</p>
<blockquote><p>if (_psTmp0[idx] == default(byte)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_psCnt[idx] &lt; PS_VALIDATE_LIMIT) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _psCnt[idx]++;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _psCnt[idx] = PS_VALIDATE_LIMIT;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _psTmp1[idx] = default(byte);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </p>
</blockquote>
<p>Else, if new byte matches with the low probability byte, we should swap them and then reset the counter, by flagging the text as in transition. </p>
<blockquote><p>else if (_psTmp1[idx] == default(byte)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_psCnt[idx] &gt;= PS_VALIDATE_LIMIT) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isTextChange = true;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _psCnt[idx] = PS_VALIDATE_LIMIT + 1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _psTmp1[idx] = _psTmp0[idx];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _psTmp0[idx] = default(byte);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </p>
</blockquote>
<p>When we have an empty byte in high probability array or new bytes does not match anything we know, we should put it into low probability array.</p>
<blockquote><p>else if (_psCnt[idx] == null) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _psTmp0[idx] = default(byte);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _psCnt[idx] = 1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _psTmp1[idx] = default(byte);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </p>
</blockquote>
<p>Now, if we marked our text as changed, we should decrement the count for all characters to prevent displaying of partical message, which in still in transition.</p>
<blockquote><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (isTextChange) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (byte i = 0; i &lt; _psCnt.Length; i++) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_psCnt[i] &gt; 1) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _psCnt[i]&#8211;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </p>
</blockquote>
<p>Then by checking PS text for incompetence, when there are characters in high probability array has been seen fewer times, that was limited by validation.<br />
<blockquote>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (byte i = 0; i &lt; _psCnt.Length; i++) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_psCnt[i] &lt; PS_VALIDATE_LIMIT) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isComplete = false;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </p>
</blockquote>
<p>Only if PS text in the high probability array is complete, we&#8217;ll copy it into display.</p>
<blockquote><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (isComplete) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (byte i = 0; i &lt; _psDisplay.Length; i++) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _psDisplay[i] = _psTmp0[i];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
</blockquote>
<p>It is not very hard to treat PS. Isn&#8217;t it? Let&#8217;s see what&#8217;s going on with RT.</p>
<p>If A and B message flag changes, we&#8217;ll try to force a display by increasing the validation count for each byte. Then, we&#8217;ll wipe any cached text.</p>
<blockquote><p>&nbsp;&nbsp; if (abFlag != _rtFlag &amp;&amp; _rtFlagValid) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // If the A/B message flag changes, try to force a display<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // by increasing the validation count of each byte<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (i = 0; i &lt; _rtCnt.Length; i++) _rtCnt[addr + i]++;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _updateRTValue();  </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Wipe out the cached text <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (i = 0; i &lt; _rtCnt.Length; i++) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtCnt[i] = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtTmp0[i] = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtTmp1[i] = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp; }</p>
</blockquote>
<p>Now A and B flags are safe, sp we can start with message processing. First of all, NULL in RDS means space <img src='http://khason.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<blockquote><p>&nbsp;&nbsp; _rtFlag = abFlag;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp; _rtFlagValid = true;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp; for (i = 0; i &lt; count; i++) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (p[i] == null) p[i] = (byte)&#8217; &#8216;; </p>
</blockquote>
<p>The new byte matches the high probability byte also in this case. We habe to recieve this bite enough to max out counters. Then we can push it into the low probability as well.<br />
<blockquote>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_rtTmp0[addr + i] == p[i]) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_rtCnt[addr + i] &lt; RT_VALIDATE_LIMIT) _rtCnt[addr + i]++;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtCnt[addr + i] = RT_VALIDATE_LIMIT;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtTmp1[addr + i] = p[i];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </p>
</blockquote>
<p>When the new byte matches with low probability byte, we&#8217;ll swap them as well and reset counters to update text in transition flag. However in this case, our counter will go higher, then the validation limit. So we&#8217;ll have to remove it down later.</p>
<blockquote><p>else if (_rtTmp1[addr + i] == p[i]) { </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_rtCnt[addr + i] &gt;= PS_VALIDATE_LIMIT) isChange = true;  </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtCnt[addr + i] = RT_VALIDATE_LIMIT + 1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtTmp1[addr + i] = _rtTmp0[addr + i];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtTmp0[addr + i] = p[i];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } </p>
</blockquote>
<p>Now, the new byte is replaced an empty byte in the high probability array. Also, if this byte does not match anything, we should move it into low probability.</p>
<blockquote><p>else if (_rtCnt[addr + i] == null) { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtTmp0[addr + i] = p[i];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtCnt[addr + i] = 1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else _rtTmp1[addr + i] = p[i]; </p>
<p>&nbsp;&nbsp; } </p>
</blockquote>
<p>Now when the text is changing, we&#8217;ll decrement the counter for all characters exactly as we did for PS.<br />
<blockquote>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (i = 0; i &lt; _rtCnt.Length; i++) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_rtCnt[i] &gt; 1) _rtCnt[i]&#8211;;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp; } </p>
</blockquote>
<p>However, right after, we&#8217;ll update display.&nbsp; </p>
<blockquote><p>&nbsp;&nbsp; _updateRTValue();<br />}</p>
</blockquote>
<h3>Displaying RT</h3>
<p>But how to convert all those byte arrays into readable message? Simple <img src='http://khason.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>First of all if text is incomplete, we should keep loading it. Also it makes sense to check whether the target array is shorter then maximum allowed to prevent junk from being displayed.</p>
<blockquote><p>for (i = 0; i &lt; _rtTmp0.Length; i++) {<br />&nbsp;&nbsp; if (_rtCnt[i] &lt; RT_VALIDATE_LIMIT) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isComplete = false;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />&nbsp;&nbsp; }<br />&nbsp;&nbsp; if (_rtTmp0[i] == 0x0d) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />&nbsp;&nbsp; }<br />} </p>
</blockquote>
<p>Now, when our Radio Text is in the high probability and it complete, we should copy buffers.</p>
<blockquote><p>if (isComplete) {<br />&nbsp;&nbsp; _Text = string.Empty; </p>
<p>&nbsp;&nbsp; for (i = 0; i &lt; _rtDisplay.Length; i += 2) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((_rtDisplay[i] != 0x0d) &amp;&amp; (_rtDisplay[i + 1] != 0x0d)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtDisplay[i] = _rtTmp0[i + 1];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtDisplay[i + 1] = _rtTmp0[i];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtDisplay[i] = _rtTmp0[i];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtDisplay[i + 1] = _rtTmp0[i + 1];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_rtDisplay[i] != 0x0d)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _Text += _rtDisplay[i];  </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_rtDisplay[i + 1] != 0x0d)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _Text += _rtDisplay[i + 1];  </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((_rtDisplay[i] == 0x0d) || (_rtDisplay[i + 1] == 0x0d))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i = (byte)_rtDisplay.Length;<br />&nbsp;&nbsp; } </p>
</blockquote>
<p>And not forget to wipe out everything after the end of the message <img src='http://khason.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<blockquote><p>&nbsp;&nbsp; for (i++; i &lt; _rtDisplay.Length; i++) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtDisplay[i] = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtCnt[i] = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtTmp0[i] = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _rtTmp1[i] = 0;<br />&nbsp;&nbsp; }<br />} </p>
</blockquote>
<p>And finally update the text</p>
<blockquote><p>Text = _Text;</p>
</blockquote>
<p>We done. Now we can handle RDS digital messages, but what to do with analog data we get? Don&#8217;t you already know? I <a href="http://khason.net/blog/sound-tone-and-dtmf-generation-by-using-managed-directsound-and-c-and-sine-tone-detection-with-pure-managed-goertzel-algorithm-implementation/">blogged about it here</a>. </p>
<p>Have a nice day and be good people, because you know how to write client, knows to get and parse radio data in managed code. </p>
<p><img border="0" alt="image" src="http://khason.net/images/2008/12/image-988a1f1f-0dec-46b1-b494-3c75a0b9ce7d.png" width="263" height="44"/></p>


<p/>]]></content:encoded>
			<wfw:commentRss>http://khason.net/blog/reading-and-decoding-rds-radio-data-system-in-c/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Creating transparent buttons, panels and other control with Compact Framework and putting one into other</title>
		<link>http://khason.net/blog/creating-transparent-buttons-panels-and-other-control-with-compact-framework-and-putting-one-into-other/</link>
		<comments>http://khason.net/blog/creating-transparent-buttons-panels-and-other-control-with-compact-framework-and-putting-one-into-other/#comments</comments>
		<pubDate>Fri, 21 Nov 2008 05:09:29 +0000</pubDate>
		<dc:creator>Tamir</dc:creator>
				<category><![CDATA[BLOG]]></category>
		<category><![CDATA[.NET 3.5]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[CodeProject]]></category>
		<category><![CDATA[DEV]]></category>
		<category><![CDATA[Interop]]></category>
		<category><![CDATA[Mobile]]></category>
		<category><![CDATA[source]]></category>
		<category><![CDATA[Tips and Tricks]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://khason.net/blog/creating-transparent-buttons-panels-and-other-control-with-compact-framework-and-putting-one-into-other/</guid>
		<description><![CDATA[In WPF/Silverlight world it&#8217;s very simple to make transparent controls and put anything inside anything. However, that&#8217;s not the situation in WinForms, and even worth in the world of compact devices with CF. Within this worlds, there is only one way to make controls transparent &#8211; to use color masks. Today, we&#8217;ll create transparent controls [...]


Related posts:<ol><li><a href='http://khason.net/dev/read-singleton-approach-in-wpf-application/' rel='bookmark' title='Permanent Link: Real singleton approach in WPF application'>Real singleton approach in WPF application</a></li>
<li><a href='http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code/' rel='bookmark' title='Permanent Link: INotifyPropertyChanged auto wiring or how to get rid of redundant code'>INotifyPropertyChanged auto wiring or how to get rid of redundant code</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>In <a href="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/WPF/default.aspx">WPF</a>/<a href="http://blogs.microsoft.co.il/blogs/tamir/archive/tags/Silverlight/default.aspx">Silverlight</a> world it&#8217;s very simple to make transparent controls and put anything inside anything. However, that&#8217;s not the situation in WinForms, and even worth in the world of compact devices with CF. Within this worlds, there is only one way to make controls transparent &#8211; to use color masks. Today, we&#8217;ll create transparent controls with Compact Framework and put it into panel, which has image background.</p>
<p><img border="0" alt="image" src="http://khason.net/images/2008/12/image-679149a5-fff2-40ab-8966-6c6ffa40b692.png" width="259" height="397"/> </p>
<p>So let&#8217;s start. First of all, we need create our own control. For this purpose, we have to inherit from Control and override couple of things. More precise: OnPaint and OnPaintBackground. We do not want to paint background for transparent control, so let&#8217;s prevent it.</p>
<blockquote><p>public class TransparentImageButton : Control</p>
<p>protected override void OnPaintBackground(PaintEventArgs e) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //prevent<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected override void OnPaint(PaintEventArgs e) {</p>
</blockquote>
<p>Next, we have to get graphics, delivered by OnPain event argument and draw our image over it. However, BitBlt (which is used by core graphics system) is not very fast method, so it&#8217;s better for us to draw everything first and then copy final image to the device.</p>
<blockquote><p>Graphics gxOff; <br />Rectangle imgRect;<br />var image = (_isPressed &amp;&amp; PressedImage != null) ? PressedImage : Image;  </p>
<p>if (_imgOffscreen == null) {<br /> _imgOffscreen = new Bitmap(ClientSize.Width, ClientSize.Height);<br />}  </p>
<p>gxOff = Graphics.FromImage(_imgOffscreen); <br />gxOff.Clear(this.BackColor);&nbsp; <br />&#8230;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>if (image != null) {<br />var imageLeft = (this.Width &#8211; image.Width) / 2;<br />var imageTop = (this.Height &#8211; image.Height) / 2;  </p>
<p>if (!_isPressed) imgRect = new Rectangle(imageLeft, imageTop, image.Width, image.Height);<br />else imgRect = new Rectangle(imageLeft + 1, imageTop + 1, image.Width, image.Height);<br />var imageAttr = new ImageAttributes();</p>
</blockquote>
<p>To make images transparent, we have to use (as mentioned earlier) transparency color key (to tell windows what color it should not draw. We can code or provide this value to detect it by hitting any pixel on the image. Just like this: </p>
<blockquote><p>public static Color BackgroundImageColor(this Bitmap bmp) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return bmp.GetPixel(0, 0);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
</blockquote>
<p>Now we can keep working.</p>
<blockquote><p>imageAttr.SetColorKey(image.BackgroundImageColor(), image.BackgroundImageColor());<br />gxOff.DrawImage(image, imgRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, imageAttr);<br />} if (_isPressed) {<br /> var rc = this.ClientRectangle;<br />&nbsp; rc.Width&#8211;;<br />&nbsp; rc.Height&#8211;;<br />&nbsp; gxOff.DrawRectangle(new Pen(Color.Black), rc);<br /> }<br />e.Graphics.DrawImage(_imgOffscreen, 0, 0);</p>
</blockquote>
<p>Also, we have to provide others with possibility to handle this even too, thus we will not forget to add base.OnPaint(e); at&nbsp; the end.</p>
<p>Next step is to detect whether our button is clicked or not. We&#8217;ll override keyboard and mouse events to detect this.</p>
<blockquote><p>protected override void OnKeyDown(KeyEventArgs e) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _isPressed = this.Focused; this.Invalidate();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; base.OnKeyDown(e);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected override void OnKeyUp(KeyEventArgs e) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _isPressed = false; this.Invalidate();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; base.OnKeyUp(e);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected override void OnMouseDown(MouseEventArgs e) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _isPressed = this.Focused; this.Invalidate();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; base.OnMouseDown(e);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }  </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected override void OnMouseUp(MouseEventArgs e) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _isPressed = false; this.Invalidate();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; base.OnMouseUp(e);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
</blockquote>
<p>Compile and run to see no problem, when our transparent button lies on solid color control, however, we want to put it into panel with background &#8211; just like this one. In this case, you can use real transparent PNG and GIF images, also you can replace transparent color with well known Magenta (or any other color).</p>
<blockquote><p>public class ImagePanel : Panel {  </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Bitmap Image { get; set; }  </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected override void OnPaintBackground(PaintEventArgs e) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.Graphics.DrawImage(Image, 0, 0);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }</p>
</blockquote>
<p>When we&#8217;ll put it onto anything, that has no background color, we&#8217;ll see that our &#8220;fake transparency&#8221; disappears. Why this happen? To provide transparency Windows uses color masks, also while confederating facts, clipping algorithm within GDI is not very trustful, thus the only thing can be taken into account is color. But what to do if we have an image? We should clip it manually. We cannot just get the handle to parent device surface (see above about trustful GDI), so the only way to do it is by providing something, that we know for sure. For example interface, telling us, that parent has image, which drawn on the screen.</p>
<blockquote><p>internal interface IHaveImage {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bitmap Image { get; set; }<br />&nbsp;&nbsp;&nbsp; }</p>
</blockquote>
<p>When we know it, all we have to do is to clip the region of this image (not device context) and draw it as part of our really transparent control.</p>
<blockquote><p>if (this.Parent is IHaveImage) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var par = this.Parent as IHaveImage;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gxOff.DrawImage(par.Image.Clip(this.Bounds), 0, 0);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
</blockquote>
<p>The implementation of Image.Clip is very straight forward.</p>
<blockquote><p>public static Bitmap GetSS(this Graphics grx, Rectangle bounds) {<br />&nbsp;&nbsp;&nbsp; var res = new Bitmap(bounds.Width, bounds.Height);<br />&nbsp;&nbsp;&nbsp; var gxc = Graphics.FromImage(res);<br />&nbsp;&nbsp;&nbsp; IntPtr hdc = grx.GetHdc();<br />&nbsp;&nbsp;&nbsp; PlatformAPI.BitBlt(gxc.GetHdc(), 0, 0, bounds.Width, bounds.Height, hdc, bounds.Left, bounds.Top, PlatformAPI.SRCCOPY);<br />&nbsp;&nbsp;&nbsp; grx.ReleaseHdc(hdc);<br />&nbsp;&nbsp;&nbsp; return res;<br />}  </p>
<p>public static Bitmap Clip(this Bitmap source, Rectangle bounds) {<br />&nbsp;&nbsp;&nbsp; var grx = Graphics.FromImage(source);<br />&nbsp;&nbsp;&nbsp; return grx.GetSS(bounds);<br />}</p>
</blockquote>
<p>We done. Compiling all together will fake transparency for controls, even when it&#8217;s parents background is not pained with&nbsp; solid color brush.</p>
<p><a href="http://cid-4e39ecd492e4eec1.skydrive.live.com/self.aspx/Public/TransButton.zip">Source code for this article</a></p>
<p>P.S. Do not even try to inherit your custom Button control from framework Button class, dev team &#8220;forgot&#8221; to expose it&#8217;s event for override. So, OnPaint, OnPaintBackground, OnKeyUp, OnKeydown, OnMouseUp and OnMouseDown aside with most of other base events will not work for you, also BaseButton class has no default constructor, so the only class you can inherit from is Control.</p>
<p>Have a nice day and be good people.</p>


<p>Related posts:<ol><li><a href='http://khason.net/dev/read-singleton-approach-in-wpf-application/' rel='bookmark' title='Permanent Link: Real singleton approach in WPF application'>Real singleton approach in WPF application</a></li>
<li><a href='http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code/' rel='bookmark' title='Permanent Link: INotifyPropertyChanged auto wiring or how to get rid of redundant code'>INotifyPropertyChanged auto wiring or how to get rid of redundant code</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://khason.net/blog/creating-transparent-buttons-panels-and-other-control-with-compact-framework-and-putting-one-into-other/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>How to P/Invoke VarArgs (variable arguments) in C#? &#8230; or hidden junk in CLR</title>
		<link>http://khason.net/blog/how-to-pinvoke-varargs-variable-arguments-in-c-or-hidden-junk-in-clr/</link>
		<comments>http://khason.net/blog/how-to-pinvoke-varargs-variable-arguments-in-c-or-hidden-junk-in-clr/#comments</comments>
		<pubDate>Wed, 19 Nov 2008 06:44:10 +0000</pubDate>
		<dc:creator>Tamir</dc:creator>
				<category><![CDATA[BLOG]]></category>
		<category><![CDATA[.NET 3.5]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[DEV]]></category>
		<category><![CDATA[Interop]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[source]]></category>
		<category><![CDATA[Tips and Tricks]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[Work process]]></category>
		<category><![CDATA[WPF]]></category>

		<guid isPermaLink="false">http://khason.net/blog/how-to-pinvoke-varargs-variable-arguments-in-c-or-hidden-junk-in-clr/</guid>
		<description><![CDATA[Recently I wrote a cheat sheet for pinvoking in .NET. Shortly after I got a question in comments about how to deal with variable arguments, when it&#8217;s more, then one parameter. Also what to do if those arguments are heterogeneous? Let&#8217;s say, that we have following method in C: int VarSum(int nargs, &#8230;){&#160;&#160;&#160; va_list argp;&#160;&#160;&#160; [...]


Related posts:<ol><li><a href='http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code/' rel='bookmark' title='Permanent Link: INotifyPropertyChanged auto wiring or how to get rid of redundant code'>INotifyPropertyChanged auto wiring or how to get rid of redundant code</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>Recently I <a href="http://khason.net/blog/pinvoke-cheat-sheet/">wrote a cheat sheet for pinvoking in .NET</a>. Shortly after I got a question in comments about how to deal with variable arguments, when it&#8217;s more, then one parameter. Also what to do if those arguments are heterogeneous? </p>
<p>Let&#8217;s say, that we have following method in C:</p>
<blockquote><p>int VarSum(int nargs, &#8230;){<br />&nbsp;&nbsp;&nbsp; va_list argp;<br />&nbsp;&nbsp;&nbsp; va_start( argp, nargs );<br />&nbsp;&nbsp;&nbsp; int sum = 0;<br />&nbsp;&nbsp;&nbsp; for( int i = 0 ; i &lt; nargs; i++ ) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int arg = va_arg( argp, int );<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum += arg;<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; va_end( argp );  </p>
<p>&nbsp;&nbsp;&nbsp; return sum;<br />}</p>
</blockquote>
<p>We can expose this method to C# as following:</p>
<blockquote><p>[System.Runtime.InteropServices.DllImportAttribute("unmanaged.dll", EntryPoint = "VarSum")]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static extern int VarSum(int nargs,int arg1); </p>
<p>[System.Runtime.InteropServices.DllImportAttribute("unmanaged.dll", EntryPoint = "VarSum")]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static extern int VarSum(int nargs,int arg1,int arg2);</p>
<p>[System.Runtime.InteropServices.DllImportAttribute("unmanaged.dll", EntryPoint = "VarSum")]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static extern int VarSum(int nargs,int arg1,int arg2,int arg3);</p>
<p>etc&#8230;</p>
</blockquote>
<p>And it will work. However, if you&#8217;ll try to expose it as int array, marshaller will fail to understand how to align things</p>
<blockquote><p>[System.Runtime.InteropServices.DllImportAttribute("unmanaged.dll", EntryPoint = "VarSum")]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static extern int VarSum(int nargs,int[] arg);</p>
</blockquote>
<p>This in spite of the fact, that this method will work properly with another signature</p>
<blockquote><p>int ArrSum(int* nargs) {<br />&nbsp;&nbsp;&nbsp; int sum = 0;<br />&nbsp;&nbsp;&nbsp; for( int i = 0 ; i &lt; 2; i++ ) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sum += nargs[i];<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; return sum;<br />}</p>
</blockquote>
<p>So what to do? The <u>official</u> answer is &#8211; <strong>you have nothing to do, rather then override all possibilities</strong>. This is very bad and absolutely not flexible. So, there is small class in C#, named ArgIterator. This one is similar to params object[], but knows to marshal into varargs. The problem is, that you have no way to add things inside. It&#8217;s &#8220;kind-of-read-only&#8221;. </p>
<p>Let&#8217;s look into reflected version of ArgIterator. We&#8217;ll see there something, named <strong>__arglist and __refvalue</strong>. OMG, isn&#8217;t it good old stuff similar to &#8220;__declspec(dllexport) int _stdcall&#8221; etc.? It is! But can we use it in C#? We can! Just sign your method as Cdecl and you have working signature for &#8220;&#8230;&#8221;</p>
<blockquote><p>[System.Runtime.InteropServices.DllImportAttribute("unmanaged.dll", EntryPoint = "VarSum", <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CallingConvention=System.Runtime.InteropServices.CallingConvention.Cdecl)]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static extern int VarSum(int nargs, __arglist);</p>
</blockquote>
<p>Yes, looks strange, and absolutely not CLR compliant. However, this is the only way to expose varargs to CLR via P/Invoke. How to use it? Simple:</p>
<blockquote><p>c = VarSum(2, __arglist(5, 10));</p>
</blockquote>
<p>Have a nice day and be good people. Also, my question to Microsoft is why this stuff is not in MSDN and we, as developers, have no way to get rid of it. </p>
<p>Is not it very good practices to use non-compliant methods? Give us another way to do it!<br />Is not it very good practices to use variable arguments in unmanaged method signatures? So why you want dynamic types in C# 4?</p>
<p><a href="http://cid-4e39ecd492e4eec1.skydrive.live.com/self.aspx/Public/VarArgsMarshaller.zip ">Source code for this article</a></p>


<p>Related posts:<ol><li><a href='http://khason.net/dev/inotifypropertychanged-auto-wiring-or-how-to-get-rid-of-redundant-code/' rel='bookmark' title='Permanent Link: INotifyPropertyChanged auto wiring or how to get rid of redundant code'>INotifyPropertyChanged auto wiring or how to get rid of redundant code</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://khason.net/blog/how-to-pinvoke-varargs-variable-arguments-in-c-or-hidden-junk-in-clr/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
