{"id":41543,"date":"2003-12-11T10:00:00","date_gmt":"2003-12-11T10:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2003\/12\/11\/how-do-i-pass-a-lot-of-data-to-a-process-when-it-starts-up\/"},"modified":"2003-12-11T10:00:00","modified_gmt":"2003-12-11T10:00:00","slug":"how-do-i-pass-a-lot-of-data-to-a-process-when-it-starts-up","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20031211-00\/?p=41543","title":{"rendered":"How do I pass a lot of data to a process when it starts up?"},"content":{"rendered":"<p>\nAs we discussed\n<a HREF=\"http:\/\/blogs.gotdotnet.com\/raymondc\/PermaLink.aspx\/4dfa171c-3dc6-4382-b38b-c82d391939f0\">yesterday<\/a>, if you need to pass more than\n32767 characters of information to a child process, you&#8217;ll\nhave to use something other than the command line.\n<\/p>\n<p>\nOne method is to wait for the child process to go input idle,\nthen <code>FindWindow<\/code> for some agreed-upon window and\nsend it a <code>WM_COPYDATA<\/code> message.  This method has\na few problems:\n<\/p>\n<ul>\n<li>You have to come up with some way of knowing that the\nchild process has created its window so you can start looking\nfor it.  (<code>WaitForInputIdle<\/code> is helpful here.)<\/p>\n<li>You have to make sure the window you found belongs to\nthe child process and isn&#8217;t just some other window which\nhappens to have the same name by coincidence.\nOr, perhaps, not by coincidence: If there is more than once\ninstance of the child process running, you will need to\nmake sure you&#8217;re talking to the right one.\n(<code>GetWindowThreadProcessId<\/code> is helpful here.)<\/p>\n<li>You have to hope that nobody else manages to find the\nwindow and send it the <code>WM_COPYDATA<\/code> before you do.\n(If they do, then they have effectively taken over your child\nprocess.)<\/p>\n<li>The child process needs to be alert for the possibility of\na rogue process sending bogus <code>WM_COPYDATA<\/code> messages in\nan attempt to confuse it.\n<\/ul>\n<\/p>\n<p>\nThe method I prefer is to use anonymous shared memory.\nThe idea is to create a shared memory block and fill it with\ngoodies. Mark the handle as inheritable, then spawn the child\nprocess, passing the numeric value of the handle on the command\nline. The child process parses the handle out of its command\nline and maps the shared memory block to see what&#8217;s in it.\n<\/p>\n<p>\nRemarks about this method:\n<\/p>\n<ul>\n<li>You need to be careful to validate the handle, in case\nsomebody tries to be sneaky and pass you something bogus\non your command line.<\/p>\n<li>In order to mess with your command line, a rogue process\nneeds PROCESS_VM_WRITE permission, and in order to mess\nwith your handle table, it needs PROCESS_DUP_HANDLE permission.\nThese are securable access masks, so proper choice of ACLs\nwill protect you.\n(And\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/security\/security\/security_descriptors_for_new_objects.asp\">\nthe default ACLs<\/a> are usually what you want anyway.)<\/p>\n<li>There are no names that can be squatted or values that\ncan be spoofed (assuming you&#8217;ve protected the process against\nPROCESS_VM_WRITE and PROCESS_DUP_HANDLE).<\/p>\n<li>Since you&#8217;re using a shared memory block, nothing actually\nis copied between the two processes; it is just remapped.\nThis is more efficient for large blocks of data.\n<\/ul>\n<\/p>\n<p>\nHere&#8217;s a sample program to illustrate the shared memory technique.\n<\/p>\n<pre>\n#include &lt;windows.h&gt;\n#include &lt;shlwapi.h&gt;\n#include &lt;strsafe.h&gt;\nstruct STARTUPPARAMS {\n    int iMagic;     \/\/ just one thing\n};\n<\/pre>\n<p>\nIn principle, the <code>STARTUPPARAMS<\/code> can be\narbitrarily complicated, but for illustrative purposes,\nI&#8217;m just going to pass a single integer.\n<\/p>\n<pre>\nSTARTUPPARAMS *CreateStartupParams(HANDLE *phMapping)\n{\n    STARTUPPARAMS *psp = NULL;\n    SECURITY_ATTRIBUTES sa;\n    sa.nLength = sizeof(sa);\n    sa.lpSecurityDescriptor = NULL;\n    <font COLOR=\"blue\">sa.bInheritHandle = TRUE<\/font>;\n    HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE,\n                &amp;sa, PAGE_READWRITE, 0,\n                <font COLOR=\"blue\">sizeof(STARTUPPARAMS)<\/font>, NULL);\n    if (hMapping) {\n        psp = (STARTUPPARAMS *)\n                    MapViewOfFile(hMapping, FILE_MAP_WRITE,\n                        0, 0, 0);\n        if (!psp) {\n            CloseHandle(hMapping);\n            hMapping = NULL;\n        }\n    }\n    *phMapping = hMapping;\n    return psp;\n}\n<\/pre>\n<p>\nThe <code>CreateStartupParams<\/code>\nfunction creates a <code>STARTUPPARAMS<\/code>\nstructure in an inheritable shared memory block.\nFirst, we fill out a\n<code>SECURITY_ATTRIBUTES<\/code> structure so we can mark the\nhandle as inheritable by child processes.  Setting the\n<code>lpSecurityDescriptor<\/code> to NULL means that we will\nuse the default security descriptor, which is fine for us.\nWe then create a shared memory object of the appropriate size,\nmap it into memory, and return both the handle and the\nmapped address.\n<\/p>\n<pre>\nSTARTUPPARAMS *GetStartupParams(LPSTR pszCmdLine, HANDLE *phMapping)\n{\n    STARTUPPARAMS *psp = NULL;\n    LONGLONG llHandle;\n    if (StrToInt64ExA(pszCmdLine, STIF_DEFAULT, &amp;llHandle)) {\n        *phMapping = (HANDLE)(INT_PTR)llHandle;\n        psp = (STARTUPPARAMS *)\n                MapViewOfFile(*phMapping, FILE_MAP_READ, 0, 0, 0);\n        if (psp) {\n            \/\/  Now that we've mapped it, do some validation\n            MEMORY_BASIC_INFORMATION mbi;\n            if (VirtualQuery(psp, &amp;mbi, sizeof(mbi)) &gt;= sizeof(mbi) &amp;&amp;\n                mbi.State == MEM_COMMIT &amp;&amp;\n                mbi.BaseAddress == psp &amp;&amp;\n                <font COLOR=\"blue\">mbi.RegionSize &gt;= sizeof(STARTUPPARAMS)<\/font>) {\n                \/\/ Success!\n            } else {\n                \/\/ Memory block was invalid - toss it\n                UnmapViewOfFile(psp);\n                psp = NULL;\n            }\n        }\n    }\n    return psp;\n}\n<\/pre>\n<p>\nThe <code>GetStartupParams<\/code>\nfunction is the counterpart to <code>CreateStartupParams<\/code>.\nIt parses a handle from the command line and attempts to map a view.\nIf the handle isn&#8217;t a file mapping handle, the call to\n<code>MapViewOfFile<\/code> will fail, so we get that part of the\nparameter validation for free.\nWe use <code>VirtualQuery<\/code> to validate the size of the memory\nblock.  (We can&#8217;t use a strict equality test since the value we\nget back will be rounded up to the nearest page multiple.)\n<\/p>\n<pre>\nvoid FreeStartupParams(STARTUPPARAMS *psp, HANDLE hMapping)\n{\n    UnmapViewOfFile(psp);\n    CloseHandle(hMapping);\n}\n<\/pre>\n<p>\nAfter we&#8217;re done with the startup parameters (either on the\ncreation side or the consumption side), we need to free them\nto avoid a memory leak.\nThat&#8217;s what <code>FreeStartupParams<\/code> is for.\n<\/p>\n<pre>\nvoid PassNumberViaSharedMemory(HANDLE hMapping)\n{\n    TCHAR szModule[MAX_PATH];\n    TCHAR szCommand[MAX_PATH * 2];\n    DWORD cch = GetModuleFileName(NULL, szModule, MAX_PATH);\n    if (cch &gt; 0 &amp;&amp; cch &lt; MAX_PATH &amp;&amp;\n        SUCCEEDED(StringCchPrintf(szCommand, MAX_PATH * 2,\n                  TEXT(\"\\\"%s\\\" %I64d\"), szModule,\n                  (INT64)(INT_PTR)hMapping))) {\n        STARTUPINFO si = { sizeof(si) };\n        PROCESS_INFORMATION pi;\n        if (CreateProcess(szModule, szCommand, NULL, NULL,\n                          <font COLOR=\"blue\">TRUE<\/font>,\n                          0, NULL, NULL, &amp;si, &amp;pi)) {\n            CloseHandle(pi.hProcess);\n            CloseHandle(pi.hThread);\n        }\n    }\n}\n<\/pre>\n<p>\nMost of the work here is just building the command line.\nWe run ourselves (using\n<a HREF=\"http:\/\/blogs.gotdotnet.com\/raymondc\/PermaLink.aspx\/4bfc253c-a714-42f1-bdc5-db9deff3075f\">\nthe <code>GetModuleFileName(NULL)<\/code>\ntrick<\/a>), passing the numerical value of the handle on the command\nline, and passing <code>TRUE<\/code> to <code>CreateProcess<\/code>\nto indicate that we want inheritable handles to be inherited.\nNote the extra quotation marks in case our program&#8217;s name contains\na space.\n<\/p>\n<pre>\nint CALLBACK\nWinMain(HINSTANCE hinst, HINSTANCE hinstPrev,\n        LPSTR pszCmdLine, int nShowCmd)\n{\n    HANDLE hMapping;\n    STARTUPPARAMS *psp;\n    if (pszCmdLine[0]) {\n        psp = GetStartupParams(pszCmdLine, &amp;hMapping);\n        if (psp) {\n            TCHAR sz[64];\n            StringCchPrintf(sz, 64, TEXT(\"%d\"), psp-&gt;iMagic);\n            MessageBox(NULL, sz, TEXT(\"The Value\"), MB_OK);\n            FreeStartupParams(psp, hMapping);\n        }\n    } else {\n        psp = CreateStartupParams(&amp;hMapping);\n        if (psp) {\n            psp-&gt;iMagic = 42;\n            PassNumberViaSharedMemory(hMapping);\n            FreeStartupParams(psp, hMapping);\n        }\n    }\n    return 0;\n}\n<\/pre>\n<p>\nAt last we put it all together.\nIf we have a command line parameter, then this means that we\nare the child process, so we convert it into a\n<code>STARTUPPARAMS<\/code> and display the number that was\npassed.\nIf we don&#8217;t have a command line parameter, then this means\nthat we are the parent process, so we create a\n<code>STARTUPPARAMS<\/code>, stuff the magic number into it\n(42, of course), and pass it to the child process.\n<\/p>\n<p>\nSo there you have it: Passing a &#8220;large&#8221; (well, okay, small in\nthis example, but it could have been megabytes if you wanted)\namount of data securely to a child process.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As we discussed yesterday, if you need to pass more than 32767 characters of information to a child process, you&#8217;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 [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-41543","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>As we discussed yesterday, if you need to pass more than 32767 characters of information to a child process, you&#8217;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 [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/41543","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=41543"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/41543\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=41543"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=41543"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=41543"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}