• The Old New Thing

    How do I pass a lot of data to a process when it starts up?

    • 18 Comments

    As we discussed yesterday, if you need to pass more than 32767 characters of information to a child process, you'll have to use something other than the command line.

    One method is to wait for the child process to go input idle, then FindWindow for some agreed-upon window and send it a WM_COPYDATA message. This method has a few problems:

    • You have to come up with some way of knowing that the child process has created its window so you can start looking for it. (WaitForInputIdle is helpful here.)
    • You have to make sure the window you found belongs to the child process and isn't just some other window which happens to have the same name by coincidence. Or, perhaps, not by coincidence: If there is more than once instance of the child process running, you will need to make sure you're talking to the right one. (GetWindowThreadProcessId is helpful here.)
    • You have to hope that nobody else manages to find the window and send it the WM_COPYDATA before you do. (If they do, then they have effectively taken over your child process.)
    • The child process needs to be alert for the possibility of a rogue process sending bogus WM_COPYDATA messages in an attempt to confuse it.

    The method I prefer is to use anonymous shared memory. The idea is to create a shared memory block and fill it with goodies. Mark the handle as inheritable, then spawn the child process, passing the numeric value of the handle on the command line. The child process parses the handle out of its command line and maps the shared memory block to see what's in it.

    Remarks about this method:

    • You need to be careful to validate the handle, in case somebody tries to be sneaky and pass you something bogus on your command line.
    • In order to mess with your command line, a rogue process needs PROCESS_VM_WRITE permission, and in order to mess with your handle table, it needs PROCESS_DUP_HANDLE permission. These are securable access masks, so proper choice of ACLs will protect you. (And the default ACLs are usually what you want anyway.)
    • There are no names that can be squatted or values that can be spoofed (assuming you've protected the process against PROCESS_VM_WRITE and PROCESS_DUP_HANDLE).
    • Since you're using a shared memory block, nothing actually is copied between the two processes; it is just remapped. This is more efficient for large blocks of data.

    Here's a sample program to illustrate the shared memory technique.

    #include <windows.h>
    #include <shlwapi.h>
    #include <strsafe.h>
    
    struct STARTUPPARAMS {
        int iMagic;     // just one thing
    };
    

    In principle, the STARTUPPARAMS can be arbitrarily complicated, but for illustrative purposes, I'm just going to pass a single integer.

    STARTUPPARAMS *CreateStartupParams(HANDLE *phMapping)
    {
        STARTUPPARAMS *psp = NULL;
        SECURITY_ATTRIBUTES sa;
        sa.nLength = sizeof(sa);
        sa.lpSecurityDescriptor = NULL;
        sa.bInheritHandle = TRUE;
        HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE,
                    &sa, PAGE_READWRITE, 0,
                    sizeof(STARTUPPARAMS), NULL);
        if (hMapping) {
            psp = (STARTUPPARAMS *)
                        MapViewOfFile(hMapping, FILE_MAP_WRITE,
                            0, 0, 0);
            if (!psp) {
                CloseHandle(hMapping);
                hMapping = NULL;
            }
        }
    
        *phMapping = hMapping;
        return psp;
    }
    

    The CreateStartupParams function creates a STARTUPPARAMS structure in an inheritable shared memory block. First, we fill out a SECURITY_ATTRIBUTES structure so we can mark the handle as inheritable by child processes. Setting the lpSecurityDescriptor to NULL means that we will use the default security descriptor, which is fine for us. We then create a shared memory object of the appropriate size, map it into memory, and return both the handle and the mapped address.

    STARTUPPARAMS *GetStartupParams(LPSTR pszCmdLine, HANDLE *phMapping)
    {
        STARTUPPARAMS *psp = NULL;
        LONGLONG llHandle;
        if (StrToInt64ExA(pszCmdLine, STIF_DEFAULT, &llHandle)) {
            *phMapping = (HANDLE)(INT_PTR)llHandle;
            psp = (STARTUPPARAMS *)
                    MapViewOfFile(*phMapping, FILE_MAP_READ, 0, 0, 0);
            if (psp) {
                //  Now that we've mapped it, do some validation
                MEMORY_BASIC_INFORMATION mbi;
                if (VirtualQuery(psp, &mbi, sizeof(mbi)) >= sizeof(mbi) &&
                    mbi.State == MEM_COMMIT &&
                    mbi.BaseAddress == psp &&
                    mbi.RegionSize >= sizeof(STARTUPPARAMS)) {
                    // Success!
                } else {
                    // Memory block was invalid - toss it
                    UnmapViewOfFile(psp);
                    psp = NULL;
                }
            }
        }
        return psp;
    }
    

    The GetStartupParams function is the counterpart to CreateStartupParams. It parses a handle from the command line and attempts to map a view. If the handle isn't a file mapping handle, the call to MapViewOfFile will fail, so we get that part of the parameter validation for free. We use VirtualQuery to validate the size of the memory block. (We can't use a strict equality test since the value we get back will be rounded up to the nearest page multiple.)

    void FreeStartupParams(STARTUPPARAMS *psp, HANDLE hMapping)
    {
        UnmapViewOfFile(psp);
        CloseHandle(hMapping);
    }
    

    After we're done with the startup parameters (either on the creation side or the consumption side), we need to free them to avoid a memory leak. That's what FreeStartupParams is for.

    void PassNumberViaSharedMemory(HANDLE hMapping)
    {
        TCHAR szModule[MAX_PATH];
        TCHAR szCommand[MAX_PATH * 2];
        DWORD cch = GetModuleFileName(NULL, szModule, MAX_PATH);
        if (cch > 0 && cch < MAX_PATH &&
            SUCCEEDED(StringCchPrintf(szCommand, MAX_PATH * 2,
                      TEXT("\"%s\" %I64d"), szModule,
                      (INT64)(INT_PTR)hMapping))) {
            STARTUPINFO si = { sizeof(si) };
            PROCESS_INFORMATION pi;
            if (CreateProcess(szModule, szCommand, NULL, NULL,
                              TRUE,
                              0, NULL, NULL, &si, &pi)) {
                CloseHandle(pi.hProcess);
                CloseHandle(pi.hThread);
            }
        }
    }
    

    Most of the work here is just building the command line. We run ourselves (using the GetModuleFileName(NULL) trick), passing the numerical value of the handle on the command line, and passing TRUE to CreateProcess to indicate that we want inheritable handles to be inherited. Note the extra quotation marks in case our program's name contains a space.

    int CALLBACK
    WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
            LPSTR pszCmdLine, int nShowCmd)
    {
        HANDLE hMapping;
        STARTUPPARAMS *psp;
        if (pszCmdLine[0]) {
            psp = GetStartupParams(pszCmdLine, &hMapping);
            if (psp) {
                TCHAR sz[64];
                StringCchPrintf(sz, 64, TEXT("%d"), psp->iMagic);
                MessageBox(NULL, sz, TEXT("The Value"), MB_OK);
                FreeStartupParams(psp, hMapping);
            }
        } else {
            psp = CreateStartupParams(&hMapping);
            if (psp) {
                psp->iMagic = 42;
                PassNumberViaSharedMemory(hMapping);
                FreeStartupParams(psp, hMapping);
            }
        }
        return 0;
    }
    

    At last we put it all together. If we have a command line parameter, then this means that we are the child process, so we convert it into a STARTUPPARAMS and display the number that was passed. If we don't have a command line parameter, then this means that we are the parent process, so we create a STARTUPPARAMS, stuff the magic number into it (42, of course), and pass it to the child process.

    So there you have it: Passing a "large" (well, okay, small in this example, but it could have been megabytes if you wanted) amount of data securely to a child process.

  • The Old New Thing

    What is the command line length limit?

    • 16 Comments

    It depends on whom you ask.

    The maximum command line length for the CreateProcess function is 32767 characters. This limitation comes from the UNICODE_STRING structure.

    CreateProcess is the core function for creating processes, so if you are talking directly to Win32, then that's the only limit you have to worry about. But if you are reaching CreateProcess by some other means, then the path you travel through may have other limits.

    If you are using the CMD.EXE command processor, then you are also subject to the 8192 character command line length limit imposed by CMD.EXE.

    If you are using the ShellExecute/Ex function, then you become subject to the INTERNET_MAX_URL_LENGTH (around 2048) command line length limit imposed by the ShellExecute/Ex functions. (If you are running on Windows 95, then the limit is only MAX_PATH.)

    While I'm here, I may as well mention another limit: The maximum size of your environment is 32767 characters. The size of the environment includes the all the variable names plus all the values.

    Okay, but what if you want to pass more than 32767 characters of information to a process? You'll have to find something other than the command line. We'll discuss some options tomorrow.

  • The Old New Thing

    Scoble's rant on UI defaults

    • 33 Comments

    Robert Scoble posted an entry in his Longhorn blog on the subject of what the UI defaults should be. It sure has stirred up a lot of controvery. I may pick at the remarks over the upcoming days, but for now I posted responses to two of the comments he kicked up.

    We recently did a survey of users of all abilities. Beginners, intermediates, experts: The number one complaint all of them had about the user interface - 30% of all respondents mentioned this, evenly spread across all categories - was "Too many icons on the desktop." So it's not just beginners. Experts also don't like the clutter. (Yes, I was surprised by the results, too.)
  • The Old New Thing

    Why you should never suspend a thread

    • 8 Comments

    It's almost as bad as terminating a thread.

    Instead of just answering a question, I'm going to ask you the questions and see if you can come up with the answers.

    Consider the following program, in (gasp) C#:

    using System.Threading;
    using SC = System.Console;
    
    class Program {
      public static void Main() {
        Thread t = new Thread(new ThreadStart(Program.worker));
        t.Start();
        SC.WriteLine("Press Enter to suspend");
        SC.ReadLine();
        t.Suspend();
        SC.WriteLine("Press Enter to resume");
        SC.ReadLine();
        t.Resume();
      }
      static void worker() {
        for (;;) SC.Write("{0}\r", System.DateTime.Now);
      }
    }
    

    When you run this program and hit Enter to suspend, the program hangs. But if you change the worker function to just "for(;;) {}" the program runs fine. Let's see if we can figure out why.

    The worker thread spends nearly all its time calling System.Console.WriteLine, so when you call Thread.Suspend(), the worker thread is almost certainly inside the System.Console.WriteLine code.

    Q: Is the System.Console.WriteLine method threadsafe?

    Okay, I'll answer this one: Yes. I didn't even have to look at any documentation to figure this out. This program calls it from two different threads without any synchronization, so it had better be threadsafe or we would be in a lot of trouble already even before we get around to suspending the thread.

    Q: How does one typically make an object threadsafe?

    Q: What is the result of suspending a thread in the middle of a threadsafe operation?

    Q: What happens if - subsequently - you try to access that same object (in this case, the console) from another thread?

    These results are not specific to C#. The same logic applies to Win32 or any other threading model. In Win32, the process heap is a threadsafe object, and since it's hard to do very much in Win32 at all without accessing the heap, suspending a thread in Win32 has a very high chance of deadlocking your process.

    So why is there even a SuspendThread function in the first place?

    Debuggers use it to freeze all the threads in a process while you are debugging it. Debuggers can also use it to freeze all but one thread in a process, so you can focus on just one thread at a time. This doesn't create deadlocks in the debugger since the debugger is a separate process.
  • The Old New Thing

    If FlushInstructionCache doesn't do anything, why do you have to call it?

    • 7 Comments

    If you look at the implementation of FlushInstructionCache on Windows 95, you'll see that it's just a return instruction. It doesn't actually do anything. So why do you have to call it?

    Because the act of calling it is the whole point. The control transfers implicit in calling a function suffice to flush the instruction cache on a Pentium. The function doesn't have to do anything else; it is fact that you called a function that is important.
  • The Old New Thing

    What's with the catcow and dogoldfish?

    • 26 Comments

    Am I the only one who find these icons bizarro?

    The catcow
    The dogoldfish

    The first time I saw them, I thought the first one was a cow and the second one was a goldfish. But apparently they're a cat and a dog.

    I never understood the need for these emoticons in the first place. If you need to add a smiley face to indicate that you're joking, then you need to work on your delivery.
  • The Old New Thing

    At least she represents the United States faithfully

    • 5 Comments

    The MPR show Marketplace (note: MPR, not NPR) had a brief story about the reaction to China being the host of the Miss World pageant. (Last story on the page.)

    What I noticed was the comment at timecode 23:50 from Miss World - United States Kim Harlan. (Not to be confused with Miss United States Julie Roberts or Miss America Erika Harold. Confused yet? Slate explains.)

    Where was I? Oh right, the actuality. Asked to comment on participating in a beauty pageant in China: [Listen]

    I just though everybody would be wearing, little, you know, Asian outfits, but they're completely normal, just the way we dress, so.
    I hope she wasn't planning to win big in the interview portion of the competition.
  • The Old New Thing

    Why do I have to return this goofy value for WM_DEVICECHANGE?

    • 12 Comments

    To deny a device removal query, you must return the special value BROADCAST_QUERY_DENY, which has the curious value 0x424D5144. What's the story behind that?

    Well, we first tried following the pattern set by WM_QUERYENDSESSION, where returning TRUE allows the operation to proceed and returning FALSE causes the operation to fail. But when we did this, we found that lots of programs were denying all Plug and Play removal requests - programs that were written for Windows 3.1 which didn't have Plug and Play! How could this be?

    These programs decided, "Well, I have the Windows 3.1 SDK right here in front of me and I looked at all the messages. The ones I care about, I handled, and for all the others, I will just return zero instead of calling DefWindowProc." And they managed to get this to work in Windows 3.1 because they read the SDK carefully and found the five or six messages that require a nonzero return value and made sure to return that nonzero value. The rest got zero.

    And then when we added a new message that required a nonzero return value (which DefWindowProc provided), these programs continued to return zero and caused all device removal queries to fail.

    So we had to change the "cancel" return value to something that wasn't zero. To play it extra safe, we also made the "cancel" return value something other than 1, since we suspected that there would be lots of programs who were just returning TRUE to all messages and we didn't want to have to rewrite the specification twice.

  • The Old New Thing

    Which access rights bits belong to whom?

    • 2 Comments

    Each ACE in a security descriptor contains a 32-bit access mask. Which bits belong to whom?

    The access rights mask is a 32-bit value. The upper 16 bits are defined by the operating system and the lower 16 bits are defined by the object being secured.

    For example, consider the value 0x00060002 for the access rights mask. This breaks down as the system-defined access rights WRITE_DAC (0x00040000), READ_CONTROL (0x00020000), and one object-defined access right 0x0002.

    The object-defined access right 0x0002 depends on the object. This particular access right might mean any of the following:

    meaningif the object is a...
    FILE_WRITE_DATAfile
    FILE_ADD_FILEdirectory
    PROCESS_CREATE_THREADprocess
    THREAD_SUSPEND_RESUMEthread
    JOB_OBJECT_SET_ATTRIBUTESjob object
    EVENT_MODIFY_STATEevent
    SEMAPHORE_MODIFY_STATEsemaphore
    TIMER_MODIFY_STATEtimer
    IO_COMPLETION_MODIFY_STATEI/O completion port
    KEY_SET_VALUEregistry key
    TOKEN_DUPLICATEtoken
    WINSTA_READATTRIBUTESwindowstation
    DESKTOP_CREATEWINDOWdesktop

    or it could mean something else entirely if it's an object of a type not listed above.

    If you ask the ConvertSecurityDescriptorToStringSecurityDescriptor function to convert a security descriptor to a string security descriptor, it tries to guess what the object is, but since there is so little information to go on, it usually guesses wrong. The access rights mask above, for example, would be rendered by SDDL as "DCRCWD". The rights RC = READ_CONTROL, WD = WRITE_DAC are standard across all objects, so there is no guessing there. But SDDL guessed that 0x0002 was DC = ADS_RIGHTS_DS_DELETE_CHILD.

    Notice that there are some system-defined access rights that are named "GENERIC", such as GENERIC_READ and GENERIC_WRITE. Each object exposes different "read-like", "write-like", and possibly "execute-like" access rights (for example, registry keys have KEY_QUERY_VALUE and KEY_SET_VALUE), but they all have to define which ones are read-like, which ones are write-like, and which ones are execute-like, so that you can request one of the GENERIC access masks and get access appropriate to the type of object you are opening.

  • The Old New Thing

    Which window style bits belong to whom?

    • 6 Comments

    There are 64 bits of styles in the parameters to CreateWindowEx. Which ones belong to whom?

    Windows defines the meanings of the high word of the dwStyle parameter and all of the bits in the dwExStyle parameter. The low 16 bits of the dwStyle parameter are defined by the implementor of the window class (by the person who calls RegisterClass).

    In Windows 95, we found several apps that noticed that some bits in the dwExStyle weren't being used, so they decided to use them for themselves. Then when we added meanings to those bits (such as WS_EX_TOOLWINDOW), these programs started acting funny.

    So don't be like those programs. Don't use bits that don't belong to you.

    If you need more than 16 bits of style information, you can design your class so additional information is passed in the lpParam parameter (the last one) to CreateWindow/Ex, which can be received from the lpCreateParams member of the CREATSTRUCT structure. Alternatively, you can set additional styles with a custom message, such as listview's LVM_SETEXTENDEDLISTVIEWSTYLE message.
Page 417 of 432 (4,318 items) «415416417418419»