Andri Yadi

Butterfly Effect
See also: Other Geeks@INDC
DyBand

Selama 5 tahun terakhir ini, nggak seharipun dilewatkan tanpa memikirkan pekerjaan. Karena kebanyakan pekerjaan IT butuh logika dan analisis, otak kiri dipakai secara intensive. Otak kanan hanya sesekali dipakai, misalnya pas lagi benerin CSS styles.

Karena tingkat stress yang sudah cukup tinggi dan demi "memuaskan" otak kanan, saatnya melirik hobby yang memerlukan kemampuan seni. Pernah mencoba photography, tapi malas keluar rumah buat cari objek-objek yang bisa difoto. Di kantor nggak ada yang bisa difoto karena nggak ada yang photogenic :P

Ada sedikit keahlian seni yang diperoleh saat SMA. Karena beberapa kali guru nggak masuk kelas, jam pelajarannya dilewatkan dengan main gitar dan nyanyi bareng sekelas. Ternyata gitar aja nggak cukup, perlu instrumen lain. Kebetulan di ruang guru ada keyboard yang jarang dipakai. Mulailah ngoprek keyboard, nggak ada yang ngajarin, cuman belajar chord dari majalah (lupa majalah apa ya). Setelah lulus SMA, nggak pernah lagi menyentuh keyboard (kecuali keyboard komputer yang disentuh rata2 10 jam setiap hari :P)

Setelah ngobrol-ngobrol dengan teman-teman DyCoders, ternyata banyak juga yang bisa main alat musik. Dan kebetulan, seleranya sama, gak jauh-jauh dari rock, terutama classic rock. Setelah ngobrol-ngobrol, ternyata bisa lengkap komposisinya buat nge-jam bareng.

  • Iman pada lead guitar,
  • Arie pada bass,
  • Yanwar pada guitar,
  • Rebby pada drum,
  • Gue coba pegang keyboard dan masih belajar. Mudah2an bisa jadi lead vocal juga :)

DyBand? Itukah nama band kami? Nggak juga, itu cuman nama yang sempat terlontarkan. We're not trying to be a professional band. Rasanya belum kepikiran ganti profesi jadi celebritis, masih betah ngoding.

Untuk sementara, Iman dan gue coba nge-jam bareng. Masih kacau abis, but we try. Here are the photos.

Pose

Iman - Yamaha guitar. Andri - Medeli keyboard. Mau beli keyboard Yamaha, mahal euy.

Jam

Lagi ngulik White Lion - Till Death Do Us Apart

 

Itulah sekelumit kegiatan lain selain melototin layar komputer tiap hari.

Kayaknya INDC perlu ngadain jam session nih, selain technical gathering :) Gue yakin banyak geeks INDC yang bisa main musik. Sebagai contoh, om Rully yang udah megang Ibanez (btw, beli dimana Rul? kayaknya mahal ya ;) ). Siapa tahu bisa lahir musisi berkualitas.

That's it. Keep rock and roll.

Share this post: | | | |
Finally It Comes...

This the first posting since I've been "awarded" as Microsoft Most Valuable Professional (MVP) of Visual Studio Tools for Office (VSTO). As announced by Naren at his blog, there are four new MVPs and three renewal MVPs for this July cycle. The formal announcement is sent via email by Lilian Quek, MVP Lead, Southeast Asia on July 1, 2008 10:13 PM. What a historic day for me :)

On July 2008 MVP Award cycle, there are 26 renewal MVPs and 12 newbies. As of July 2008, there are 126 active MVPs in Southeast Asia (SEA). As my self, I am an MVP of VSTO, one of eleven (11) MVPs of VSTO available all over the world (at least, that is the list show me. I have the list but I can't publish it here). My MVP profile can be viewed here.

As declared in the notification email that Microsoft will soon send MVP Award gift package..Hmmm..sound interesting. As a new MVP, off course that makes me curious about what is in the package. After waiting for 7 days, the package finally arrives to my office today morning. Curious what's in the box? Here they are.

Package and Box

Package and gift box

What's in the box

In the box

The gadgets

The gadgets (left to right: bluetooth dongle, bluetooth mouse, bluetooth earphone, audio adapter). All have MVP logo, I'm sure none of these are sold in the market :)

scanned_MVP_cert-r

Certificate

 

Wow...cool package. Thanks.

From July 1st, 2008 until today, almost everyday I received emails from our MVP Lead that notified about benefits of become MVP. Start from MVP jacket, discount at TechEd, access to company store and electronic book shelf, etc, until the cool package I've received today. Hmmm...it's nice being MVP. All hard (and sometime free of charge :P) works and long journey are finally rewarded.

Finally, thanks to everyone that helps to make this award happens to me. This award is an honor and also a responsibility. I will do my best to be more and more involved in community by any means. Special thanks to Naren for recommending me.

For who are not yet MVP, keep my opinion in mind:  MVP is a consequence, not an objective :)

Now, I can proudly use this prestigious logo

MVP_Horizontal_Logo

That's it. Keep good works.

Share this post: | | | |
Posted: Jul 08 2008, 02:38 AM by andriyadi | with 11 comment(s) |
Filed under: , ,
GCOE - Web Site Publik Berorientasi Kependudukan

