June 2006 - Posts

xtoi (Hex to Integer) C function

C language has many functions for converting numerical values to and from string. Here are some from the standard C library:

atof   converts a string to a double
atol   converts a string to a long
atoi   converts a string to an integer

Then you can get the reverse by using sprintf function to get the string representation of numerical values using various formating. That's great for ready made stuffs, though the C library is lacking some functions to convert to and from a hexadecimal string function, we can simply write our own. Here's something I wrote to convert hexadecimal string to integer, I called it xtoi to follow the same function naming used by C library:

// Converts a hexadecimal string to integer
int xtoi(const char* xs, unsigned int* result)
{
 size_t szlen = strlen(xs);
 int i, xv, fact;

 if (szlen > 0)
 {
  // Converting more than 32bit hexadecimal value?
  if (szlen>8) return 2; // exit

  // Begin conversion here
  *result = 0;
  fact = 1;

  // Run until no more character to convert
  for(i=szlen-1; i>=0 ;i--)
  {
   if (isxdigit(*(xs+i)))
   {
    if (*(xs+i)>=97)
    {
     xv = ( *(xs+i) - 97) + 10;
    }
    else if ( *(xs+i) >= 65)
    {
     xv = (*(xs+i) - 65) + 10;
    }
    else
    {
     xv = *(xs+i) - 48;
    }
    *result += (xv * fact);
    fact *= 16;
   }
   else
   {
    // Conversion was abnormally terminated
    // by non hexadecimal digit, hence
    // returning only the converted with
    // an error value 4 (illegal hex character)

    return 4;
   }
  }
 }

 // Nothing to convert
 return 1;
}


The function above is a little different, as it return the result back to the parameter, hence you need to provide storage to where to put the result, while capturing what the function may return as the conversion status.

xtoi conversion status:

  0    - Conversion is successful
  1    - String is empty
  2    - String has more than 8 bytes
  4    - Conversion is in process but abnormally terminated by
         illegal hexadecimal character



Sample usage:

  unsigned int result;

  if (xtoi("ABCD", &result) == 0)
  {
     printf("ABCD = %d", result);
  }


Output:

  ABCD = 43981

That's for this time, and next time I will see what I can come up for a function that will convert a 64bit hexadecimal string to its 64bit numerical value.

For now, enjoy and hopefully you may find the function useful.


Edit:
From the comment below the page, you can see the C way of decoding hex from string using sscanf function:

unsigned int hex = 0;
const char* hexstr = "ABCD";

sscanf(hexstr, "%X", &hex);
printf("%s = %d\n", hexstr, hex);

Posted by cvega with 4 comment(s)

HexDump utility (converted to MASM)

For the benefit of assembly language programmers working with file mapping (and also for the purpose of honing my personal skills), I converted my file mapping functions posted previously to assembly language, particularly in MASM.

I also converted (and managed to made few optimizations) the source code of Hex Dump utility to assembly language, this is by the way the example utility program I posted as an example for the file mapping functions.

The difference between what's compiled in C and what's assembled in MASM in term of executable size is quite big, DUMP.EXE in C is around the average of 26KB, while the DUMP.EXE assembled in MASM is just 3KB!

Anyway, most of the discussion regarding the file mapping functions and the HexDump utility has been covered in my previous post - so there's no much point in keeping you with lengthy explanation.

Here's the converted source code in MASM, and enjoy:

; --------------------- filemap.inc ---------------------

ifndef  __FILEMAPPING__
        __FILEMAPPING__ equ 1

; Prototypes
OpenAndMapFile    proto :LPCSTR, :PHANDLE, :PHANDLE, :PHANDLE
CloseAndUnmapFile proto :PHANDLE, :PHANDLE, :PHANDLE

; Maximum length of filename
MAX_FNAME_LEN           equ 256

; Constants
SUCCESS                 equ 0
ERROR_OPENFILE_FAILED   equ 1
ERROR_CREATEMAP_FAILED  equ 2
ERROR_MAPVIEW_FAILED    equ 3

; OpenAndMapFile function settings
fm_desired_access       equ GENERIC_READ
fm_share_mode           equ FILE_SHARE_READ
fm_file_attributes      equ FILE_ATTRIBUTE_NORMAL or \
                            FILE_ATTRIBUTE_ARCHIVE
fm_page_access          equ PAGE_READONLY
fm_map_desired_access   equ FILE_MAP_READ

;
; This function will try to open and memory map a specified file,
; and return all the handles for the opened file, created mapping
; object and the base of the memory mapped file. This function returns
; any of the status defined above for errors or success; Also, this 
; function handles the closing of the handles for any errors that may
; happen during the process of memory mapping of the file.
;

.code

OpenAndMapFile proc lpszFilename:LPCSTR, \
                    _hFile:PHANDLE, \
                    _hFileMap:PHANDLE, \
                    _hFileBase:PHANDLE

 local dwFileSize:DWORD
 
 ; Open the specified file
 invoke CreateFile, \
           lpszFilename, \
           fm_desired_access, \
           fm_share_mode, \
           NULL, \
           OPEN_EXISTING, \
           fm_file_attributes, \
           NULL
 mov    edx, _hFile
 mov    [edx], eax
 
 .if eax != INVALID_HANDLE_VALUE

  ; Create FileMapping object for the file
  push   edx
  invoke GetFileSize, [edx], NULL
  mov    dwFileSize, eax
  pop    edx

  invoke CreateFileMapping, \
              [edx], \
              NULL, \
              fm_page_access, \
              0, \
              dwFileSize, \
              NULL

  mov    edx, _hFileMap
  mov    [edx], eax

  .if eax != INVALID_HANDLE_VALUE
  
   ; Map the file to the process memory
   invoke  MapViewOfFile, \
              [edx], \
              fm_map_desired_access, \
              0, \
              0, \
              0

   mov     edx, _hFileBase
   mov     [edx], eax

   .if eax != INVALID_HANDLE_VALUE
    mov    eax, SUCCESS
    ret
   .else
    mov   
