{"id":23593,"date":"2008-02-04T10:00:00","date_gmt":"2008-02-04T10:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2008\/02\/04\/dll-forwarding-is-not-the-same-as-delay-loading\/"},"modified":"2008-02-04T10:00:00","modified_gmt":"2008-02-04T10:00:00","slug":"dll-forwarding-is-not-the-same-as-delay-loading","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20080204-00\/?p=23593","title":{"rendered":"DLL forwarding is not the same as delay-loading"},"content":{"rendered":"<p>\nAs I noted earlier,\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2006\/07\/19\/671238.aspx\">\nwhen you\ncreate a forwarder entry in an export table,\nthe corresponding target DLL is not loaded until somebody\nlinks to the forwarder entry<\/a>.\nIt looks like\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2006\/07\/19\/671238.aspx#685293\">\nsome people misread this statement to suggest some sort of\ndelay-loading<\/a>\nso I&#8217;m going to state it again with an example in mind in the\nhopes of clearing up any confusion\n(and risking creating more confusion than I clear up).\n<\/p>\n<p>\nSuppose that you have a DLL called <code>A.DLL<\/code> that has a forwarder\nentry to <code>B.DLL<\/code>:\n<\/p>\n<pre>\n; A.DEF\nEXPORTS\n  Dial = B.Call\n  Pour\n  Refill\n<\/pre>\n<p>\nThis specifies that if somebody wants the function <code>Dial<\/code>\nfrom <code>A.DLL<\/code>, they will actually get the function\n<code>Call<\/code> from <code>B.DLL<\/code>.\nThe delay-load-like behavior is that <code>B.DLL<\/code> is not\nloaded until somebody asks for the <code>Dial<\/code> function.\n<\/p>\n<p>\nI will use the notation <code>DLLNAME!FunctionName<\/code> to mean\n&#8220;the function <code>FunctionName<\/code> from the DLL named\n<code>DLLNAME<\/code>.&#8221;\nThis is the notation used by the <code>ntsd<\/code> debugger.\n<\/p>\n<p>\nConsider this program:\n<\/p>\n<pre>\nPOURME.EXE\n Imports from A.DLL\n  Pour\n  Refill\n<\/pre>\n<p>\nThe <code>POURME<\/code> program will not result in <code>B.DLL<\/code>\nbeing loaded since it never links to <code>A!Dial<\/code>.\nOf course <code>A.DLL<\/code> will get loaded because the program\nwants the functions <code>A!Pour<\/code> and <code>A!Refill<\/code>.\nThis is the &#8220;delay-load-like behavior&#8221; I mentioned in the original\nentry:\nIf you don&#8217;t call a function that forwards to <code>B.DLL<\/code>,\nthen <code>B.DLL<\/code> won&#8217;t get loaded.\n<\/p>\n<p>\nAlternative, you could have used this method to do the forwarding:\n<\/p>\n<pre>\n; A2.DEF\nEXPORTS\n Dial\n Pour\n Refill\n\/* a2.c *\/\n\/\/ Forward Dial to B!Call\nHRESULT Dial()\n{\n return Call();\n}\n<\/pre>\n<p>\nThis pseudo-forwarder is not a forwarder in the linker sense;\nit is an attempt to emulate linker forwarding in code.\nNow let&#8217;s look at the corresponding alternate <code>POURME<\/code> program:\n<\/p>\n<pre>\nPOURME2.EXE\n Imports from A2.DLL\n  Pour\n  Refill\n<\/pre>\n<p>\nEven though <code>POURME2<\/code> doesn&#8217;t call <code>A2!Dial<\/code>,\nthe file <code>B.DLL<\/code> will nevertheless be loaded when\n<code>POURME2<\/code> runs because <code>A2.DLL<\/code> contains\na dependency on <code>B.DLL<\/code> in its own import table:\n<\/p>\n<pre>\n; dump of headers of A2.DLL\n Imports from B.DLL\n  Call\n<\/pre>\n<p>\nLoading <code>A2.DLL<\/code> will cause <code>B.DLL<\/code> to be\nloaded since <code>B.DLL<\/code> is listed as one of <code>A2<\/code>&#8216;s\ndependencies.\n<\/p>\n<p>\nCommenter bruteforce got off on the wrong foot by calling the above\nmechanism a delay-loading feature.\n<\/p>\n<blockquote CLASS=\"q\"><p>\nI tried to take advantage of the delay-loading feature\ndescribed above for the forwarder DLLs&#8230;\n<\/p><\/blockquote>\n<p>\nThe mechanism is not delay-loading and I never said that it was.\nThe quasi-delay-load behavior is that a forwarded-to DLL is not\nloaded until somebody links to it.\nThe term delay-loading typically is used to apply to delaying the\nload of a module until a function in that module is called.\nBut import resolution happens at load time, not run time.\n<\/p>\n<p>\nCommenter bruteforce tried to create a forwarder to a nonexistent\nfunction, and then tried to link to the forwarder DLL.\nAs we saw above, this triggers an attempt to resolve the forward\nby loading the forwarded-to DLL and looking for the function.\nIf this fails, then the original import request is declared to have\nfailed.\nThis all happens as part of the import resolution process.\nAnd as we saw many years ago,\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2003\/09\/16\/54938.aspx\">\nWin32 fails a module load if an import cannot be resolved<\/a>.\nSince the forwarder cannot be resolved, the load fails.\nImport forwarding functionality is completely unsuitable for\nfunctions whose presence you wish to detect and respond to at runtime.\nAs with all imports, an import failure is considered a fatal error.\nIf you want delay-loading, then you need to do delay-loading.\nForwarding is not delay-loading.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As I noted earlier, when you create a forwarder entry in an export table, the corresponding target DLL is not loaded until somebody links to the forwarder entry. It looks like some people misread this statement to suggest some sort of delay-loading so I&#8217;m going to state it again with an example in mind in [&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-23593","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>As I noted earlier, when you create a forwarder entry in an export table, the corresponding target DLL is not loaded until somebody links to the forwarder entry. It looks like some people misread this statement to suggest some sort of delay-loading so I&#8217;m going to state it again with an example in mind in [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/23593","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=23593"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/23593\/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=23593"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=23593"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=23593"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}