Pada hari kamis, 26 Juni 2008 lalu, saya berkesempatan mengisi batch terakhir dari serangkaian kegiatan Government Center of Excellence (GCOE) training di Microsoft Innovation Center (MIC) ITB. Training terakhir ini bertajuk "Web Site Publik Berorientasi Kependudukan". Lebih jauh ttg GCOE training bisa dilihat di sini dan di sini.

Pada hari yang sama, di MIC UI juga berlangsung training GCOE dengan judul yang sama, yang di-deliver oleh Agung Riyadi. Jika Agung membahas Windows Live dan DotNetNuke-nya, seperti yang ia tulis di sini, saya membahas ASP.NET Dynamic Data sebagai teknologi utama untuk membangun web site berorientasi kependudukan. Honestly, tidak terlalu jelas juga apa yang dimaksud "Website Publik Berorientasi Kependudukan". Apakah yang dimaksud itu adalah aplikasi untuk mengelola data penduduk, atau website yang menawarkan services (apapun) bagi citizen, atau yang lainnya. Tapi satu yang pasti, saya berhasil memukau peserta - yang semuanya berasal dari Pemerintah Daerah - dengan membuat aplikasi manajemen data penduduk dalam waktu kurang dari 2 menit. Saya bahkan meminta salah satu peserta untuk mengaktifkan timer dan mulai menghitung bertepatan dengan saya membuka Visual Studio. Hasilnya, kurang dari 2 menit, sebuah aplikasi yang fully functional-pun selesai. That's the magic that ASP.NET Dynamic Data can bring :)

Mungkin ada yang berpendapat, kok tega2nya saya "membodohi" peserta dengan teknologi "fast food". Well, ada beberapa alasan:

  1. Walaupun punya background programming, tidak satupun dari peserta yang hadir pernah menyentuh .NET, ASP.NET, or any .NET technologies. So, daripada membuat mereka pusing dengan mengajarkan .NET, ASP.NET, AJAX, LINQ, LINQ to SQL dari awal, why don't start with something easy and encourage them to explore more about .NET.
  2. Dynamic Data is not really "fast food". Jika tahu cara "memakan"nya, kita bisa "sehat" dengan Dynamic Data :) Dynamic Data is actually a framework di atas ASP.NET, yang bisa fully customized melebihi yang kita bayangkan. Dynamic Data juga bisa diintegrasikan dengan existing web site.

Beberapa contoh ASP.NET Dynamic Data customization, sekaligus capture dari sample yang saya deliver.

Default:

image

Customized:

image

Default:

image

Customized:

image

 

Berikut foto-foto selama training. (Sorry atas kesalahan tanggal pada foto, seharusnya 06/26/2008)

100_1775

100_1776

This is me...with moustache now. Ceritanya mau numbuhin beard biar bisa kayak Tony Stark :P

100_1777

Peserta training
(left to right: Risgiyanto dari BAPESITELDA Jabar, Bela Negara dari Dinas Peternakan Jabar, Susan dan {waduh...punten kang, abdi hilap namina} dari BAPEDA Tasikmalaya)

 

Slide dan sample code training ini dapat di-download di sini.

Mudah2an training ini bisa men-trigger teman-teman kita di government untuk meng-explore lebih jauh dan mengimplementasikan .NET and related technologies sebagai pilihan platform development mereka. Berdasarkan pengalaman saya yang pernah exist di Pemda selama kurang lebih 3 tahun (2004 - 2007), banyak Satuan Kerja Pemerintah Daerah (SKPD), terutama Jawa Barat, lebih memilih open source technologies (PHP, Java) sebagai platform development. Melalui event training GCOE ini, let re-introduce them with the power that .NET can bring to life :)

Share this post: | | | |
SQL Server-based SiteMap Provider

