Richard's profileRichard RudekPhotosBlog Tools Help

Blog


    December 31

    The symbols you have when you don’t have symbols

    I had a problem the other day where I could not set any breakpoints on code which, at first glance, appeared to have the appropriate debugging symbols. It was especially annoying when I recompiled the code, yet could still not set any breakpoints on the code I just compiled. I’ll first show how I confirmed the cause of the problem, then I’ll show this can be reproduced.

    The cause is actually quite simple – it really didn’t have the required symbols, despite the fact that the PDB matched the DLL in question. For example, here’s what my reproduced sample, PdbTest01, does during a debugging session in Visual Studio 2005sp1/vista (an executable rather than a DLL, but the problem manifests the same way):

    PdbTest01_SymbolsLoaded

    PdbTest01_BreakPointNotHit




    Here’s what happens using WinDBG:

    PdbTest01, WinDbg breakpoint, missing symbols 01

    PdbTest01, WinDbg breakpoint, missing symbols 02

    Yet the symbols also appear to be loaded:

    0:000> lmm pdbtest01
    start    end        module name
    00400000 00405000   PdbTest01   (private pdb symbols)  d:\symbols\PdbTest01.pdb\...\PdbTest01.pdb
    



    Investigating

    What we need is a tool that lets us examine what’s actually in the PDB. Thankfully, a free tool for this already exists – the Debugging Tools for Windows includes a utility named DBH, and it’s elines command can enumerate all of the source-code lines contained within the symbols file. It’s a command-line tool, and here is a portion of it’s output when run against the appropriate PDB:

    C:\Users\Richard>dbh D:\DEVELOP\Tinkering\release\PdbTest01.pdb
    
    PdbTest01 [1000000]: elines
    
    OBJ:.\release\PdbTest01_Lib.obj
       d:\develop\tinkering\pdbtest01_lib\pdbtest01_lib.cpp
          24 25 26
    ...

    Now I suppose I should first explain that the executable being examined here (PdbTest01.exe) is a Windows Console-mode program, and it’s built from a ‘main’ source file (PdbTest01.cpp), and a static Library (PdbTest01_Lib.LIB). See the next section for the details.

    Clearly from the output of the elines command shown above, there’s only one (user-supplied) object file that contains (debugging) source lines, and this object file came from the static Library. That is, you can see that there is a pdbTest01_Lib.obj, which was built from a pdbtest01_lib.cpp source file, and we have lines 24, 25 and 26 from that cpp file. We do not have any source lines from the PdbTest01.cpp (no _Lib in the name). That’s the clear indicator. Simple, eh ?

    NOTE: Although I’m using version 6.10.3.233 of the Debugging Tools for Windows, be aware that this version has a C-String termination issue related to the Symbols API’s. As the system I’m writing this up on is not one that I would normally use in production, I’m not too concerned about this problem, which typically manifests itself as ‘garbled’ stack traces in third-party tools such as Process Explorer, etc. One of the reasons for doing this version, though, is so that I can use the updated documentation that shipped with this version.



    Reproducing

    I first stumbled across this odd situation whilst working on some old source code using Microsoft Visual Studio 6.0. But for the reproduction, here, I’m going to use Visual Studio 2005sp1/vista.

    The trick here is that we need to produce at least two object files, but only one of those, the static library, will be built with symbols. The object file for the main executable is configured not to produce symbols (debug Info).

    Add two C++ projects to a solution: a Windows Console app and a static library.

    The static library consists of two files (well four if you count the pre-compiled header stuff): a library header and a cpp file. Here is the source for the header file, PdbTest01_Lib.h (single line):

    int Func1();
    

    and here’s the source for the static library’s cpp file, PdbTest01_Lib.cpp:

    #include "stdafx.h"
    #include "PdbTest01_Lib.h"
    
    int Func1()
    {
        return 1;
    }

    Now here’s the source for the main executable, PdbTest.cpp:

    // PdbTest01.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include <conio.h>
    #include "..\PdbTest01_Lib\PdbTest01_Lib.h"
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        int n = Func1();
        printf("Func1(): %d\n", n);
    
        getchar();
        return 0;
    }

    For the main executable’s project, I also check that it has a dependency upon the static library:

    PdbTest01, VS2005 Project dependencies 01    PdbTest01, VS2005 Project dependencies 02

    Finally, edit the main executable’s project properties. Under C/C++, General, set the Debug Information Format to ‘Disabled’:

    PdbTest01, VS2005 Project properties, Cpp, General, Debug Info Disabled

    That’s pretty much it.

    Here’s the output of a Rebuild of the main executable:

    1>------ Rebuild All started: Project: PdbTest01_Lib, Configuration: Release Win32 ------
    1>Deleting intermediate and output files for project 'PdbTest01_Lib', configuration 'Release|Win32'
    1>Compiling...
    1>stdafx.cpp
    1>Compiling...
    1>PdbTest01_Lib.cpp
    1>Creating library...
    1>Build log was saved at "file://d:\DEVELOP\Tinkering\PdbTest01_Lib\Release\BuildLog.htm"
    1>PdbTest01_Lib - 0 error(s), 0 warning(s)
    2>------ Rebuild All started: Project: PdbTest01, Configuration: Release Win32 ------
    2>Deleting intermediate and output files for project 'PdbTest01', configuration 'Release|Win32'
    2>Compiling...
    2>stdafx.cpp
    2>Compiling...
    2>PdbTest01.cpp
    2>Linking...
    2>Generating code
    2>Finished generating code
    2>Embedding manifest...
    2>Build log was saved at "file://d:\DEVELOP\Tinkering\PdbTest01\Release\BuildLog.htm"
    2>PdbTest01 - 0 error(s), 0 warning(s)
    ========== Rebuild All: 2 succeeded, 0 failed, 0 skipped ==========
    
    September 28

    Enabling and Disabling a Network Adapter in Window XP


    There are a number of ways to Enable or Disable a Network Adapter in Windows, both via the User Interface (UI) and via the various Windows Programming Interfaces. For this short entry, I’ll stick to just using the UI. But as you’ll see, there are still a lot of ways…

    The quickest method to Enable or Disable a Network Adapter in Windows, other than simply unplugging the cable, is via the Network Properties. Here’s a quick Screencast:

     

    The easiest is by Right-Clicking the Network Connections Icon, and choosing Properties from the context menu.

    You can then select and/or Right-Click the entry for the particular Network Adapter you want to change, then choose the action from the Context Menu. eg Enable or Disable.




    You can also do this via Device Manager.

    The advantage of using Device Manager is that once you know how to Device Manager, you can do more than simply Enabling or Disabling the Network Adapter. For example, enable things like Wake On LAN, etc.

    But anyway, here are the equivalent Enabling and Disabling actions using Device Manager:

     
    April 13

    Tinkering with STL #2 - Functors and examining the compiler output


    In Tinkering with STL #1, I briefly described what the C++ Standard Template Library's Functors and Binders were for. Today, I'm going to show what the compiler does when faced with this with this type of source code.

    All code show here will be compiled using the Professional version of Visual Studio 2005 with Service Pack 1 applied. Compiler version 14.00.50727.762.

    1a. Static parameters passed to a Functor

    In the following code snippet, the integers a and b are statically set to 2 and 1, respectively:

      // 1a. Functor.
      int a=2, b=1;
      if ( std::greater<int>()(a, b) )
        cout << "a is Greater then b." << endl;
    

    So at compile-time, the compiler should be able able to deduce that the condition (whether a is greater than b) is not going to change at runtime, and hopefully eliminate any conditional checks.

    Let's think about that for a minute. The code appears to be making a call to a greater function, and yet I'm expecting the compiler to reason about this call ; that greater is functional - it's result is conditional only upon it's parameters, and that if it's parameters are static, the result will not change - ever. Of course that is not what is actually happening with this templated code - it's “expanded” and “inlined” before it's fully compiled. But it's an interesting concept, none-the-less. I suppose the fact that greater lives in a header file named functional should alert users about this. But that assumes your users are aware of this concept to begin with...

    Here is the resulting assembly code, as seen within the Disassembly view in VS2005:

      // 1a. Functor.
      int a=2, b=1;
      if ( std::greater()(a, b) )
        cout << "a is Greater then b." << endl;
    1.  00401007  mov         eax,dword ptr [__imp_std::endl (40204Ch)] 
    2.  0040100C  push        esi  
    3.  0040100D  push        eax  
    4.  0040100E  push        ecx  
    5.  0040100F  mov         ecx,dword ptr [__imp_std::cout (402044h)] 
    6.  00401015  push        offset string "a is Greater then b." (402134h) 
    7.  0040101A  push        ecx  
    8.  0040101B  call        std::operator<< > (401210h) 
    9.  00401020  add         esp,0Ch 
    10. 00401023  mov         ecx,eax 
    11. 00401025  call        dword ptr [__imp_std::basic_ostream >::operator<< (402048h)] 
    

    Wonderful. No conditional code, at all. Let's step through the assembly code just this once, so you can get a feel for what it's doing. (I added the line numbers above).

    1. Starting at code address 0x401007, we move the address of the endl function into the eax register. How do I know that this symbol is a pointer to code and not data ?.  I cheated ; I saw how it used later in the program. Similarly, you can use Intellisense or the Code Definition Window within Visual Studio, or given that it's an import, you can use a tool like Dependency Viewer and check the DLL's exports.
    2. The esi register is pushed onto the stack. It's not obvious at this point why it is doing this. But after a bit of sleuthing, you soon realise that it's simply saving the register ; this code just happens to be hard up against the main entry point. Later, just before main exits, the original contents of this esi register is popped back off the stack.
    3. This pushes the address of endl (that we got in step 1) onto the stack in preparation for the (second) call to operator<< in step 12 below.
    4. The contents of the ecx register is pushed onto the stack. This is just saving the register so it can be used in the next step.
    5. The ecx register is loaded with the address of the global cout output stream object.
    6. The address of the string we're going to print out on the console is pushed onto the stack. Thankfully, the debugger shows us the string.
    7. The cout object's address, that we got in step 5, is now pushed onto the stack, ready for the call in the next step.
    8. We call operator<<. The parameters it needs are on the stack, in reverse order. That is the output stream (step 7), and the string (step 6).
    9. We manually fix-up the stack pointer rather than popping off the parameters passed in the previous call (step 8).
    10. We move a copy of the cout object's address in the ecx register.
    11. Finally, we call operator<< again. But curiously, this time it's an indirect call into msvcp80.dll. This program was compiled to use the standard libraries as DLLs.


    1b. Variable parameters passed to a Functor

    Next, I add a proper conditional by assigning the result of the rand function to b.

      // 1b. Functor.
      b = (rand() > RAND_MAX/2) ? 2 : 0;
      if ( std::greater<int>()(a, b) )
        cout << "a is Greater then b." << endl;
    

    Here is the assembly code it produces:

      // 1b. Functor.
      b = (rand() > RAND_MAX/2) ? 2 : 0;
     1. 0040102B  mov         esi,dword ptr [__imp__rand (4020D0h)] 
     2. 00401031  call        esi  
     3. 00401033  xor         edx,edx 
     4. 00401035  cmp         eax,3FFFh 
     5. 0040103A  setle       dl   
     6. 0040103D  sub         edx,1 
     7. 00401040  and         edx,2 
      if ( std::greater()(a, b) )
     8. 00401043  cmp         edx,2 
     9. 00401046  jge         main+6Bh (40106Bh) 
        cout << "a is Greater then b." << endl;
    10. 00401048  mov         eax,dword ptr [__imp_std::endl (402054h)] 
    11. 0040104D  push        eax  
    12. 0040104E  push        ecx  
    13. 0040104F  mov         ecx,dword ptr [__imp_std::cout (40204Ch)] 
    14. 00401055  push        offset string "a is Greater then b." (402134h) 
    15. 0040105A  push        ecx  
    16. 0040105B  call        std::operator<< > (401240h) 
    17. 00401060  add         esp,0Ch 
    18. 00401063  mov         ecx,eax 
    19. 00401065  call        dword ptr [__imp_std::basic_ostream >::operator<< (402050h)] 

    Notice how the code from line 10 onwards is almost identical to earlier. Also notice how it uses some creative mathematics to set b (edx register) in lines 3 through to 7. It's interesting enough, so I'll step though it.

    1. moves the address of the rand function into the esi register.
    2. calls the rand function, indirectly, via esi.
    3. Zeroes the edx register.
    4. compares edx with 0x3fff - half of the maximum range that rand can return.
    5. Sets dl to one if the comparison in step 4 was less than or equal, and zero otherwise. dl is the lower 8-bits of the edx register.
    6. subtracts one from edx. If edx was zero, all bits become set (minus one).
    7. masks edx so that if any bits were set, only bit-1 remains. ie edx will contain the value two or zero.
    8. Now we finally get to the code from the greater Functor. edx is compared with two.
    9. If the result of the comparison is greater or equal, then it jumps over the cout code.


    1c. Volatile parameter passed to a Functor

    Finally, I was interested to see how the compiler behaved if I modified the first code snippet by using a parameter that's been attributed with the volatile keyword.

      // 1c. Functor.
      volatile int v=1;
      if ( std::greater<int>()(a, (int)v) )
        cout << "a is Greater then v." << endl;
    

    Here is the assembly code:

      // 1c. Functor.
      volatile int v=1;
     1. 0040106B  mov         dword ptr [esp+4],1 
      if ( std::greater()(a, (int)v) )
     2. 00401073  cmp         dword ptr [esp+4],2 
     3. 00401078  jge         main+9Dh (40109Dh) 
        cout << "a is Greater then v." << endl;
     4. 0040107A  mov         edx,dword ptr [__imp_std::endl (402054h)] 
     5. 00401080  mov         eax,dword ptr [__imp_std::cout (40204Ch)] 
     6. 00401085  push        edx  
     7. 00401086  push        ecx  
     8. 00401087  push        offset string "a is Greater then v." (40214Ch) 
     9. 0040108C  push        eax  
    10. 0040108D  call        std::operator<< > (401240h) 
    11. 00401092  add         esp,0Ch 
    12. 00401095  mov         ecx,eax 
    13. 00401097  call        dword ptr [__imp_std::basic_ostream >::operator<< (402050h)] 
    

    Here we can see that v is given a real memory location, and that it does not optimise away the conditional.

    April 09

    Tinkering with STL #1 - creating a Predicate usable by find_if on a vector of string (C++)


    This article has been completely rewritten since I first published it this morning, and posted my question on the Channel9 forums.

    So yesterday I was tinkering around with the Standard Template Library (STL), trying to learn better ‘generic’ practices. But ended up getting stuck trying to solve a problem that occurs when you try to create a Predicate of string::compare() - the adaptors end up trying to create a reference of a reference, and the Compiler effectively says, no, go away.

    OK, now some of you are probably saying: “Um, string ? Why are trying to use string::compare, just use the built-in operators. They have overloads”.

    Well, as tends to happen with these types of things, it started innocently enough, but was rooted in a stupid mistake. I was converting a Microsoft (ptr_fun) sample which used a vector of char pointers. I thought, that was too old-school, and modified it to use std::string instead. The problem was that I forgot to rename the #include <cstring> to <string>, as was kindly pointed out to me by Sven. Thanks Sven.

    This had the curious effect of not defining various parts of std::basic_string, such that Functors like equal_to failed to compile with complaints about operator==. Anyway, this lead to me trying to workaround the ‘missing’ operators by attempting to construct a suitable Predicate calling a member function, in this case string::compare().

    Anyway, having been suitably embarrassed, I'm still curious about whether it can be done. Say, for example, because I need to use a Class that does define Iterators, but not a full compliment of operator overloads.

    So let's just start off with the definition of find_if, and then go into to some of the concepts behind what I'm trying to do. Having said that, I suppose I should point out that I'm not an Academic, so I'm probably going to mess up the terminology - but let's give it a crack, anyway...

    Here's the basic definition of find_if:

    template<class _InIt, class _Pr> inline
        _InIt find_if(_InIt _First, _InIt _Last, _Pr _Pred)
        { ...

    find_if uses two generic classes: an Input Iterator and a Predicate. OK, an Iterator is pretty easy to understand, but what is a Predicate ?

    Well in this context, it's a Function that is passed an “item” (dereferenced Iterator) and returns a boolean result. So what find_if does is iterate over a range that you specify via the _First and _Last Input Iterators, and it continues until it either reaches the end of the range, or the Predicate _Pred returns true.

    When I was first introduced to this algorithm, the predicate would usually be an ordinary C++ function call. That made it easy to understand, but from a practical point of view, a PITA to use.

    Functors
    At this point, if you've been lucky, you'll have next been introduced to Functors. A Functor is basically an object that has defined (overloaded) the function operator - operator(). Because of this, a Functor (object) can basically be used as though you were calling an ordinary C++ function, with a twist:

      int a=2, b=1;
      if ( std::greater<int>()(a, b) )
        cout << "a is Greater than b." << endl;
    

    Notice how in the if statement, I have to explicitly specify the brackets () before the parameter list. That's in addition to explicitly specifying the “Type” Template parameter - <int> in this case.

    Binding
    The next thing is Binding. STL has two Binders: bind1st and bind2nd. Actually, these are helpers for binder1st and binder2nd, but you wouldn't normally use these, directly.

    A Binder basically allows you bind one of the parameters of a Function (or Functor), effectively changing a two parameter Function call into single parameter one: bind1st statically binds to the first parameter, and bind2nd with the second parameter.

      int c=2, d=1;
      std::binder1st<std::greater<int> > b1fn = bind1st(std::greater<int>(), c);
      if ( b1fn(d) )  // Equivalent to std::greater<int>()(c, d)
        cout << "c is Greater then d." << endl;
    

    Bringing it all together
    Now this is where I'm having trouble. In the following code, v1 is a vector of string, where each item is a word.

    1. & 2. After I setup v1, I print out each word using a for loop and Iterators.

    Next, I tried searching for the word “pearly” using various techniques.

    3. Searching using find. Once I had the correct header included, it worked fine.

    4. Using find_if and the equal_to Functor, adapted as a Predicate using bind2nd.

    5. This is where I run into the reference to reference problem. I attempt to create a Predicate using mem_fun_ref and bind2nd., but I can't get it to compile:

    error C2529: '_Right' : reference to reference is illegal C:\Program Files\Microsoft Visual Studio 8\VC\include\functional    312

    From that point on, I tried breaking up the Predicate into it's various concepts, in the hopes of trying to locate where it was failing.

    6. The first thing was trying to get mem_fun_ref working using a parameterless function. Nothing too spectacular here, though I chose to include the explicit call (6.3).

    7. Finally, I try to get mem_fun_ref working using a single-parametered function - compare. You'll notice that I could not get the implicit call (7.2) to work. It fails with errors about not being able to deduce the template parameters - compare() has 6 overloads.

    This therefore means that you must use explicit calls, as shown with 7.3 and 7.4.

    All in all, it's been an interesting exercise. My original hopes of producing more easily consumed source code has quickly evaporated with the realisation that we must use an explicit call - trying to create a Predicate from a member function worsens the consumability, quite considerably.

    However, I'd still like to see whether the reference from reference issue can be solved, even though now it's just an academic one.

    The following code is for a console program:

    #include <vector>
    #include <algorithm>
    #include <functional>
    #include <string>
    #include <iostream>
    
    int main( )
    {
      using namespace std;
    
      // 1. Setup and tinker with a vector of string.
      vector <std::string>            v1;
      vector <std::string>::iterator  Iter1, RIter;
      
      v1.push_back ( "Open" );
      v1.push_back ( "up" );
      v1.push_back ( "the" );
      v1.push_back ( "pearly" );
      v1.push_back ( "gates" );
    
      // 2. Let's start off by printing out the original sequence.
      cout << "Original sequence contains: " ;
      for ( Iter1 = v1.begin( ) ; Iter1 != v1.end( ) ; Iter1++ )
        cout << Iter1->c_str() << " ";
      cout << endl;
    
      ///////////////////////////////////////////////////////////////////////////
      // Now lets try various ways of searching for "pearly"
      ///////////////////////////////////////////////////////////////////////////
      
      const string kSRCH("pearly");
      
      // 3. use std::find
      RIter = std::find(v1.begin(), v1.end(), kSRCH );
      if ( RIter != v1.end() )
        cout << "using std::find ->" << *RIter << endl;
    
      // 4. use equal_to Functor for predicate.
      RIter = find_if( v1.begin(), v1.end(), 
        bind2nd ( equal_to<string>(), kSRCH )
        );  
      if ( RIter != v1.end() )
        cout << "using std::find_if and equal_to Functor ->" << *RIter << endl;
    
      // 5. Now assume the object (std::string) doesn't have the appropriate operator
      // overloads, but does have a compare() member. DOES NOT COMPILE !.
      RIter = find_if( v1.begin(), v1.end(), 
        bind2nd (mem_fun_ref<int, string, const string&>(&string::compare), string("pearly") )
        );  
      if ( RIter != v1.end() )
        cout << "using std::find_if and mem_func_ref ->" << *RIter << endl;
      
      // 6. OK, the above fails. So lets break the above predicate down, and see
      //    what we can get to compile. Well start with a parameterless member
      //    function (method) call. string::size().
      size_t z = v1[0].size();  // 6.1 Simulate an iteration, where an item is passed in.
      z = mem_fun_ref(&string::size)(v1[0]);                            // 6.2 implicit.
      z = mem_fun_ref<string::size_type, string>(&string::size)(v1[0]); // 6.3 explicit.
    
      // 7. Now let's try single parametered member function (method) function.
      //    string::compare().
      v1[0].compare(kSRCH);   // 7.1 Simulate a single iteration, again.
      mem_fun_ref(&string::compare)(v1[0], kSRCH); // 7.2 Doesn't compile: can't deduce template argument.
      mem_fun_ref<int, string, const string&>(&string::compare)(v1[0], "pearly" );  // 7.3 explicit 1
      mem_fun_ref<int, string, const string&>(&string::compare)(v1[0], kSRCH );     // 7.4 explicit 2
    
      // Done.
      getchar();
    }
    

    Just to add another data point, I tried compiling this under Ubuntu Linux 7.10 with GCC 4.1.3, and it fails as well (manually edited by me):

    /usr/include/c++/4.1.3/bits/stl_function.h: In instantiation of 
      ‘std::binder2nd
      <
        std::const_mem_fun1_ref_t
        <
          int,
          std::basic_string
          <
            char,
            std::char_traits,
            std::allocator 
          >,
          const std::string&
        >
      >’:
      stl_mem_fun_ref_01.cpp:48: instantiated from here /usr/include/c++/4.1.3/bits/stl_function.h:435:
      error: forming reference to reference type ‘const std::string&’
      
    /usr/include/c++/4.1.3/bits/stl_function.h: In function 
      ‘std::binder2nd<_Operation> std::bind2nd(const _Operation&, const _Tp&) 
      [
        with _Operation = std::const_mem_fun1_ref_t
        <
          int,
          std::basic_string
          <
            char,
            std::char_traits,
            std::allocator
          >,
          const std::string&
        >,
        _Tp = std::string
      ]’:
      stl_mem_fun_ref_01.cpp:48:  instantiated from here /usr/include/c++/4.1.3/bits/stl_function.h:455:
      error: no matching function for call to 
      ‘std::binder2nd
      <
        std::const_mem_fun1_ref_t
        <
          int,
          std::basic_string
          <
            char,
            std::char_traits,
            std::allocator
          >,
          const std::string&
        >
      >::binder2nd
      (
        const std::const_mem_fun1_ref_t
        <
          int,
          std::basic_string
          <
            char,
            std::char_traits,
            std::allocator
          >,
          const std::string&
        >&,
        const std::basic_string
        <
          char,
          std::char_traits,
          std::allocator
        >&
      )’
      /usr/include/c++/4.1.3/bits/stl_function.h:429:
      note: candidates are:
        std::binder2nd
        <
          std::const_mem_fun1_ref_t
          <
            int,
            std::basic_string
            <
              char,
              std::char_traits,
              std::allocator
            >,
            const std::string&
          >
        >::binder2nd
        (
          const std::binder2nd
          <
            std::const_mem_fun1_ref_t
            <
              int,
              std::basic_string
              <
                char,
                std::char_traits,
                std::allocator
              >,
              const std::string&
            >
          >&
        )

    March 22

    Copying Table Row text using Word VBA


    Automating a Microsoft Word document can make sense in certain situations, though due to misuse, Microsoft has found it necessary to limit the operation of 'Macros' within Word, Excel and other Office component products. With that in mind, here is something you might be able to use or adapt to your needs.

    blog TableButtonCellCopy 01In the picture at the left, you can see that I have three tables in a Word 2000 sp3 document. Each table has a varying number of columns, and each cell within each table has text, with exception of the last column. Instead, the cell at the end of each row has a CommandButton control.

    Note that you need to show the Control Toolbox in order to place the CommandButtons, and to switch in and out of Design Mode.

    Word's Macro Language is a form of Visual Basic known as Visual Basic for Applications (VBA). But VBA doesn't support what's know as Control Arrays in Visual Basic proper. So instead, each of the CommandButtons shown above need to have their own click-event procedure. Indeed, you can't even override the default procedure name. So rather than repeating, or hard-coding specifics into each of these procedures, it's better to try and tease-out the functionality and place it into a 'Generic' procedure instead - and that's what I've done here.

    There are eight CommandButtons in this Document. But the functionality of retrieving the text from each cell within that row lives in a procedure that I named GetTableText. Thus the only thing in the CommandButton's click-event procedure is a call to GetTableText, and nothing else. This makes it easy to add new tables, rows and columns - new buttons simply need to call the GetTableText procedure, and it takes care of the rest.

    Here's the code:

    Private Sub GetTableText()
      If Not Selection.Information(wdWithInTable) Then Exit Sub
    
      'Get Column and Row of cell containing the button that launched this event.
      Dim c As Long, r As Long
      c = CLng(Selection.Information(wdStartOfRangeColumnNumber))
      r = CLng(Selection.Information(wdStartOfRangeRowNumber))
    
      'Get all text from each cell in this row, bar the current cell.
      Dim cels As Cells, i As Long, s As String
      cels = Selection.Range.Tables(1).Rows(r).Cells
      For i = 1 To c - 1
        s = s & StripJunk(cels(i).Range.Text) & vbTab
      Next
      s = Left(s, Len(s) - 1)
    
      MsgBox("|" & s & "|")
    End Sub
    
    Private Function StripJunk(ByVal s As String)
      StripJunk = Trim(Replace(s, vbCr & Chr(7), ""))
    End Function
    
    Private Sub CommandButton1_Click()
      GetTableText()
    End Sub
    
    Private Sub CommandButton2_Click()
      GetTableText()
    End Sub
    
    Private Sub CommandButton3_Click()
      GetTableText()
    End Sub
    
    Private Sub CommandButton4_Click()
      GetTableText()
    End Sub
    
    Private Sub CommandButton5_Click()
      GetTableText()
    End Sub
    
    Private Sub CommandButton6_Click()
      GetTableText()
    End Sub
    
    Private Sub CommandButton7_Click()
      GetTableText()
    End Sub
    
    Private Sub CommandButton8_Click()
      GetTableText()
    End Sub
    

    So how does this work ?

    If Macros are enabled, and your not in Design mode, when you click one of the CommandButtons, it first selects the button, then calls any event procedures associated with the control. In this case, we have procedures for the click event, and each of these simply call the GetTableText procedure - in this case a Sub (Subroutine).

    The GetTableText subroutine takes advantage of the fact that the Selection object changes with the act of clicking the CommandButton. The first thing, though, is that it checks that the current Selection is indeed within a Table - the very first If statement.

    The next block of code recovers the row and column information of the current table cell, which is the cell where the CommandButton has been placed.

    The third block of code recovers all the cells of the appropriate row of the the current table. It then iterates through each of the cells up until the column where the CommandButton was, recovering their text, and building up a string in the s variable. The text from each cell is separated with a tab character, and the StripJunk function is used to remove extraneous characters that are part of Word's housekeeping for table text.

    Finally, the string is cleaned up (the last tab character stripped off), and the string is shown via a Message box. Additionally, I've also wrapped the Message box text with '|' characters, just to ensure that I am indeed cleaning up the string:

    blog TableButtonCellCopy 02

    In the picture above, note that I've modified the last table by adding an extra column to the right of the CommandButtons. I did this to highlight the fact that the GetTableText subroutine only copies the text from the cells to the left of the cell which contains the CommandButton. In the picture, I clicked the CommandButton in the second row of the last table. Notice that the Message box only shows the text from columns 1 through to 4, and nothing from columns 5 and 6.

    March 19

    Compaq Presario V6000 power connector retainer


    I was just working on a Compaq Presario model V6002AU. The computer would regularly complain about the battery running low, even though it was supposedly running from the power brick (Mains). A quick wiggle of the power connector and it was right again.

    Initially I thought it was like an earlier problem I had with the power socket's centre connection being bad on the motherboard. But having checked for broken/intermittent connections in both the power lead and the connector on the Notebook, it appears to be simply a mechanical issue where the plug pops out of it's connector socket.

    After a little thought, I whipped up this retainer by carefully bending a paper-clip.

    blog Power Connector retainer 01 I straightened the paper-clip, then using needle-nose pliers, I bent one end around the base of the lead's plug (right), being careful not to crimp the lead.

    Then bent the other end (left) down at approximately 90 degrees so that it hooks into one of the laptop's case screw recesses.

    That's not going to be popping out too easily, now. Time will tell...

    February 15

    Using RUNAS.EXE to run as a limited user


    In this increasingly security conscientious age, it's common advice now that you should be running your day-to-day desktop as a limited-user. That is, you login using a user account which does not have any administrative privileges. Then, when necessary, you go through an elevation process.

    With Windows Vista, this is now a built-in and fully integrated feature. Back on earlier versions of Windows, though (such as XP or 2000), whilst you can achieve a similar result, it's not natural, and it is not a fully integrated experience.

    But what if you want to operate in the opposite fashion - you want to stay logged in as a Administrative or Power User, but want to run certain programs at a lower privilege ?

    blog RunAs, shortcut context menu 01

    Well you can, though it's once again not a fully integrated experience. Some of you may be aware that when you right-click executables or their shortcuts, you get a RunAs menu option.

    Unfortunately, this option filters out the lower privileged users. So it's not of any use in this situation. Instead, you have to use RUNAS.EXE, which is a command-line executable program that is shipped with Windows.

    blog RunAs 02, edit shortcutSo let's start off by creating a shortcut - I'll create one for the 2xExplorer program in the Quick Launch toolbar.

    2xExplorer is a third-party, Explorer-like program which I find very useful when I need to perform administrative tasks operating as limited user - the opposite of what's being described in this post.

    In the picture at the left, I've created the shortcut (the yellow folder icon to the right of the Start menu), and I've right-clicked this icon and chosen the Properties menu item.

    You can see that the Target is 2xExplorer.exe living in Program Files. Add in front of this the following:

    %windir%\system32\runas.exe /user:test

     

    blog RunAs 03, edit shortcut The full path and filename to the runas executable is in black, whilst the user we want to run as is in blue - in this case, I have a user with a name of test.

    blog RunAs 04, edit shortcut Though, one side effect of changing the Target executable is that the icon gets stomped on. So you have to remember to customise the icon used by the shortcut - click the Change Icon button at the bottom of this dialog.

    In this case, I point back to the original executable.

    blog RunAs 05, check service For runas to work, though, this user (test) must have a password set, AND the Secondary Logon service must running.

    blog RunAs 06, run shortcut When all is working, you should see a command prompt window asking you to enter the password for the particular user account.

    Remember, the user account must have a password set. Additionally the user must have logged into their Desktop at least once from the main login screen. ie There must be a folder for the user in the Documents and Settings folder.

    One final thing. Because of security restrictions, certain user interface behaviours are specifically prohibited by the Windows Shell. For example, dragging and dropping from a program running as a lower privileged user onto a higher privileged program or desktop simply does nothing. This can be annoying, if your aware of it, and is downright infuriating when your not.

    February 01

    Stubborn USB Hard Disk - wrong connection


    I was reminded today of an incident where a client was complaining that an External USB Hard Disk would not work on his Home Computer, yet had no trouble when connecting it to their work computer.

    I thought, oh, somebody must have disabled the hard disk device in Device Manager. So I stepped through the process over the phone, awkward as it was, but the client proclaimed that there was no entry for anything other than their internal Hard disk (in the Hard Disks branch, of Device Manager). Weird.

    Anyway, I eventually go onsite and fixed the problem in about 5 seconds. You just wouldn't believe what it was - well maybe you would. Look at these two pictures, where I've recreated the situation. The Laptop is on the right in each shot, and the USB Hard Disk sits on top of an external Liteon USB CD-DVD/RW drive - the cable for which is completely out of shot:

    USB HDD, wrong cable USB HDD, correct cable

    Here's a hint. USB Hard Disk cables need to be able to cope with the situation where the power drawn by the Hard Disk within the External Enclosure needs more than half an Amp (500mA), which is the maximum power output a USB port needs to be able to provide to a device, as defined in the USB standard. So the USB device manufacturers provide a cable which has a pig-tail that you can plug into a second USB to get the additional power - up to 1 Amp with the two.

    Still can't see it ?

    OK, you need to know that the pig-tail ONLY connects the power, and the client was using ONLY the pig-tail between the device and their laptop - the left picture. D'oh.

    Normally this wouldn't happen, because the USB standard defines the use of different connectors between a device and host. In the shots above, the cheap USB enclosure tries to save money by not adhering to the standard and using the 'A' connectors, instead of the 'B' connectors. This would not have been possible if the USB standard was being adhered to.

    Just something to keep in mind...

    January 18

    Using the UBCD to maintain your Windows Personal Computer


    It's probably a good idea to have your PC serviced at least once a year. However, to save some money, you can perform at least some of this yourself - only taking it in, should a problem be found, or if the thing is clogged with dust.

    Obviously, I'm assuming that your PC's hard disk is not already making unusual clicking type noises. For example, watch and listen to the video in this blog entry.

    So what you need to get is the Ultimate Boot CD. Unfortunately, whilst learning how to service your PC is an admiral goal, because of the copyrights (held predominately by Microsoft), pre-built UBCD's are not allowed to be distributed. So if your not PC savvy, or your PC has already failed to the point where you can't perform the build, then I'm afraid your just going to have to take it in and have your friendly Service Professional (Computer man) do it for you.

    IMPORTANT: If you want to recover any data, do not attempt the recovery yourself, or have someone who is not a professional do this for you. You have been warned !.

    OK, on with the show...

    The starting point here, is that you've already built, or somehow acquired a copy of the UBCD. I'm using one built with the default setup of version 3.10, released 25-Nov-2007. Whack in the CD, then (re)start your PC.

    NOTE: I'm assuming your PC is already setup to boot from the CD. If it doesn't start booting the UBCD, then you'll need to reconfigure your PC to do so - pressing the appropriate key(s) to either select a boot device, or go into your PC's BIOS setup and change the start or boot sequence there.

    These are the first two screens you should see when booting from the UBCD disk.

    blog UBCD Boot screen 01 blog UBCD Boot screen 02

    The second (blue) screen is a boot menu, with the default being to start the normal UBCD. So just press enter (or wait for it to timeout), and it will (eventually) boot up to the full UBCD environment - yes it's slow, about 6½ minutes.

    NOTE: On this menu is a choice to run a memory test (MemTest86). If your having problems with your RAM (perhaps you've just add some), then that's what you should use, instead.

    Once booted, it should ask you wether you want to start Networking.

    blog UBCD Boot screen 03

    If you've got a local Network (eg you have a Broadband Router/Modem), then start the Networking. The default DHCP option should be fine, and yes, it's annoying that they seem to ask this the question twice (though 'worded' differently). But do click OK, both times.

    blog UBCD Boot screen 04

    blog UBCD Boot screen 06


    Because this is a rather long topic, I have broken the rest of this into multiple sub-postings:

    Note that the Disk defragmentation link is actually a much earlier posting, unrelated to this series. But that doesn't mean it's irrelevant.


    Depending on whether you decide to do a backup or not, it will likely take between 2 to 3 hours... easy, eh... :-)

    But be thankful I didn't describe a full service, which can add an extra 1 to 3 hours on top. This includes cleaning out dust, servicing the fans and all of the mechanicals. Included in the mechanicals is dealing with any corrosion, as is typically found with Dell Tower systems, predominantly around the front and rear of the case - the floppy drive seems to be especially susceptible.


    UBCD - Running the Physical Disk Checks


    Note that this is one part of a series of posts on PC Maintenance.

    The Western Digital Digital Life Guard utility is a cool utility which can check wether your hard disk has failed, or is about to fail.

    To start it, from the Start menu: Programs, Disk Tools, Diagnostic, WD WinDlg.

    blog UBCD, WinDLG 01


    Accept the license, then right-click the drive you want to test, and choose Run Diagnostics:

    blog UBCD, WinDLG 03

    blog UBCD, WinDLG 04

    Now choose EXTENDED TEST.

    NOTE: DO NOT choose the WRITE ZEROS option. This will completely wipe your hard disk. That means Windows, your data, the lot. ie You will have to reinstall an Operating System again, from scratch. Also note that we are operating on the entire disk. So if the disk is partitioned, you will wiping those Volumes as well. eg C: and D:


    blog UBCD, WinDLG 05 This warning shouldn't be an issue for us. WinDlg is normally run in Windows proper, and not in a Pre-Execution (PE) environment like UBCD. Nothing else should be touching the disk.

    blog UBCD, WinDLG 06

    Now walk away, and let the Diagnostics run. When done, it will tell you wether it encountered a problem or not.

    IF A PROBLEM IS FOUND, TURN OFF THE PC, AND DO NOT SWITCH IT ON AGAIN.

    You need to contact a service professional, and the more the disk is used, the greater the risk of complete failure. Of course, I'm assuming you want whatever data happens to be on the particular disk...




    UBCD - Performing a Backup


    Note that this is one part of a series of posts on PC Maintenance.

    Having a Backup is considered a fundamental requirement for anybody that considers themselves to be even mildly competent. So many times I've seen people, or businesses claim that they are 'professional', yet they consistently avoid taking even the most basic steps of backing up either their, or a client's data.

    Heck, even if the documents and settings are not important, unless your dealing with some majorly lock-down enterprise box, performing a full back up is an insurance policy that a service professional can fall back on should something 'go bang', or the client starts making up stories - fine, I'll restore it back to the way it was before I touched it...

    OK, so what tools should you use ?

    For me Disk Imaging. No ifs or buts about it. All you need is a large enough back-up store, and some half decent disk-imaging software, of which there are ample choices: Ghost, True Image, etc.

    One thing I hadn't tried, though, is the free one shipped with the UBCD - DriveImage XML. So what I'm about to show here should not be considered as an endorsement by me - as I become more familiar with this software, that position may change. Time will tell.

    One thing, though. Most commercial/consumer disk-imaging tools I've used, all have one major limitation - the disk must not have ANY physical faults, and some won't tolerate anything but the simplest of filesystem errors. So whilst I typically advise people to back-up their systems before running a CHKDSK, some times the back-up software fails to do so.

    In those situations, don't fight it. Take it to service professional. Good ones will have better tools and/or techniques in getting the thing backed-up, so that you don't loose any more data than you may have already.

    My first trial of DriveImage XML

    From the UBCD, you can start DriveImage XML via Start, Program, Disk Tools, Backup and Cloning, DriveImage XML.

    blog UBCD, DriveImage XML 01

    blog UBCD, DriveImage XML 02

    The program doesn't look too bad. Normally, I would tinker around a bit with it. But at this stage, I just need to see how it works, so lets just stick to the welcome screen and do a backup.

    blog UBCD, DriveImage XML 03

    Let's backup the C: drive.

    Notice, also, that I have a D: drive. That is important. We need somewhere to backup to. Unlike in this example, you should use an externally connected USB hard disk, or a mapped Network drive.

    blog UBCD, DriveImage XML 05

    The Drive that will store the backup image we are about to create should obviously have enough free space to contain the compressed contents (in this case), plus allow for some overhead. I'm not sure what that overhead should be, but I would expect the DriveImage program will tell if you don't have enough.

    blog UBCD, DriveImage XML 06 OK, make sure we have the compressed option ticked, then click Next.

    blog UBCD, DriveImage XML 07 It's now backing up.

    Note the time it estimates. We'll see how accurate this is at the end.

    blog UBCD, DriveImage XML 09Done.

    Now let's see how long it took, and how much disk space it used.

    blog UBCD, DriveImage XML 10

    OK, so here are the results - I've highlighted the important bits.

    It took ~1 hour to complete (that was a bloody good guess at the start). The original C: volume had ~2.23GB in use, and it produced three files, totaling ~1.146GB (1,230,375,557 Bytes). That's a compression ratio of ~1.946 : 1. Nearly 2:1.

    So, the numbers are on par with what I would have expected with either of the commercial (paid for) tools. I'll definitely have to tinker with this program...

    UBCD - Check the filesystem using CHKDSK


    Note that this is one part of a series of posts on PC Maintenance.

    Once the physical hard disk has been tested (prior post), we need to ensure that the filesystem is in good shape. This is different from the physical disk check we just made using WinDlg. If you think of a book, the physical check (WinDlg), checks that the book's covers, bindings and pages are in operational condition. The filesystem check, in this analogy, would be the state of the contents table and the indexes.

    Remember, that unlike a normal book, which for all intents and purposes is not modifiable by it readers (users), the contents of a hard disk is modifiable, and even if you don't change any documents, files are always being modified. As a consequence, the contents and indexes need to be kept in sync with what's being modified. When this does not happen, for whatever reason, then the filesystem is said to have errors, and CHKDSK is the tool which tries to make sense of what it finds, attempting to repair any inconsistencies.

    Because there is a possibility CHKDSK could get it wrong, if you don't have a backup, then now is the time make it.

    blog UBCD, Check Disk 01

    blog UBCD, Check Disk 02

    Above, you can see that this Disk Check is just a script which asks us a couple of questions, and then runs CHKDSK using our responses. I've marked my two responses above, in Red.

    C: means the first disk volume, which is usually your Boot and System Volume. Option 2 is the one you would normally run, though option 1 can be advisable if you don't have, and would like to try and avoid having to do a backup. But my advise is that you can never have too many backups... well, within reason...

    Option 3 wouldn't normally be run by a non-professional - it can really cause some major problems, and is usually the one after which people conclude that CHKDSK is a POS - because they didn't use a tool like WinDlg, first. One could argue that if the disk was/is failing, these would be unavoidable problems. At any rate, I think that using WinDlg is a better, safer way of assuring the operational state of a disk drive (and it's volumes), and we've already done that. So it's redundant. But I will say this:

    Don't use option 3 without a current, known working backup, or you have assured that the disk is not failing - you have tested the backup, as well, right ?

    blog UBCD, Check Disk 03



    UBCD - Checking for Malware


    Note that this is one part of a series of posts on PC Maintenance.

    The easiest tool to use for this SpyBot. Version 1.5 is included with this version of UBCD, though you should still update the program before you start a scan. Obviously, this means that you needed to have started Networking, and that the Network is actually working.

    Note that SpyBot has a specific option that allows it to scan 'inactive' Windows installations. This should already be configured in UBCD menus, so you shouldn't need to do anything more.

    blog UBCD, SpyBot 01The quickest way to launch SpyBot is to use the Quick-Launch icon for it.

    blog UBCD, SpyBot 03

    Now if you have Networking, then click the Search for Updates button (third down, in the right pane).

    blog UBCD, SpyBot 04 It should find a stack of updates, and have already pre-selected all of them for download. Now click the Download Updates button.

    Note: you can change the site from which it downloads these updates, should the one it chose is slow or not responding - the button with a downward chevron shows a drop-down list from which you can choose.

    blog UBCD, SpyBot 05

    It should 'tick' each successful download item.

    blog UBCD, SpyBot 06 Once updated, to get back to the original screen, click the Spybot-S&D 'button', which I highlighted on the left.

    Now click the Check for problems button.

    blog UBCD, SpyBot 07

    Walk away, and let it do it's thing. When done, you can now deal with anything it finds.

    So what should you delete ?

    Well, that takes experience, unfortunately. Generally you can just delete/remove everything it flags, but there may be some instances where you do not want it remove the cookies for certain sites. These cookies may contain, or link information specific to the site, like for example, things you've place into a shopping cart, or site preferences, etc. So be aware of this.

    Here's a tip. If you can't read the result details, you can copy the results to the clipboard and then paste into Notepad. Just right-click one of the results, etc:

     blog UBCD, SpyBot 08

     blog UBCD, SpyBot 09

    UBCD - Checking for Viruses


    Note that this is one part of a series of posts on PC Maintenance.

    The last thing on this list now, is checking for Viruses. This release of UBCD includes AntiVir Personal.

    blog UBCD, AntiVir 01Clicking the Quick-Launch icon extracts, installs, and finally runs AntiVir.

    blog UBCD, AntiVir 03If you have setup Networking, then click Start update.

    blog UBCD, AntiVir 04  Let it complete it's update.

    blog UBCD, AntiVir 05

    blog UBCD, AntiVir 06Now click the Scanner tab, and then click the Scan toolbar button.

    blog UBCD, AntiVir 07Now walk away, and let it complete it's scan.

    blog UBCD, AntiVir 08Note that AntiVir has one rather annoying problem - it detects a number of programs which make up the UBCD as various viruses. As far as I can tell, these are all false-positives, and a known problem.

    Be careful to note what's being complained about, though. Here, I've highlighted a number of things. First is that anything on the X: drive is actually a part of the UBCD disk. It's read-only, so you couldn't, even if you wanted to, move or delete the 'detected' program. Instead, you should select the Ignore option, and then tick the Apply selection to all following detections.

    Actually, once we've started to scan the X: drive, there is no point continuing the scan, because there probably isn't anything else of interest that you want scanned. So once you click OK, to dismiss this dialog, just click the Stop button.

    January 14

    Debugging using API Monitor


    I've been using BartPE in quite a lot of my maintenance and troubleshooting tasks for quite a while. But there was one thing lacking - being able to Defragment. Sure there were some add-ons for things like O&O Defrag, but I'm a naturally suspicious fellow. Plus, I was curious as to why nobody (at that stage) hadn't done it.

    Anyway, fast forward to the new year break, where I decided to give it crack - it can't be that hard, right ?

    Well the short answer is to go and use Ultimate Boot CD. The long answer, take a look at the sorts of things you'll need to be doing, as mentioned in this article. But in my usual fleeting style (I do have bills to pay), I'm only going to steer you the right direction with regard to the process. It's not a tutorial.

    When working on stuff like this, it's much easier to use a Virtual Machine. In this case I used VMWare Workstation, though you could just as easily have used Virtual PC 2007. I found a spare 256MB VPC with XPsp2, and booted up BartPE. I then started using the Debugging Tools, which I have on a Network share.

    Basically, you install the Debugging Tools on a 'host' system, then copy it's folder, in it's entirety to the share - the debugging tools help file has a topic on running on Windows 9x platforms, which is the basic process I'm talking about here.

    The first problem with using the Debugging Tools under BartPE is that it didn't have enough of the Internet/Networking stack for Symbols to download - fine, I could point it at a folder on the network, that another system can populate for me - tedious, to say the least.

    Then there was the issue with the Defrag programs themselves - they use COM Automation, both the GUI and command-line versions. So it didn't take long to realise that this was not going to be easy. I worked my way through to almost getting COM up and running under BartPE when I eventually found a discussion on one of the Ultimate Boot CD forums which effectively enumerated what I had done, so far, plus more. In their case, however, they used a shot gun approach, injecting whole swaths of the registry entries from a non PE system, and whittling down. Instead of my approach, which was to find what's needed, and then add it.

    Anyway, I downloaded the most recent version of UBCD and saw that they had the full MS Defrag, GUI and Command-line working. With that, I was just about to pack it in when I remembered API Monitor and thought, geeze, I probably could have saved myself a whole lot of pain by using that. So I quickly extracted it to a Network folder, and gave it try.

    Here's a quick rundown on how I used it.

    Accessing the Program, from a PE disk

    blog Net use, Start Run This is easy, once the Networking is setup, I just open a command prompt and just type in:

    net use z: \\{server}\{share}

    where {server} and {share} are the particular server and share names on your network.

    That then initiates a login, asking me for a username and password. Note that using the /user command-line option does not work on my Network. It obviously has something to do with the login method (NTLMv2, etc).

    blog Start Run API Monitor Once I have my z: drive, I can just run it using the Go (Start), Run menu option: z:\tools\apimon\apimonitor.exe 

     


    OK, with API Monitor up and running, lets set it up to log all of the user-mode API calls made by defrag.exe.

    1. Setup the process filters

    Here, I know that defrag.exe will startup dfrgntfs.exe. so I add them to the include list. That is, I click the spanner and hammer icon in the toolbar, click Include Filter option, then type defrag into the Process text-box, enter key, dfrgntfs, enter, and then click the OK button to dismiss the dialog:

    blog API Monitor, Defrag exe 01

     

    2. Setup the API Filter

    We're only interested in the User-mode API's, so after clicking the funnel (with filter paper) icon from the toolbar, select all of Win32 API items (Select All at bottom):

    blog API Monitor, Defrag exe 02

     

    3. Start capturing

    Click the camera icon in the toolbar.

     blog API Monitor, Defrag exe 03

     

    4. Run defrag.exe

    blog API Monitor, Defrag exe 04

    Now run defrag.exe by using the Load Process option in the Capture menu.

    blog API Monitor, Defrag exe 05

    blog API Monitor, Defrag exe 06 


    5. Examine the results

    blog API Monitor, Defrag exe 07

    Double-clicking an entry, as shown above, will get an API Details dialog.

    Obviously the documentation icon is not going to work from a PE environment (the Help icon button next to the API Called panel). But if you save the log, you can reopen it (using API Monitor) on a system that has the old MSDN Library installed (the one not using DExplorer), and API Monitor will take you to the appropriate page.

    blog API Monitor, Defrag exe 08


    Have fun...

    January 08

    Defragmenting the Boot Volume


    A lot of Windows Systems are setup so that their Boot and System Volumes [1] are one and the same. Similarly, whilst it is possible to setup Windows so that your page-file is not on your C: (Boot) Volume [2], it typically is.

    So what difference does this make with regards to Defragmenting ?

    Basically, it is extremely difficult to defragment files which are in active use, such as the page-file or the registry hives - the backup issue is effectively solved with the Volume Shadow Service. But this defragmenting problem is still an issue, today.

    Well, let's back pedal a little. Why is Defragmenting even important ?

    Disk drives are electro-mechanical devices, and one of the mechanical parts, the heads, has quite a bit of latency associated with it's operation. The heads need to move across the disk platters, then settle on, and track the centre of the magnetic 'track'. So there's inertia involved with both the start and stop of the mechanical movement, and then there's the settling of not only the sideways movement across the disk platters, but also the 'flying height' of the heads over the media. I suppose you can also add to that, the time needed for a specific sector to rotate underneath the head.

    Anyway, the point being that the more you can avoid 'large' (and random) movements of the disk heads, the better the overall throughput (performance) is.

    [1] In my opinion, the definitions for Boot and System Volumes are reversed. I find that I have to continually remind myself to switch the terms around. Argh !

    [2] If you decide to move the page-file, or that you don't need a page file at all, you must be aware of the fact that you do loose some functionality. Specifically, you loose the ability to produce full system dumps, making the troubleshooting of system crashes difficult.

    Let's Defragment a Boot Volume

    Windows ships a Defragmenting tool which, for most situations, is sufficient. But there are certain situations that will require you to do more than just a 'simple' Defragment.

    Let's take this laptop running Windows XPsp2. It has a 60GB 2.5" hard disk drive, partitioned into 18GB and 38GB volumes. The boot volume (C:) has a 1.5GB page-file - the 1GB hibernation file was disabled/removed.

    After a simple defragmentation, the page-file still remains in 191 fragments. In fact, even after a number of defragmentations, the page-file is left untouched (fragmented).


     
    When booted into Safe-Mode, you can see the previously Green, unmovable sections from above are now Red (the previous image's lower graph compared to upper in this one). But it will still not fully defragment the page-file. But just 10 fragments is certainly better.

    About the only way to get a fully defragmented page-file on a volume like this is to disable the page-file (or move it to another volume), make sure the page-file was actually deleted, defragment the free disk space, and then recreate it.

    It's this defragment the free disk space step that is the problem.


    So why was this page-file in so many fragments ?

    Well, Windows was left in it's default setup, allowing it to manage it's size. With XP, Windows would typically create quite a small page-file, and then grow it as required. Because the User Profiles are also (by default) on the Boot Volume (so we now have the User Profiles, and the Registry and Temp files all continually competing for disk space), when Windows needs to grow the page-file, the disk's free space will usually be quite fragmented, and over time, this leads to a fragmented page-file.

    blog XPsp2, set PageFile One way to avoid this type of fragmentation is to create a fixed, or minimum sized page-file.

    I've found that setting the minimum size to 1.5 times (150%) the amount of RAM you have is usually about right. In fact, in this case, when Windows was set to managed the size of the page-file (dynamically), it had already grown the page-file to 1.49GB by itself - the laptop has 1GB RAM.

    If you look at the image on the left, you'll see that Windows will now (since SP2?) also make this same recommendation.


     

    So how do you go about Defragmenting the free disk space ?

    In the past, I found it extremely useful to install a second copy of Windows onto the system, so that you can boot using this second copy for not only disaster recovery, but also for defragmenting and various other maintenance and troubleshooting tasks.

    But these days, I find the various Pre-execution Environments (PE) available from Microsoft and third parties makes such a secondary Windows Installation redundant. In this case, I'm going to be using the Ultimate Boot CD for Windows - XPsp2 was used to build the CD.

    Compared to the original BartPE, UBCD is dog slow to start: ~6½ minutes to boot up. But with it's default configuration, you do get a lot. In this case, we are particularly interested in the defragmenting tools: Microsoft's standard defragmenting tool, and the third-party DIRMS and AusLogics defragmenters.

    When not trying to defragment the Boot Volume (because we've booted from the CD), Microsoft's defragmenter (defrag) is better able to defragment more of the disk volume. However, it still has some peculiarities associated with it, in that will tend to spread out the disk usage within the volume. So if your trying create the largest continuous amount of free space (eg. so you can create a large, unfragmented file), then this is not the defragmenter to use. Though if you persist with it, it will occasionally move files back towards the start of the disk volume, defragmenting free space.

    I'm not sure what AusLogics defragmenter actually does. After running it, I couldn't see anything obvious when comparing the before and after shots and analyses produced by defrag nor DiskView. I'll reserve my judgment on this tool, and move on.

    blog UBCD, DIRMS On the other hand, my tests with DIRMS (so far) are favourable - especially when it comes to moving files to the 'front of the disk' and defragmenting free space.

    It's a command prompt tool, but it has been 'scripted' for easier use. Apart from it's lame user interface, the default '1.' option works well.


    blog UBCD Defrag 07f DV However, even DIRMS when run using either options 1 or 6, does not move or consolidate some rather stubborn 'Filesystem' metadata, as can be seen marked in Green, in this image at the left.

    Note the position of the slider in the lower graph, in what should free space.

    Also note that both the hibernation and page files have been deleted. Upon a restart, Windows will recreate these files. But because of these 'little droppings', it usually ends up fragmenting these new files.



    blog UBCD Defrag 07c DV, pagefileThe next three shots (one image) shows the end result after booting into Windows proper, and then restarting back into the UBCD environment.

    The original images were full-screen (800x573 pixels), and I've selected pagefile.sys, which DiskView highlights in the disk usage region using Yellow.

    There are at least four fragments visible here, though the defrag report says the page-file has five fragments.

    Also note, that although you can't tell from these images, the blue segment just to the left of the rightmost red segment, is the hibernation file, hiberfil.sys. Blue (in the lower graph) means that it is not fragmented. It's good to see that at least the 1GB hibernation file can be created on this volume, unfragmented.

    FYI: The lower graph in each DiskView screenshot shows the entire extent of the disk volume. The large upper graph shows the zoomed view of the disk usage. The region viewable is indicated in the lower graph by a pair of black markings, which can scroll from left to right along the lower graph. 

    So what's needed, is something which can defragment and consolidate these annoying little dropping. Diskeeper and O&O Defrag claim that they can, and if you read into their literature, they need to do this during Windows startup - on Windows XP systems, anyway.

    Sounds good, but, that costs money...


    I remember an old trick using Norton Ghost 2003. Basically, this old version of Ghost doesn't so much do a sector-by-sector disk image, like the old PowerQuest Drive Image program did. But rather, it effectively did a file backup. So when you restore from a Ghost image, it recreates the underlying filesystem, and copies the files across, though with certain, special precautions. Anyway, take a look at this.

    blog UBCD Defrag 08a, GHOST2K3, DV It looks completely different, doesn't it ?
    Yes, it is the same disk volume. Basically, I booted into DOS and created a partition image using Ghost 2003. Then wiped the disk partition, and restored back into the same partition. The backup took ~40 minutes, and the restore ~20 minutes (fast compression).

    This is the result immediately booting back into the UBCD environment, running DiskView. And look at the free space - no little droppings...


     

    blog UBCD Defrag 08c DV After quick restart into Windows proper, so it can create the page and hibernation files, then back into the UBCD environment, then a defrag, and look, the page and hibernation files are both unfragmented - in one piece !.

    Mission accomplished !


    January 01

    Installing or Removing SODIMM


    Laptop and Notebook type computers typically use Small Outline Dual Inline Memory Modules, or SODIMM for short. Whilst the Industry has tried hard to make the installation and removal of these modules easy, there are some things you need to be aware of.

    For those of you who understand spoken English (Aussie English), here I've uploaded a video.

    Important notice:

    Although I mentioned the issue of static discharge last, in the video, that's only because it is an accepted standard practice (for any good service professional), to use work areas that have been specifically prepared against it. It does not take a lot of effort, and it does not mean it is not important - protecting against Electro-Static Discharge (ESD) it is very important !.

    In addition to preparing a work area, you can even prepare yourself. eg I prefer to wear clothing made from Cotton. When working with ESD sensitive devices, such as Electronic components, it's best not to wear any synthetic materials, nor wool. Basically, if you remember back to your school days, whatever you did to build up static electricity to 'zap' your classmates, you need to be doing (almost) the exact opposite. However, unlike in your zapping days, you will most likely be unaware of the damage you've inflicted upon an ESD sensitive device - the voltages needed nowadays is so low, that you'd probably not feel the discharge occur. The last time I looked into it, it was down to as low 30 Volts. It's the voltage that kills devices, not the current or energy discharged.

    Anyway, here is the video:

     

    A quick rundown of the video

    When opening the lower covers, it's important you use good tools, and know how to use them properly. So many times I've come across damaged Philips-head screws and screw drivers due to sloppy and/or improper usage - it's extremely annoying. Each time the driver 'skips', it takes off a piece of metal !

    Make sure your well grounded before you start touching anything inside. You do not want to discharge yourself through a component like RAM - assuming you still want to use a reliable computer.

    When handling the devices, do not touch any of the metal traces or contacts, especially the gold plated ones. Preferably handle the devices via their edges without any contacts.

    When removing or installing the SODIMM, make sure your not forcing the modules in or out of the guides in socket. For example, once you've unclipped the retaining mechanism, and the module pops up, if you slightly wobble the module up and down as you are trying to remove it, you'll feel a spot where little effort is required to complete the step. The same goes for installing. However, make sure you orient the module correctly. It is 'keyed', so it will only insert one way. As with removing, make sure your not forcing the module in until the very last few millimetres - watch and listen to the video.

     blog Removing 512MB SODIMM 04

    blog Removing 512MB SODIMM 02 blog Removing 512MB SODIMM 01

    December 31

    Troubleshooting using DependencyWalker


    DependencyWalker a cool utility that can be used for various types of troubleshooting. The coolest feature is it's profiling ability. For example, if you need to troubleshoot the startup of the Windows Control Panel, you can find a lot of problems by profiling it using DependencyWalker.

    The first problem, though, is setting it up, as it does not include an installer. On Windows Vista, this mean that if you want to extract the program into the Program Files folder, you need to be using an elevated tool to do so. If you don't have a standalone archiving program which you can right-click to Run as Administrator, then the next best choice would be to extract the files from the depends ZIP archive into a temp folder, and then open a command prompt started using Run as Administrator. From this command prompt, you just make a directory for depends, and then move/copy the extracted files there. For example, if I explore/open the depends ZIP archive (double-click), Ctrl-A to select all, then Ctrl-C to copy, then traverse back to a temp folder (C:\TEMP), then the final steps in the command prompt would be:

    cd c:\temp
    cd "c:\Program Files" md Depends cd Depends move c:\temp\* .

    This assumes, of course that c:\temp doesn't have anything else there other than the freshly extract files from the depends ZIP archive. Also note that the last statement (move) has a space between the '*' and the final '.'

    The last step would be to create a shortcut or icon from which you can right-click and Run as Administrator. The easiest is perhaps to take advantage of Windows' recent programs feature, and initially run depends.exe using Windows (File) Explorer. You can then drag it from the lower part of the Start menu, and pin it (the area at the top of the Start menu).

    Anyway, once to have a shortcut, you can right-click it, and Run as Administrator, and allow the elevation:

    blog Depends, RunAs Admin 01   blog Depends, RunAs Admin 02


    With DependencyWalker running, you now need to open the executable you want to troubleshoot (Control.exe) by using the File menu, then profile it:

    blog Depends, Control Panel 01

    blog Depends, Control Panel 02

    blog Depends, Control Panel 03

    Look carefully at the options provided. To troubleshoot, you should at least have the fifth option and it's sub-options (Hook the process..), and the eighth option (Log debug output...). No Program arguments are necessary with this example, but you can provide them if appropriate for the executable your about to profile.

    Here's an example of a successfully completed profiling session of the Vista control panel. Note there were errors, as noted in the lower output pane, marked in red. But not all errors detected are a problem.

    blog Depends, Control Panel 04

    Also note that I didn't need to elevate again, as we were already running with the full Administrator security token. Had we not run DependencyWalker using Run as Administrator, we would have got an "Elevation Required" error 740 - the elevation prompt will only occur if the process is started using ShellExecute with it's runas verb.

    Final tip, you can select all of the text from the output pane, and paste it into WordPad as RTF, which will keep the embolding and colouring. Likewise, you can save the results into a DWI for later examination or reference.

    December 28

    ShellExec_RunDLL flags ?


    I was curious about wether I could use RunDLL32.exe to launch an executable via the Windows SHELL using the RunAs verb. That is, if you run RunDLL32.exe SHELL32.DLL,ShellExec_RunDLL Control.exe from a command prompt (or Run dialog), the Control Panel (in this example) will be launched using the SHELL's default, Open verb. So instead of using the default, I wanted to see wether I could somehow add to the command-line, and perhaps have it invoke the RunAs verb.

    I'll save you from the suspense now, and say no you can't. But what I did find is that ShellExec_RunDLL does support a way of passing in the SHELLEXECUTEINFO.fMask value. I don't know what use that is, but I think it's interesting nonetheless.

    Basically, you just surround the numeric "flag" you want to pass with question marks. The number can be either decimal, or hex (using the C notation). Like this:

    RunDLL32.exe SHELL32.DLL,ShellExec_RunDLL ?0x80?Control.exe

    The "flags" are documented in the SHELLEXECUTEINFO documentation, but the actual numeric values you need are only documented in the shellapi.h header file. So you'll need to have the Windows SDK installed, or at the very least, extracted, so you can recover them from the shellapi.h file. In this case, 0x80 is the value for the SEE_MASK_CONNECTNETDRV flag.

    That's it. But if your interested in how I went about finding this, or verifying my results, then read on.

    Debugging ShellExec_RunDLL

    To find out stuff like this, you naturally have to resort reverse engineering techniques. Whilst Microsoft, and most software authors make you agree to not doing this, in a lot of jurisdictions, common-law overrides such agreements. Anyway, I'm not a lawyer, and frankly, this kind of "violation" is not usually an issue, as your only doing so in a superficial way - not revealing any real Intellectual Property, like Kernel Mode stuff, etc.

    The first step is to decide what your going to use to step into the native code. As I'm not compiling any native code, myself, I installed and setup the latest Debugging Tools on my test Virtual PC. Unfortunately, I just updated that VPC with the latest Vista SP1rc, and it appears that Microsoft are yet to (if ever) make the appropriate debugging symbols available on their public symbol server.

    So in despair, I switched back to one of my Development systems running XP SP2. Although I'm specifically interested in a Vista solution, this would at least give me some familiarity with the code, hopefully making my Vista debugging session (without symbols) easier - and it did.

    Having said that, there may be some discrepancies in the screen-shots I'm about to show you. Specifically, you may notice that many of the addresses might be different between each debugging session (which is new feature of Vista), and I unfortunately, needed to restart my debugging session a number of times - though I did recapture many of the screen-shots, to try and avoid any such discrepancies.

    WinDbg, Vista, RunDll32, ShellExec_RunDLLW, Flags, 01 1. With the the Debugging Tools installed and the Symbols setup, you need to open the executable (RunDLL32.exe) and add the command-line. The Arguments field here is:
    SHELL32.DLL,ShellExec_RunDLL ?0x80?Control.exe.

    Obviously, I'm making it easier to debug by having the CPU execute the appropriate path by adding the question marks now. This is much easier than having to manually traverse the code like I did back on Windows XPsp2, as you'll see at step 5.

    2. Below, I've already setup and docked all of the usual Windows. My VPC is setup with a small desktop, so I've had to make do with the limited screen size. Notably, I've turned off the disassembly codes (.asm no_code_bytes), and setup the Memory pane to show the full command-line by having it traverse through the Program Environment Block ($peb), using the pointer operator to dereference each pointer plus offset. You could also have used the !peb command in the Command pane, etc. The Locals pane doesn't work at all - completely blank.

    blog WinDbg, Vista, RunDll32, ShellExec_RunDLLW, Flags, 02


    3. Add a Breakpoint (Alt-F9 key) for SHELL!ShellExec_RunDLLW. note the trailing 'W':

    WinDbg, Vista, RunDll32, ShellExec_RunDLLW, Flags, 03 

     

    4a. Now Run (F5 key). This should break on the entry into ShellExec_RunDLLW.

    In this screen-shot, I've stepped into the code to the point just after it's setup it's stack frame, and about to load the address of the command-line ; the line in the Disassembly pane with the blue highlight (the red highlight is the breakpoint). The Memory pane, on the right, shows the stack frame (based using the ebp register):

    blog WinDbg, Vista, RunDll32, ShellExec_RunDLLW, Flags, 04a


    blog WinDbg, Vista, RunDll32, ShellExec_RunDLLW, Flags, 04-14b. Here is the Memory pane setup to show the unprocessed portion of the command-line. Routines that are compatible with RunDLL32 must have this function signature:

    void CALLBACK EntryPoint(HWND hwnd, HINSTANCE hinst, 
    LPSTR lpszCmdLine, int nCmdShow);

    The third parameter starts on the twelfth Byte of the Stack, but this becomes the sixteenth Byte when you correct for the saved ebp register.

    The pointer operator dereferences this: poi(ebp+10)
    The '10' is hex. ie 16 in Decimal.

    FYI: If you trawl SHELL32.DLL (and other DLLs) using a program like DependencyWalker, I've found that the entry-points which have a suffix of _RunDLL are the ones compatible with RunDLL32.exe.

    5. This next screen-shot is where it begins processing the command-line, and it has just detected the first question mark. The Disassembly pane is stopped on the jne (jump if not equal) instruction, and if you look in the lower Command pane, at then end of the line, the [br=0] hint means that the branch will not be taken. Also, if you look at the extreme right, in the Registers pane, I've customised it so that it shows the zero flag (zf), the value of which is in red (has changed):

    blog WinDbg, Vista, RunDll32, ShellExec_RunDLLW, Flags, 07


    6. Next, the Windows StrToIntExW API function is just about to be called. Though to see this, you need to be reading the Command pane, which is able to figure out the name of the API call. Obviously the Disassembly view needs more work here, being disrupted by the lack  of symbols, as explained earlier.

    Also notice that I've changed the Memory pane to use the esi register, which the code has setup to point to the current Unicode character being parsed. In other words, the StrToIntExW function is about to convert the '0x80' from text into an Integer.

    blog WinDbg, Vista, RunDll32, ShellExec_RunDLLW, Flags, 08

    7a. Next, the result of the StrToIntExW is moved into the ebx register, and 0x100 (SEE_MASK_FLAG_DDEWAIT) is being ORed in.

    blog WinDbg, Vista, RunDll32, ShellExec_RunDLLW, Flags, 09

    7b. and a few instructions later, the closing question mark is located via a call to StrChrW.

    blog WinDbg, Vista, RunDll32, ShellExec_RunDLLW, Flags, 10

    8. Finally, in the Memory pane, we see the SHELLEXECUTEINFO structure setup, just before the call to ShellExecuteEx. Unfortunately, we have to deconstruct this chunk of memory without the aid of the dt command - this is also true even if we do have the appropriate symbols as Microsoft strip out the information of this structure from their symbols. D'oh !.

    Here's a table I use to aid the deconstruction:


    Source
    Stack
    Offset
    Offset
    (Visa)
    Offset
    (XP)
    typedef struct _SHELLEXECUTEINFO {
    DWORD cbSize;
    ULONG fMask;
    HWND hwnd;
    LPCTSTR lpVerb;
    LPCTSTR lpFile;
    LPCTSTR lpParameters;
    LPCTSTR lpDirectory;
    int nShow;
    HINSTANCE hInstApp;
    LPVOID lpIDList;
    LPCTSTR lpClass;
    HKEY hkeyClass;
    DWORD dwHotKey;
    union {
    HANDLE hIcon;
    HANDLE hMonitor;
    } DUMMYUNIONNAME;
    HANDLE hProcess;
    } // SIZE = 60 Bytes (0x3C)
    00
    04
    08
    0C
    10
    14
    18
    1C
    20
    24
    28
    2C
    30
    34



    38
    [ebp-4C]
    [ebp-48]
    [ebp-44]
    [ebp-40]
    [ebp-3C]
    [ebp-38]
    [ebp-34]
    [ebp-30]
    [ebp-2C]
    [ebp-28]
    [ebp-24]
    [ebp-20]
    [ebp-1C]
    [ebp-18]



    [ebp-14]
    [ebp-458]
    [ebp-454]
    [ebp-450]
    [ebp-44C]
    [ebp-448]
    [ebp-444]
    [ebp-440]
    [ebp-43C]
    [ebp-438]
    [ebp-434]
    [ebp-430]
    [ebp-42C]
    [ebp-428]
    [ebp-424]



    [ebp-420]

    Use the second column (above) when working with the Memory dump of the Stack (below).
    Use one of the other two columns (as appropriate) when working with the Disassembly pane.

    blog WinDbg, Vista, RunDll32, ShellExec_RunDLLW, Flags, 12

    The important bit to notice is that in the Disassembly pane, the ebx register in stored into fMask [ebp-48h] - the line immediately above the blue highlighted line. This ebx register was set back in step 7a, and it value has not been touched, since.

    December 18

    Contracting Work ?


    Wow, I've definitely had it too easy for the last decade and a half. I've been winding down my service business, so I can try to focus more on doing research and development - which was the real reason why I started my business in the first place. In all of that time, most of the work I got was through other peoples referrals, and mostly for service work - PC Hardware, Software, Networking, the whole gamut. The problem is, everything around me keeps going up in price, and I started to find myself being pulled back into the world of servicing PC's, as happen back when I first start my business 16+ years ago.

    So rather than have to take on new customers again, with all the inevitable long-term commitments, etc, I thought I could supplement my income my entering the contracting market. Boy was I wrong. You don't the qualifications that people are looking for, etc, etc. What a PITA.

    So here I am now, in an almost endless cycle of having to update and "refine" my Resume so I can convey my skills and extensive experience to the HR types, who are looking for "certifications", which I don't have. Worse still, I don't want to spend money on some poxy piece of paper for an area in IT that I'm trying to leave.

    What to do... suck it up I suppose. Not fun.

    Now, what's the software contracting side like...