{"id":105387,"date":"2021-07-01T07:00:00","date_gmt":"2021-07-01T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105387"},"modified":"2021-07-01T07:43:27","modified_gmt":"2021-07-01T14:43:27","slug":"20210701-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210701-00\/?p=105387","title":{"rendered":"Studying linker error messages to find the cause of the unresolved external: Character sets"},"content":{"rendered":"<p>A customer was encountering an unresolved external linker error when trying to link a plug-in with a static library.<\/p>\n<pre>\/\/ header.h\r\n#define UNICODE\r\n#define <a title=\"TEXT vs. _TEXT vs. _T, and UNICODE vs. _UNICODE\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040212-00\/?p=40643\">_UNICODE<\/a>\r\n#include &lt;windows.h&gt;\r\n\r\nBOOL SetSessionName(LPCTSTR name);\r\n<\/pre>\n<p>This header file is used both by the implementation:<\/p>\n<pre>\/\/ implementation.cpp\r\n#include \"pch.h\"\r\n#include &lt;header.h&gt;\r\n\r\nBOOL SetSessionName(LPCTSTR name)\r\n{\r\n    \/* ... *\/\r\n}\r\n<\/pre>\n<p>And it is used by the plug-in:<\/p>\n<pre>\/\/ plugin.cpp\r\n#include \"pch.h\"\r\n#include &lt;header.h&gt;\r\n\r\nvoid Initialize()\r\n{\r\n    SetSessionName(TEXT(\"Fred\"));\r\n}\r\n<\/pre>\n<p>The result when building the plug-in is an unresolved external:<\/p>\n<pre style=\"white-space: pre-wrap;\">LNK2019 unresolved external \"int __cdecl SetSessionName(char const*)\" (?SetSessionName@@YAHPBD@Z)\r\n<\/pre>\n<p>What&#8217;s going on? The header file sets Unicode as the default before including the <code>windows.h<\/code> header file, so everything should be Unicode, shouldn&#8217;t it?<\/p>\n<p>Let&#8217;s look at what the error message is telling us. It says that the plug-in wants a function that takes a <code>char const*<\/code>, which is what <code>LPCTSTR<\/code> maps to when ANSI is the default. So somehow the Unicode setting isn&#8217;t sticking when the plug-in is using it.<\/p>\n<p>I usd my psychic powers to guess that the plug-in had already performed its own <code>#include &lt;windows.h&gt;<\/code> before including <code>header.h<\/code>, and that initial inclusion of <code>windows.h<\/code> was done with ANSI as the default character set. The <code>header.h<\/code> is changing the character set too late.<\/p>\n<p>Okay, so now that we understand the problem, how do we solve it?<\/p>\n<p>One option is to give up on ANSI. Just be all-Unicode all the time. After all, any plug-in that is ANSI-based is going to have problems with file names, user names, all sorts of things.<\/p>\n<p>But the customer said that they wanted to support both ANSI and Unicode plug-ins. Mind you, I&#8217;m not sure I believe that, seeing as their header file tried to <code>#define UNICODE<\/code> to force Unicode, but I&#8217;m going to take them at their word. Maybe the <code>#define UNICODE<\/code> was just an experiment.<\/p>\n<p>If you want to support either character set, then you need to define two versions, one for each character set. (While you&#8217;re at it, specify a calling convention already.) Classic Win32 uses C-style bindings, so you would have to decorate the function names manually:<\/p>\n<pre>EXTERN_C BOOL WINAPI SetSessionNameA(LPCSTR name);\r\nEXTERN_C BOOL WINAPI SetSessionNameW(LPCWSTR name);\r\n<\/pre>\n<p>Your implementation file would have to implement both the <a href=\"https:\/\/awrestaurants.com\/\"> A and W<\/a> versions.<\/p>\n<p>Another option is to use C++ decoration and overloads.<\/p>\n<pre>BOOL WINAPI SetSessionName(LPCSTR name);\r\nBOOL WINAPI SetSessionName(LPCWSTR name);\r\n<\/pre>\n<p>The downside of this is that it requires the plug-in to use the same compiler that your framework is written in. This is generally not a great idea, since your customers will probably have a preferred toolchain, and forcing them to use a specific compiler (and perhaps even a specific version of a specific compiler) will make it harder for you to make friends.<\/p>\n<p>Even worse: If your toolchain is the Microsoft Visual C compiler, then you have to deal with the <tt>\/Zc:wchar_t<\/tt> option, which means <a title=\"What is __wchar_t (with the leading double underscores) and why am I getting errors about it?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20161201-00\/?p=94836\"> bringing <code>__wchar_t<\/code> into the picture<\/a>.<\/p>\n<pre>BOOL WINAPI SetSessionName(LPCSTR name);\r\nBOOL WINAPI SetSessionName(unsigned short const* name);\r\nBOOL WINAPI SetSessionName(__wchar_t const* name);\r\n<\/pre>\n<p>You now have to implement three versions of the function, although you presumably could have them all be wrappers around a common helper that takes <code>LPCWSTR<\/code>.<\/p>\n<p><b>Related reading<\/b>: <a title=\"Diagnosing a problem with calling conventions\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040706-00\/?p=38543\"> Diagnosing a problem with calling conventions<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Puzzling out why the symbols don&#8217;t match exactly.<\/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-105387","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Puzzling out why the symbols don&#8217;t match exactly.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105387","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=105387"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105387\/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=105387"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105387"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105387"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}