Menanggapi pertanyaan di Milist dotnet (http://dotnet.netindonesia.net/?0::3593) tentang bagaimana membuat menu yang dynamically generated from database, rasanya gw perlu menulis posting ini. Mungkin ini topik lama, tapi demi mendokumentasikan knowledge, nggak ada salahnya gw posting di sini, ya gak?

Jika yang dimaksud pertanyaan pada milist tsb adalah pemenuan untuk ASP.NET, bisa bertumpu pada control <asp:Menu> dengan attribut DataSourceID di-set dengan ID dari control <asp:SiteMapDataSource>. Kemudian, kita harus men-set attribut SiteMapProvider dari control SiteMapDataSource tsb dengan sebuah provider yang memungkinkan untuk meng-query element-element menu dari sebuah table di database. As far as I know, provider seperti itu belum ada di ASP.NET, so we need to create it.

Ok, lets get started. In this case I use SQL Server database.

1. Create a table and name it with whatever, such as: Sitemap

image

Then, insert some records, like this:

image

Please note the menu hierarchy. For example: menu with title Live Video is the child of Video menu because its PARENT is set to Video menu SITEMAP_ID

 

2. Create provider class, name it SqlSiteMapProvider.cs, and place it inside App_Code folder. The code should be self-explained. I'll attach the code in this posting

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Runtime.CompilerServices;
using System.Security.Permissions;
using System.Web;
using System.Web.Caching;
using System.Web.Configuration;
 
/// <summary>
/// SqlSiteMapProvider
/// This class is a site map provider implementation for site map data stored in SQL Server Database.
/// This provider provides caching feature so that it will return root SiteMapNode if available in cache.
/// </summary>
 
namespace DyCode.Provider
{
    [SqlClientPermission(SecurityAction.Demand, Unrestricted = true)]
    public class SqlSiteMapProvider : StaticSiteMapProvider
    {
        private const string ErrMsg1 = "Missing node ID";
        private const string ErrMsg2 = "Duplicate node ID";
        private const string ErrMsg3 = "Missing parent ID";
        private const string ErrMsg4 = "Invalid parent ID";
        private const string ErrMsg5 = "Empty or missing connectionStringName";
        private const string ErrMsg6 = "Missing connection string";
        private const string ErrMsg7 = "Empty connection string";
 
        public const string CacheDependencyName = "__SiteMapCacheDependency";
 
        private string m_ConnectionString;   // Database connection string
        private string m_TableName = "sys_sitemap";     // TableName that store site map
        private string m_CacheKey = "SQLSiteMapProvider_Nodes";
        private bool m_CacheEnabled = false;
        private int m_IndexID, m_IndexTitle, m_IndexUrl, m_IndexDesc, m_IndexRoles, m_IndexParent;
        private Dictionary<int, SiteMapNode> m_NodesDictionary = new Dictionary<int, SiteMapNode>(16);
        private readonly object m_Lock = new object();
        private SiteMapNode m_RootNode;
 
        public override void Initialize(string name, NameValueCollection config)
        {
            // Verify that config isn't null
            if (config == null)
                throw new ArgumentNullException("config");
 
            // Assign the provider a default name if it doesn't have one
            if (String.IsNullOrEmpty(name))
                name = "SqlSiteMapProvider";
 
            // Add a default "description" attribute to config if the
            // attribute doesn’t exist or is empty
            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "SQL Server site map provider");
            }
 
            // Call the base class's Initialize method
            base.Initialize(name, config);
 
            // Initialize m_ConnectionString
            string connect = config["connectionStringName"];
            if (String.IsNullOrEmpty(connect))
                throw new ProviderException(ErrMsg5);
            config.Remove("connectionStringName");
 
            if (WebConfigurationManager.ConnectionStrings[connect] == null)
                throw new ProviderException(ErrMsg6);
 
            m_ConnectionString = WebConfigurationManager.ConnectionStrings[connect].ConnectionString;
            if (String.IsNullOrEmpty(m_ConnectionString))
                throw new ProviderException(ErrMsg7);
 
            // Initialize Table Name
            string tableName = config["tableName"];
            if (!String.IsNullOrEmpty(tableName))
            {
                m_TableName = tableName;
            }
            config.Remove("tableName");
 
            // Initialize Cache Enabled/Disabled
            string cacheEnabled = config["cacheEnabled"];
            if (!String.IsNullOrEmpty(cacheEnabled))
            {
                m_CacheEnabled = Convert.ToBoolean(cacheEnabled);
            }
            config.Remove("cacheEnabled");
 
            // Initialize Cache Key
            string cacheKey = config["cacheKey"];
            if (!String.IsNullOrEmpty(cacheKey))
            {
                m_CacheKey = cacheKey;
            }
            config.Remove("cacheKey");
 
            // SiteMapProvider processes the securityTrimmingEnabled
            // attribute but fails to remove it. Remove it now so we can
            // check for unrecognized configuration attributes.
 
            if (config["securityTrimmingEnabled"] != null)
                config.Remove("securityTrimmingEnabled");
 
            // Throw an exception if unrecognized attributes remain
            if (config.Count > 0)
            {
                string attr = config.GetKey(0);
                if (!String.IsNullOrEmpty(attr))
                    throw new ProviderException("Unrecognized attribute: " + attr);
            }
        }
 
        [MethodImpl(MethodImplOptions.Synchronized)]
        public override SiteMapNode BuildSiteMap()
        {
            lock (m_Lock)
            {
                SqlConnection connection = new SqlConnection(m_ConnectionString);
                SqlCommand command = null;
 
                if (m_CacheEnabled)
                {
                    SiteMapNode rootNode = (SiteMapNode)HttpRuntime.Cache.Get(m_CacheKey);
                    if (rootNode != null)
                    {
                        //check dependency
                        int count = 0;
                        try
                        {
                            command = new SqlCommand("SELECT COUNT(*) FROM " + this.m_TableName, connection);
                            command.CommandType = CommandType.Text;
                            connection.Open();
                            count = Convert.ToInt32(command.ExecuteScalar());
                        }
                        finally
                        {
                            connection.Close();
                        }
 
                        int siteMapNodeCount = Convert.ToInt32(HttpRuntime.Cache.Get(CacheDependencyName));
 
                        if (count != siteMapNodeCount)
                        {
                            //HttpContext.Current.Response.Write("Remove cache dependency<br/>");
                            //If table records that store sitemap are changed,
                            //remove the cache item so that OnSiteMapChanged event is triggered
                            HttpRuntime.Cache.Remove(CacheDependencyName);
                        }
                        else
                        {
                            //HttpContext.Current.Response.Write("Return SiteMapNode from cache<br/>");
                            return (SiteMapNode)HttpRuntime.Cache.Get(m_CacheKey);
                        }
                    }
                }
 
                // Make sure site map is cleared if it exists before continue             
                if (m_RootNode != null)
                {
                    ClearSiteMap();
                }
 
                // Query the database for site map nodes            
                try
                {
                    command = new SqlCommand("SELECT SITEMAP_ID, TITLE, DESCRIPTION, URL, ROLES, PARENT FROM " + this.m_TableName + " ORDER BY SITEMAP_ID", connection);
                    command.CommandType = CommandType.Text;
                    connection.Open();
 
                    SqlDataReader reader = command.ExecuteReader();
                    m_IndexID = reader.GetOrdinal("SITEMAP_ID");
                    m_IndexUrl = reader.GetOrdinal("URL");
                    m_IndexTitle = reader.GetOrdinal("TITLE");
                    m_IndexDesc = reader.GetOrdinal("DESCRIPTION");
                    m_IndexRoles = reader.GetOrdinal("ROLES");
                    m_IndexParent = reader.GetOrdinal("PARENT");
 
                    if (reader.Read())
                    {
                        // Create the root SiteMapNode and add it to the site map
                        m_RootNode = CreateSiteMapNodeFromDataReader(reader);
                        AddNode(m_RootNode, null);
 
                        // Build a tree of SiteMapNodes underneath the root node
                        int counter = 1;
                        while (reader.Read())
                        {
                            // Create another site map node and add it to the site map
                            SiteMapNode node = CreateSiteMapNodeFromDataReader(reader);
                            AddNode(node, GetParentNodeFromDataReader(reader));
                            counter++;
                        }
 
                        if (m_CacheEnabled && (HttpRuntime.Cache.Get(m_CacheKey) == null))
                        {
                            //Set a value for the cache entry that will serve as the 
                            //key for the dependency to be created on
                            HttpRuntime.Cache[CacheDependencyName] = counter;
 
                            //Create the array of cache key item names
                            string[] keys = new String[1];
                            keys[0] = CacheDependencyName;
 
                            //Create a dependency object referencing the array of cachekeys (keys)
                            CacheDependency dependency = new CacheDependency(null, keys);
 
                            HttpRuntime.Cache.Insert(m_CacheKey, m_RootNode, dependency,
                                                     Cache.NoAbsoluteExpiration,
                                                     Cache.NoSlidingExpiration,
                                                     CacheItemPriority.NotRemovable,
                                                     new CacheItemRemovedCallback(OnSiteMapChanged));
                        }
 
                    }
                }
                finally
                {
                    connection.Close();
                }
 
                // Return the root SiteMapNode
                return m_RootNode;
            }
        }
 
        private void ClearSiteMap()
        {
            Clear();
            m_NodesDictionary.Clear();
            m_RootNode = null;
        }
 
        protected override SiteMapNode GetRootNodeCore()
        {
            lock (m_Lock)
            {
                BuildSiteMap();
                return m_RootNode;
            }
        }
 
        // Helper methods
        private SiteMapNode CreateSiteMapNodeFromDataReader(DbDataReader reader)
        {
            // Make sure the node ID is present
            if (reader.IsDBNull(m_IndexID))
                throw new ProviderException(ErrMsg1);
 
            // Get the node ID from the DataReader
            int id = reader.GetInt32(m_IndexID);
 
            // Make sure the node ID is unique
            if (m_NodesDictionary.ContainsKey(id))
                throw new ProviderException(ErrMsg2 + "; Title: " + reader.GetString(m_IndexTitle));
 
            // Get title, URL, description, and roles from the DataReader
            string title = reader.IsDBNull(m_IndexTitle) ? null : reader.GetString(m_IndexTitle).Trim();
            string url = reader.IsDBNull(m_IndexUrl) ? null : reader.GetString(m_IndexUrl).Trim();
            string description = reader.IsDBNull(m_IndexDesc) ? null : reader.GetString(m_IndexDesc).Trim();
            string roles = reader.IsDBNull(m_IndexRoles) ? null : reader.GetString(m_IndexRoles).Trim();
 
            // If roles were specified, turn the list into a string array
            string[] rolelist = null;
            if (!String.IsNullOrEmpty(roles))
                rolelist = roles.Split(new char[] { ',', ';' }, 4000);
 
            // Create a SiteMapNode
            SiteMapNode node = new SiteMapNode(this, id.ToString(), url, title, description, rolelist, null, null, null);
 
            // Record the node in the m_NodesDictionary dictionary
            m_NodesDictionary.Add(id, node);
 
            // Return the node        
            return node;
        }
 
        private SiteMapNode GetParentNodeFromDataReader(DbDataReader reader)
        {
            // Make sure the parent ID is present
            if (reader.IsDBNull(m_IndexParent))
                throw new ProviderException(ErrMsg3 + "; Title: " + reader.GetString(m_IndexTitle));
 
            // Get the parent ID from the DataReader
            int pid = reader.GetInt32(m_IndexParent);
 
            // Make sure the parent ID is valid
            if (!m_NodesDictionary.ContainsKey(pid))
                throw new ProviderException(ErrMsg4 + "; Title: " + reader.GetString(m_IndexTitle));
 
            // Return the parent SiteMapNode
            return m_NodesDictionary[pid];
        }
 
        public void OnSiteMapChanged(string key, object item, CacheItemRemovedReason reason)
        {
            lock (m_Lock)
            {
                if (key == CacheDependencyName && reason == CacheItemRemovedReason.DependencyChanged)
                {
                    // Clear the site map
                    ClearSiteMap();
                }
            }
        }
    }
}

 

