<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4300967513113257645</id><updated>2012-02-17T04:31:08.657+07:00</updated><category term='1d'/><category term='sqlce'/><category term='pda'/><category term='quickmark'/><category term='sql'/><category term='decrypt'/><category term='stored procedure'/><category term='sqlceresultset'/><category term='datamatrix'/><category term='optimize'/><category term='function'/><category term='2d'/><category term='compact framework'/><category term='.net'/><category term='like'/><category term='more'/><category term='windows mobile'/><category term='activesync'/><category term='parameter sniffing'/><category term='sql server'/><category term='wildcard'/><category term='barcode'/><title type='text'>.NET Share</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://dotnet-share.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4300967513113257645/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://dotnet-share.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Alex Wijoyo</name><uri>http://www.blogger.com/profile/02815034937798828381</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>6</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4300967513113257645.post-7493641178920700622</id><published>2010-04-19T09:08:00.008+07:00</published><updated>2010-04-19T11:05:59.772+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='function'/><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='sql server'/><category scheme='http://www.blogger.com/atom/ns#' term='decrypt'/><category scheme='http://www.blogger.com/atom/ns#' term='stored procedure'/><title type='text'>Decrypt SQL 2005 long stored functions (scalar, inline table-valued, multi statement table-valued)</title><content type='html'>I needed to decrypt an encrypted stored function in SQL Server 2005 and I found this promising T-SQL script from Omri Bahat. Omri has done a great job on creating this script. This script has bugs on stored function DML creation and printing decrypted text over 4,000 characters. I have fixed these two bugs so this script can be used for decrypting long stored function. My modification has small flaw. My script can't differ inline and multi statement table-valued, so user must tell the script whether the function is inline or multi statement (@IsInlineTableValued).&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 95%; height: 500px;"&gt;&lt;code&gt;/*==================================================================================&lt;br /&gt;&lt;br /&gt;NAME:          Decrypt SQL 2005 stored procedures, functions, views, and triggers&lt;br /&gt;&lt;br /&gt;DESCRIPTION:            HEADS UP: In order to run this script you must log in&lt;br /&gt;                      to the server in DAC mode: To do so, type&lt;br /&gt;                      ADMIN:&lt;sqlinstancename&gt; as your server name and use the "sa"&lt;br /&gt;                      or any other server admin user with the appropriate password.&lt;br /&gt;                      Example: sqlcmd -S (local) -A -E -i C:\Decrypt.sql -o C:\Output.txt&lt;br /&gt;                    &lt;br /&gt;                      CAUTION! DAC (dedicated admin access) will kick out all other&lt;br /&gt;                      server users.&lt;br /&gt;                    &lt;br /&gt;                      The script below accepts an object (schema name + object name)&lt;br /&gt;                      that were created using the WITH ENCRYPTION option and returns&lt;br /&gt;                      the decrypted script that creates the object. This script&lt;br /&gt;                      is useful to decrypt stored procedures, views, functions,&lt;br /&gt;                      and triggers that were created WITH ENCRYPTION.&lt;br /&gt;                    &lt;br /&gt;                      The algorithm used below is the following:&lt;br /&gt;                      1. Check that the object exists and that it is encrypted.&lt;br /&gt;                      2. In order to decrypt the object, the script ALTER (!!!) it&lt;br /&gt;                      and later restores the object to its original one. This is&lt;br /&gt;                      required as part of the decryption process: The object&lt;br /&gt;                      is altered to contain dummy text (the ALTER uses WITH ENCRYPTION)&lt;br /&gt;                      and then compared to the CREATE statement of the same dummy&lt;br /&gt;                      content.&lt;br /&gt;                    &lt;br /&gt;                      Note: The object is altered in a transaction, which is rolled&lt;br /&gt;                      back immediately after the object is changed to restore&lt;br /&gt;                      all previous settings.&lt;br /&gt;                    &lt;br /&gt;                      3. A XOR operation between the original binary stream of the&lt;br /&gt;                      enrypted object with the binary representation of the dummy&lt;br /&gt;                      object and the binary version of the object in clear-text&lt;br /&gt;                      is used to decrypt the original object.&lt;br /&gt;                 &lt;br /&gt;&lt;br /&gt;USER PARAMETERS:        @ObjectOwnerOrSchema&lt;br /&gt;                      @ObjectName&lt;br /&gt;                      @IsInlineTableValued&lt;br /&gt;&lt;br /&gt;RESULTSET:              NA&lt;br /&gt;&lt;br /&gt;RESULTSET SORT:         NA&lt;br /&gt;&lt;br /&gt;USING TABLES/VIEWS:     sys.sysobjvalues&lt;br /&gt;                      syscomments&lt;br /&gt;&lt;br /&gt;REVISIONS&lt;br /&gt;&lt;br /&gt;DATE         DEVELOPER          DESCRIPTION OF REVISION             VERSION&lt;br /&gt;=========    ===============    =================================   ===========&lt;br /&gt;01/01/2007   Omri Bahat         Initial release                     1.00&lt;br /&gt;19/04/2010   Alex Wijoyo        Support function and size&gt;4000      1.10&lt;br /&gt;&lt;br /&gt;==================================================================================&lt;br /&gt;Copyright  SQL Farms Solutions, www.sqlfarms.com. All rights reserved.&lt;br /&gt;This code can be used only for non-redistributable purposes.&lt;br /&gt;The code can be used for free as long as this copyright notice is not removed.&lt;br /&gt;==================================================================================*/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;DECLARE @ObjectOwnerOrSchema NVARCHAR(128)&lt;br /&gt;DECLARE @ObjectName NVARCHAR(128)&lt;br /&gt;DECLARE @IsInlineTableValued BIT&lt;br /&gt;USE NORTHWIND;&lt;br /&gt;SET @ObjectOwnerOrSchema = 'dbo'&lt;br /&gt;SET @ObjectName = 'Foo'&lt;br /&gt;SET @IsInlineTableValued=0&lt;br /&gt;&lt;br /&gt;DECLARE @IsTableValued BIT&lt;br /&gt;DECLARE @i INT&lt;br /&gt;DECLARE @ObjectDataLength INT&lt;br /&gt;DECLARE @ContentOfEncryptedObject NVARCHAR(MAX)&lt;br /&gt;DECLARE @ContentOfDecryptedObject NVARCHAR(MAX)&lt;br /&gt;DECLARE @ContentOfFakeObject NVARCHAR(MAX)&lt;br /&gt;DECLARE @ContentOfFakeEncryptedObject NVARCHAR(MAX)&lt;br /&gt;DECLARE @ObjectType NVARCHAR(128)&lt;br /&gt;DECLARE @ObjectID INT&lt;br /&gt;DECLARE @DecryptedChar NCHAR&lt;br /&gt;DECLARE @DecryptedLine NVARCHAR(MAX)&lt;br /&gt;&lt;br /&gt;SET NOCOUNT ON&lt;br /&gt;&lt;br /&gt;SET @IsTableValued=0&lt;br /&gt;&lt;br /&gt;SET @ObjectID = OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']')&lt;br /&gt;&lt;br /&gt;-- Check that the provided object exists in the database.&lt;br /&gt;IF @ObjectID IS NULL&lt;br /&gt;BEGIN&lt;br /&gt;  RAISERROR('The object name or schema provided does not exist in the database', 16, 1)&lt;br /&gt;  RETURN&lt;br /&gt;END&lt;br /&gt;&lt;br /&gt;-- Check that the provided object is encrypted.&lt;br /&gt;IF NOT EXISTS(SELECT TOP 1 * FROM syscomments WHERE id = @ObjectID AND encrypted = 1)&lt;br /&gt;BEGIN&lt;br /&gt;  RAISERROR('The object provided exists however it is not encrypted. Aborting.', 16, 1)&lt;br /&gt;  RETURN&lt;br /&gt;END&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;-- Determine the type of the object&lt;br /&gt;IF OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']', 'PROCEDURE') IS NOT NULL&lt;br /&gt;  SET @ObjectType = 'PROCEDURE'&lt;br /&gt;ELSE&lt;br /&gt;  IF OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']', 'TRIGGER') IS NOT NULL&lt;br /&gt;      SET @ObjectType = 'TRIGGER'&lt;br /&gt;  ELSE&lt;br /&gt;      IF OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']', 'VIEW') IS NOT NULL&lt;br /&gt;          SET @ObjectType = 'VIEW'&lt;br /&gt;      ELSE&lt;br /&gt;          SET @ObjectType = 'FUNCTION'&lt;br /&gt;&lt;br /&gt;IF @ObjectType='FUNCTION'          &lt;br /&gt;  SELECT @IsTableValued=CASE WHEN COUNT(*)&gt;0 THEN 1 ELSE 0 END&lt;br /&gt;  FROM INFORMATION_SCHEMA.ROUTINES&lt;br /&gt;  WHERE ROUTINE_SCHEMA=@ObjectOwnerOrSchema and ROUTINE_NAME=@ObjectName and DATA_TYPE='TABLE'&lt;br /&gt;&lt;br /&gt;-- Get the binary representation of the object- syscomments no longer holds&lt;br /&gt;-- the content of encrypted object.&lt;br /&gt;SELECT TOP 1 @ContentOfEncryptedObject = imageval&lt;br /&gt;FROM sys.sysobjvalues&lt;br /&gt;WHERE objid = OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']')&lt;br /&gt;      AND valclass = 1 --and subobjid = 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SET @ObjectDataLength = DATALENGTH(@ContentOfEncryptedObject)/2&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;-- We need to alter the existing object and make it into a dummy object&lt;br /&gt;-- in order to decrypt its content. This is done in a transaction&lt;br /&gt;-- (which is later rolled back) to ensure that all changes have a minimal&lt;br /&gt;-- impact on the database.&lt;br /&gt;SET @ContentOfFakeObject = N'ALTER ' + @ObjectType + N' [' + @ObjectOwnerOrSchema + N'].[' + @ObjectName + N']'+CASE @ObjectType WHEN 'FUNCTION' THEN '() RETURNS INT WITH ENCRYPTION AS BEGIN RETURN 1;END;' ELSE ' WITH ENCRYPTION AS' END&lt;br /&gt;&lt;br /&gt;-- Modify table-valued function DML&lt;br /&gt;IF @IsTableValued=1&lt;br /&gt;BEGIN&lt;br /&gt;  IF @IsInlineTableValued=1&lt;br /&gt;  BEGIN&lt;br /&gt;      SET @ContentOfFakeObject = REPLACE(@ContentOfFakeObject,N'INT',N'TABLE')&lt;br /&gt;      SET @ContentOfFakeObject = REPLACE(@ContentOfFakeObject,N'BEGIN RETURN 1;END;',N'RETURN(SELECT 1 c);')&lt;br /&gt;  END&lt;br /&gt;  ELSE&lt;br /&gt;  BEGIN&lt;br /&gt;      SET @ContentOfFakeObject = REPLACE(@ContentOfFakeObject,N'INT',N'@t TABLE(c INT)')&lt;br /&gt;      SET @ContentOfFakeObject = REPLACE(@ContentOfFakeObject,N'RETURN 1;',N'RETURN;')&lt;br /&gt;  END&lt;br /&gt;END&lt;br /&gt;&lt;br /&gt;WHILE DATALENGTH(@ContentOfFakeObject)/2 &lt; @ObjectDataLength BEGIN         IF DATALENGTH(@ContentOfFakeObject)/2 + 4000 &lt; @ObjectDataLength                 SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', 4000)         ELSE                 SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', @ObjectDataLength - (DATALENGTH(@ContentOfFakeObject)/2)) END  -- Since we need to alter the object in order to decrypt it, this is done -- in a transaction SET XACT_ABORT OFF BEGIN TRAN  EXEC(@ContentOfFakeObject)  IF @@ERROR &lt;&gt; 0&lt;br /&gt;      ROLLBACK TRAN&lt;br /&gt;&lt;br /&gt;-- Get the encrypted content of the new "fake" object.&lt;br /&gt;SELECT TOP 1 @ContentOfFakeEncryptedObject = imageval&lt;br /&gt;FROM sys.sysobjvalues&lt;br /&gt;WHERE objid = OBJECT_ID('[' + @ObjectOwnerOrSchema + '].[' + @ObjectName + ']')&lt;br /&gt;      AND valclass = 1 --and subobjid = 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;IF @@TRANCOUNT &gt; 0&lt;br /&gt;      ROLLBACK TRAN&lt;br /&gt;&lt;br /&gt;-- Generate a CREATE script for the dummy object text.&lt;br /&gt;SET @ContentOfFakeObject = N'CREATE ' + @ObjectType + N' [' + @ObjectOwnerOrSchema + N'].[' + @ObjectName + N']'+CASE @ObjectType WHEN 'FUNCTION' THEN '() RETURNS INT WITH ENCRYPTION AS BEGIN RETURN 1;END;' ELSE ' WITH ENCRYPTION AS' END&lt;br /&gt;&lt;br /&gt;-- Modify table-valued function DML&lt;br /&gt;IF @IsTableValued=1&lt;br /&gt;BEGIN&lt;br /&gt;  IF @IsInlineTableValued=1&lt;br /&gt;  BEGIN&lt;br /&gt;      SET @ContentOfFakeObject = REPLACE(@ContentOfFakeObject,N'INT',N'TABLE')&lt;br /&gt;      SET @ContentOfFakeObject = REPLACE(@ContentOfFakeObject,N'BEGIN RETURN 1;END;',N'RETURN(SELECT 1 c);')&lt;br /&gt;  END&lt;br /&gt;  ELSE&lt;br /&gt;  BEGIN&lt;br /&gt;      SET @ContentOfFakeObject = REPLACE(@ContentOfFakeObject,N'INT',N'@t TABLE(c INT)')&lt;br /&gt;      SET @ContentOfFakeObject = REPLACE(@ContentOfFakeObject,N'RETURN 1;',N'RETURN;')&lt;br /&gt;  END&lt;br /&gt;END&lt;br /&gt;&lt;br /&gt;WHILE DATALENGTH(@ContentOfFakeObject)/2 &lt; @ObjectDataLength BEGIN         IF DATALENGTH(@ContentOfFakeObject)/2 + 4000 &lt; @ObjectDataLength                 SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', 4000)         ELSE                 SET @ContentOfFakeObject = @ContentOfFakeObject + REPLICATE(N'-', @ObjectDataLength - (DATALENGTH(@ContentOfFakeObject)/2)) END  SET @i = 1  --Fill the variable that holds the decrypted data with a filler character SET @ContentOfDecryptedObject = N''  WHILE DATALENGTH(@ContentOfDecryptedObject)/2 &lt; @ObjectDataLength BEGIN         IF DATALENGTH(@ContentOfDecryptedObject)/2 + 4000 &lt; @ObjectDataLength                 SET @ContentOfDecryptedObject = @ContentOfDecryptedObject + REPLICATE(N'A', 4000)         ELSE                 SET @ContentOfDecryptedObject = @ContentOfDecryptedObject + REPLICATE(N'A', @ObjectDataLength - (DATALENGTH(@ContentOfDecryptedObject)/2)) END  SET @DecryptedLine=N''  WHILE @i &lt;= @ObjectDataLength BEGIN         --xor real &amp;amp; fake &amp;amp; fake encrypted         SET @DecryptedChar=NCHAR(                         UNICODE(SUBSTRING(@ContentOfEncryptedObject, @i, 1)) ^                         (                                 UNICODE(SUBSTRING(@ContentOfFakeObject, @i, 1)) ^                                 UNICODE(SUBSTRING(@ContentOfFakeEncryptedObject, @i, 1))                         ))                 IF @DecryptedChar=NCHAR(10)         BEGIN             PRINT @DecryptedLine             SET @DecryptedLine=N''                     END         ELSE             IF @DecryptedChar&lt;&gt;NCHAR(13)&lt;br /&gt;              SET @DecryptedLine=@DecryptedLine+@DecryptedChar&lt;br /&gt;&lt;br /&gt;      SET @ContentOfDecryptedObject = STUFF(@ContentOfDecryptedObject, @i, 1,@DecryptedChar)&lt;br /&gt;&lt;br /&gt;      SET @i = @i + 1&lt;br /&gt;END&lt;br /&gt;PRINT @DecryptedLine&lt;br /&gt;&lt;br /&gt;-- PRINT the content of the decrypted object&lt;br /&gt;--PRINT(@ContentOfDecryptedObject)&lt;br /&gt;&lt;br /&gt;&lt;/sqlinstancename&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4300967513113257645-7493641178920700622?l=dotnet-share.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dotnet-share.blogspot.com/feeds/7493641178920700622/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dotnet-share.blogspot.com/2010/04/decrypt-sql-2005-long-stored-functions.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4300967513113257645/posts/default/7493641178920700622'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4300967513113257645/posts/default/7493641178920700622'/><link rel='alternate' type='text/html' href='http://dotnet-share.blogspot.com/2010/04/decrypt-sql-2005-long-stored-functions.html' title='Decrypt SQL 2005 long stored functions (scalar, inline table-valued, multi statement table-valued)'/><author><name>Alex Wijoyo</name><uri>http://www.blogger.com/profile/02815034937798828381</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4300967513113257645.post-7730436151790596048</id><published>2009-10-10T10:45:00.007+07:00</published><updated>2009-10-12T08:30:22.197+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='wildcard'/><category scheme='http://www.blogger.com/atom/ns#' term='like'/><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='sql server'/><category scheme='http://www.blogger.com/atom/ns#' term='parameter sniffing'/><category scheme='http://www.blogger.com/atom/ns#' term='optimize'/><title type='text'>Optimize SQL Like Comparison for Retrieving Specific or All Result</title><content type='html'>My colleague found a strange case with his SQL. The SQL took more than two minutes to execute.&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;pre   style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); line-height: 14px; width: 95%;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;font-size:12px;"&gt;&lt;code&gt;declare @Num int;&lt;br /&gt;declare @Date datetime;&lt;br /&gt;declare @CategoryId varchar(255);&lt;br /&gt;declare @ProductionId varchar(50);&lt;br /&gt;declare @UserId varchar(50);&lt;br /&gt;select @Num=-1,@Date=getdate(),@CategoryId='All',@ProductionId='All',@UserId='ALEX';&lt;br /&gt;select distinct (ROW_NUMBER() OVER (ORDER BY v.ProductVariantId ASC)+1)*-1 as FGLongtermPlanDetailNum,@Num as LongTermPlanNum,v.Warehouse_Dep,v.ProductVariantId,v.ProductVariantName,u.UnitId,isnull(SOQty,0) as TotalQtySO,isnull(fg.FGStock,0) as TotalFGStock,0 as FGRequirementQty,p.Weight,v.WeightPerBatch,@UserId as UpdateUserId,GETDATE() as UpdateTime,@UserId as CreateUserId,GETDATE() as CreateTime&lt;br /&gt;from productvariant as v inner join productunit as pu on v.productid=pu.productid and pu.Warehouse_Dep=v.Warehouse_Dep inner join product p on p.productid=pu.productid and pu.Warehouse_Dep=p.Warehouse_Dep and (CategoryId like case when @CategoryId='All' then '%' else @CategoryId end) and (ProductionId like case when @ProductionId='All' then '%' else @ProductionId end)&lt;br /&gt;inner join unit as u on pu.unitid=u.unitid&lt;br /&gt;left join (select UnitId,VariantId,ProductId,Warehouse_Dep,sum(LeftQuantity) SOQty from soproduct where LeftQuantity&lt;&gt;0 and SODate&lt;dateadd(day,1,@date) group="" by="" sop="" on="" variantid="v.ProductVariantId" warehouse_dep="v.Warehouse_Dep" productid="v.ProductId" and="" unitid="u.UnitId" left="" join="" select="" fgstock="" from="" activestockproductperiod="" activedate=""&gt;&lt;&gt;'GR')&lt;br /&gt;group by ProductVariantId,UnitId,ProductId) fg on fg.ProductId=v.ProductId and fg.ProductVariantId=v.ProductVariantId and v.Warehouse_Dep='FG' and u.UnitId=fg.UnitId&lt;br /&gt;where v.Warehouse_Dep='FG' and u.IsExtended=0 and v.IsDisable=0;&lt;br /&gt;&lt;/dateadd(day,1,@date)&gt;&lt;/code&gt;&lt;/pre&gt;But when he hard coded @ProductionId and @CategoryId into &lt;span style="font-weight: bold;"&gt;'All'&lt;/span&gt; (in bold):&lt;br /&gt;&lt;pre   style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); line-height: 14px; width: 95%;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;font-size:12px;"&gt;&lt;code&gt;declare @Num int;&lt;br /&gt;declare @Date datetime;&lt;br /&gt;declare @CategoryId varchar(255);&lt;br /&gt;declare @ProductionId varchar(50);&lt;br /&gt;declare @UserId varchar(50);&lt;br /&gt;select @Num=-1,@Date=getdate(),@CategoryId='All',@ProductionId='All',@UserId='ALEX';&lt;br /&gt;select distinct (ROW_NUMBER() OVER (ORDER BY v.ProductVariantId ASC)+1)*-1 as FGLongtermPlanDetailNum,@Num as LongTermPlanNum,v.Warehouse_Dep,v.ProductVariantId,v.ProductVariantName,u.UnitId,isnull(SOQty,0) as TotalQtySO,isnull(fg.FGStock,0) as TotalFGStock,0 as FGRequirementQty,p.Weight,v.WeightPerBatch,@UserId as UpdateUserId,GETDATE() as UpdateTime,@UserId as CreateUserId,GETDATE() as CreateTime&lt;br /&gt;from productvariant as v inner join productunit as pu on v.productid=pu.productid and pu.Warehouse_Dep=v.Warehouse_Dep inner join product p on p.productid=pu.productid and pu.Warehouse_Dep=p.Warehouse_Dep and (CategoryId like case when &lt;span style="font-weight: bold;"&gt;'All'&lt;/span&gt;='All' then '%' else &lt;span style="font-weight: bold;"&gt;'All'&lt;/span&gt; end) and (ProductionId like case when &lt;span style="font-weight: bold;"&gt;'All'&lt;/span&gt;='All' then '%' else &lt;span style="font-weight: bold;"&gt;'All'&lt;/span&gt; end)&lt;br /&gt;inner join unit as u on pu.unitid=u.unitid&lt;br /&gt;left join (select UnitId,VariantId,ProductId,Warehouse_Dep,sum(LeftQuantity) SOQty from soproduct where LeftQuantity&lt;&gt;0 and SODate&lt;dateadd(day,1,@date) group="" by="" sop="" on="" variantid="v.ProductVariantId" warehouse_dep="v.Warehouse_Dep" productid="v.ProductId" and="" unitid="u.UnitId" left="" join="" select="" fgstock="" from="" activestockproductperiod="" activedate=""&gt;&lt;&gt;'GR')&lt;br /&gt;group by ProductVariantId,UnitId,ProductId) fg on fg.ProductId=v.ProductId and fg.ProductVariantId=v.ProductVariantId and v.Warehouse_Dep='FG' and u.UnitId=fg.UnitId&lt;br /&gt;where v.Warehouse_Dep='FG' and u.IsExtended=0 and v.IsDisable=0;&lt;br /&gt;&lt;/dateadd(day,1,@date)&gt;&lt;/code&gt;&lt;/pre&gt;The query only took two seconds to execute. After several trial and error efforts, we finally found the root cause. SQL Server seems to hate our like expression. Like expression used for giving freedom to user whether to retrieve specific or all result. So we change the like operator into equal operator (=) like this (changes are in bold):&lt;br /&gt;&lt;pre   style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); line-height: 14px; width: 95%;font-family:Andale Mono,Lucida Console,Monaco,fixed,monospace;font-size:12px;"&gt;&lt;code&gt;declare @Num int;&lt;br /&gt;declare @Date datetime;&lt;br /&gt;declare @CategoryId varchar(255);&lt;br /&gt;declare @ProductionId varchar(50);&lt;br /&gt;declare @UserId varchar(50);&lt;br /&gt;select @Num=-1,@Date=getdate(),@CategoryId='All',@ProductionId='All',@UserId='ALEX';&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(255, 0, 0);"&gt;declare @MyCategoryId varchar(255);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(255, 0, 0);"&gt;declare @MyProductionId varchar(50);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;  set @MyCategoryId=case when @CategoryId='All' then '%' else @CategoryId end;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;  set @MyProductionId=case when @ProductionId='All' then '%' else @ProductionId end;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;select distinct (ROW_NUMBER() OVER (ORDER BY v.ProductVariantId ASC)+1)*-1 as FGLongtermPlanDetailNum,@Num as LongTermPlanNum,v.Warehouse_Dep,v.ProductVariantId,v.ProductVariantName,u.UnitId,isnull(SOQty,0) as TotalQtySO,isnull(fg.FGStock,0) as TotalFGStock,0 as FGRequirementQty,p.Weight,v.WeightPerBatch,@UserId as UpdateUserId,GETDATE() as UpdateTime,@UserId as CreateUserId,GETDATE() as CreateTime&lt;br /&gt;from productvariant as v inner join productunit as pu on v.productid=pu.productid and pu.Warehouse_Dep=v.Warehouse_Dep inner join product p on p.productid=pu.productid and pu.Warehouse_Dep=p.Warehouse_Dep and (CategoryId&lt;span style="font-weight: bold;"&gt;=@MyCategoryId or @MyCategoryId='%'&lt;/span&gt;) and (ProductionId=&lt;span style="font-weight: bold;"&gt;@MyProductionId or @MyProductionId='%'&lt;/span&gt;)&lt;br /&gt;inner join unit as u on pu.unitid=u.unitid&lt;br /&gt;left join (select UnitId,VariantId,ProductId,Warehouse_Dep,sum(LeftQuantity) SOQty from soproduct where LeftQuantity&lt;&gt;0 and SODate&lt;dateadd(day,1,@date) group="" by="" sop="" on="" variantid="v.ProductVariantId" warehouse_dep="v.Warehouse_Dep" productid="v.ProductId" and="" unitid="u.UnitId" left="" join="" select="" fgstock="" from="" activestockproductperiod="" activedate=""&gt;&lt;&gt;'GR')&lt;br /&gt;group by ProductVariantId,UnitId,ProductId) fg on fg.ProductId=v.ProductId and fg.ProductVariantId=v.ProductVariantId and v.Warehouse_Dep='FG' and u.UnitId=fg.UnitId&lt;br /&gt;where v.Warehouse_Dep='FG' and u.IsExtended=0 and v.IsDisable=0;&lt;br /&gt;&lt;/dateadd(day,1,@date)&gt;&lt;/code&gt;&lt;/pre&gt;And it took only one second to execute, EUREKA. The only changes are:&lt;br /&gt;&lt;pre face="Andale Mono,Lucida Console,Monaco,fixed,monospace" size="12px" style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); line-height: 14px; width: 95%;"&gt;&lt;code&gt;CategoryId &lt;span style="font-weight: bold;"&gt;like case when @CategoryId='All' then '%' else @CategoryId end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;Changed into:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); line-height: 14px; width: 95%; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; font-size: 12px;"&gt;&lt;code&gt;CategoryId&lt;span style="font-weight: bold;"&gt;=@MyCategoryId or @MyCategoryId='%'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;May be you're wondering why should I redeclare @ProductionId and @CategoryId into &lt;span style="color: rgb(255, 0, 0);"&gt;@MyProductionId&lt;/span&gt; and &lt;span style="color: rgb(255, 0, 0);"&gt;@MyCategoryId&lt;/span&gt;. I need to redeclare the variable for avoiding SQL Server's parameter sniffing disease in stored procedure. Parameter sniffing will cause your stored procedure execute way slower than the bare query. Just follow this rule of thumb for avoiding parameter sniffing: Do not assign values to parameters. Redeclare your parameters into local variables and assign values to local variables instead of parameters.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4300967513113257645-7730436151790596048?l=dotnet-share.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dotnet-share.blogspot.com/feeds/7730436151790596048/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dotnet-share.blogspot.com/2009/10/optimize-sql-like-comparison-for.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4300967513113257645/posts/default/7730436151790596048'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4300967513113257645/posts/default/7730436151790596048'/><link rel='alternate' type='text/html' href='http://dotnet-share.blogspot.com/2009/10/optimize-sql-like-comparison-for.html' title='Optimize SQL Like Comparison for Retrieving Specific or All Result'/><author><name>Alex Wijoyo</name><uri>http://www.blogger.com/profile/02815034937798828381</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4300967513113257645.post-2306492510474040905</id><published>2009-09-12T08:59:00.007+07:00</published><updated>2009-09-12T10:15:21.815+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='datamatrix'/><category scheme='http://www.blogger.com/atom/ns#' term='barcode'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><title type='text'>Generate DataMatrix Barcode from Your .NET Application</title><content type='html'>&lt;a href="http://en.wikipedia.org/wiki/Datamatrix"&gt;Data Matrix&lt;/a&gt; barcode has several advantages over 1D barcodes such as:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Contains more character even alphanumeric.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Has square form for easier layout on printout.&lt;/li&gt;&lt;li&gt;Digital camera friendly even using your old cellphone camera.&lt;/li&gt;&lt;li&gt;Redundant information, you can scan 75% area of the barcode only for scanning.&lt;/li&gt;&lt;li&gt;A little bit omni scan direction.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Too bad my trusted DevExpress XtraReport XRBarcode &lt;span class="fullpost"&gt; doesn't have this symbology. Since I can't wait for DevExpress to implement Data Matrix so I decided to find open source solutions. Thanks God, I found external link on &lt;a href="http://en.wikipedia.org/wiki/Datamatrix"&gt;Data Matrix Wiki page&lt;/a&gt; for &lt;a href="http://sourceforge.net/projects/iec16022sharp/"&gt;iec16022sharp&lt;/a&gt; an open source .NET Data Matrix generator library. Since I use VB.NET, I built the iec16022sharp assembly and use it by adding a reference to it. I am glad that iec16022sharp usage is very easy.&lt;br /&gt;Here is my iec16022sharp usage snippet:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 95%;"&gt;&lt;code&gt;...&lt;br /&gt;Imports IEC16022Sharp&lt;br /&gt;...&lt;br /&gt;   Dim aReport As New RptBarcode&lt;br /&gt;   Dim aDataMatrix As New DataMatrix(BindSrcMaster.GetDataValue("BarcodeNum").ToString.Trim)&lt;br /&gt;&lt;br /&gt;   HierarchyRefreshDataRow(BindSrcMaster.GetDataRow)&lt;br /&gt;   With aReport&lt;br /&gt;       .CallingForm = Me&lt;br /&gt;       .PbDataMatrix.Image = DMImgUtility.SimpleResizeBmp(aDataMatrix.Image, 6, 0)&lt;br /&gt;       .QueryLoad(Application.StartupPath + "\CustomReport\Barcode.xml", BindSrcMaster)&lt;br /&gt;       .ShowPreview()&lt;br /&gt;   End With&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;The only functions I called from this library is a DataMatrix constructor and a static function DMImgUtility.SimpleResizeBmp. From the DataMatrix constructor I already have a small DataMatrix barcode. For enlarging the generated DataMatrix I just called the SimpleResizeBmp for enlarging it six times with zero pixel border. Hope my post give you an idea on using DataMatrix. Thank you.&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4300967513113257645-2306492510474040905?l=dotnet-share.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dotnet-share.blogspot.com/feeds/2306492510474040905/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dotnet-share.blogspot.com/2009/09/generate-datamatrix-barcode-from-your.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4300967513113257645/posts/default/2306492510474040905'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4300967513113257645/posts/default/2306492510474040905'/><link rel='alternate' type='text/html' href='http://dotnet-share.blogspot.com/2009/09/generate-datamatrix-barcode-from-your.html' title='Generate DataMatrix Barcode from Your .NET Application'/><author><name>Alex Wijoyo</name><uri>http://www.blogger.com/profile/02815034937798828381</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4300967513113257645.post-3468373887599791790</id><published>2009-09-02T11:48:00.019+07:00</published><updated>2009-09-12T08:54:44.198+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='windows mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='compact framework'/><category scheme='http://www.blogger.com/atom/ns#' term='barcode'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='2d'/><category scheme='http://www.blogger.com/atom/ns#' term='1d'/><category scheme='http://www.blogger.com/atom/ns#' term='quickmark'/><title type='text'>Application for Enabling Your WM Camera into a Barcode Scanner</title><content type='html'>I was really excited when I found QuickMark (I am not affiliated with QuickMark). This Taiwan company makes 2D and 1D barcode scanner software &lt;span class="fullpost"&gt;for Windows Mobile and Symbian. You can find a lot of similar software like QuickMark on the web. But QuickMark has one thing that stands in the crowd. You can call QuickMark using its API from your .NET Compact Framework 2.0 application and get the scan result. I already tested their API and it worked even for my EAN13 1D barcode. For only U$ 10 your WM Camera becomes a barcode scanner yippeee. I have polished their API demo below (sorry the source codes are not colored).&lt;br /&gt;FrmQuickMarkAPIDemo (the form that uses QuickMarkAPI):&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 95%;"&gt;&lt;code&gt;Public Class FrmQuickMarkAPIDemo&lt;br /&gt;   Private WithEvents aQuickMarkAPI As New QuickMarkAPI(AddressOf GetMsgString)&lt;br /&gt;&lt;br /&gt;   Public Sub New()&lt;br /&gt;       InitializeComponent()&lt;br /&gt;   End Sub&lt;br /&gt;&lt;br /&gt;   Private Sub MnuOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MnuOK.Click&lt;br /&gt;       aQuickMarkAPI.LaunchQuickMark()&lt;br /&gt;   End Sub&lt;br /&gt;&lt;br /&gt;   Private Sub GetMsgString(ByVal MsgString As String)&lt;br /&gt;       MsgBox(MsgString)&lt;br /&gt;   End Sub&lt;br /&gt;   'Just an alternative receive the message string using an event&lt;br /&gt;   'Private Sub aQuickMarkAPI_OnGetMsgString(ByVal MsgString As String) Handles aQuickMarkAPI.OnGetMsgString&lt;br /&gt;   '    MsgBox(MsgString)&lt;br /&gt;   'End Sub&lt;br /&gt;End Class&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="fullpost"&gt;QuickMarkAPIDemo (The API itself):&lt;/span&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 95%; height: 300px;"&gt;&lt;code&gt;Imports System.Diagnostics&lt;br /&gt;Imports System.Runtime.InteropServices&lt;br /&gt;Imports Microsoft.Win32&lt;br /&gt;Imports System.IO&lt;br /&gt;Imports Microsoft.WindowsCE.Forms&lt;br /&gt;'QuickMark&lt;br /&gt;&lt;br /&gt;'Flow of QuickMark Messenger&lt;br /&gt;'Open msgapi --&amp;gt; Register a window message for inter-application communication (RegisterWindowMessage)&lt;br /&gt;'--&amp;gt;launch QuickMark (Press OK button) --&amp;gt; send a unique message for inter-application communication(HWND_BROADCAST)&lt;br /&gt;' --&amp;gt; QuickMark recevie the handle of msgapi's window. --&amp;gt; Decoding using QuickMark&lt;br /&gt;' --&amp;gt; QuickMark send result to handle. --&amp;gt; msgapi show the result.&lt;br /&gt;&lt;br /&gt;Class QuickMarkAPI&lt;br /&gt;    Inherits MessageWindow&lt;br /&gt;&lt;br /&gt;    &amp;lt;DllImport(&amp;quot;coredll.dll&amp;quot;)&amp;gt; _&lt;br /&gt;    Private Shared Function RegisterWindowMessage(ByVal lpString As String) As UInteger&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;    &amp;lt;DllImport(&amp;quot;coredll.dll&amp;quot;, SetLastError:=True, CharSet:=CharSet.Auto)&amp;gt; _&lt;br /&gt;    Private Shared Function PostMessageW( _&lt;br /&gt;     ByVal hWnd As IntPtr, _&lt;br /&gt;     ByVal Msg As UInteger, _&lt;br /&gt;     ByVal wParam As IntPtr, _&lt;br /&gt;     ByVal lParam As IntPtr) As Boolean&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;    Friend Shared ReadOnly HWND_BROADCAST As New IntPtr(65535)&lt;br /&gt;&lt;br /&gt;    'a unique message for inter-application communication&lt;br /&gt;    Private RM_QuickMarkMessengerAPIApp As UInt32 = 0&lt;br /&gt;&lt;br /&gt;    'Handle to the window whose window procedure will receive the decoded result.&lt;br /&gt;    Public Delegate Sub OnGetMsgStringHandler(ByVal MsgString As String)&lt;br /&gt;    Public DefaultBarcodeType As Integer = 2 'Default is 2D scanning.&lt;br /&gt;    Event OnGetMsgString As OnGetMsgStringHandler&lt;br /&gt;    Private Const WM_COPYDATA As Integer = 74&lt;br /&gt;    Private aGetMsgString As OnGetMsgStringHandler = Nothing&lt;br /&gt;    Public Sub LaunchQuickMark()&lt;br /&gt;        ' Step 1: Get application path of QuickMark from registry.&lt;br /&gt;        Dim szQuickMarkApp As String = &amp;quot;&amp;quot;&lt;br /&gt;        Dim QuickMarkKey As RegistryKey = Registry.CurrentUser&lt;br /&gt;        QuickMarkKey = QuickMarkKey.OpenSubKey(&amp;quot;Software\QuickMark&amp;quot;, False)&lt;br /&gt;        szQuickMarkApp = QuickMarkKey.GetValue(&amp;quot;QuickMarkAppPath&amp;quot;).ToString()&lt;br /&gt;        QuickMarkKey.Close()&lt;br /&gt;&lt;br /&gt;        ' Step 2: Set default path of QuickMark.&lt;br /&gt;        If szQuickMarkApp.Length = 0 Then&lt;br /&gt;            szQuickMarkApp = &amp;quot;\Program Files\QuickMark\QuickMark.exe&amp;quot;&lt;br /&gt;        End If&lt;br /&gt;&lt;br /&gt;        ' Step 3: Launch QuickMark.&lt;br /&gt;        If File.Exists(szQuickMarkApp) Then&lt;br /&gt;            'Launch application&lt;br /&gt;            Dim P As New Process()&lt;br /&gt;            P.StartInfo.FileName = szQuickMarkApp&lt;br /&gt;            P.StartInfo.Verb = &amp;quot;Open&amp;quot;&lt;br /&gt;            P.Start()&lt;br /&gt;            P.WaitForExit(1000)&lt;br /&gt;            'wait 1 second&lt;br /&gt;            'step 4: send RM_QuickMarkMessengerAPIApp to all top windows.&lt;br /&gt;            'parameters: &lt;br /&gt;            '        HWND hWnd        : HWND_BROADCAST , all top windows.&lt;br /&gt;            '        UINT Msg        : A unique message for inter-application communication.&lt;br /&gt;            '        WPARAM wParam   : Handle to the window whose window procedure will receive the decoded result.&lt;br /&gt;            '        LPARAM lParam    : Set QuickMark application to 1D or 2D scanning.  (1:1D   2:2D)&lt;br /&gt;            PostMessageW(HWND_BROADCAST, RM_QuickMarkMessengerAPIApp, Me.Hwnd, DefaultBarcodeType)&lt;br /&gt;        Else&lt;br /&gt;            MessageBox.Show(&amp;quot;QuickMark not found!&amp;quot;)&lt;br /&gt;        End If&lt;br /&gt;    End Sub&lt;br /&gt;    'Receive the decoded result.&lt;br /&gt;    'STRUCT COPYDATASTRUCT member&lt;br /&gt;    'dwData:Data types of data.&lt;br /&gt;    '         TYPE_CHAR: The data is a Binary data.&lt;br /&gt;    '         TYPE_WIDECHAR: The data is a Unicode string.&lt;br /&gt;    'lpData:Long pointer to data.&lt;br /&gt;    'cbData:Specifies the size, in bytes, of the data pointed to by the lpData member.&lt;br /&gt;    Public Structure COPYDATASTRUCT&lt;br /&gt;        Public dwData As Integer&lt;br /&gt;        Public cbData As Integer&lt;br /&gt;        Public lpData As IntPtr&lt;br /&gt;    End Structure&lt;br /&gt;    Public Sub New(ByVal aGetMsgString As OnGetMsgStringHandler)&lt;br /&gt;        Me.aGetMsgString = aGetMsgString&lt;br /&gt;        'Register a window message for inter-application communication&lt;br /&gt;        Me.RM_QuickMarkMessengerAPIApp = RegisterWindowMessage(&amp;quot;QuickMarkMessengerAPIApplication&amp;quot;)&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Protected Overloads Overrides Sub WndProc(ByRef msg As Message)&lt;br /&gt;        Select Case msg.Msg&lt;br /&gt;            Case WM_COPYDATA&lt;br /&gt;                Dim str As String = GetMsgString(msg.LParam)&lt;br /&gt;                'TODO:Add your code here to process with the str.&lt;br /&gt;                'MessageBox.Show(str, &amp;quot;Result&amp;quot;)&lt;br /&gt;                If aGetMsgString IsNot Nothing Then&lt;br /&gt;                    aGetMsgString.Invoke(str)&lt;br /&gt;                End If&lt;br /&gt;                RaiseEvent OnGetMsgString(str)&lt;br /&gt;                Exit Select&lt;br /&gt;        End Select&lt;br /&gt;        MyBase.WndProc(msg)&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Public Shared Function GetMsgString(ByVal lParam As IntPtr) As String&lt;br /&gt;        If lParam &amp;lt;&amp;gt; IntPtr.Zero Then&lt;br /&gt;            Dim st As COPYDATASTRUCT = DirectCast(Marshal.PtrToStructure(lParam, GetType(COPYDATASTRUCT)), COPYDATASTRUCT)&lt;br /&gt;            Dim str As String = Marshal.PtrToStringUni(st.lpData)&lt;br /&gt;            Return str&lt;br /&gt;        Else&lt;br /&gt;            Return Nothing&lt;br /&gt;        End If&lt;br /&gt;    End Function&lt;br /&gt;End Class&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4300967513113257645-3468373887599791790?l=dotnet-share.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dotnet-share.blogspot.com/feeds/3468373887599791790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dotnet-share.blogspot.com/2009/09/application-for-enabling-your-wm-camera.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4300967513113257645/posts/default/3468373887599791790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4300967513113257645/posts/default/3468373887599791790'/><link rel='alternate' type='text/html' href='http://dotnet-share.blogspot.com/2009/09/application-for-enabling-your-wm-camera.html' title='Application for Enabling Your WM Camera into a Barcode Scanner'/><author><name>Alex Wijoyo</name><uri>http://www.blogger.com/profile/02815034937798828381</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4300967513113257645.post-1483821712798968235</id><published>2009-08-31T10:04:00.017+07:00</published><updated>2009-09-03T12:24:19.402+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='windows mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='sqlceresultset'/><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='compact framework'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='sqlce'/><title type='text'>A SearchDialog Using SqlCeResultSet</title><content type='html'>On my previous post &lt;a href="http://dotnet-share.blogspot.com/2009/08/my-first-journey-to-mobile-database.html"&gt;My First Journey to Mobile Database Application&lt;/a&gt;, I have mentioned using SearchDialog instead of ComboBox. On this post I will explain about the SearchDialog used by us. OK enough talking, let's get to the code.&lt;br /&gt;I have documented almost every lines of our codes (FrmSearchDialog.vb).&lt;span class="fullpost"&gt; I hope you get the idea behind the SearchDialog. Here they are:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 95%; height: 300px;"&gt;&lt;code&gt;Imports System.Data&lt;br /&gt;Imports System.Data.SqlServerCe&lt;br /&gt;Public Class FrmSearchDialog&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' Connection with connection string retrieved from a dictionary. &lt;br /&gt;    ''' The dictionary values are loaded from a RSA encrypted text file.&lt;br /&gt;    ''' The connection must be always opened for browsing SqlCeResultSet.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    Protected CconCurrent As New SqlCeConnection(DictConnectionString(&amp;quot;SqlCeConnectionString&amp;quot;))&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' Command used for searching.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    Protected CcmdSearch As New SqlCeCommand(&amp;quot;&amp;quot;, CconCurrent)&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' ResultSet for storing select result.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    Protected CrsSearch As SqlCeResultSet&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' HashTable for storing search result row.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    Protected HashSearchResult As Hashtable = Nothing&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' String for storing SQL passed by user.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    Protected strSelect As String = &amp;quot;&amp;quot;&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' String for storing Criteria passed by user.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    Protected strCriteria As String = &amp;quot;&amp;quot;&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' String for storing strSelect + strCriteriaClause + strCritera + incremental search criteria.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    Protected strSearchSelect As String = &amp;quot;&amp;quot;&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' List for storing ColumnTypes string for each column.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    Protected strColumnTypes As New List(Of String)&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' Boolean for storing whether strSelect is a group by SQL.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    Protected boolGroupBy As Boolean = False&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' String for storing criteria clause where for ordinary select and having for boolGroupBy=true.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    Protected strCriteriaClause As String = &amp;quot; where &amp;quot;&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' A static function for opening a SearchDialog for the select statement and Criteria passed by user.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;SelectSql&amp;quot;&amp;gt;The select statement.&amp;lt;/param&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;HashSearchResult&amp;quot;&amp;gt;The search result row HashTable reference.&amp;lt;/param&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;DefaultCriteria&amp;quot;&amp;gt;The select criteria.&amp;lt;/param&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;AllowSearch&amp;quot;&amp;gt;if set to &amp;lt;c&amp;gt;true&amp;lt;/c&amp;gt; allow search.&amp;lt;/param&amp;gt;&lt;br /&gt;    ''' &amp;lt;returns&amp;gt;True if OK is clicked and false if Cancel is clicked.&amp;lt;/returns&amp;gt;&lt;br /&gt;    Shared Function Execute(ByVal SelectSql As String, ByRef HashSearchResult As Hashtable, Optional ByVal DefaultCriteria As String = &amp;quot;&amp;quot;, Optional ByVal AllowSearch As Boolean = True) As Boolean&lt;br /&gt;        'Create an instance of FrmSearchDialog because this function is static.&lt;br /&gt;        Dim aFrmSearchDialog As New FrmSearchDialog&lt;br /&gt;        With aFrmSearchDialog&lt;br /&gt;            'Initializing allow search state&lt;br /&gt;            .MnuSearch.Enabled = AllowSearch&lt;br /&gt;            .CmbColumn.Enabled = AllowSearch&lt;br /&gt;            .TxtSearch.Enabled = AllowSearch&lt;br /&gt;            'Initializing the select statement and populating the grid.&lt;br /&gt;            .Initialize(SelectSql, DefaultCriteria)&lt;br /&gt;            'Showing modally and checking for dialog result&lt;br /&gt;            If .ShowDialog() = Windows.Forms.DialogResult.OK Then&lt;br /&gt;                HashSearchResult = .HashSearchResult&lt;br /&gt;                Return True&lt;br /&gt;            Else&lt;br /&gt;                Return False&lt;br /&gt;            End If&lt;br /&gt;        End With&lt;br /&gt;    End Function&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' A procedure for initializing the specified select statement and default criteria.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;SelectSql&amp;quot;&amp;gt;The select statement.&amp;lt;/param&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;DefaultCriteria&amp;quot;&amp;gt;The default criteria.&amp;lt;/param&amp;gt;&lt;br /&gt;    Public Sub Initialize(ByVal SelectSql As String, Optional ByVal DefaultCriteria As String = &amp;quot;&amp;quot;)&lt;br /&gt;        'Initializing the DialogResult to None&lt;br /&gt;        DialogResult = Windows.Forms.DialogResult.None&lt;br /&gt;        'Storing the SelectSql and DefaultCriteria to data members&lt;br /&gt;        Me.strSelect = SelectSql&lt;br /&gt;        Me.strCriteria = DefaultCriteria&lt;br /&gt;        'Open the Connection if it is not opened yet.&lt;br /&gt;        If CconCurrent.State &amp;lt;&amp;gt; ConnectionState.Open Then&lt;br /&gt;            CconCurrent.Open()&lt;br /&gt;        End If&lt;br /&gt;        'There are DefaultCriteria.&lt;br /&gt;        If strCriteria.Length &amp;gt; 0 Then&lt;br /&gt;            'Initializing the boolGroupBy by searching for group by clause&lt;br /&gt;            boolGroupBy = strSelect.ToLower.IndexOf(&amp;quot;group by&amp;quot;) &amp;gt;= 0&lt;br /&gt;            'It is a group by SelectSql&lt;br /&gt;            If boolGroupBy Then&lt;br /&gt;                'Initializing the strCriteriaClause as having for group by select.&lt;br /&gt;                strCriteriaClause = &amp;quot; having &amp;quot;&lt;br /&gt;            End If&lt;br /&gt;            'Constructing the initial Search SQL.&lt;br /&gt;            strSearchSelect = strSelect + strCriteriaClause + strCriteria&lt;br /&gt;        Else 'There are no DefaultCriteria&lt;br /&gt;            'Constructing the initial Search SQL without critera.&lt;br /&gt;            strSearchSelect = strSelect&lt;br /&gt;        End If&lt;br /&gt;        'Setting the CommandText to strSearchSelect and execute a scrollable ResultSet from it.&lt;br /&gt;        CcmdSearch.CommandText = strSearchSelect&lt;br /&gt;        CrsSearch = CcmdSearch.ExecuteResultSet(ResultSetOptions.Scrollable)&lt;br /&gt;        'There are row(s) returned.&lt;br /&gt;        If CrsSearch.Read Then&lt;br /&gt;            'Iterating all columns of ResultSet.&lt;br /&gt;            For i As Integer = 0 To CrsSearch.FieldCount - 1&lt;br /&gt;                'Storing columns names to combo box items.&lt;br /&gt;                CmbColumn.Items.Add(CrsSearch.GetName(i))&lt;br /&gt;                'Storing columns type names&lt;br /&gt;                strColumnTypes.Add(CrsSearch.GetProviderSpecificFieldType(i).ToString)&lt;br /&gt;            Next&lt;br /&gt;        End If&lt;br /&gt;        'Setting the combo box search column to first column.&lt;br /&gt;        If CmbColumn.Items.Count &amp;gt; 0 Then&lt;br /&gt;            CmbColumn.SelectedIndex = 0&lt;br /&gt;        End If&lt;br /&gt;        'Binding DataGrid to ResultSet returned.&lt;br /&gt;        DgSearch.DataSource = CrsSearch&lt;br /&gt;        'There are no incremental search yet, so empty the strSearchSelect.&lt;br /&gt;        strSearchSelect = &amp;quot;&amp;quot;&lt;br /&gt;    End Sub&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' A procedure for storing a row of ResultSet in certain position to the HashTable.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;RowPosition&amp;quot;&amp;gt;The row position.&amp;lt;/param&amp;gt;&lt;br /&gt;    Sub ResultSetRowToHashTable(ByVal RowPosition As Integer)&lt;br /&gt;        'The row position is valid.&lt;br /&gt;        If RowPosition &amp;gt;= 0 Then&lt;br /&gt;            'Scrolling the ResultSet to RowPosition.&lt;br /&gt;            CrsSearch.ReadAbsolute(RowPosition)&lt;br /&gt;            'Creating an instance of HashTable.&lt;br /&gt;            HashSearchResult = New Hashtable&lt;br /&gt;            'Iterating all columns of ResultSet and storing their values to HashTable.&lt;br /&gt;            For i As Integer = 0 To CrsSearch.FieldCount - 1&lt;br /&gt;                HashSearchResult.Add(CrsSearch.GetName(i), CrsSearch.GetValue(i))&lt;br /&gt;            Next&lt;br /&gt;            'Closing and disposing the ResultSet.&lt;br /&gt;            If Not CrsSearch.IsClosed Then&lt;br /&gt;                CrsSearch.Close()&lt;br /&gt;            End If&lt;br /&gt;            CrsSearch.Dispose()&lt;br /&gt;        Else 'The row position is invalid.&lt;br /&gt;            'Triggering a cancel click.&lt;br /&gt;            MnuCancel_Click(Nothing, Nothing)&lt;br /&gt;        End If&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' Handles the DoubleClick event of the GridSearch control for storing current row to HashTable and closing the dialog.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;sender&amp;quot;&amp;gt;The source of the event.&amp;lt;/param&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;e&amp;quot;&amp;gt;The &amp;lt;see cref=&amp;quot;System.EventArgs&amp;quot; /&amp;gt; instance containing the event data.&amp;lt;/param&amp;gt;&lt;br /&gt;    Private Sub GridSearch_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles DgSearch.DoubleClick&lt;br /&gt;        'The grid current row index is valid.&lt;br /&gt;        If DgSearch.CurrentRowIndex &amp;gt;= 0 Then&lt;br /&gt;            Try&lt;br /&gt;                'Storing ResultSet on current row index to HashTable&lt;br /&gt;                ResultSetRowToHashTable(DgSearch.CurrentRowIndex)&lt;br /&gt;                'Returning OK dialog result and close the form.&lt;br /&gt;                DialogResult = Windows.Forms.DialogResult.OK&lt;br /&gt;                Close()&lt;br /&gt;            Catch ex As Exception&lt;br /&gt;                MsgBox(ex.Message)&lt;br /&gt;            End Try&lt;br /&gt;        End If&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' Handles the Click event of the MnuOK control for storing current row to HashTable and closing the dialog.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;sender&amp;quot;&amp;gt;The source of the event.&amp;lt;/param&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;e&amp;quot;&amp;gt;The &amp;lt;see cref=&amp;quot;System.EventArgs&amp;quot; /&amp;gt; instance containing the event data.&amp;lt;/param&amp;gt;&lt;br /&gt;    Private Sub MnuOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MnuOK.Click&lt;br /&gt;        'The grid current row index is valid.&lt;br /&gt;        If DgSearch.CurrentRowIndex &amp;gt;= 0 Then&lt;br /&gt;            DialogResult = Windows.Forms.DialogResult.OK&lt;br /&gt;            Try&lt;br /&gt;                'Storing ResultSet on current row index to HashTable&lt;br /&gt;                ResultSetRowToHashTable(DgSearch.CurrentRowIndex)&lt;br /&gt;                'Returning OK dialog result and close the form.&lt;br /&gt;                DialogResult = Windows.Forms.DialogResult.OK&lt;br /&gt;                Close()&lt;br /&gt;            Catch ex As Exception&lt;br /&gt;                MsgBox(ex.Message)&lt;br /&gt;            End Try&lt;br /&gt;        End If&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' Handles the Click event of the MnuCancel control for closing the dialog.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;sender&amp;quot;&amp;gt;The source of the event.&amp;lt;/param&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;e&amp;quot;&amp;gt;The &amp;lt;see cref=&amp;quot;System.EventArgs&amp;quot; /&amp;gt; instance containing the event data.&amp;lt;/param&amp;gt;&lt;br /&gt;    Private Sub MnuCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MnuCancel.Click&lt;br /&gt;        'Closing and disposing the ResultSet.&lt;br /&gt;        If Not CrsSearch.IsClosed Then&lt;br /&gt;            CrsSearch.Close()&lt;br /&gt;        End If&lt;br /&gt;        CrsSearch.Dispose()&lt;br /&gt;        'Returning Cancel dialog result and close the form.&lt;br /&gt;        DialogResult = Windows.Forms.DialogResult.Cancel&lt;br /&gt;        Close()&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' Handles the Click event of the MnuSearch control for constructing incremental search.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;sender&amp;quot;&amp;gt;The source of the event.&amp;lt;/param&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;e&amp;quot;&amp;gt;The &amp;lt;see cref=&amp;quot;System.EventArgs&amp;quot; /&amp;gt; instance containing the event data.&amp;lt;/param&amp;gt;&lt;br /&gt;    Private Sub MnuSearch_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MnuSearch.Click&lt;br /&gt;        'There is no incremental search on going.&lt;br /&gt;        If strSearchSelect.Length = 0 Then&lt;br /&gt;            'Constructing initial select and concating with and for adding a new search criterion.&lt;br /&gt;            strSearchSelect = strSelect&lt;br /&gt;            If strCriteria.Length &amp;gt; 0 Then&lt;br /&gt;                strSearchSelect += strCriteriaClause + strCriteria + &amp;quot; and &amp;quot;&lt;br /&gt;            Else&lt;br /&gt;                strSearchSelect += strCriteriaClause&lt;br /&gt;            End If&lt;br /&gt;        Else 'There is incremental search on going.&lt;br /&gt;            'Concating with and for adding a new search criterion.&lt;br /&gt;            strSearchSelect += &amp;quot; and &amp;quot;&lt;br /&gt;        End If&lt;br /&gt;        'Current search column in combo box's type is a string.&lt;br /&gt;        If strColumnTypes(CmbColumn.SelectedIndex).EndsWith(&amp;quot;String&amp;quot;) Then&lt;br /&gt;            strSearchSelect += String.Format(&amp;quot;{0} like '%{1}%'&amp;quot;, CmbColumn.Text, TxtSearch.Text)&lt;br /&gt;            'Current search column in combo box's type is a DateTime.&lt;br /&gt;        ElseIf strColumnTypes(CmbColumn.SelectedIndex).EndsWith(&amp;quot;DateTime&amp;quot;) Then&lt;br /&gt;            strSearchSelect += String.Format(&amp;quot;convert(nvarchar,{0},3) like '%{1}%'&amp;quot;, CmbColumn.Text, TxtSearch.Text)&lt;br /&gt;        Else 'The other type of current search column in combo box.&lt;br /&gt;            strSearchSelect += String.Format(&amp;quot;convert(nvarchar,{0}) like '%{1}%'&amp;quot;, CmbColumn.Text, TxtSearch.Text)&lt;br /&gt;        End If&lt;br /&gt;        'Closing and disposing the ResultSet before retrieving new ResultSet.&lt;br /&gt;        If Not CrsSearch.IsClosed Then&lt;br /&gt;            CrsSearch.Close()&lt;br /&gt;        End If&lt;br /&gt;        CrsSearch.Dispose()&lt;br /&gt;        'Setting the CommandText to strSearchSelect and execute a scrollable ResultSet from it.&lt;br /&gt;        CcmdSearch.CommandText = strSearchSelect&lt;br /&gt;        CrsSearch = CcmdSearch.ExecuteResultSet(ResultSetOptions.Scrollable)&lt;br /&gt;        'Binding DataGrid to ResultSet returned.&lt;br /&gt;        DgSearch.DataSource = CrsSearch&lt;br /&gt;        'Focusing to search column combo box for next search.&lt;br /&gt;        CmbColumn.Focus()&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' Handles the Click event of the MnuReset control for resetting incremental search to initial state.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;sender&amp;quot;&amp;gt;The source of the event.&amp;lt;/param&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;e&amp;quot;&amp;gt;The &amp;lt;see cref=&amp;quot;System.EventArgs&amp;quot; /&amp;gt; instance containing the event data.&amp;lt;/param&amp;gt;&lt;br /&gt;    Private Sub MnuReset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MnuReset.Click&lt;br /&gt;        'Closing and disposing the ResultSet before retrieving new ResultSet.&lt;br /&gt;        If Not CrsSearch.IsClosed Then&lt;br /&gt;            CrsSearch.Close()&lt;br /&gt;        End If&lt;br /&gt;        CrsSearch.Dispose()&lt;br /&gt;        'There are DefaultCriteria.&lt;br /&gt;        If strCriteria.Length &amp;gt; 0 Then&lt;br /&gt;            'Constructing the initial Search SQL.&lt;br /&gt;            strSearchSelect = strSelect + strCriteriaClause + strCriteria&lt;br /&gt;        Else&lt;br /&gt;            'Constructing the initial Search SQL.&lt;br /&gt;            strSearchSelect = strSelect&lt;br /&gt;        End If&lt;br /&gt;        'Setting the CommandText to strSearchSelect and execute a scrollable ResultSet from it.&lt;br /&gt;        CcmdSearch.CommandText = strSearchSelect&lt;br /&gt;        CrsSearch = CcmdSearch.ExecuteResultSet(ResultSetOptions.Scrollable)&lt;br /&gt;        'Binding DataGrid to ResultSet returned.&lt;br /&gt;        DgSearch.DataSource = CrsSearch&lt;br /&gt;        'There are no incremental search yet, so empty the strSearchSelect.&lt;br /&gt;        strSearchSelect = &amp;quot;&amp;quot;&lt;br /&gt;        'Empty the search text.&lt;br /&gt;        TxtSearch.Text = &amp;quot;&amp;quot;&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' Handles the LostFocus event of the TxtSearch control for triggering Search click.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;sender&amp;quot;&amp;gt;The source of the event.&amp;lt;/param&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;e&amp;quot;&amp;gt;The &amp;lt;see cref=&amp;quot;System.EventArgs&amp;quot; /&amp;gt; instance containing the event data.&amp;lt;/param&amp;gt;&lt;br /&gt;    Private Sub TxtSearch_LostFocus(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TxtSearch.LostFocus&lt;br /&gt;        'Triggering search click.&lt;br /&gt;        MnuSearch_Click(Nothing, Nothing)&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    ''' &amp;lt;summary&amp;gt;&lt;br /&gt;    ''' Handles the Deactivate event of the FrmSearchDialog control for triggering dialog close.&lt;br /&gt;    ''' &amp;lt;/summary&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;sender&amp;quot;&amp;gt;The source of the event.&amp;lt;/param&amp;gt;&lt;br /&gt;    ''' &amp;lt;param name=&amp;quot;e&amp;quot;&amp;gt;The &amp;lt;see cref=&amp;quot;System.EventArgs&amp;quot; /&amp;gt; instance containing the event data.&amp;lt;/param&amp;gt;&lt;br /&gt;    Private Sub FrmSearchDialog_Deactivate(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Deactivate&lt;br /&gt;        'The DialogResult is OK and no search result HashTable&lt;br /&gt;        If DialogResult = Windows.Forms.DialogResult.OK AndAlso HashSearchResult Is Nothing Then&lt;br /&gt;            'The grid current row index is valid.&lt;br /&gt;            If DgSearch.CurrentRowIndex &amp;gt;= 0 Then&lt;br /&gt;                'Triggering OK click.&lt;br /&gt;                MnuOK_Click(Nothing, Nothing)&lt;br /&gt;            Else 'Setting Cancel DialogResult.&lt;br /&gt;                DialogResult = Windows.Forms.DialogResult.Cancel&lt;br /&gt;            End If&lt;br /&gt;        End If&lt;br /&gt;    End Sub&lt;br /&gt;End Class&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4300967513113257645-1483821712798968235?l=dotnet-share.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dotnet-share.blogspot.com/feeds/1483821712798968235/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dotnet-share.blogspot.com/2009/08/searchdialog-using-sqlceresultset.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4300967513113257645/posts/default/1483821712798968235'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4300967513113257645/posts/default/1483821712798968235'/><link rel='alternate' type='text/html' href='http://dotnet-share.blogspot.com/2009/08/searchdialog-using-sqlceresultset.html' title='A SearchDialog Using SqlCeResultSet'/><author><name>Alex Wijoyo</name><uri>http://www.blogger.com/profile/02815034937798828381</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4300967513113257645.post-3008588640245056304</id><published>2009-08-25T12:36:00.005+07:00</published><updated>2009-09-02T15:01:22.145+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='windows mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='compact framework'/><category scheme='http://www.blogger.com/atom/ns#' term='activesync'/><category scheme='http://www.blogger.com/atom/ns#' term='barcode'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='sqlce'/><category scheme='http://www.blogger.com/atom/ns#' term='pda'/><title type='text'>My First Journey to Mobile Database Application</title><content type='html'>My company assigns me and my team mates to automate our warehouses processes. We need to automate the manual recording of inventory flows processes.&lt;br /&gt;Our first development approach is ASP.NET 2.0.&lt;span class="fullpost"&gt; This approach is the fastest to get done because we don't need to think about mobile database and server database. As long as there is wireless connection and a capable browser, the mobile devices can be used for data input. Our intended device is Motorola Symbol MC5590 with QWERTY keypad and built in barcode scanner. For development purpose we use HTC TyTN II with Windows Mobile 6.1 Pro OS. We found a China company specializing in barcodes, Sumlung and tried their software for reading a barcode through PDA camera. But their software is incompatible with our mobile device. When budget is your main consideration in developing a barcode application, you can review their solutions.  We use AJAX control toolkit for automating our web pages. But to be our disappointment this ASP.NET 2.0 is failed. Our ASP.NET 2.0 website was running fine on &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=1A7A6B52-F89E-4354-84CE-5D19C204498A&amp;amp;displaylang=en"&gt;Windows Mobile 6 Pro Emulator Images&lt;/a&gt; IE Mobile. But  some features failed on our HTC TyTN II IE Mobile. The most obvious one is asp:Login control, the button doesn't react to a mouse click. So we find alternative browsers for our WM6. Opera Mobile can be used for running our web pages but the layout size doesn't seem right to us. Luckily we found the Iris browser. The browser can run our web pages just like we want them. Since there are no problems with software development, the project rolls onto hardware infrastructures planning. We are pretty surprised with the wireless infrastructure site survey result. Our company needs to spend a large amount of money for a full wireless LAN coverages on its warehouses. We thought an alternative from in house wireless infrastructures to unlimited HSDPA data plan from mobile telco operators. We use Hamachi Mobile from LogMeIn for connecting our mobile devices to our server. Everything seems right unless one crucial problem, Indonesia mobile telco operators are unreliable. At least there is 1 day in a week we can not connect from our mobile telco operator. We came back to wireless LAN infrastructures with reduced coverages. It seems there is no easy path on Mobile Database Application, we have to develop a .NET CF 2.0 database application.&lt;br /&gt;&lt;br /&gt;Second approach, .NET CF 2.0 database application.  First things we do are download all necessary software:&lt;br /&gt;&lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=1A7A6B52-F89E-4354-84CE-5D19C204498A&amp;amp;displaylang=en"&gt;Windows Mobile 6 Pro Emulator Images&lt;/a&gt; (downloaded previously)&lt;br /&gt;&lt;a href="http://www.microsoft.com/downloads/info.aspx?na=22&amp;amp;p=4&amp;amp;SrcDisplayLang=en&amp;amp;SrcCategoryId=&amp;amp;SrcFamilyId=&amp;amp;u=/downloads/details.aspx?FamilyID=06111a3a-a651-4745-88ef-3d48091a390b&amp;amp;DisplayLang=en"&gt;Windows Mobile 6 SDK&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.microsoft.com/windowsmobile/activesync/activesync45.mspx"&gt;ActiveSync 4.5 for Windows XP&lt;/a&gt; (allow DMA connection for docking emulator)&lt;br /&gt;&lt;a href="http://www.microsoft.com/downloads/info.aspx?na=22&amp;amp;p=1&amp;amp;SrcDisplayLang=en&amp;amp;SrcCategoryId=&amp;amp;SrcFamilyId=&amp;amp;u=/downloads/details.aspx?FamilyID=83d513ea-9df9-4920-af33-3a0e2e4e7beb&amp;amp;DisplayLang=en"&gt;Windows Mobile Device Center for Windows Vista&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.microsoft.com/downloads/info.aspx?na=22&amp;amp;p=1&amp;amp;SrcDisplayLang=en&amp;amp;SrcCategoryId=&amp;amp;SrcFamilyId=&amp;amp;u=/downloads/details.aspx?FamilyID=aea55f2f-07b5-4a8c-8a44-b4e1b196d5c0&amp;amp;DisplayLang=en"&gt;.NET CF 2.0 SP 2 for our Visual Studio 2005&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.microsoft.com/Sqlserver/2005/en/us/compact-downloads.aspx"&gt;SQL Server CE 3.1&lt;/a&gt; (we download the Developer SDK and Tools for Visual Studio SP 1)&lt;br /&gt;We are pretty surprised there are no significant differences between developing a desktop ADO.NET 2.0 application and a mobile ADO.NET 2.0 application. To cut the learning curve, we do not use Microsoft Synchronization Services, we use compact framework SqlClient directly to upload to and download from SQL Server 2005. There are several things about SqlCE we bumped into:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Do not use ComboBox for looking up a table with a lot of records. We create a SearchDialog that utilizes a scrollable SqlCeResultSet bound to a DataGrid instead of ComboBox.  The SqlCeConnection must be always connected for using SqlCeResultSet. SqlCeResultSet will only fetch shown records on DataGrid so you don't need to wait the whole records to be loaded. Our SearchDialog construct incremental search select SQL and return a HashTable of a row selected.&lt;/li&gt;&lt;li&gt;SqlCE doesn't support update and delete using join, sub query and batch command. So we must keep this in our minds.&lt;/li&gt;&lt;li&gt;Eliminate unneeded columns on SqlCE.&lt;/li&gt;&lt;li&gt;Denormalize wisely for gaining performance.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;We use untyped dataset and it took time to load it so we serialize the dataset schema to database and deserialize it on form load. PS: We hate typed dataset :-).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Our select, insert, update and delete command constructed on the fly based on table schema and it took time to load also. So we serialize and deserialize them using XML into database also.&lt;/li&gt;&lt;/ol&gt;The problem about performance and incompatible SQL make us considering several alternatives such as: SQLite, VistaDB and SSCE Direct. But time constraint makes us stay with SqlCE. For SQL Server 2005 side, all tables to be synchronized to SqlCE equipped with timestamp columns CreateTime and UpdateTime and Deleted tables for storing the deleted primary keys and their DeleteTime for each tables. We also applied these for all SqlCE tables to be uploaded to SQL Server. Records inserted in SqlCE use negative integer primary key, they will have positive integer primary key after synchronize with server.&lt;br /&gt;&lt;br /&gt;I hope my share about our experience in developing mobile database application can be useful for all of you. Thank you.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4300967513113257645-3008588640245056304?l=dotnet-share.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dotnet-share.blogspot.com/feeds/3008588640245056304/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://dotnet-share.blogspot.com/2009/08/my-first-journey-to-mobile-database.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4300967513113257645/posts/default/3008588640245056304'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4300967513113257645/posts/default/3008588640245056304'/><link rel='alternate' type='text/html' href='http://dotnet-share.blogspot.com/2009/08/my-first-journey-to-mobile-database.html' title='My First Journey to Mobile Database Application'/><author><name>Alex Wijoyo</name><uri>http://www.blogger.com/profile/02815034937798828381</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
