{"id":44163,"date":"2015-04-24T07:00:00","date_gmt":"2015-04-24T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2015\/04\/24\/why-cant-i-have-variadic-com-methods\/"},"modified":"2015-04-24T07:00:00","modified_gmt":"2015-04-24T07:00:00","slug":"why-cant-i-have-variadic-com-methods","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20150424-00\/?p=44163","title":{"rendered":"Why can&#039;t I have variadic COM methods?"},"content":{"rendered":"<p>\nCOM methods cannot be variadic.\nWhy not?\n<\/p>\n<p>\nAnswer:\nBecause the marshaler doesn&#8217;t know when to stop.\n<\/p>\n<p>\nSuppose variadic COM methods were possible.\nAnd then you wrote this code:\n<\/p>\n<pre>\ninterface IVariadic\n{\n HRESULT Mystery([in] int code, ...);\n};\nIVariadic *variadic = something;\nuint32_t ipaddr;\nHRESULT hr = variadic-&gt;Mystery(9, 192, 168, 1, 1, &amp;ipaddr);\n<\/pre>\n<p>\nHow would COM know how to marshal this function call?\nIn other words, suppose that <code>variadic<\/code>\nis a pointer to a proxy that refers to an object in\nanother process.\nThe COM marshaler needs to take all the parameters\nto <code>IVariadic::Mystery<\/code>,\npackage them up,\nsend them to the other process,\nthen unpack the parameters,\nand pass them to the implementation.\nAnd then when the implementation returns,\nit needs to take the return value\nand any output parameters,\npackage them up,\nsend them back to the originating process,\nwhere they are unpacked and applied to the\noriginal parameters.\n<\/p>\n<p>\nConsider, for example,\n<\/p>\n<pre>\ninterface IDyadic\n{\n HRESULT Enigma([in] int a, [out] int *b);\n};\nIDyadic *dyadic = something;\nint b;\nHRESULT hr = dyadic-&gt;Enigma(1, &amp;b);\n<\/pre>\n<p>\nIf <code>dyadic<\/code> refers to an object\nin another process,\nthe marshaler does this:\n<\/p>\n<ul>\n<li>Allocate a block of memory containing the\n    following information:<\/p>\n<ul>\n<li>Information to identify the <code>dyadic<\/code>\n    object in the other process,<\/p>\n<li>the integer 1.\n    <\/ul>\n<li>Transmit that block of memory to the other process.\n<\/ul>\n<p>\nThe other process receives the block of memory and\ndoes the following:\n<\/p>\n<ul>\n<li>Use the information in the memory block\n    to identify the <code>dyadic<\/code> object.<\/p>\n<li>Extract the parameter <code>1<\/code> from the\n    memory block.<\/p>\n<li>Allocate a local integer variable, call it <code>x<\/code>.\n<li>Call <code>dyadic-&gt;Enigma(1, &amp;x)<\/code>.\n    Let&#8217;s say that the function stores 42 into <code>x<\/code>,\n    and it returns <code>E_PENDING<\/code>.<\/p>\n<li>Allocate a block of memory containing the following\n    information:<\/p>\n<ul>\n<li>The value <code>E_PENDING<\/code>\n    (the <code>HRESULT<\/code> returned by\n    <code>dyadic-&gt;Enigma<\/code>),<\/p>\n<li>The integer 42 (the value that <code>dyadic-&gt;Enigma<\/code>\n    stored in the local variable <code>x<\/code>).\n    <\/ul>\n<li>Transmit that block of memory to the originating process.\n<\/ul>\n<p>\nThe originating process receives the block of memory\nand does the following:\n<\/p>\n<ul>\n<li>Extracts the <code>HRESULT<\/code> <code>E_PENDING<\/code>.\n<li>Extracts the value 42.\n<li>Stores the value 42 into <code>b<\/code>.\n<li>Returns the value <code>E_PENDING<\/code> to the caller.\n<\/ul>\n<p>\nNote that in order for the marshaler to do its job,\nit needs to know every parameter to the method,\nwhether that parameter is an input parameter\n(which is sent from the originating process to the remote process),\nan output parameter\n(which is sent from the remote process to the originating process),\nand how to send that parameter.\nIn our case, the parameter is just an integer, so sending it is just\ncopying the bits,\nbut in the more general case, the parameter could be a more complicated\ndata structure.\n<\/p>\n<p>\nNow let&#8217;s look at that variadic method again.\nHow is the marshaler supposed to know what to do with the\n<code>...<\/code>?\nIt doesn&#8217;t know how many parameters it needs to transfer.\nIt doesn&#8217;t know what types those parameters are.\nIt doesn&#8217;t know which ones are input parameters\nand which ones are output parameters.\n<\/p>\n<p>\nIn order to know that, it would have to reverse-engineer the\nimplementation of the <code>IVariadic::Mystery<\/code>\nfunction and figure out that the first parameter, the\n<a HREF=\"http:\/\/en.wikipedia.org\/wiki\/Revolution_9\">\nnumber 9<\/a>,\nis a code that means that the method takes four 8-bit integers as\ninput and outputs a 32-bit integer.\n<\/p>\n<p>\nThis is a rather tall order for the client side of the marshaler,\nsince it has to do its work without access\nto the other process.\nIt would have to use its psychic powers to figure out how to package\nup the parameters, as well as how to unpack them afterward.\n<\/p>\n<p>\nTherefore, COM says,\n&#8220;Sorry, you can&#8217;t do that.&#8221;\n<\/p>\n<p>\nBut what you can do is encode the parameters in a form that\nthe marshaler understands.\nFor example, you might use a counted array of <code>VARIANT<\/code>s\nor a <code>SAFEARRAY<\/code>.\nThe COM folks already did the work to teach the marshaler how\nto, for example,\ndecode the <code>vt<\/code> member of the <code>VARIANT<\/code>\nand understand that,\n&#8220;Oh, if the value is <code>VT_I4<\/code>, then the <code>VARIANT<\/code>\ncontains a 32-bit signed integer.&#8221;\n<\/p>\n<p>\n<b>Bonus chatter<\/b>:\nBut wait,\n<a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/aa367304.aspx\">\nthere is a MIDL attribute called\n<code>[vararg]<\/code><\/a>.\nYou said that COM doesn&#8217;t support variadic methods,\nbut there is a MIDL keyword that says variadic right on the tin!\n<\/p>\n<p>\nAh, but that <code>[varargs]<\/code> attribute is just a sleight of hand\ntrick.\nBceause when you say <code>[varargs]<\/code>,\nwhat you&#8217;re saying is,\n&#8220;The last parameter of this method is a <code>SAFEARRAY<\/code>\nof <code>VARIANT<\/code>s.\nA scripting language can expose this method to scripts as variadic,\nbut what it actually does is take all the variadic parameters\nand store them into a <code>SAFEARRAY<\/code>,\nand then pass the <code>SAFEARRAY<\/code>.&#8221;\n<\/p>\n<p>\nIn other words, it indicates that the last parameter of the\nmethod acts like the C# <code>params<\/code> keyword.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>COM methods cannot be variadic. Why not? Answer: Because the marshaler doesn&#8217;t know when to stop. Suppose variadic COM methods were possible. And then you wrote this code: interface IVariadic { HRESULT Mystery([in] int code, &#8230;); }; IVariadic *variadic = something; uint32_t ipaddr; HRESULT hr = variadic-&gt;Mystery(9, 192, 168, 1, 1, &amp;ipaddr); How would COM [&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-44163","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>COM methods cannot be variadic. Why not? Answer: Because the marshaler doesn&#8217;t know when to stop. Suppose variadic COM methods were possible. And then you wrote this code: interface IVariadic { HRESULT Mystery([in] int code, &#8230;); }; IVariadic *variadic = something; uint32_t ipaddr; HRESULT hr = variadic-&gt;Mystery(9, 192, 168, 1, 1, &amp;ipaddr); How would COM [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/44163","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=44163"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/44163\/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=44163"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=44163"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=44163"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}