3. Create some configurations in web.config, like this:

3.1. Make sure you have a connection string to be used to connect to database that contains the Sitemap table.

<connectionStrings>
    <add name="SqlConnectionString" 
connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=YOURDB;Integrated Security=True"/>
</connectionStrings>

 

3.2. Add SiteMap tag inside <system.web> tag

<siteMap enabled="true" defaultProvider="AspNetSqlSiteMapProvider">
      <providers>
        <add    name="AspNetSqlSiteMapProvider" 
                type="DyCode.Provider.SqlSiteMapProvider" 
                securityTrimmingEnabled="true" 
                connectionStringName="SqlConnectionString" 
                tableName="Sitemap" 
                cacheEnabled="true" 
                cacheKey="MemberSiteMapProvider_Cache"/>
      </providers>
    </siteMap>

If you notice, tableName attribute above is set with table name that we have created previously.

 

4. In the page that will display menu, write this code:

<asp:Menu id="SiteMenu" runat="server" DataSourceID="SiteMapDataSource1" 
Orientation="Horizontal"/>
<asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" 
SiteMapProvider="AspNetSqlSiteMapProvider" ShowStartingNode="false" />

Please note that, SiteMapProvider attribute of SiteMapDataSource control is set with the name of provider that we have registered in web.config at step 3.

 

