November, 2009

Posts
  • PFE - Developer Notes for the Field

    Creating non-user specific printer mappings

    • 0 Comments

    I was delivering a PowerShell class, and the question of how to create a remote printer mapping came up.

    Turns out that enterprise administrators may have the need to help users with their printer connections, setting them up for them.

    As I started thinking about the problem, it dawned on me that drive and printer mappings are user specific. They are stored as part of a user’s profile, and since the user profile does not get loaded until the user is actually logged on, looks like little can be done.

    Domain user accounts do have a couple of properties that allow administrators to establish a “Home” share, and to assign it a drive letter. Beyond that, they are on their own.

    However, there is a solution!

    For printers, in particular, turns out that there is a feature that allows setting up a “global” printer mapping. This mapping will be created for the computer, and any user that logs on will be able to use it (provided (s)he has permissions).

    The regular Add Printer wizard only allows to create a printer mapping for the user running the wizard:

    clip_image002

    So that will not help. However, the PrintUI.dll library exposes the functionality to create a computer specific printer mappings!

    Simply run the PrintUIEntry (case sensitive) entry point for that library using rundll32 and pass the appropriate parameters… and voilà, the printer mapping is created.

    What? How do you do that? Thanks for asking!

    Open a PowerShell window (if you still are in the old days, go ahead, use CMD.exe instead) and type:

    RunDLL32 PrintUI.dll PrintUIEntry /?

    to get a help dialog with information on usage:

    clip_image004

    To create a global mapping, use the Global Add command with the /n parameter:

    RunDLL32 PrintUI.dll PrintUIEntry /ga /n\\SERVER\PRINTER_NAME

    To delete the mapping, use the Global Delete command:

    RunDLL32 PrintUI.dll PrintUIEntry /gd /n\\SERVER\PRINTER_NAME

    If you want to remotely do this on another computer, the usage indicates that you can specify the computer name with the /c parameter:

    RunDLL32 PrintUI.dll PrintUIEntry /gd /c\\Computer /n\\SERVER\PRINTER_NAME

    However, this didn’t work for me, so I just used the first command but ran it remote”ly using PowerShell:

    $p = [WMICLASS]"//COMPUTER/root/CIMv2:WIN32_Process"

    $p.Create("RunDLL32 PrintUI.dll PrintUIEntry /ga /n\\SERVER\PRINTER_NAME")

    Note: Make sure that the Print Spooler service in the box where the new share was added is restarted after the addition/deletion of the mapping.

    I found this to be pretty handy, even in my home network.

    Happy printer mapping!

    Santiago

  • PFE - Developer Notes for the Field

    How do I dump out a string?

    • 0 Comments

    The topic came up a while back on one of our aliases – How can I dump out a string?

    There are a lot of ways do this in the debugger and there are also debugger extensions that will help out with.  What most people in WinDBG do is us the “du” command for Unicode strings and “da” for ASCII strings. However this has a problem in that output looks like:

    00000001`40234dd8 "Server=com-appdb-01\moss1;Databa"
    00000001`40234e18 "se=SharedServices2_55;Trusted_Co"
    00000001`40234e58 "nnection=yes;App=Windows SharePo"
    00000001`40234e98 "int Services;Timeout=15"

    You cannot easily cut and paste this into other places.  You have the addresses and such.  Also, this only handles a few lines.  If you check out the help for the du commands there is /c switch which says only dump the characters instead of all the characters.  You would end up with a command like - “du /c”.  To take it one step further you can actually create an alias:

    as !ds du /c 100 c+

    Then all you have to do is - !ds <address to String object>

    So this combines both aliases and come special switches.

    Thanks,

    Zach

  • PFE - Developer Notes for the Field

    What does High Performance Mean?

    • 0 Comments

    Over my years in PFE and Microsoft as a whole I have worked on some REALLY cool websites and applications that are used around the world and drive a lot of business.  Getting work on these cool projects is one of the main reasons that I love what I do.  Getting to see what cool problems people are solving with Microsoft technology.  One of those cases was highlighted a few months back on Channel9!

    http://channel9.msdn.com/shows/ARCast.TV/ARCastTV-Steve-Michelotti-of-eimagination-on-High-Performance-Web-Solutions/

    Steve Michelotti does a great job of explaining some of the stuff that we did and what the applications look like.  So check it out!

    Thanks,

    Zach

  • PFE - Developer Notes for the Field

    Passing a Managed Function Pointer to Unmanaged Code

    • 0 Comments

    I was working with a customer a while back and they had a situation where they wanted to be able to register a managed callback function with a native API.  This is not a big problem and the sample that I extended did this already.  The question that arose was – How do I pass a managed object to the native API so that it can pass that object to the callback function.  Then I need to be able to determine the type of the managed object because it is possible that any number of objects could get passed to this call back function.  I took the code sample that is provided on MSDN and extended it a bit to handle this.  Here is the original sample - http://msdn.microsoft.com/en-us/library/367eeye0.aspx 

    The first commend here is that this is some DANGEROUS territory.  It is really easy to corrupt things and get mess up.  For those that are used to manage code this is very much C++ territory where you have all the power and with that comes all of the rope that you need to hang your self.

    As I mentioned about the customer wanted to be able to pass different types of objects in the lpUserData so the callback can handle different types of data.  So the way to do this is with a GCHandle.  Basically this gives you a native handle the object.  You pass that around on the native size and when it arrives on the managed side you can “trade it in” for the managed object.

    So let’s say you have some sort of managed class.  In this case I called it “callbackData” and you want to pass it off. 

    callbackData^ cd = gcnew callbackData();

    First you need to get a “pointer” to the object that can be passed off:

    GCHandle gch2 = GCHandle::Alloc(cd);
    IntPtr ip2 = GCHandle::ToIntPtr(gch2);
    int obj = ip2.ToInt32();

    You need to keep the GCHandle around so you can free it up later.  It is very possible to leak GCHandles.  In addition GCHandles are roots to CLR objects and therefore you are rooting any memory referenced by the object you have the GCHandle for so the GC will not release any of that memory.  This however is not as bad as pinning the memory.  The GCHandle is a reference to the object that can be used later to retrieve the address of the actual object when needed.

    From the GCHandle you can convert that to a an IntPtr.  Once you have an Int from the IntPtr that you can pass around to the lpUserData parameter.  You could also just directly pass the IntPtr but I just wanted to demonstrate taking it all the way down to the int. 

    Then you can pass that value as part of an LPARAM or whatever you want to the native code. 

    int answer = TakesCallback(cb, 243, 257, obj);

    That will get passed to your callback that uses your delegate to call into your managed function.  The native code might do something simple like this:

    typedef int (__stdcall *ANSWERCB)(int, int, int);
    static ANSWERCB cb;
     
    int TakesCallback(ANSWERCB fp, int n, int m, int ptr) {
       cb = fp;
       if (cb) {
          printf_s("[unmanaged] got callback address (%d), calling it...\n", cb);
          return cb(n, m, ptr);
       }
       printf_s("[unmanaged] unregistering callback");
       return 0;
    }

    In inside your callback function you simply get back an object:

    public delegate int GetTheAnswerDelegate(int, int, int);
     
    int GetNumber(int n, int m, int ptr) {
       Console::WriteLine("[managed] callback!");
       static int x = 0;
       ++x;
     
       IntPtr ip2(ptr);
       GCHandle val = GCHandle::FromIntPtr(ip2);
       System::Object ^obj = val.Target;
       Console::WriteLine("Type - " + obj->GetType()->ToString() );
     
       return n + m + x;
    }

    Once you have the object you can cast it or do whatever you need to move forward.  This approach allows you to bridge across the managed and native world when you have a native function that makes a callback.  I will attach a complete sample for you to play with if you interested and let me know if there are any questions.  There are other ways to approach this I am sure but this seemed to work pretty well and as long as you manage you rGCHandles and callback references you should be good to go!

    Thanks,

    Zach

    REFERENCES

Page 1 of 1 (4 items)