Tuesday, June 05, 2007
« Sore | Main | Objectivism in Bioshock »

According to the DasBlog stats engine, loyal Damon readers are surpassed by Googlers looking for a specific answer to a specific design or code question by an order of magnitude.  I suppose I could apply political spin to this and state that I am Very Useful, but not too popular.  At any rate, Googlers should utilize the Ctrl+F chord about now, since this article contains various loosely related but hard won observations.

I wrote about  a program I gave to my support people that did something simple and gave me an excuse to dig around in WPF.  Since I am always looking for ways to lessen the burden of our support department (so I can tap them for more dedicated testing, which is in their rational self interest anyway) I try to identify commonalities in support calls and do something about it.  Sometimes “doing something” means new features or bug fixes disguised as new features.  Yep, I said that out loud.  Sometimes these are knowledge base articles; sometimes they are internal only Software Tools.

My company has a special camera that uses a barcode scanner (not TTL) to embed data within images.  Sometimes our illustrious employees and customers make innocent mistakes like “accidentally doing a days’ work with their daughter’s Fisher Price camera and not our ultrahighend special camera” and end up with 1000 photos with no data in them.  This is a conundrum.  Luckily the massively parallel nature of the human brain makes us very good at pattern recognition tasks such as “These 20 photos go with that 1999 dodge”, and rather than the ultra painful manual process our support crew does now to assign these, I thought I’d just spend an hour and write a tool that allowed them to right-click on a group of photos in Explorer and assign some scan data to them using Exif.  It took slightly more than an hour.

File Types in a Setup Project

Like a mathematician who cannot be bothered to determine the sum of the square root of four with itself, I generally find it to be a better use of my time to talk to Tools and APIs than to learn the guts of HKEY_CLASSES_ROOT.  In my last WPF example I used VS2005 and an MSI project’s “View File Types” designer to assign all JPG files to a custom shell verb that runs my program.  Oddly enough it seems to be impossible to find information related to making more than one program a windows shell command for a single file type.  I’m free to add “Output from program one” and “Output from program two” as actions, however Windows is nice enough to employ a WIWO (whichever in, whichever out) technique to randomly choose which program is actually run  out of the two JPG associated programs via the installer.  Looking up the meaning of the “Verb” option on MSDN it would appear that the Verb gives me further command information (which menu item the user clicked) on the JPG and therefore will allow my program to determine what to do with the command line argument of the file path.  Of course I wrote completely separate programs before finding this.  This lack of foresight aside, the “verb” is not passed as a command line argument nor is it set as an environment variable or passed to my program in any way that I can see as useful.

To use multiple custom programs from the windows shell for the same file type, alter the arguments to contain the verb and use a launcher program:

If you want to “run program A for JPG files” and also “run program B for JPG files”, create two commands, two main executables, and a launcher executable.  In some cases the two executables may not make sense but in my case I already had two entirely separate solutions built in Expression Blend and I wasn’t about to look into merging them.  My launcher program is of project type Windows Application yet does not run a Form; this prevents users from seeing the black flash of a console window.

Inter process communication between Windows programs is harder than it needs to be in managed code:

So, as I said in the intro I want to right click on the 30 photos taken of the 1995 Dodge Minivan and work with them all at once inside one program instance. I found that the Windows shell, by default, will open up one instance of the associated program per file selected when dragging the mouse.  Do this with enough files and Windows is nice enough to warn me that “Edit Exif Data for this many files may be slow, continue?”  I would have assumed that each file would be passed as a separate command line argument to a single instance.  Without the ole Init() vs. InitInstance() from win32, I had to find another way to communicate quickly between multiple running Windows applications, WPF applications in this case.

My first attempt was using SendMessage() through P/Invoke and sending a message containing all the command line arguments to the first running instance.  WPF is an entirely new model, obviously, so I had to determine how to get a hold of the wndproc and filter messages.  On WPF this is done like so:

            System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(this);

            System.Windows.Interop.HwndSource hwndSource = System.Windows.Interop.HwndSource.FromHwnd(helper.Handle);

            hwndSource.AddHook(new System.Windows.Interop.HwndSourceHook(MessageHook));

Where the HwndSourceHook delegate looks like:

protected IntPtr MessageHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)