5. Test it

image

 

This provider is what we DyCoders always use in many projects and no known problem so far.

Some advantages of this provider are:

  1. Most of settings are configurable, like table name that stores menu data
  2. Cached. So it will not query menu data to db every time the menu displayed.

In order to use other database, you should change all SQL Server-related stuffs (used ADO.NET classes, SQL statements, and created table) to desired database. You can adapt this code to use OR Mapping technology so database independency can be achieved.

That's it. Enjoy. Please refer to attachment to get the complete code.

Share this post: | | | |
Posted: Jun 13 2008, 01:49 PM by andriyadi | with 2 comment(s)
Filed under:
Send SMS to Outlook Contact - A VSTO Outlook Add-in

Imagine you can send an SMS message to a mobile phone number of an Outlook contact directly from your Outlook. Despite there are some Outlook Add-ins those allow to do that, why don't build by your own. All you need are VSTO and GSM modem, either dedicated GSM modem or a mobile phone that can be connected via COM (using USB, Bluetooth, or Infrared).

Here are some capture how our add-in will look like.

Toolbar

Custom toolbar to connect to GSM modem and display SMS form

GSM Modem Form

When you click GSM Modem button, GSM Modem form will be displayed. Fill the textfields and click connect, "Phone is connected" dialog will be shown if connection is successful.

SMS Form

Clicking Send SMS button will display SMS Form. Number will be filled automatically from Contact's mobile phone.

Ribbon

By double clicking to a contact in contact list, contact detail will be displayed. I add two controls to ribbon to connect to GSM modem. If connection has been made previously, it will not connect again. There is also a form region to send SMS directly from Contact detail.

GSM Communication

For GSM communication, I don't make it from the scratch. Instead, I use GSM Communication Library available at: http://www.scampers.org/steve/sms. Some basic knowledge about GSM Comm:

1. Make a connection to GSM modem device

public GsmCommMain GsmComm {set; get;}
 