edx, _hFileMap
    invoke CloseHandle, [edx]
    mov    edx, _hFile
    invoke CloseHandle, [edx]
    mov    eax, ERROR_MAPVIEW_FAILED
    ret
   .endif
  .else
   mov    
edx, _hFile
   invoke  CloseHandle, [edx]
   mov     eax, ERROR_CREATEMAP_FAILED
   ret
  .endif
 .endif

 mov    eax, ERROR_OPENFILE_FAILED
 ret
OpenAndMapFile endp

;
; This function will cloase all the handles opened and created by
; OpenAndMapFile function
;

CloseAndUnmapFile proc _hFile:PHANDLE, \
                       _hFileMap:PHANDLE, \
                       _hFileBase:PHANDLE
 mov    edx, _hFileBase
 invoke UnmapViewOfFile, [edx]
 mov    edx, _hFileMap
 invoke CloseHandle, [edx]
 mov    edx, _hFile
 invoke CloseHandle, [edx]
 ret
CloseAndUnmapFile endp
 
endif


; --------------------- dump.asm ---------------------

;
; HexDump utility written by Chris Vega.
;

;
; Copy both dump.asm and filemap.inc to bin directory of your
; MASM32 package, and execute these commands:
;
; ml /c /coff /Cp /nologo /I"..\include" dump.asm
; link /SUBSYSTEM:CONSOLE /OPT:NOREF dump.obj "..\lib\kernel32.lib"
; "..\lib\user32.lib" "..\lib\masm32.lib"
;

.386
.model flat, stdcall

include windows.inc

include kernel32.inc
includelib kernel32.lib

include user32.inc
includelib user32.lib

include masm32.inc
includelib masm32.lib

include filemap.inc

.data
 filename  db MAX_FNAME_LEN dup(0)
 fname_cnt dd 0
 linebytes db 17 dup(0)
 
 ; Strings
 dump000   db "usage: DUMP [filename]", 0ah, 0dh, 0
 dump001   db "DUMP has failed to open the specified file."
           db 0ah, 0dh, 0
 dump0x0   db "%08X: ", 0
 dump0x1   db "%02X ", 0
 dump0x2   db ": ", 0
 dump0x3   db ": %s", 0ah, 0dh, 0
 dump0x4   db ".. ", 0
 
.data?
 result    dd ?
 hFile     dd ?
 hFileMap  dd ?
 hFileBase dd ?
 
 buffer    db 128 dup(?)

.code
start:
 call   ProcessCommandLine
 cmp    fname_cnt, 0
 jnz    @_proceed
 
 ; Print usage
 invoke StdOut, addr dump000
 jz     @_exit
 
 @_proceed:
 
 ; Open and map the specified file
 invoke OpenAndMapFile, addr filename, \
        addr hFile, addr hFileMap, addr hFileBase
 mov    result, eax
 cmp    eax, SUCCESS
 jz     @_00
 
 ; Failed to open the file? Print 'open error' and exit
 invoke StdOut, offset dump001
 ret
 
 @_00:
 
 ; Get the file size and dump the file
 invoke GetFileSize, hFile, NULL
 push   eax
 push   hFileBase
 push   hFileBase
 call   HexDump

 invoke CloseAndUnmapFile, addr hFile, \
        addr hFileMap, addr hFileBase
 
 ; Exit the program
 @_exit:
 invoke ExitProcess, 0

; Procedure to extract the filename from the command line.
; This procedure is done for that purpose only, and not designed
; to be reusable.

ProcessCommandLine proc uses esi edi ecx
 mov    edi, offset filename
 mov    byte ptr [edi], 0
 xor    ecx, ecx
 invoke GetCommandLine
 
 ; Find the pointer to the first argument
 mov    esi, eax
 @_@:
 cmp    byte ptr [esi], 32
 je     @_0
 cmp    byte ptr [esi], 0
 je     @_cx
 inc    esi
 jmp    @_@
 @_0:
 inc    esi
 cmp    byte ptr [esi], 32
 jne    @__
 cmp    byte ptr [esi], 0
 je     @_cx
 jmp    @_0
 @__:
 
 ; Copy the first argument to the filename buffer
 cmp    byte ptr [esi], '"'
 je     @_cq
 
 ; Filename is not quoted
 @_c0:
 lodsb
 cmp    al, 32
 je     @_c2
 cmp    al, 0
 je     @_c2
 inc    ecx
 stosb
 jmp    @_c0
 
 ; Filename is quoted
 @_cq:
 inc    esi
 @_c1:
 cmp    byte ptr [esi], '"'
 je     @_c2
 cmp    byte ptr [esi], 0
 je     @_c2
 lodsb
 inc    ecx
 stosb
 jmp    @_c1
 
 ; Close the string
 @_c2:
 xor    eax, eax
 stosb
 
 ; Return to caller
 mov    fname_cnt, ecx
 @_cx:
 ret
ProcessCommandLine endp

; This is the main reusable hex dump procedure. Just specify the
; Base memory address, the memory address to dump and the
; count of bytes to dump, and everything will be done the the
; procedure, except that it doesn't check for read permission, so
; you have to manually capture that kind of error.