The problem here was that although I could P/Invoke SendMessage to a window after obtaining its main Window handle using System.Diagnostics.Process, and even though I could convince it to marshal wParam or lParam as type LPTSTR, the string in the received message was unconvertible by any means back to a string.  After trying every  Marshal.PtrToStringXXX(IntPtr) method I had to assume something was fundamentally wrong with this method.  Windows pointers are relative to the base memory space of the process.  If a string is marshaled as something like TCHAR* then all the second process is getting is a pointer to a memory address that is only valid in the calling address space. Either more P/Invoke or Marshal calls to do something to memory are needed or this won’t work.  I found a potentially cleaner method anyway.

Remoting

Some people are afraid of Remoting in the same way they are afraid of Threads and probably say things like “We should never use threads or Remoting on this project.”  I have to admit I’ve done almost zero Remoting outside of studying for MCXX tests but I was convinced by my favorite Iron Man/Software Architect/Short Order Cook that Remoting was a fine way to implement trivial inter process communication between applications on the same machine.  Fine, it’s easy enough.  

    public class InterprocessListener : IDisposable

    {

        public const int PORT = 5785;

        private TcpChannel _channel;

 

        public InterprocessListener()

        {

            _channel = new TcpChannel(PORT);

            ChannelServices.RegisterChannel(_channel,false);

            RemotingConfiguration.RegisterWellKnownServiceType(typeof(ArgServer), "ExifEditor", WellKnownObjectMode.Singleton);

        }

 

        public void Dispose()

        {

            if (null != _channel)

            {               

                _channel.StopListening(null);

                ChannelServices.UnregisterChannel(_channel);

            }

        }

 

    }

So, let’s assume we right click on 30 JPG images and choose to run “Edit Exif Data”.  The first instance up would instantiate this simple InterprocessListener on my favorite port, 5785.  The ArgServer implementation is equally trivial:

    public class ArgServer : MarshalByRefObject

    {

        /// <summary>

        /// Delegate to let listeners in this process know we got a message

        /// </summary>

        /// <param name="newArg"></param>

        public delegate void ArgAddedCallback(string newArg);

 

        public static event ArgAddedCallback ArgAdded;

 

        /// <summary>

        /// Pass an arbitrary string out of process

        /// </summary>

        /// <param name="argument"></param>

        public void PassArg(string argument)

        {

            OnArgAdded(argument);

        }

 

        protected void OnArgAdded(string arg)

        {

            if (null != ArgAdded)

            {

                ArgAdded(arg);

            }

        }

    }

Now, as our WPF programs are starting up we can execute some simple logic:

If(this is the first ExifEditor instance to start)

{

Start Interprocess listener

 add ourselves to the static arg passed event
} else {

Get a remote reference and pass our own arguments

Exit as we are no longer needed
}

The SendArgs() method shows the simple Remoting client code used to contact the primary instance.

        /// <summary>

        /// Find a local server and try to send it my arguments

        /// </summary>

        private void SendArguments()

        {

            TcpChannel chan = new TcpChannel();

            ChannelServices.RegisterChannel(chan, false);

            ArgServer argServer = (ArgServer)Activator.GetObject(typeof(ExifEditor.ArgServer), "tcp://localhost:5785/ExifEditor");

            if (null != argServer && Environment.GetCommandLineArgs().Length > 1)

            {

                argServer.PassArg(Environment.GetCommandLineArgs()[1]);

            }

 

            chan.StopListening(null);

        }

Mutexen