public void Connect(int comPort, int baudRate, int timeout)
{
    try
    {
        GsmComm = new GsmCommMain(comPort, baudRate, timeout);
        GsmComm.PhoneConnected += new EventHandler(GsmComm_PhoneConnected);
        GsmComm.PhoneDisconnected += new EventHandler(GsmComm_PhoneDisconnected);
 
        GsmComm.Open();
    }
    catch (System.Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
void GsmComm_PhoneDisconnected(object sender, EventArgs e)
{
    MessageBox.Show("Phone is disconnected");
}
 
void GsmComm_PhoneConnected(object sender, EventArgs e)
{
    MessageBox.Show("Phone is connected");
}

2. Disconnect

public void Disconnect()
{
    if (GsmComm != null && GsmComm.IsOpen())
    {
        GsmComm.PhoneConnected -= new EventHandler(GsmComm_PhoneConnected);
        GsmComm.PhoneDisconnected -= new EventHandler(GsmComm_PhoneDisconnected);
 
        GsmComm.Close();
    }
}

3. Send Short Message

public void SendSms(String phoneNo, String msg)
{
    if ((GsmComm == null) || (!GsmComm.IsOpen()))
    {
        MessageBox.Show("Phone is not connected");
        return;
    }
 
    try
    {
        SmsSubmitPdu pdu = new SmsSubmitPdu(msg, phoneNo, String.Empty);
        GsmComm.SendMessage(pdu);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

The rest code are used for building Outlook Add-in, add custom toolbar, add custom ribbon, form region, and other Windows Forms. Complete code can be downloaded at: http://dycode.com/files/folders/codesamples/entry125.aspx

I make a screencast to show you how to build this add-in step by step.

http://images.soapbox.msn.com/flash/soapbox1_1.swf?c=v&v=c2985270-5ec6-412b-bf3b-32485adf428a <p><a href="http://images.soapbox.msn.com/flash/soapbox1_1.swf?c=v&v=c2985270-5ec6-412b-bf3b-32485adf428a" target="_blank">http://images.soapbox.msn.com/flash/soapbox1_1.swf?c=v&v=c2985270-5ec6-412b-bf3b-32485adf428a</a></p>

View original media here: http://video.msn.com/video.aspx?vid=c2985270-5ec6-412b-bf3b-32485adf428a

You can download good quality video by following this link: http://cid-0cbe0e395b6f9491.skydrive.live.com/self.aspx/Public/SendSMSOutlookAddIn.zip 

That's it for now. Enjoy.

Share this post: | | | |
100 Years Indonesia National Awakening

Sungguh ironis, 4 hari setelah peringatan 100 tahun Kebangkitan Nasional Indonesia, harga BBM ikut bangkit (baca: naik - red). Apakah Indonesia sudah benar-benar bangkit dalam 100 tahun terakhir ini? Sebuah pertanyaan besar.

Terlepas dari carut marutnya kondisi multi dimensional di Indonesia, ada secerca harapan di area teknologi, khususnya teknologi informasi. Simaklah artikel bertajuk "Kebangkitan, LAHIR dan TUMBUH dalam GELANGGANG", yang dimuat di majalah Teknopreneur, Edisi 18|Mei 2008, yang bercerita tentang bagaimana geliat kebangkitan bidang teknologi di tengah kisah buram di negeri tercinta kita. Berikut hasil scan artikel tersebut (mudah2an publikasi hasil scan berikut tidak menyalahi aturan. Jika iya, tolong segera beritahukan agar segera bisa dihilangkan). Surprisingly, DyCode ikut diceritakan dalam artikel ini. Selamat membaca...

Page 1

Page 1

Page 2

Page 2

Page 3

Page 3. As you can see...DyCode and me are told in this article.

Page 4

Page 4

Dan, di halaman 4 dimuat juga foto saya yang ganteng itu :P (foto ber-border merah). Itu bukan hasil editing dengan image manipulation software. You can buy the magazine if you don't trust me :)

Cukup membanggakan, bisa disejajarkan dengan foto pak Habibie. Mudah2an saya dan perusahaan saya bisa berkontribusi dalam membangkitkan Indonesia di dalam negeri dan dunia internasional dari sisi IT, dengan tetap bersemangat Indonesia.

Bangkitlah Indonesiaku!!!

Share this post: | | | |
Posted: May 25 2008, 03:17 AM by andriyadi | with 4 comment(s)
Filed under: ,
"Poisoning" PHP Developers at MSDN Day | PHP on Windows

On Thursday, May 15, 2008, I talk about Interoperability between .NET and PHP through XML/SOAP Web Services at MSDN Day | PHP on Windows. During my session, I do a little "poisoning" to PHP developers by showing to them how easy creating web services and proxy classes to access web services in .NET using Visual Studio. And it's quite successfull, a participant finally asks Visual Studio price since he is interested on Visual Studio features. I think I should be a Visual Studio marketer :)

But honestly, it's all about tool. I think the simplification of Web Services-related stuffs can also be done in PHP world if PHP IDE support those kind of stuffs. I don't know whether the famous PHP IDE, Zend Studio, support them. Please somebody from Zend Indonesia explain about Web Services-related stuffs. Honestly, I do wanna know to enrich my knowledge :)

For this MSDN Day, I have to install PHP runtime, PHP editor, and dig to well-buried PHP manual after I've left PHP for more than two years. For PHP editor, I still can't find a comprehensive and free PHP editor. Too bad I can't build one :) Since I love Visual Studio and I want to be able to create PHP script within VS environment, I finally find a plugin for VS to create PHP project. That plugin is called Vs.Php from Jcx.Software. The 30 days-trial version is available for download. Since I think I still have no intention to go back to PHP after 30 days later, I just install that trial version plugin, and sure I will uninstall it several days after the event :)

During this talk, I talk about:

  • Integration options between PHP and .NET
  • Web Services integration option
  • Calling ASP.NET Web Services using Microsoft AJAX Library from PHP or non .NET code

The presentation slide can be downloaded at: http://dycode.com/files/folders/msdnday/entry123.aspx

I do some live codings to show how easy to develop interop between PHP and .NET through XML/SOAP Web Services. For example, in #1 demo I create an ASP.NET Web Services, and then create the consumer of that web service in PHP script. For that purpose, I use a library called NuSOAP to develop PHP SOAP Web Service client.

ASP.NET Web Service code. This is deployed at URL: http://localhost/MiscWebServices/WebService.asmx

<%@ WebService Language="C#" Class="WebService" %>
 
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
 
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
[System.Web.Script.Services.ScriptService]
public class WebService : System.Web.Services.WebService
{
    [WebMethod]
    public string GetServerTime()
    {
        return DateTime.Now.ToString();
    }
 
    [WebMethod(Description = "Get Server Time with supplied format")]
    public string GetFormattedServerTime(String format)
    {
        return DateTime.Now.ToString(format);
    }
}

And the PHP code as the client:

<?php
//Refer to NuSOAP lib
require_once('../../nusoap-0.7.3/lib/nusoap.php');
 
//Create WS client
$client = new nusoap_client("http://localhost/MiscWebServices/WebService.asmx?WSDL", 'false');
 
//Check if there is error
$err = $client->getError();
if ($err) {
    echo ('<h2>Constructor error</h2><pre>'.$err.'</pre>');
}
 
//Create param
$param = array();
 
//Call WS
$result = $client->call('GetServerTime', $param);
 
//Check error
if ($client->fault) {
    echo '<h2>Fault</h2><pre>';
    print_r($result);
    echo '</pre>';
} else {
    echo '<h2>Result</h2><pre>';
    echo ($result['GetServerTimeResult']);
    echo '</pre>';
}
?>

What if the accessed web method need parameter(s)? Well, you can just supply the $param array with needed parameter(s).

 

I'm so glad to be involved in this MSDN Day. Hopefully, my talk can be a starting point to integrate your PHP to .NET or Windows platform.

That's it. Enjoy. Have a nice weekend.

Share this post: | | | |
Posted: May 17 2008, 02:36 PM by andriyadi | with no comments
Filed under:
VSTO & MOSS with Visual Studio 2008

As part of Developer Workshop for Financial Services, on May 8, 2008, I delivered a training about VSTO and MOSS with Visual Studio 2008. The training was held at Solusi Training Center (Sarana Solusindo Informatika), sponsored by Microsoft Indonesia.

Since MOSS cannot be installed on my Windows Vista machine, I install MOSS on Windows Server 2008 running on Virtual PC. So, entire coding is done in VPC. Most of training time I use for demo and hands on lab, so not many slide I presented. As demo and training scenario, I choose:

  • Binding Word content control to SharePoint content type
  • Creating simple SharePoint workflow using VSTO 3.0
  • Accessing MOSS Search web service

That's it.

Share this post: | | | |
WSS 3.0 Installation Screencast

Just want to show you how easy WSS 3.0 installation on Windows Server 2008, I've made a screencast as follow. Currently, it has no audio explaining installation process, but I will add it later. I think you can grab the explaination just by seeing the video.

FYI, not like Windows Server 2003, Windows Server 2008 doesn't bundle and install Windows Sharepoint Services by default. You have to download and install it separately.

Sorry for crappy video quality. I encode it using Expression Encoder and the quality is good in my PC. But after I upload it to Silverlight Streaming Services and view it, it doesn't look good. Please find the attachment to download better video.

The installation use SharePoint3.0WithSP1.exe installer. If you attent the last MSDN Day about Sharepoint, I'm sure you get the DVD contains that file. If you don't, you can download it at http://www.microsoft.com/downloads/details.aspx?familyid=EF93E453-75F1-45DF-8C6F-4565E8549C2A&displaylang=en

What are system requirements to install WSS 3.0 you ask? Here they are: (taken from Technet)

  • Operating System: Windows Server 2003 SP1 or Windows Server 2003 x64 or Windows Small Business Server 2003; .NET Framework 3.0; Internet Information Services (IIS) 6.0 or later with common files, Simple Mail Transfer Protocol (SMTP) service, World Wide Web service
  • Hardware for Single-Box Installation: Server with a processor speed of at least 2.5 GHz; RAM capacity of 1 GB minimum, 2 GB recommended; disk space up to 2 GB for installation minimum, 5 GB or more minimum for data.

That's it. For Single-Box Installation, you don't even need SQL Server, since WSS will install SQL Express as content and configuration database. Actually, I don't have computer with 2.5 GHz processor speed, but I have it installed successfully :)

That's it. Have a nice try. Please give you comments.

Share this post: | | | |
Posted: May 05 2008, 05:41 AM by andriyadi | with 4 comment(s)
Filed under: ,
WSS 3.0 Demo

For you who haven't know what Windows Sharepoint Services (WSS) is all about and the newest features of WSS 3.0, you have to see this demo at: http://www.microsoft.com/sharepoint/demo.mspx

FYI, most of demonstrated features are available for free, except Office Sharepoint Server 2007 and Office Sharepoint Designer 2007, as long as you have Windows Server 2003/2008 & SQL Server 2000/2005 (those are should be bought). As this site says, WSS 3.0 is available as a separate download  that is available to customers at no additional charge.

I'm not trying to sell something here, Microsoft doesn't pay me to do that :) I just wanna share with you about WSS 3.0 from developer & entrepreneur perspective. From developer perspective, WSS 3.0 is a comprehensive web application platform that can be extensively customized to develop LOB web application. From entrepreneur perspective, you can sell the solutions based on WSS, such as deployment, customization. etc. Not like Community Server (sorry CS lover, actually I'm also CS lover :P) that its license should be bought by the end user (beside the Windows Server), there is no money to spend to implement WSS within end user's environment. The only things should be bought are Windows Server 2003/2008 and SQL Server 2000/2005 (correct me if I'm wrong :P), while SQL Server 2005 can be replaced by SQL Server Express 2005 (I haven't try to use SQL Server Express, but I will & let you know how it will come).

That's it. Have a great day.

Share this post: | | | |
Posted: May 03 2008, 03:34 AM by andriyadi | with no comments |
Filed under: ,
The Full Report of Windows Live TTT, Bangkok - The Trip to Bangkok

WARNING!!! Before you open this posting, be prepared that this posting contains many photos that may slow down page loading. Banyak foto orang-orang good looking dan banci kamera yang bisa membuat Anda tergila-gila :) Juga banyak foto-foto yang belum mendapat persetujuan dari objek fotonya untuk dimuat di sini, sehingga ada kemungkinan nantinya untuk dihilangkan jika ada complaint dari yang bersangkutan. No animals are harmed in these photos :P

Semuanya berawal dari kira-kira awal Maret, mungkin tanggal 3 Maret 2008, ketika ngobrol dengan pak Risman tentang adanya Windows Live Training for Trainers (TTT) di Bangkok dan saya diajak sebagai salah satu peserta. Tentu saja sangat menyenangkan, karena bisa ikut training tentang teknologi baru dan memang kebetulan saya belum pernah ke luar negeri sebelumnya. Segeralah membuat passport, yang dari kapan tahun belum kesampaian untuk bikin. Peserta dari Indonesia adalah:

Pada beberapa posting sebelumnya, saya sudah sedikit menyinggung tentang materi yang diberikan dalam training tsb. However, those posting are lack of details about the things beside the training itself. Dalam posting ini dan beberapa posting ke depan saya ingin men-share full report tentang training tersebut, dimulai dari perjalanan berangkat ke Bangkok, selama di Bangkok, dan ketika perjalanan pulang. Karena saya bukan reporter, saya akan sampaikan report tsb dengan gaya bahasa sendiri. Posting pertama ini akan diceritakan tentang perjalan dari Indonesia sampai ke Bangkok, dan akhirnya sampai di Hotel.

Perjalanan dimulai dari Indonesia International Airport, Soekarno - Hatta. Keempat partners in crime berkumpul sekitar jam 17, 23 April 2008, di terminal D. Setelah semua berkumpul, dan karena perut mulai berontak, kami makan dulu di Mc*.

100_1220 100_1224

Notes: Karena adanya kesalahan setting date pada camera saya, beberapa foto pada tgl 23 April ini jadinya ter-stamp tanggal 29 April.

Setelah makan, langsung menuju loket check in, lalu bayar fiskal dulu. Honestly, gue gak tahu apa itu fiskal dan untuk apa uangnya, somebody please explain to me. After that, kami langsung berjalan menuju ruang tunggu. Dalam perjalanan ke ruang tunggu, sempat singgah dulu ke toko rokok karena dikhawatirkan nggak ada rokok Indonesia di Bangkok. Saya sendiri kebetulan udah prepare rokok dari Bandung karena pasti susah mencari rokok A M*** di Bangkok.

100_1228

Lagi cari-cari rokok. Sorry banyak iklan.

Around 6.40 pm, we're all aboard to Thai Airways airplane. Sambutan hangat dari pramugari Thailand dengan sebuah ucapan yang nggak tahu apa itu, segera menghampiri begitu memasuki pesawat. Inilah para banci kamera berfoto ria di dalam pesawat :)

100_1235 100_1234

Tidak seperti penerbangan domestik, di dalam penerbangan kali ini kami menemui banyak (lebih dari 4) pramugari Thailand yang cantik.

100_1248

Dengan keberanian yang tinggi, saya memutuskan untuk berfoto bersama dengan seorang pramugari. Di awali dengan pertanyaan "May I take a picture with you?", pramugari itupun mengangguk dan segera berpose manis. Keren nggak bo'?

100_1249

Pesawatpun take off sekitar jam 6.50pm. Inilah beberapa foto selama penerbangan:

100_1272 100_1271

Beberapa tertidur pulas setelah meneguk segelas *****

100_1260

Coba tebak kepala siapa ini? :)

100_1269

Almost there

Dan sampailah kami di Airport Suvarnabhumi, Bangkok, dengan selamat. Kemudian langsung naik bus menuju terminal kedatangan internasional.

100_1283 100_1282

Setelah memasuki terminal kedatangan, kami berjalan menuju loket Passport Control sebelum bisa memasuki Bangkok. Sempat berfoto dulu di depan patung yang gede banget, nggak tahu patung apa itu.

100_1293

Setelah sampai di depan loket Passport Control, langsung mengantri dan mulailah pemeriksaan satu persatu.

100_1298 100_1297

Semuanya lolos passport control, walaupun salah satu dari kami berempat ada yang berjenggot cukup lebat :) Kalau nggak lolos bisa-bisa kejadian terperangkap di daerah internasional kayak di film The Terminal, the most expensive romantic comedy movie ever made, by Steven Spielberg.