HexDump proc uses ebx edi esi base:DWORD, mem_ptr:DWORD, count:DWORD

 mov    byte ptr [linebytes + 16], 0
 mov    ebx, mem_ptr
 mov    edi, 0
 
 @_loop_ebx_lt_count:
 mov    ecx, mem_ptr
 add    ecx, count
 cmp    ebx, ecx
 jnb    @_exit_hexdump
 push   ebx
 
 cmp    edi, 0
 jnz    @_get_a_byte

 ; Print the address
 mov    eax, ebx
 sub    eax, base
 invoke wsprintf, addr buffer, addr dump0x0, eax
 invoke StdOut, addr buffer
 
 @_get_a_byte:
 ; Get a byte from pointer
 xor    eax, eax
 mov    al, byte ptr [ebx]
 
 ; Save it for later text printing, but remove all
 ; non printable characters by replacing them with
 ; a dot (.) character

 cmp    al, 255
 jz     @_non_printable
 cmp    al, 13
 jbe    @_non_printable
 
 mov    byte ptr [linebytes + edi], al
 jmp    @_print_the_byte
 
 @_non_printable:
 mov    byte ptr [linebytes + edi], '.'
 
 ; Print the byte
 @_print_the_byte:
 invoke wsprintf, addr buffer, addr dump0x1, eax
 invoke StdOut, addr buffer
 inc    edi
 
 ; Print 8th byte separator
 cmp    edi, 8
 jne    @_no_8th_byte_separator
 invoke StdOut, addr dump0x2
 @_no_8th_byte_separator:
 
 ; print end of line
 cmp    edi, 15
 jne    @_not_end_of_line
 invoke wsprintf, addr buffer, addr dump0x3, addr linebytes
 invoke StdOut, addr buffer
 mov    edi, 0
 @_not_end_of_line:
 
 ; This is similar to the entire procedure, except this
 ; part is especially crafted as the last line scenario
 ; where the loop is expected to be terminated after
 ; this last line printing

 cmp    edi, 0
 jbe    @_not_last_line
 mov    ecx, base
 add    ecx, count
 dec    ecx
 cmp    ebx, ecx
 jne    @_not_last_line
 
 ; Is this the last line?
 mov    ebx, edi
 inc    ebx
 @_loop_last_line:
 cmp    ebx, 16
 jnb    @_eloop_last_line
 
 invoke StdOut, addr dump0x4
 cmp    ebx, 8
 jne    @_il_no8th
 invoke StdOut, addr dump0x2
 @_il_no8th:
 
 inc    ebx
 jmp    @_loop_last_line
 @_eloop_last_line:
 
 mov    byte ptr [linebytes + edi], 0
 invoke wsprintf, addr buffer, addr dump0x3, addr linebytes
 invoke StdOut, addr buffer
 
 jmp    @_exit_hexdump
 @_not_last_line:
 
 pop    ebx
 inc    ebx
 jmp    @_loop_ebx_lt_count 
  
 @_exit_hexdump:
 ret
HexDump endp

end start

Posted by cvega with no comments
Filed under:

Visit NETFX3 community sites

If you've read my old post listing the community sites of WinFX, it seems that the sites are already being consolidated to the new official site, and it's called the NETFX3 community site.

You can find it here:
http://wcs.netfx3.com/

Or if you want, you can visit these subsites:

Windows Presentation Foundation: http://wpf.netfx3.com/
Windows Communication Foundation: http://wcf.netfx3.com/
Windows Workflow Foundation: http://wf.netfx3.com/
Windows CardSpace: http://wcs.netfx3.com/


\
-chris

Posted by cvega with no comments

Filemapping and a hex dump utility

Last time, I blogged about "how to reference function parameters to and from a function". This time I would like to demonstrate how to put something of that approach in action. I wrote two functions that will allow you to use file mapping in less C/C++ codes. In order for you to understand how the function uses the parameters as its referenced variables, I suggest read my other post about it.

File mapping functions:
Here are the two functions I created, see below for instructions on using them in your application, and download the demo application dump.exe which is a hex dump utility, attached in this post.

The functions:

HRESULT OpenAndMapFile(
                LPCSTR lpszFilename, 
                PHANDLE _hFile, 
                PHANDLE _hFileMap, 
                PHANDLE _hFileBase)
{
    int dwFileSize;

    // Open the specified file
    if ((*_hFile = CreateFile(
        (char*)lpszFilename, 
        OPEN_EXISTING, 
        FILE_SHARE_READ, 
        NULL, 
        OPEN_EXISTING, 
        FILE_ATTRIBUTE_NORMAL,
        NULL )) != INVALID_HANDLE_VALUE)
    {
        // Create FileMapping object for the file
        dwFileSize = GetFileSize(*_hFile, NULL);

        if ((*_hFileMap = CreateFileMapping(
            *_hFile,
            NULL,
            PAGE_READONLY,
            0,
            dwFileSize,
            NULL)) != INVALID_HANDLE_VALUE)
        {
            // Map the file to the process memory
            if ((*_hFileBase = MapViewOfFile(
                *_hFileMap, 
                GENERIC_READ, 
                0,
                0,
                0)) != INVALID_HANDLE_VALUE)
            {
                return FILEMAPPING_SUCCEEDED;
            }
            else
            {
                CloseHandle(*_hFileMap);
                CloseHandle(*_hFile);
                return ERROR_MAPVIEW_FAILED;
            }
        }
        else
        {
            CloseHandle(*_hFile);
            return ERROR_CREATEMAP_FAILED;
        }
    }

    return ERROR_OPENFILE_FAILED;
}

void CloseAndUnmapFile(
        PHANDLE _hFile, 
        PHANDLE _hFileMap, 
        PHANDLE _hFileBase)
{
    UnmapViewOfFile(*_hFileBase);
    CloseHandle(*_hFileMap);
    CloseHandle(*_hFile);
}


Dry Documentation:
The OpenAndMapFile function will try to open the file, create mapping object if file was successfully opened, and create a view then sends back all the handles for the opened file, created mapping object and created view. If the function is not successful, the result will be non-zero and can be interpreted as one of the following errors:

#define ERROR_OPENFILE_FAILED     1
#define ERROR_CREATEMAP_FAILED    2
#define ERROR_MAPVIEW_FAILED      3

There are many reasons why the function will fail, like for example, the function is trying to open file that does not exist, or the process has insufficient access rights to open the file and so on. For details on all the other possible errors that may happen during the process of file mapping, I suggest to read the documentations in MSDN for the following functions:

CreateFile
CreateFileMapping
MapViewOfFile

Also, when the function has failed, there will be no guarantee that the values pointer by parameters _hFile, _hFileMap and _hFileBase are not altered.

If the function is successful, the return will be zero, and all the handles will be saved to the memory location pointed by the parameters.

OpenAndMapFile function parameters are:

lpszFilename
Pointer the null terminated string that contains the filename of the file to open and map.

_hFile
Pointer to a HANDLE where to save the handle of the file opened.

_hFileMap
Pointers to a HANDLE where to save the created mapping object.

_hFileBase
Pointer to a HANDLE where to save the base address of the viewed file.

The other function CloseAndUnmapFile will try to close all the handles opened by the function OpenAndMapFile, and the parameters must be in the same order when you opened it, for closing to be successful. There will be no return value, since if the closing succeeded or failed, the values pointed by the parameters will be modified.

CloseAndUnmapFile function parameters are:

_hFile
Pointer to a opened file HANDLE to be closed.

_hFileMap
Pointers to a HANDLE of created mapping object to be destroyed.

_hFileBase
Pointer to a HANDLE of the view, in which will be unviewed.

Those are the quick and rough descriptions of the two functions for file mapping. If you understand how file mapping works and you've read the codes, you can notice it pretty quickly that the functions are designed for read-only access. I've attached the header file filemap.h where I wrote the two functions, and provided set of defines to help you customize the functions easily, or feel free to modify it to suit your needs.

Dump.EXE (Hex Dump Utility):
Now to put the functions in action, I wrote a program called dump.exe, a C utility program to be used in dumping any file into hexadecimal (similar to how hex-editor presents files in hex). The utility dump.exe was written way back when I was studying various file formats (I can’t afford to buy costly hex-editors back then, so I wrote something I can use).

Here’s the source code for the main routine (download the attachment for the complete source code):

//
// HexDump utility written by Chris Vega.
// This source code is downloaded from Chris Vega's technical blog
//


#include <stdio.h>
#include <windows.h>
#include "filemap.h"

int main(int argc, char* argv[])
{
    HRESULT result;
    HANDLE hFile, hFileMap, hFileBase;
    PBYTE pbase;
    int fSize, index, linecount = 0;
    BYTE linebytes[17];

    if (argc>=1)
    {
        if ((result = OpenAndMapFile(
                argv[1], 
                &hFile, 
                &hFileMap, 
                &hFileBase)) == SUCCESS)
        {
            linebytes[16] = 0;
            pbase = (PBYTE)hFileBase;
            fSize = GetFileSize(hFile, NULL);

            // Crawl through the bytes
            for(index=0; index<fSize; index++)
            {
                if (linecount==0)
                {
                    // Print the file offset
                    printf("%08X: "
                        pbase – (DWORD_PTR)hFileBase);
                }

                // Print the byte
                printf("%02X "
                        linebytes[linecount] = *pbase++);

                // Print 8th byte separator
                if (linecount == 8) printf(": ");

                // Replace non-printable characters
                if (linebytes[linecount]<=13 ||
                    linebytes[linecount]==255 ||
                    linebytes[linecount]=='\t')
                    linebytes[linecount] = '.';

                if (++linecount>15)
                {
                    // Proceed to the next line
                    printf(": %s\n", linebytes);
                    linecount = 0;
                }

                // Are we at the last line?
                if (index==fSize-1)
                {
                    // Print the final line of bytes

                    for(index=linecount; index<16; index++)
                    {
                        printf(".. ");
                        if (index == 8) printf(": ");
                    }
                    linebytes[linecount+1] = 0;
                    printf(": %s\n", linebytes);
                    break;
                }
            }
        }
        else printf("DUMP has failed to open "
                    "the specified file.\n");
        return result;
    }
    else return 1;
}


The source itself lacks comments, though I trust that most of its parts are self-explanatory.


Screenshots:

c:\>dump dump.exe




c:\>dump c:\windows\system32\kernel32.dll > kernel32.txt
c:\>notepad kernel32.txt




Download the attached file dump.zip for the source code, and project files for both DevC++ and Visual C++. The source code is written in C, and should be compatible with any C compilers capable of producing Win32 executable.

If you can't view the screenshots above, you can see a sample output below. This is the output I got when I dumped dump.c from the source code:

00000000: 2F 2F 0D 0A 2F 2F 20 48 65 : 78 44 75 6D 70 20 75 : //..// HexDump u
00000010: 74 69 6C 69 74 79 20 77 72 : 69 74 74 65 6E 20 62 : tility written b
00000020: 79 20 43 68 72 69 73 20 56 : 65 67 61 2E 0D 0A 2F : y Chris Vega.../
00000030: 2F 20 54 68 69 73 20 73 6F : 75 72 63 65 20 63 6F : / This source co
00000040: 64 65 20 69 73 20 64 6F 77 : 6E 6C 6F 61 64 65 64 : de is downloaded
00000050: 20 66 72 6F 6D 20 43 68 72 : 69 73 20 56 65 67 61 :  from Chris Vega
00000060: 27 73 20 74 65 63 68 6E 69 : 63 61 6C 20 62 6C 6F : 's technical blo
00000070: 67 0D 0A 2F 2F 0D 0A 0D 0A : 2F 2F 0D 0A 2F 2F 20 : g..//....//..//
00000080: 43 6F 6D 70 69 6C 65 20 77 : 69 74 68 20 47 47 43 : Compile with GGC
00000090: 3A 20 67 63 63 20 64 75 6D : 70 2E 63 20 2D 6F 20 : : gcc dump.c -o
000000A0: 64 75 6D 70 2E 65 78 65 0D : 0A 2F 2F 0D 0A 0D 0A : dump.exe..//....
000000B0: 23 69 6E 63 6C 75 64 65 20 : 3C 73 74 64 69 6F 2E : #include <stdio.
000000C0: 68 3E 0D 0A 23 69 6E 63 6C : 75 64 65 20 3C 77 69 : h>..#include <wi
000000D0: 6E 64 6F 77 73 2E 68 3E 0D : 0A 23 69 6E 63 6C 75 : ndows.h>..#inclu
000000E0: 64 65 20 22 66 69 6C 65 6D : 61 70 2E 68 22 0D 0A : de "filemap.h"..
000000F0: 0D 0A 69 6E 74 20 5F 5F 63 : 64 65 63 6C 20 6D 61 : ..int __cdecl ma
00000100: 69 6E 28 69 6E 74 20 61 72 : 67 63 2C 20 63 68 61 : in(int argc, cha
00000110: 72 2A 20 61 72 67 76 5B 5D : 29 0D 0A 7B 0D 0A 09 : r* argv[])..{...
00000120: 48 52 45 53 55 4C 54 20 72 : 65 73 75 6C 74 3B 0D : HRESULT result;.
00000130: 0A 09 48 41 4E 44 4C 45 20 : 68 46 69 6C 65 2C 20 : ..HANDLE hFile,
00000140: 68 46 69 6C 65 4D 61 70 2C : 20 68 46 69 6C 65 42 : hFileMap, hFileB
00000150: 61 73 65 3B 0D 0A 09 50 42 : 59 54 45 20 70 62 61 : ase;...PBYTE pba
00000160: 73 65 3B 0D 0A 09 69 6E 74 : 20 66 53 69 7A 65 2C : se;...int fSize,
00000170: 20 69 6E 64 65 78 2C 20 6C : 69 6E 65 63 6F 75 6E :  index, linecoun
00000180: 74 20 3D 20 30 3B 0D 0A 09 : 42 59 54 45 20 6C 69 : t = 0;...BYTE li
00000190: 6E 65 62 79 74 65 73 5B 31 : 37 5D 3B 0D 0A 0D 0A : nebytes[17];....
000001A0: 09 69 66 20 28 61 72 67 63 : 3E 31 29 0D 0A 09 7B : .if (argc>1)...{
000001B0: 0D 0A 09 09 69 66 20 28 28 : 72 65 73 75 6C 74 20 : ....if ((result
000001C0: 3D 20 4F 70 65 6E 41 6E 64 : 4D 61 70 46 69 6C 65 : = OpenAndMapFile
000001D0: 28 61 72 67 76 5B 31 5D 2C : 20 26 68 46 69 6C 65 : (argv[1], &hFile
000001E0: 2C 20 26 68 46 69 6C 65 4D : 61 70 2C 20 26 68 46 : , &hFileMap, &hF
000001F0: 69 6C 65 42 61 73 65 29 29 : 20 3D 3D 20 53 55 43 : ileBase)) == SUC
00000200: 43 45 53 53 29 0D 0A 09 09 : 7B 0D 0A 09 09 09 6C : CESS)....{.....l
00000210: 69 6E 65 62 79 74 65 73 5B : 31 36 5D 20 3D 20 30 : inebytes[16] = 0
00000220: 3B 0D 0A 09 09 09 70 62 61 : 73 65 20 3D 20 28 50 : ;.....pbase = (P
00000230: 42 59 54 45 29 68 46 69 6C : 65 42 61 73 65 3B 0D : BYTE)hFileBase;.
00000240: 0A 09 09 09 66 53 69 7A 65 : 20 3D 20 47 65 74 46 : ....fSize = GetF
00000250: 69 6C 65 53 69 7A 65 28 68 : 46 69 6C 65 2C 20 4E : ileSize(hFile, N
00000260: 55 4C 4C 29 3B 0D 0A 09 09 : 09 66 6F 72 28 69 6E : ULL);.....for(in
00000270: 64 65 78 3D 30 3B 20 69 6E : 64 65 78 3C 66 53 69 : dex=0; index<fSi
00000280: 7A 65 3B 20 69 6E 64 65 78 : 2B 2B 29 0D 0A 09 09 : ze; index++)....
00000290: 09 7B 0D 0A 09 09 09 09 69 : 66 20 28 6C 69 6E 65 : .{......if (line
000002A0: 63 6F 75 6E 74 3D 3D 30 29 : 0D 0A 09 09 09 09 7B : count==0)......{
000002B0: 0D 0A 09 09 09 09 09 70 72 : 69 6E 74 66 28 22 25 : .......printf("%
000002C0: 30 38 58 3A 20 22 2C 20 70 : 62 61 73 65 20 2D 20 : 08X: ", pbase -
000002D0: 28 44 57 4F 52 44 5F 50 54 : 52 29 68 46 69 6C 65 : (DWORD_PTR)hFile
000002E0: 42 61 73 65 29 3B 0D 0A 09 : 09 09 09 7D 0D 0A 0D : Base);......}...
000002F0: 0A 09 09 09 09 70 72 69 6E : 74 66 28 22 25 30 32 : .....printf("%02
00000300: 58 20 22 2C 20 6C 69 6E 65 : 62 79 74 65 73 5B 6C : X ", linebytes[l
00000310: 69 6E 65 63 6F 75 6E 74 5D : 20 3D 20 2A 70 62 61 : inecount] = *pba
00000320: 73 65 2B 2B 29 3B 0D 0A 09 : 09 09 09 69 66 20 28 : se++);......if (
00000330: 6C 69 6E 65 63 6F 75 6E 74 : 20 3D 3D 20 38 29 20 : linecount == 8)
00000340: 70 72 69 6E 74 66 28 22 3A : 20 22 29 3B 0D 0A 0D : printf(": ");...
00000350: 0A 09 09 09 09 69 66 20 28 : 6C 69 6E 65 62 79 74 : .....if (linebyt
00000360: 65 73 5B 6C 69 6E 65 63 6F : 75 6E 74 5D 3C 3D 31 : es[linecount]<=1
00000370: 33 20 7C 7C 0D 0A 09 09 09 : 09 09 6C 69 6E 65 62 : 3 ||.......lineb
00000380: 79 74 65 73 5B 6C 69 6E 65 : 63 6F 75 6E 74 5D 3D : ytes[linecount]=
00000390: 3D 32 35 35 20 7C 7C 0D 0A : 09 09 09 09 09 6C 69 : =255 ||.......li
000003A0: 6E 65 62 79 74 65 73 5B 6C : 69 6E 65 63 6F 75 6E : nebytes[linecoun
000003B0: 74 5D 3D 3D 27 5C 74 27 29 : 0D 0A 09 09 09 09 09 : t]=='\t').......
000003C0: 6C 69 6E 65 62 79 74 65 73 : 5B 6C 69 6E 65 63 6F : linebytes[lineco
000003D0: 75 6E 74 5D 20 3D 20 27 2E : 27 3B 0D 0A 0D 0A 09 : unt] = '.';.....
000003E0: 09 09 09 69 66 20 28 2B 2B : 6C 69 6E 65 63 6F 75 : ...if (++linecou
000003F0: 6E 74 3E 31 35 29 0D 0A 09 : 09 09 09 7B 0D 0A 09 : nt>15)......{...
00000400: 09 09 09 09 70 72 69 6E 74 : 66 28 22 3A 20 25 73 : ....printf(": %s
00000410: 5C 6E 22 2C 20 6C 69 6E 65 : 62 79 74 65 73 29 3B : \n", linebytes);
00000420: 0D 0A 09 09 09 09 09 6C 69 : 6E 65 63 6F 75 6E 74 : .......linecount
00000430: 20 3D 20 30 3B 0D 0A 09 09 : 09 09 7D 0D 0A 0D 0A :  = 0;......}....
00000440: 09 09 09 09 69 66 20 28 69 : 6E 64 65 78 3D 3D 66 : ....if (index==f
00000450: 53 69 7A 65 2D 31 20 26 26 : 20 6C 69 6E 65 63 6F : Size-1 && lineco
00000460: 75 6E 74 3E 30 29 0D 0A 09 : 09 09 09 7B 0D 0A 09 : unt>0)......{...
00000470: 09 09 09 09 66 6F 72 28 69 : 6E 64 65 78 3D 6C 69 : ....for(index=li
00000480: 6E 65 63 6F 75 6E 74 3B 20 : 69 6E 64 65 78 3C 31 : necount; index<1
00000490: 36 3B 20 69 6E 64 65 78 2B : 2B 29 0D 0A 09 09 09 : 6; index++).....
000004A0: 09 09 7B 0D 0A 09 09 09 09 : 09 09 70 72 69 6E 74 : ..{........print
000004B0: 66 28 22 2E 2E 20 22 29 3B : 0D 0A 09 09 09 09 09 : f(".. ");.......
000004C0: 09 69 66 20 28 69 6E 64 65 : 78 20 3D 3D 20 38 29 : .if (index == 8)
000004D0: 20 70 72 69 6E 74 66 28 22 : 3A 20 22 29 3B 0D 0A :  printf(": ");..
000004E0: 09 09 09 09 09 7D 0D 0A 09 : 09 09 09 09 6C 69 6E : .....}.......lin
000004F0: 65 62 79 74 65 73 5B 6C 69 : 6E 65 63 6F 75 6E 74 : ebytes[linecount
00000500: 2B 31 5D 20 3D 20 30 3B 0D : 0A 09 09 09 09 09 70 : +1] = 0;.......p
00000510: 72 69 6E 74 66 28 22 3A 20 : 25 73 5C 6E 22 2C 20 : rintf(": %s\n",
00000520: 6C 69 6E 65 62 79 74 65 73 : 29 3B 0D 0A 09 09 09 : linebytes);.....
00000530: 09 09 62 72 65 61 6B 3B 0D : 0A 09 09 09 09 7D 0D : ..break;......}.
00000540: 0A 09 09 09 7D 0D 0A 09 09 : 7D 0D 0A 09 09 65 6C : ....}....}....el
00000550: 73 65 20 70 72 69 6E 74 66 : 28 22 44 55 4D 50 20 : se printf("DUMP
00000560: 68 61 73 20 66 61 69 6C 65 : 64 20 74 6F 20 6F 70 : has failed to op
00000570: 65 6E 20 74 68 65 20 73 70 : 65 63 69 66 69 65 64 : en the specified
00000580: 20 66 69 6C 65 2E 5C 6E 22 : 29 3B 0D 0A 09 09 72 :  file.\n");....r
00000590: 65 74 75 72 6E 20 72 65 73 : 75 6C 74 3B 0D 0A 09 : eturn result;...
000005A0: 7D 0D 0A 09 65 6C 73 65 20 : 0D 0A 20 09 7B 0D 0A : }...else .. .{..
000005B0: 20 09 09 70 72 69 6E 74 66 : 28 22 75 73 61 67 65 :  ..printf("usage
000005C0: 3A 20 44 55 4D 50 20 5B 66 : 69 6C 65 6E 61 6D 65 : : DUMP [filename
000005D0: 5D 5C 6E 22 29 3B 0D 0A 20 : 20 09 09 72 65 74 75 : ]\n");..  ..retu
000005E0: 72 6E 20 31 3B 0D 0A 09 7D : 0D 0A 7D 0D 0A .. .. : rn 1;...}..}..t


Disclaimer:
Although the utility dump.exe will open the file in read-only mode, and never write anything back to the file being hex-dumped; I would like to disclaim any possible damage that the utility may cause due its misuse.

Posted by cvega with 3 comment(s)
Filed under: ,

Returning function result to parameter

Writing a function with a pass-by-reference parameter that will be used to receive the result of the function's execution is a good practice in functions that returns status-code while the actual output is placed in the out parameter(s):

Here's an example:

int getvalue(int valueKind, char** retValue)
{
 switch(valueKind)
 {
 case 0:
  *value = "Hello World";
  return 1;
 case 1:
  *value = "Wakeup";
  return 1;
 case 2:
  *value = "Something's up?";
  return 1;
 case 3:
  *value = "Goodbye World";
  return 1;
 }
 return 0; // status = failed
}


The function above performs a very simple task -- to evaluate the passed valueKind and return the matching char* data back to parameter, and return zero to indicate failure or non-zero for success.

The caller of the function can benefit from this approach, as it can treat the call to the function on a conditional expression, while retrieving the actual char* back to passed variable. The call to the function may look like:

char* retValue;

if (getvalue(0, &retValue))
{
 printf("getvalue(0,..) returned '%s'\n", retValue);
}

And here's a main() function for the test:

int main()
{
 char* retValue;
 int i;

 for(i=0;;i++)
 {
  if (getvalue(i, &retValue))
  {
   printf("getvalue(%d,..) returns '%s'\n", i, retValue);
  }
  else
  {
   printf("getvalue(%d,..) has failed\n\n", i);
   break;
  }
 }

 return 0;
}

Simple isn't? That because function is very simple. Now let's add a little complexity by passing a result that the type is known during runtime, an integer, a float, a char*, a structured data or even an object's instance. This approach can be achieve using casting technique known as pointer reinterpretation, a low-level casting of one typed pointer into another typed pointer, hence reinterpreting of pointer. The prototype below uses void* pointer to address changing types as any pointer type, and we will going to reinterpret the result we want back to the caller. The new getvalue function now look something like the prototype:

int getvalue(int valueKind, void* retValue)
{
 switch(valueKind)
 {
 case 0:
  // return an integer
  return 1;
 case 1:
  // return a float back
  return 1;
 case 2:
  // return a string back
  return 1;
 case 3:
  // return a double back
  return 1;
 }
 return 0; // status = failed
}


Notice in the prototype above, the valueKind parameter has another role, it is also used to tell the type of what we want our function getvalue to return. If we pass 0, then we expect to receive an integer result, and if we pass 1, we expect to receive float result, and so on.

Most in the prototype is fairly done, and the only thing left is how to perform the casting. C++ has the reinterpret_cast typecast operator that is very handy for what we want to achieve:

   reinterpret_cast<type>(expression);

But in C, we only have the standard casting method which is one of two ways:

   (type)expression;

or

   type(expression);

A quick workaround to support both casting methods is to use macro, which determine what casting method will be used by checking __cplusplus symbol internally defined by most C++ compilers:

#ifdef __cplusplus
 #define reinterpret_cast(type, expression) \
reinterpret_cast<type>(expression)
#else
 #define reinterpret_cast(type, expression) \
((type)(BLOCKED EXPRESSION
#endif


Note that this is to support both methods, although we can actually just use the C standard casting, because C++ is supporting both the C casting and its native typecast operators.

Using the macro above -- if we want to return a integer pointer back to void pointer, we can call it this way:

int someIntegerValue = 1001;
*(reinterpret_cast(int*, void_pointer)) = someIntegerValue;

or

*(reinterpret_cast(int*, void_pointer)) = 1001;

Quite simple a call. The macro will perform the casting for us. 
Now to complete our getvalue function:

int getvalue(int valueKind, void* retValue)
{
 switch(valueKind)
 {
 case 0:
  // return an integer
  *(reinterpret_cast(int*, retValue)) = 1001;
  return 1;
 case 1:
  // return a float back
  *(reinterpret_cast(float*, retValue)) = 3.1415926F;
  return 1;
 case 2:
  // return a string back
  *(reinterpret_cast(char**, retValue)) = "Hello World";
  return 1;
 case 3:
  // return a double back
*(reinterpret_cast(double*, retValue)) = 3.1415926535897931;
  return 1;
 }
 return 0;
}


The general idea is to get pass the compiler's safe type checking for the function to be able to return a value of varying type to its parameter (considered as unsafe). To test the function, here is the main() function that will call the above function to retrieve value and print it to the screen:

int main()
{
 // locals
 int myInt;
 float myFloat;
 char* myString;
 double myDouble;

 // Get an integer
 if (getvalue(0, &myInt))
 {
  printf("getvalue returned: %d\n", myInt);
 }

 // Get a float
 if (getvalue(1, &myFloat))
 {
  printf("getvalue returned: %f\n", myFloat);
 }

 // Get a string
 if (getvalue(2, &myString))
 {
  printf("getvalue returned: %s\n", myString);
 }

 // Get a double
 if (getvalue(3, &myDouble))
 {
  printf("getvalue returned: %.16f\n", myDouble);
 }
}


For complete example, you can download the attachment pass_ptr.ZIP.

Posted by cvega with 1 comment(s)
Filed under: ,

Using preprocessor directives in C#

As I write an answer to a question posted in the local Microsoft Community forum (http://msforums.ph/forums) asking "How to programmatically create and share a folder" the #define preprocessor directive has come in handy when providing an answer that can be done in different ways. With regard to the question, we can share a folder in .NET in either using the Win32 API, using the command line or use the Windows Management Instrumentation (WMI), hence I used of #if/#else/#endif directives on my posted example code.

Preprocessor directives are common to all C-family programming languages, and C# also has rich support over those preprocessor directives which is largely documented in MSDN site:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vclrfpreprocessordirectives.asp

Note that the #define preprocessor directive in C# is different than in C/C++, the C#'s #define would simply let you define a symbol. Also, there's no need to give any value to the symbol since the defined symbol will be used by its name. C# has no #ifdef/#ifndef directives to check whether the symbol is defined or not, since you use #if/#else/#elif/#endif already for that particular purpose. Here's an example:

   1:  #define _DEBUG
   2:
   3:  using System;
   4:  using System.Windows.Forms;
   5:
   6:  public class UtilityClass
   7:  {
   8:      public static void DoSomething()
   9:      {
  10:          try
  11:          {
  12:              // ..do something here...
  13:          }
  14:          catch
  15:          {
  16:              // ... log the exception
  17:          #if _DEBUG
  18:          MessageBox.Show("Something happened chief! check the logs.");
  19:          #endif
  20:          }
  21:      }
  22:  }

The code above might have probably given you an idea how useful #define is to your code, it can be used to mark your project between the debug mode and behave differently when it was released (by removing the #define DEBUG line, or set the /define in the compiler option).

Here's an example of using #define to mark different ways of doing something:

   1:  #define OPTION_2
   2:  
   3:  using System;
   4:  using System.Windows.Forms;
   5:  
   6:  public class UtilityClass
   7:  {
   8:      public static void DoSomething()
   9:      {
  10:          #if OPTION_1
  11:                // ... do option 1 for DoSomehing();
  12:          #elif OPTION_2
  13:                // ... do option 2 for DoSomehing();
  14:          #else
  15:                // ... do default option for DoSomehing();
  16:          #endif
  17:      }
  18:  }


Which is simplier than actually commenting piece of code from time to time just to choose which code option will be used for a project (believe me, there are coders who does commenting code, I've seen it done number of times, and I keep on telling them about preprosessor directives).

That's for now, hopefully message has reached you -- #region/#endregion are not the only preprossesor directive for C#, and are actually the least useful directives in my opinion.

Check the documentation for more.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vclrfpreprocessordirectives.asp

Posted by cvega with 1 comment(s)
Filed under:

Get the list of country names from the system registry

One of commonly asked questions in online communities I am participating, when talking about data, is where to get the list of countries. Not that it's commonly asked question, but at least I've seen it asked for more than 20 times already -- an example is in MSDN community forums:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=390606&SiteID=1

Which I directed the guy to download country list in SQL format, since he is asking for an internet location (I assumed he is asking for downloadable location). That is one solution for the question, all he need is run the SQL script against his database and he is done -- but there is another solution that is available if you are working off-line, and it is found in the system registry. This is the list of countries that is stored in the registry by the Telephony component of your operating system, you can find it in this key:

HKEY_LOCAL_MACHINE\
     Software\
          Microsoft\
               Windows\
                    CurrentVersion\
                         Telephony\
                              Country List


The registry key above has the list of countries you willl need, and each country is stored in its registry subkey named after the country code.

You can get this list in .NET Framework by creating a simple registry key enumerator, and .NET has classes that we can use under Microsoft.Win32 namespace. You can read the MSDN article about the registry to learn how to do this exactly:
http://msdn2.microsoft.com/en-us/microsoft.win32.registrykey.aspx

We don’t have to write anything to the registry, we simply need to read from it, using Registry.OpenSubkey() method. I wrote a small WinForms program that will show you how to do this; you can get from this blog entry’s attachment. The piece of code where I did the enumeration is here (for C# and Managed C++):

// C#
  private void GetCountries()
  {
   RegistryKey countries =
    Registry.LocalMachine.OpenSubKey(
     "Software\\Microsoft\\Windows\\CurrentVersion" +
     "\\Telephony\\Country List");

   if ( countries!=null )
   {
    // Clear the list
    lvCountries.Items.Clear();

    // Get the list of subkeys,
    // which is actually the country code

    string[] subkeys = countries.GetSubKeyNames();

    foreach(string ccode in subkeys)
    {
     // Open the subkey, to get the country name
     RegistryKey country = countries.OpenSubKey(ccode);

     // ..get the country name from country key
     string countryName = country.GetValue("Name").ToString();
     // .. save it wherever you like

     // We're done using the key, we should close it
     country.Close();

    }

    // It's now safe to close the country list key
    countries.Close();
   }
  }


// Managed C++
private:
void
GetCountries()
  {
   RegistryKey __gc* countries =
    Registry::LocalMachine->OpenSubKey(
     S"Software\\Microsoft\\Windows\\CurrentVersion\\"
     S"Telephony\\Country List"
);

   if ( countries!=NULL )
   {
    // Clear the list
    lvCountries->Items->Clear();

    // Get the list of subkeys, which is actually the country code
    System::String __gc* subkeys __gc[] = countries->GetSubKeyNames();

    for(int i=0;i<subkeys->Count;i++)
    {
     // Open the subkey, to get the country name
     RegistryKey __gc* country = countries->OpenSubKey(subkeys[ i ]);

     // ..get the country name from country key
     String __gc* countryName =
          country->GetValue(S"Name")->ToString();
     // .. save it wherever you like

     // We're done using the key, we should close it
     country->Close();

    }

    // It's now safe to close the country list key
    countries->Close();
   }
  }

For complete project (source included), I've attached Countries.ZIP file, where it contains project files for C# and Managed C++. Download the attachment.

Happy coding!

-chris

Posted by cvega with 1 comment(s)
Filed under: