|
|
 |
 |
January 8th, 2009
There are several tutorials out there for C# that show you how to make an application so that it can only have one instance. However most of the example code out there doesn’t do it properly or it uses the FindWindow function which is not the proper way to identify an application.
The code below basically registers a window message using your application’s GUID. Then it sends that message to all the open processes, seeing if any will respond to it. Processes that do respond to the window message that we registered, are ours, because we have code that also responds to our own window message. Once we identify that a window is ours, we then send a COPYDATA window message to it with the command line parameter that we want to pass to the instance of our application that is visible to the user.
Source Code:
public partial class App : Application
{
private static Mutex SingleMutex;
public static uint MessageId;
private void Application_Startup(object sender, StartupEventArgs e)
{
IntPtr Result;
IntPtr SendOk;
Win32.COPYDATASTRUCT CopyData;
string[] Args;
IntPtr CopyDataMem;
bool AllowMultipleInstances = false;
Args = Environment.GetCommandLineArgs();
// TODO: Replace {00000000-0000-0000-0000-000000000000} with your application's GUID
MessageId = Win32.RegisterWindowMessage("{00000000-0000-0000-0000-000000000000}");
SingleMutex = new Mutex(false, "AppName");
if ((AllowMultipleInstances) || (!AllowMultipleInstances && SingleMutex.WaitOne(1, true)))
{
new Main();
}
else if (Args.Length > 1)
{
foreach (Process Proc in Process.GetProcesses())
{
SendOk = Win32.SendMessageTimeout(Proc.MainWindowHandle, MessageId, IntPtr.Zero, IntPtr.Zero,
Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
2000, out Result);
if (SendOk == IntPtr.Zero)
continue;
if ((uint)Result != MessageId)
continue;
CopyDataMem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.COPYDATASTRUCT)));
CopyData.dwData = IntPtr.Zero;
CopyData.cbData = Args[1].Length*2;
CopyData.lpData = Marshal.StringToHGlobalUni(Args[1]);
Marshal.StructureToPtr(CopyData, CopyDataMem, false);
Win32.SendMessageTimeout(Proc.MainWindowHandle, Win32.WM_COPYDATA, IntPtr.Zero, CopyDataMem,
Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
5000, out Result);
Marshal.FreeHGlobal(CopyData.lpData);
Marshal.FreeHGlobal(CopyDataMem);
}
Shutdown(0);
}
}
}
/***********************************************************************/
public partial class Main : Window
{
private void Window_Loaded(object sender, RoutedEventArgs e)
{
HwndSource Source;
Source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
Source.AddHook(new HwndSourceHook(Window_Proc));
}
private IntPtr Window_Proc(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam, ref bool Handled)
{
Win32.COPYDATASTRUCT CopyData;
string Path;
if (Msg == Win32.WM_COPYDATA)
{
CopyData = (Win32.COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.COPYDATASTRUCT));
Path = Marshal.PtrToStringUni(CopyData.lpData, CopyData.cbData / 2);
if (WindowState == WindowState.Minimized)
{
// Restore window from tray
}
// Do whatever we want with information
Activate();
Focus();
}
if (Msg == App.MessageId)
{
Handled = true;
return new IntPtr(App.MessageId);
}
return IntPtr.Zero;
}
}
/***********************************************************************/
public class Win32
{
public const uint WM_COPYDATA = 0x004A;
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
[Flags]
public enum SendMessageTimeoutFlags : uint
{
SMTO_NORMAL = 0x0000,
SMTO_BLOCK = 0x0001,
SMTO_ABORTIFHUNG = 0x0002,
SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
}
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
public static extern uint RegisterWindowMessage(string lpString);
[DllImport("user32.dll")]
public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam,
SendMessageTimeoutFlags fuFlags, uint uTimeout, out IntPtr lpdwResult);
}
January 8th, 2009
Here are some string extensions that I use to compare wildcards. CompareWildcard compares only one wildcard (ie “*.txt”), while CompareWildcards compares multiple wildcards (ig “*.txt;*.zip;”).
Usage Example:
string Path = "c:\readme.txt";
if (Path.CompareWildcards("*.txt;*.zip;", true) == true)
{
// Path matches wildcard
}
Source code:
public static bool CompareWildcards(this string WildString, string Mask, bool IgnoreCase)
{
int i = 0;
if (String.IsNullOrEmpty(Mask))
return false;
if (Mask == "*")
return true;
while (i != Mask.Length)
{
if (CompareWildcard(WildString, Mask.Substring(i), IgnoreCase))
return true;
while (i != Mask.Length && Mask[i] != ';')
i += 1;
if (i != Mask.Length && Mask[i] == ';')
{
i += 1;
while (i != Mask.Length && Mask[i] == ' ')
i += 1;
}
}
return false;
}
public static bool CompareWildcard(this string WildString, string Mask, bool IgnoreCase)
{
int i = 0, k = 0;
while (k != WildString.Length)
{
switch (Mask[i])
{
case '*':
if ((i + 1) == Mask.Length)
return true;
while (k != WildString.Length)
{
if (CompareWildcard(WildString.Substring(k + 1), Mask.Substring(i + 1), IgnoreCase))
return true;
k += 1;
}
return false;
case '?':
break;
default:
if (IgnoreCase == false && WildString[k] != Mask[i])
return false;
if (IgnoreCase && Char.ToLower(WildString[k]) != Char.ToLower(Mask[i]))
return false;
break;
}
i += 1;
k += 1;
}
if (k == WildString.Length)
{
if (i == Mask.Length || Mask[i] == ';' || Mask[i] == '*')
return true;
}
return false;
}
Please feel free to post fixes, and suggestions in the comments below.
June 26th, 2008
I previously wrote about how to quickly convert a byte array to its hex representation and back again in C. Now I’ve converted the same source code to C#. Even though there is a built in method using String.Format, it is too slow.
Source Code:
public static string ByteArrayToHexString(byte[] Bytes)
{
StringBuilder Result = new StringBuilder();
string HexAlphabet = "0123456789ABCDEF";
foreach (byte B in Bytes)
{
Result.Append(HexAlphabet[(int)(B >> 4)]);
Result.Append(HexAlphabet[(int)(B & 0xF)]);
}
return Result.ToString();
}
public static byte[] HexStringToByteArray(string Hex)
{
byte[] Bytes = new byte[Hex.Length / 2];
int[] HexValue = new int[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D,
0x0E, 0x0F };
for (int x = 0, i = 0; i < Hex.Length; i += 2, x += 1)
{
Bytes[x] = (byte)(HexValue[Char.ToUpper(Hex[i + 0]) - '0'] << 4 |
HexValue[Char.ToUpper(Hex[i + 1]) - '0']);
}
return Bytes;
}
December 14th, 2007
Recently, even as recently as today, I was stretching my C# programming legs for work. We had an application that I’ve been maintaining that would update the user interface with statistics every so often. What I noticed was that as the updates occured, the user interface would become less responsive. I tracked down the issue to the fact that we were using the PropertyGrid incorrectly. What we had before was something along the lines of:
public void Event_OnUpdate(Class Sender)
{
PropertyGrid_ObjectInfo.SelectedObject = new ObjectInfo(Sender);
}
Where ObjectInfo is basically a property wrapper for the Sender object. The slow responsiveness of the user interface was due to the fact that the PropertyGrid had to reload the SelectedObject each time it updated which, I guess is expensive. To solve the problem I simply used the following code instead.
public void Event_OnUpdate(Class Sender)
{
if(PropertyGrid_ObjectInfo.SelectedObject == null)
PropertyGrid_ObjectInfo.SelectedObject = new ObjectInfo(Sender);
else
PropertyGrid_ObjectInfo.Refresh();
}
I hadn’t seen anybody on Google run across the same problem with the PropertyGrid performing slowly so I decided to blog it. And that is that.
July 5th, 2007
I made it through and am now MCAD certified. I wish they had a certification for C programming, but they only seem to have them for their .NET framework. I took the test this morning and scored a passing 810. The wording on the questions were the trickiest so far. I didn’t read the book for it cause I knew most of the information already and because I couldn’t stand to read another one of those books that are so full of filler that it is sick. Instead I just skimmed through the 800 page book and went over stuff I didn’t know. I did practice questions I found on the website I mentioned earlier and that helped the most. I am not sure if I will go for my MCSD. Now I have to wait 7 days for Microsoft to mail me my MCP ID and Access Code.
July 2nd, 2007
Today, early this morning at around 11, I took my second exam for MCAD certification. I scored a 920 out of a possible 1000. I did a lot better on this test in part to BrainDumpCentral.com which is a website where people go and do brain dumps of the questions that they’ve had after they’ve taken the test themselves. A lot of the questions on BDC were almost exactly the same as on the real test. For this test I read the Microsoft certified book as I did for the last test. I didn’t bother with Transcender tests this time, as they didn’t really help me that much last time. There were a few things in there that I didn’t know about previously, such as .NET Remoting Objects, etc. I am now only one test away from getting the certification I need. Afterward I might get the MCSD (Microsoft Certified Solutions Developer) certificate, but I probably will take my time doing that. My incentive to get MCAD certified is a $1500 bonus my work is shelling out to the first two people who do.
June 28th, 2007
I took my 70-316 test yesterday. I scored a 780/1000. A 700 is required to pass. 780 isn’t that great, but then again I am not really .NET programmer, so I guess it is ok. I’ve done .NET windows and web applications and web services before but I don’t regularly program in .NET. Prior to taking the test, I read the whole Microsoft certified book for it. The book helped me gain a better understanding of the concepts and helped me learn the Microsoft terminology. But some of the questions on the test were not covered in the book. Other than that I used one Transcender Practice Exam and it was alright. I am not really looking forward to taking the other two tests, I kinda wish Microsoft would just award/give me the certification.
|