Thursday, October 22, 2009
List DLL's exported functions in C#
I started by using the code from this post:
http://www.gamedev.net/community/forums/topic.asp?topic_id=409936 by ReZimE
not much was left of the original code, but it gave me the idea I needed in order to approach the problem.
If MSDN jumps to your head, you probably didn't try to use it at all. The thing contains everything you need, ordered by structure. Looks OK, unless you're in a habit of solving a certain point. For those cases the best order is by orientation (aka keywords, categories, etc.) And thus Googling is the best method to use.
For the code I needed to define the structures to be used. PInvoke.NET did a good job on many of the structures. If you need them it's probably one of the best places, though I had to create a few missing structures; based on MSDN.
Now to the code:)
static void Main(string[] args)
{
string DllName = args[0];
unsafe
{
fixed (char* pszModule = DllName)
{
void* hMod = InterOp.GetModuleHandleW(pszModule);
if (hMod != null)
{
Console.WriteLine("Got handle"); uint BaseAddress = (uint)hMod;
// Verify DOS header
IMAGE_DOS_HEADER pDOSHeader = (IMAGE_DOS_HEADER)Marshal.PtrToStructure((IntPtr)hMod, typeof(IMAGE_DOS_HEADER));
if (pDOSHeader.e_magic != IMAGE_DOS_SIGNATURE) // "MZ"
{
Console.WriteLine("Error in DOS signature");
}
// NT Header
void* pNTHeader = InterOp.ImageNtHeader(hMod);
IMAGE_NT_HEADERS NTHeader = (IMAGE_NT_HEADERS)Marshal.PtrToStructure((IntPtr)pNTHeader, typeof(IMAGE_NT_HEADERS));
if (NTHeader.Signature != IMAGE_NT_SIGNATURE) // "PE"
{
Console.WriteLine("Error in NT signature");
}
// RVA of export table
uint lExportOffset = NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
uint lExportSize = NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
// Export table
IMAGE_EXPORT_DIRECTORY pExportDir = (IMAGE_EXPORT_DIRECTORY)Marshal.PtrToStructure((IntPtr)(BaseAddress + lExportOffset), typeof(IMAGE_EXPORT_DIRECTORY));
// Length of AddressOfNames array
int lNumberOfNames = (int)pExportDir.NumberOfNames;
Console.WriteLine("Found {0} names, size {1} at offset {2}", lNumberOfNames, lExportSize, lExportOffset);
for (int i = 0; i < lNumberOfNames; i++)
{
char* szName = (char*)(BaseAddress + Marshal.ReadInt32((IntPtr)(BaseAddress + pExportDir.AddressOfNames + 4 * i)));
string name = Marshal.PtrToStringAnsi((IntPtr)szName);
Console.WriteLine(name);
}
}
}
}
}
As you'll see; at least I did, because I forgot; it requires the "Allow unsafe code" flag in order to compile.
The constants used in the code are defined as
private static readonly ushort IMAGE_DIRECTORY_ENTRY_EXPORT = 0;
private static readonly ushort IMAGE_DOS_SIGNATURE = 0x5A4D;
private static readonly ushort IMAGE_NT_SIGNATURE = 0x4550;
private const string KERNEL_DLL = "kernel32";
private const string DBGHELP_DLL = "Dbghelp";
private const CallingConvention CALLING_CONVENTION = CallingConvention.Winapi;
I used 6 structures defined in the code, as far as I remember most of them are from the above links so I'll just list them here. In any case it's quite easy to create a new structure based on the definition in the MSDN. Just make sure to define it as "Explicit" instead of "Sequential" if there's a non-standard declaration.
The structures used in the code:
IMAGE_DOS_HEADER
IMAGE_NT_HEADERS
IMAGE_EXPORT_DIRECTORY
IMAGE_FILE_HEADER
IMAGE_OPTIONAL_HEADER32
IMAGE_DATA_DIRECTORY
The last thing is the "access class", I built it especially to access the external functions in the "unsafe" way.
private unsafe class InterOp
{
[DllImport(KERNEL_DLL, CallingConvention = CALLING_CONVENTION, EntryPoint = "GetModuleHandleW"), SuppressUnmanagedCodeSecurity]
public static extern void* GetModuleHandleW(/*IN*/ char* lpModuleName);
[DllImport(DBGHELP_DLL, CallingConvention = CALLING_CONVENTION, EntryPoint = "ImageNtHeader"), SuppressUnmanagedCodeSecurity]
public static extern void* ImageNtHeader(void* Base);
}
Wednesday, October 14, 2009
Disabling Screensaver for domain computer
In any other situation simply setting the registry key
HKEY_CURRENT_USER\Control Panel\Desktop\ScreenSaveActiveto 0 can work. But on a domain it's not working.
Neither does:
const int HWND_BROADCAST = 0xFFFF;
const int SC_MONITORPOWER = 0xF170;
const short WM_SYSCOMMAND = 0x112;
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, -1);
So I began to dig which message to send in order to fake a mouse movement (or a key press) I've found the answer on this link: http://www.dreamincode.net/forums/showtopic19021.htm
After manipulating it a bit to fit my need I ended up with this code:
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, int dwExtraInfo);
const int HWND_BROADCAST = 0xFFFF;
[Flags]
private enum MouseEventFlags
{
LEFTDOWN = 0x00000002,
LEFTUP = 0x00000004,
MIDDLEDOWN = 0x00000020,
MIDDLEUP = 0x00000040,
MOVE = 0x00000001,
ABSOLUTE = 0x00008000,
RIGHTDOWN = 0x00000008,
RIGHTUP = 0x00000010
}
static void Main(string[] args)
{
while (true)
{
System.Threading.Thread.Sleep(1000);
mouse_event((uint)MouseEventFlags.MOVE, 0, 0, 0, 0);
}
}
It doesn't interfere with my work, CPU stays low.
This mini-program is a saver for me.
Some rantings from a programmer
Now for the real part of the blog. The preface was just because of some red and green lines appearing on my screen for reasons that Ol' Bill markets as gold. Though I guess it's your gold.
When building a program there appear to be lots of methods to "get it done". While some methods focus on speed, others are focused on scalability. At the bottom line it's about a "customer" and a "product". The usual implementations apply, customer doesn't want to pay too much, product has base cost.
For most cases (meaning the small needed programs) the cycle is
1. Get a list of requirements
2. Set the price (based on the list)
3. Build a demo
4. Show the demo
5. Build a real model
6. Customer gives a list of rejects
7. Fix rejects
8. Put final version
9. Get the paycheck
Note that some customers repeat steps 5 & 6 for a very long time.
Strangely enough this cycle is the base for large projects too. Even if a few other things like milestones, meetings and committees are added.
From my own experience I can tell that the steps involving the customer's decision are the worst. And the closer you are to the customer the worse it gets.
I did a small app for my dad once; he just forgot to mention that the check numbers should be up to 8 digits with a possibility for leading zeros. He mentioned it after he was using the program for a few months, by then he already found a way to bypass it: put 1 instead of a leading 0. Once the checks with the 1 came it was very messy business… Of course I couldn't charge him for the hours spent fixing this problem (he's my dad after all.)
At the current time I'm trying to get another family computer, it's the only explanation to putting Hebrew UI (the family.) It's not that I don't know Hebrew or have problem reading it. It's just that Windows Hebrew UI is at best inconsistent; the terms change from version to version, sometimes the translation is ludicrous, at other times the translation does not exist, and to top it all M$ tested it on a bunch of apes which have nothing to do with Hebrew. If it was a paying customer I'd put the price high enough for him to run away for someone who's shameless. But when it's from your family you can't, thus you end up trashing a newly bought computer with a UI that's not better than a Linux UI (sorry fellows, but that's the point Linux is missing.)
That's all for today, no source code, no punch line, just some rantings and an insight to how they can be reached.
Monday, October 12, 2009
Get the pixel's color
First thing, the reason:
A few days ago I needed to copy a certain color from the document into the app I'm developing. As usual the "color" was copied from a website by the "printscreen - crop - paste" method, which left me with the image and a customer saying "like this".
Digging a bit into the .NET I've found the easiest way would be to do a similar thing:
1. Capture the screen into an image.
2. Get the pixel corresponding to the mouse location
3. Display it's RGB values.
Easy said, and thanks to the M$ .NET team easy done.
The code for the 1st step is:
int w = Screen.PrimaryScreen.Bounds.Width;
int h = Screen.PrimaryScreen.Bounds.Height;
Bitmap img = new Bitmap(w, h);
Graphics g = Graphics.FromImage(img);
g.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(w, h));
For the second step a call to the user32.dll is needed:
[System.Runtime.InteropServices.DllImport("user32")]
public static extern int GetCursorPos(out POINTAPI lpPoint);
public struct POINTAPI
{
public int x;
public int y;
}
And the actual code is:
POINTAPI tPOS;
GetCursorPos(out tPOS);
The 3rd step is the trivial step:
Color color = img.GetPixel(tPOS.x, tPOS.y);
label1.Text = color.A.ToString() + ":" + color.R.ToString() + ":" + color.G.ToString() + ":" + color.B.ToString();
I've wrapped it all in a timer's tick (laziness again...) The resulting method is:
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
POINTAPI tPOS;
GetCursorPos(out tPOS);
int w = Screen.PrimaryScreen.Bounds.Width;
int h = Screen.PrimaryScreen.Bounds.Height;
Bitmap img = new Bitmap(w, h);
Graphics g = Graphics.FromImage(img);
g.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(w, h));
Color color = img.GetPixel(tPOS.x, tPOS.y);
label1.Text = color.A.ToString() + ":" + color.R.ToString() + ":" + color.G.ToString() + ":" + color.B.ToString();
label1.ForeColor = color;
System.Threading.Thread.Sleep(1);
timer1.Start();
}
This code works, though the screen capturing is a bit consuming in CPU terms.
Sunday, September 6, 2009
Dynamic loading – debug a form without the program
Once you're in such a project you want to check the work of a certain DLL without the rest of them getting in the way. Not to mention without passing through the log-in procedure and the parent (or parents in some cases) of the form. You need the quick and dirty method – load it, launch it.
Probably the most important lines of code can be summarized as follows:
System.Reflection.Assembly asmbly = System.Reflection.Assembly.LoadFile(DLL_Path);
Type tp = asmbly.GetType(FormName);
object obj = asmbly.CreateInstance(tp.FullName);
That's it, obj contains the form ready for whatever we want.
But, usually that's not enough (except for some weird cases where the constructor does everything.) So I've added some colors to the picture. First thing to do was show a list of forms. Note that I need only the FORMS.
Using a basic form, a textbox for the DLL's name, a "browse" button, a combo to be filled, and a "load DLL" button.
The load DLL function is:
private void LoadDLL_btn_Click(object sender, EventArgs e)
{
Cursor = Cursors.WaitCursor;
Application.DoEvents();
try
{
if (System.IO.File.Exists(DLL_Path_txt.Text))
{
System.Reflection.Assembly asmbly = System.Reflection.Assembly.LoadFile(DLL_Path_txt.Text);
System.Reflection.AssemblyName[] refs = asmbly.GetReferencedAssemblies();
string base_path = DLL_Path_txt.Text.Substring(0, DLL_Path_txt.Text.LastIndexOf('\\') + 1);
foreach (string fl in System.IO.Directory.GetFiles(base_path))
{
try
{
System.IO.File.Copy(fl, Application.StartupPath + fl.Substring(fl.LastIndexOf('\\')), true);
}
catch { }
}
FormNamespace_cmb.Items.Clear();
foreach (Type tp in asmbly.GetTypes())
{
if (tp.IsSubclassOf(typeof(System.Windows.Forms.Form)))
{
FormNamespace_cmb.Items.Add(tp.FullName);
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
Cursor = Cursors.Default;
}
}
Since this utility is for debugging purposes forget security, hiding the exception is like trying to put shoes on in the darkness.
Actually that's all, the only piece of code you need now is a "Fire", or in a nicer phrasing "Launch" button, which has the following code
private void Launch_btn_Click(object sender, EventArgs e)
{
Cursor = Cursors.WaitCursor;
Application.DoEvents();
try
{
if (System.IO.File.Exists(DLL_Path_txt.Text))
{
System.Reflection.Assembly asmbly = System.Reflection.Assembly.LoadFile(DLL_Path_txt.Text);
System.Reflection.AssemblyName[] refs = asmbly.GetReferencedAssemblies();
string base_path = DLL_Path_txt.Text.Substring(0, DLL_Path_txt.Text.LastIndexOf('\\') + 1);
Environment.CurrentDirectory = base_path;
Application.DoEvents();
Type tp = asmbly.GetType(FormNamespace_cmb.Text);
object obj = asmbly.CreateInstance(tp.FullName);
if (obj is Form)
{
Form frm = (Form)obj;
frm.ShowDialog();
}
else
{
MessageBox.Show(tp.FullName + " is not a System.Windows.Form, please choose a different object.");
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
Cursor = Cursors.Default;
}
}
That's all, this code works for me. I use it mainly when the need to debug some new "error" arises (Ahhhm… null references… Ahhhm…)
It's by far lighter than the whole application, and I can manage it without the fear of changing a vital part in the project.
Thursday, April 9, 2009
Logging – how to do it effectively
Hopefully I won't delay the next post too much.
Lately I was part of a team implementing new logging technique.
These are the summarized points of interest when starting the "logs".
First, make sure you have defined the levels of logging. It was hell of a time to look for every log-line and re-level it. We had a problem with the definitions so I did those remakes a few times, to tell the truth I don't know what's the results are.
Second thing to do is find / build a suitable logger (NLog is a good choice for an open source logger). We chose it for our "win services," it's definitely a good choice. It supports 6 levels of logging, multiple logs and writes to the event log.
Third thing is merge the logger levels with the levels you defined. This "trivial task" requires about 2 hours of the team; otherwise it requires a complete re-write of the code every time a fly lands on your screen. We had the chance to bypass this idea, luckily for us we got hold of ourselves quickly enough so only a week worth of code had to be re-written for the job.
Once everything is in place, working and appears rock stable comes the "funny part". Actually we found strange behaviors in some old apps (probably auto-rewritten from the VB6 source into the .NET C#.) Some parts had unused methods q sections. Others on the other hand became useless; did you know "Biztalk doesn't compile empty expressions, it throws an error." Other parts hadn't got enough logging in the first run; usually it means add a few lines of code.As a rule of a thumb, make sure that every variable, message, program is logged with its value when you're using the highest log level.
Another important issue is conventions when writing. We had to write down "failed XMLs", in order to find "who's the developer which needs to fix the bug," we had to open them and read the values. It took only a day for us to realize that marking the module's ID is by far more important for the initial handling than the ID or type of the XML.
One last note – never, but never run stress test when logging in the highest level. The results are usually funny (well for anyone except the poor programmer who can't understand WTF went wrong)