Setelah lolos dari Passport Control, kami berjalan menuju ke luar airport sambil mencari jemputan dari hotel. Setelah ketemu jemputannya, nggak lama kemudian naik van, dan mulailah perjalanan menuju Hotel.

Setelah kurang lebih 30 menit perjalanan dari airport ke hotel, sampailah kami di Unico Grande Asoke Hotel, tempat kami menginap selama 3 hari ke depan. Setelah check in, tanpa basa basi, langsung menuju M2 Cafe untuk makan malam. Saatnya menikmati the delicious Thailand's Tom Yam.

100_1327

Tasting the spicy Tom Yam

Lagi-lagi dengan keberanian dan charming saya, saya berhasil meminta waitress di cafe itu untuk berfoto bersama :)

100_1326

Sama-sama berbaju hitam, kayaknya udah janjian nih...Sepertinya pasangan serasi :)

Setelah makan, enaknya sih smoking. But unfortunately, you can't smoke inside a AC room anywhere in Thailand, including in bar, club, etc. So, we decide directly go to hotel room.

Kamarnya ok banget, selayaknya hotel bintang 4. Berikut beberapa foto di dalam kamar.

100_1328 100_1331

100_1333 100_1345

There're even some c*nd*ms available here. Kayaknya memang hal-hal yang berkaitan dengan s*x udah biasa di sini.

Tanpa mandi dan basa-basi, saatnya merebahkan diri di kasur yang empuk setelah perjalanan yang melelahkan.

Demikian posting pertama ini tentang perjalanan dari Indonesia ke Bangkok. Next posting akan berbicara tentang hal-hal yang terjadi selama training.

Just stay tune!!!

Share this post: | | |