Now we need to handle running only a single Primary instance of our program and quickly detecting that program exists and sending arguments to it without trying to use the same TCP port from 30 different programs at once.  We do this with two Mutexes.   Mutices?  Mutexen?  Ox is to Oxen as Mutex is to Mutexen?    Here is the first chunk of code from my Window class, another cool WPF example in my opinion.  The Window is shaped like the camera we give to most of our clients, with image previews and buttons where the camera LCD screen and buttons where the camera buttons are.  I have some WPF clip geometry to take care of to get circular buttons.  Cool?  Lame?  I’m just the .NET software architect slinging code.

       public partial class CameraWindow

       {

        InterprocessListener _listener;

        Mutex _m;

        private delegate void VoidDelegate();

 

        /// <summary>

        /// Check to see if this instance should run or share its arguments with an already established instance

        /// </summary>

              public CameraWindow()

              {

            Application.Current.DispatcherUnhandledException +=

                new System.Windows.Threading.DispatcherUnhandledExceptionEventHandler(Current_DispatcherUnhandledException);

            bool createdNew;

            _m = new Mutex(false, "ExifEditorMutex", out createdNew);

            _m.WaitOne(5, false);

            // All other instances will now fail to create the mutex

            if (createdNew)

            {

                //First instance

                _listener = new InterprocessListener();                   

                ArgServer.ArgAdded += new ArgServer.ArgAddedCallback(ArgServer_ArgAdded);                   

            }

            else

            {

                //Another program is listening already

                Mutex mChild = new Mutex(false, "ExifEditorChild");

                mChild.WaitOne();

 

                SendArguments();

               

                mChild.ReleaseMutex();

                Close();

            }

 

            this.InitializeComponent();

            if (Environment.GetCommandLineArgs().Length > 1)

            {

                _imageList.Items.Add(Environment.GetCommandLineArgs()[1]);

            }

            else if (Environment.GetCommandLineArgs().Length == 1)

            {

                _imageList.Items.Add(Environment.GetCommandLineArgs()[0]);

            }

            //Sorry we couldn't work things out, SendMessage(), it's not you, it's me...

            //System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(this);

            //System.Windows.Interop.HwndSource hwndSource = System.Windows.Interop.HwndSource.FromHwnd(helper.Handle);

            //hwndSource.AddHook(new System.Windows.Interop.HwndSourceHook(MessageHook));

              }

        /// <summary>

        /// When the in-process arg server gets a message, add arguments to our list of images

        /// </summary>

        /// <param name="newArg"></param>

        void ArgServer_ArgAdded(string newArg)

        {

            Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,

               (VoidDelegate)delegate()

               {

                   _imageList.Items.Add(newArg);

                   _countTxt.Text = _imageList.Items.Count + " selected";

               });

        }

        private void Exif_Editor_Closed(object sender, EventArgs e)

        {

            if (null != _listener)

            {

                _listener.Dispose();

            }

        }

 

        private void Exif_Editor_Closing(object sender, System.ComponentModel.CancelEventArgs e)

        {

            if (null != _m)

            {

                _m.ReleaseMutex();                

            }

        }

So, 30 of these are trying to start at once.  One and only one of them will gain an exclusive lock on a System level Mutex named Underbar M.   Because it has a Name this synchronization primitive is visible outside the current process.  In the Exif_Editor_Closing event we release this mutex, failure to do so would be abandoning the mutex and is a serious error condition in Windows.  The other 29 instances trying to start up wait for 5 milliseconds and fail to acquire the Mutex.  They go into the else block where the second Mutex is encountered.  This is where I have to confess I didn’t look at the Remoting threading model too closely but it seems likely that 29 programs trying to write to port 5785 at the same time could be an issue.  Lucky numbers 2 through 29 WaitOne() indefinately to obtain the mChild Mutex for their turn at sending a message, the mChild mutex is then released and the Window kills itself.  Gracefuly.

I noticed some Google near-hits in the DasBlog stats of people looking for a simple example of the Control.Invoke() equivalent in WPF.  Take a look at my descriptive VoidDelegate and ArgServer_ArgAdded above.

Wrapping it Up

So, we have several useful things here.  You can use a System Mutex to keep more than one instance of your app from running.  Of course there is a Windows Forms property (IsSingleInstance in VB) that does this for you in .NET 2.0 but so far I didn’t see the same property for a Window in WPF.  You could use the same logic and a Semaphor to allow n instances of your program to run.  We also have an extremely simple inter process communication example using .Net Remoting.  Finally, if you wish to have some fancy windows shell actions but want to use APIs and managed code rather than figuring out how to properly hack the registry to get your program to get multiple files passed as arguments, you can put it all together and do that.

Tuesday, June 05, 2007 3:22:54 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback Related posts:
AGT so far
Run time is design time for AGT [12]
Run time is design time for AGT [11]
Run time is design time for AGT [10]
Run time is design time for AGT[9]
Run time is design time for AGT[8]
Tracked by:
"Interesting Finds: June 6, 2007" (Jason Haley) [Trackback]
http://9qs-information.info/13615605/index.html [Pingback]
http://9ow-information.info/96724812/index.html [Pingback]
http://9qm-information.info/19046700/home-work-opportunity.html [Pingback]
http://9qg-information.info/10895424/index.html [Pingback]
http://9ot-information.info/84813543/building-a-enterprise-network.html [Pingback]
http://9qo-information.info/86238518/vegetarian-health-email-inquiry.html [Pingback]
"Interesting Finds: June 6, 2007" (Jason Haley) [Trackback]
http://9on-information.info/09602178/index.html [Pingback]
http://9rb-information.info/86957908/adams-county-national-bank-scott-miller.htm... [Pingback]
http://9rb-information.info/81085371/index.html [Pingback]
http://9rn-information.info/49757782/dog-harness-sweden-mr.html [Pingback]
http://9ro-information.info/86343701/index.html [Pingback]
http://9ra-information.info/12217559/index.html [Pingback]
"And some else, come here..." (FlasheR!) [Trackback]
http://9re-information.info/32571158/vegas-card-decks-online.html [Pingback]
http://9rw-information.info/92449571/index.html [Pingback]
http://9rx-information.info/28331019/bush-garden-va-vacation-packages.html [Pingback]
http://9uaff-le-informazioni.info/09263448/index.html [Pingback]
http://9uafd-le-informazioni.info/46489188/index.html [Pingback]
http://9uafe-le-informazioni.info/14767138/index.html [Pingback]
http://9uafc-le-informazioni.info/71316323/www-running.html [Pingback]
http://9uafh-le-informazioni.info/71620666/appunti-materiale.html [Pingback]
http://9uafj-le-informazioni.info/42695755/traduzione-run-baby-run.html [Pingback]
http://9uafd-le-informazioni.info/97523967/index.html [Pingback]
http://9uafg-le-informazioni.info/75753874/emanuela-arcuri.html [Pingback]
http://9uaem-le-informazioni.info/41988686/magix-music-studio.html [Pingback]
http://9uahs-le-informazioni.info/95745575/gioco-non-scaricare-ma-fare-gratis.ht... [Pingback]
http://9uage-le-informazioni.info/06729795/index.html [Pingback]
http://9uahj-le-informazioni.info/84574714/porto-marghera.html [Pingback]
http://9uagk-le-informazioni.info/36471468/index.html [Pingback]
http://9uagm-le-informazioni.info/06072043/index.html [Pingback]
http://9uahb-le-informazioni.info/75876468/formal-power-series.html [Pingback]
http://9uagf-le-informazioni.info/54866499/nds-sat.html [Pingback]
http://9uahg-le-informazioni.info/94916144/index.html [Pingback]
http://9uagf-le-informazioni.info/19408252/index.html [Pingback]
http://9uahr-le-informazioni.info/73853449/voli-su-turku.html [Pingback]
http://kevruublog.tripod.com/153.html [Pingback]
http://fartooblog.tripod.com/170.html [Pingback]
http://rma7dj.org/sitemap54.html [Pingback]
http://vyz07b.org/www-my-scranton-edu.html [Pingback]
http://pinofranc.homestead.com/03/brass-knuckles.html [Pingback]
http://pinofranc.homestead.com/03/www-limewire-com.html [Pingback]
http://zelkuunews.tripod.com/152.html [Pingback]
http://minveenews.angelfire.com/25.html [Pingback]
http://nazlyynews.tripod.com/84.html [Pingback]
http://ovx5a-eee.com/dirty-humor.html [Pingback]
http://unibetkom.netfirms.com/005-blog.html [Pingback]
http://ramambo.nl.eu.org/00/bankone.html [Pingback]
http://ramambo.nl.eu.org/www-bikinidare-com.html [Pingback]
http://ramambo.nl.eu.org/hot-women-lover.html [Pingback]
http://grgh1aa.biz/lakeland-toyota.html [Pingback]
http://ri9spbn.biz/mary-carey-hardcore.html [Pingback]
http://hlia9o7.com/erosive-lichen-planus.html [Pingback]
http://fjuxwgq.com/chris-daughtry-pictures.html [Pingback]
http://sanoblog.nl.eu.org/babez.html [Pingback]
http://wbseqlc.biz/muff-diving.html [Pingback]
http://nasferablog.netfirms.com/265.html [Pingback]
http://toh--blog.nl.eu.org/houghton-mifflin-company.html [Pingback]
http://nasferablog.netfirms.com/444.html [Pingback]
http://vot--kom.nl.eu.org/may-calender.html [Pingback]
http://www.nonedotweb.org/st11.html [Pingback]
http://www.nonedotweb.org/st67.html [Pingback]
http://msve--lono.nl.eu.org/ww-hertz-com.html [Pingback]
http://repola--loto.nl.eu.org/fayetteville-newspaper.html [Pingback]
http://nasferablog.netfirms.com/115.html [Pingback]
http://mromaner.tripod.com/15.html [Pingback]
http://mromaner.tripod.com/19.html [Pingback]
http://mumareg.tripod.com/151.html [Pingback]
http://jmqp7tr.biz/nylottorey.html [Pingback]
http://nuflind.tripod.com/37.html [Pingback]
http://wwad6lf.biz/craigslsit.com.html [Pingback]
http://derfoblog.ifrance.com/sitemap1.html [Pingback]
http://galetgah.homestead.com/182.html [Pingback]
http://smapper12.ifrance.com/66.html [Pingback]
http://aqupofot.nl.eu.org/www-windowsmediaplayer.html [Pingback]
http://petmeds.hooyack.com/131.html [Pingback]
http://mazzoliks.ifrance.com/201.html [Pingback]
http://kubaluin.ifrance.com/224.html [Pingback]
http://halloweenus.net/545.html [Pingback]
http://halloweenus.net/130.html [Pingback]
http://discovercard.usalegaldirect.org/32.html [Pingback]
http://auter.homestead.com/01/karaoke-revolution.html [Pingback]
http://duter.homestead.com/01/sitemap10.html [Pingback]
http://euter.homestead.com/01/nextel-i730.html [Pingback]
http://2909071.ifrance.com/172.html [Pingback]
http://bodaltega.ifrance.com/471.html [Pingback]
http://0210071.ifrance.com/134.html [Pingback]
http://0210071.ifrance.com/225.html [Pingback]
http://03100711.ifrance.com/60.html [Pingback]
http://mikalkoin.ifrance.com/74.html [Pingback]
http://fasxen.netfirms.com/2.html [Pingback]
http://mambubuli.tripod.com/148.html [Pingback]
http://mambubuli.tripod.com/631.html [Pingback]
http://zanzibuli.tripod.com/561.html [Pingback]
http://zavernuli.0catch.com/909.html [Pingback]
http://zavernuli.0catch.com/856.html [Pingback]
http://www6.donden.biz/152.html [Pingback]
http://www8.donden.biz/471.html [Pingback]
http://karlopupik.tripod.com/20.html [Pingback]
http://karlopupik.tripod.com/36.html [Pingback]
http://krumlopol.tripod.com/297.html [Pingback]
http://krumlopol.tripod.com/7.html [Pingback]
http://kurochkin.ifrance.com/51.html [Pingback]
http://kurochkin.ifrance.com/77.html [Pingback]
http://adultwebmasetrempire.com/ [Pingback]
http://malunok.ifrance.com [Pingback]
http://tory.atwebpages.com [Pingback]
http://gramulik.150m.com/32.html [Pingback]
http://fy533706.ki6dgrr.info/sitemap5.html [Pingback]
http://hi318651.lqcykdv.info/sitemap2.html [Pingback]
http://kh9qeci.net/03/sitemap46.html [Pingback]
http://tr5d5iu.net/03/sitemap1.html [Pingback]
http://weujmru.net/events/sitemap1.html [Pingback]
http://fwmwly7.net/arizona/sitemap1.html [Pingback]
http://split-dalmatia.com/split-dalmatia.com/images/pages/81160650/cialis-over-t... [Pingback]
http://add2rss.com/img/design/pages/35212940/all-james-bond-movies.html [Pingback]
http://jivest2006.com/pages/71437517/cialis-soft-gelss-10.html [Pingback]
http://ncdtnanotechportal.info/generator/pages/85302415/scaly-hentai-straight.ht... [Pingback]
http://witze-humor.de/templates/images/pages/templates/images/pages/50211456/ind... [Pingback]
http://thejohnslater.com/pix/img/pages/17332733/what-does-cialis-do-.html [Pingback]
http://swellhead.net/53989848/pictures-of-pablo-picasso.html [Pingback]
http://thejohnslater.com/pix/img/pages/46526635/22-cali-lipid-infamil-premature.... [Pingback]
http://plantmol.com/pages/82925982/soft-tab-geneneric-cialis.html [Pingback]
http://swellhead.net/10430138/clonazepam-cialis-pass-out-.html [Pingback]
http://split-dalmatia.com/split-dalmatia.com/images/pages/91234049/source-for-ci... [Pingback]
http://ukpuuq8.net/rideshare/sitemap1.html [Pingback]
http://modena.intergate.ca/arezzojewelry/prozac.html [Pingback]
http://modena.intergate.ca/arezzojewelry/zoloft.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/melatonin/index.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/wellbutrin/index.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/rainbow-brite/index.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/tramadol/index.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/cialis/index.html [Pingback]
http://modena.intergate.ca/arezzojewelry/prilosec.html [Pingback]
http://modena.intergate.ca/arezzojewelry/cymbalta.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/prilosec/index.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/paxil/index.html [Pingback]
http://modena.intergate.ca/arezzojewelry/hoodia.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/celebrex/index.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/accutane/index.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/prozac/index.html [Pingback]
http://modena.intergate.ca/arezzojewelry/soma.html [Pingback]
http://modena.intergate.ca/arezzojewelry/paxil.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/coumadin/index.html [Pingback]
http://modena.intergate.ca/arezzojewelry/celexa.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/cymbalta/index.html [Pingback]
http://modena.intergate.ca/arezzojewelry/lipitor.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/synthroid/index.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/clomid/index.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/hoodia/index.html [Pingback]
http://modena.intergate.ca/arezzojewelry/cialis.html [Pingback]
http://modena.intergate.ca/arezzojewelry/nexium.html [Pingback]
http://modena.intergate.ca/arezzojewelry/clomid.html [Pingback]
http://blastpr.com/blog/wp-includes/js/pages/lexapro/index.html [Pingback]
http://modena.intergate.ca/arezzojewelry/claritin.html [Pingback]
http://modena.intergate.ca/arezzojewelry/tramadol.html [Pingback]
http://modena.intergate.ca/arezzojewelry/lexapro.html [Pingback]
http://ofiyspg.net/sitemap1.html [Pingback]
http://morningside.edu/mics/_notes/pages/melatonin/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/rainbow-brite/index.html [Pingback]
http://blastpr.com/wiki/js/pages/cymbalta/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/paxil/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/tramadol/index.html [Pingback]
http://blastpr.com/wiki/js/pages/zoloft/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/effexor/index.html [Pingback]
http://blastpr.com/wiki/js/pages/ultram/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/wellbutrin/index.html [Pingback]
http://blastpr.com/wiki/js/pages/tramadol/index.html [Pingback]
http://blastpr.com/wiki/js/pages/celebrex/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/lexapro/index.html [Pingback]
http://blastpr.com/wiki/js/pages/synthroid/index.html [Pingback]
http://blastpr.com/wiki/js/pages/cialis/index.html [Pingback]
http://blastpr.com/wiki/js/pages/lexapro/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/celebrex/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/synthroid/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/cialis/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/soma/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/clomid/index.html [Pingback]
http://blastpr.com/wiki/js/pages/wellbutrin/index.html [Pingback]
http://blastpr.com/wiki/js/pages/coumadin/index.html [Pingback]
http://blastpr.com/wiki/js/pages/nexium/index.html [Pingback]
http://blastpr.com/wiki/js/pages/celexa/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/lipitor/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/prilosec/index.html [Pingback]
http://blastpr.com/wiki/js/pages/prozac/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/nexium/index.html [Pingback]
http://blastpr.com/wiki/js/pages/effexor/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/cymbalta/index.html [Pingback]
http://morningside.edu/mics/_notes/pages/claritin/index.html [Pingback]