Open NFC - Error LNK2019: unresolved external symbol __imp__ShellExecuteW@24

1.6k views Asked by At

I have an error and I don´t have idea what´s the problem.

I'm trying to compile one example of Open NFC for a project. The example is test_ndef_url. I do not know if the problem to a coding error or a library of windows is due, shellapi.h.

Test_ndef_url code is:

    /*
 * Copyright (c) 2007-2012 Inside Secure, All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*******************************************************************************

  Implementation of the Example 1 for Open NFC.

*******************************************************************************/

/* Allow use of features specific to Windows XP or later. */
#ifndef _WIN32_WINNT
/* Change this to the appropriate value to target other versions of Windows. */
#define _WIN32_WINNT 0x0601
#endif

/* Suppress many files from the compilation */
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#include "shellapi.h"
#include <stdio.h>
#include <stdlib.h>

#include "open_nfc.h"

#include "win32_test_core.h"

/* Type definititions */
/* ------------------ */

/* Local prototypes */
/* ---------------- */

/* Global variables */
/* ---------------- */

struct __testContext
{
   char16_t*      pURL;
   bool_t        bVirtualTag;
   bool_t        bReadTest;
   W_HANDLE    hRegistry;
} ;

struct __testContext g_testCtx = { null, W_FALSE, W_FALSE, W_NULL_HANDLE };

/* Callback function of type tWBasicGenericHandleCallbackFunction */
static void ReadMessageOnAnyTagCompletedURL(void *pCallbackParameter, W_HANDLE hMessage, W_ERROR nError)
{
   if(nError == W_SUCCESS)
   {
      W_HANDLE hRecord = WNDEFGetRecord(hMessage, 0);
      WBasicCloseHandle(hMessage);

      if(WRTDIsURIRecord(hRecord))
      {
         char16_t aURIValue[256];

         if(WRTDURIGetValue(hRecord, (char16_t*)&aURIValue, 256) == W_SUCCESS)
         {
            printf("The URL read in the tag is \"%S\"\n", aURIValue);

            ShellExecute(NULL, L"open", (char16_t*)aURIValue, NULL, NULL, SW_SHOWNORMAL);
         }
      }
      else
      {
         printf("Error URL: Error detected in the tag message.\n");
      }

      WBasicCloseHandle(hRecord);
   }
   else if(nError == W_ERROR_ITEM_NOT_FOUND)
   {
      printf("Error URL: This tag does not contain a message of the right type.\n");
   }
   else if(nError == W_ERROR_TIMEOUT)
   {
      printf("Error URL: Lost the communication with the tag.\n");
   }
   else
   {
      printf("Error URL: Error 0x%08X returned during the tag detection.\n", nError);
   }

   printf("Present a new tag to be read or press Ctrl+C to stop the application\n");
}

/* Callback function of type tWBasicGenericCallbackFunction */
static void WriteMessageOnAnyTagCompleted(void *pCallbackParameter, W_ERROR nError)
{
   switch(nError)
   {
      case W_SUCCESS:
         printf("The message is written in the tag.\n");
         break;
      case W_ERROR_LOCKED_TAG:
         printf("The operation failed because the tag is locked.\n");
         break;
      case W_ERROR_TAG_FULL:
         printf("The message is too large for the remaining free space in the tag.\n");
         break;
      default:
         printf("An error occured during the writing operation (code 0x%x)\n", nError);
         break;
   }
   StopApplication();
}

/* Receive the event of the virtual tag */
static void VirtualTagEventHandler(void *pCallbackParameter, uint32_t nEventCode)
{
   switch(nEventCode)
   {
      case W_VIRTUAL_TAG_EVENT_SELECTION:
         printf("The tag is selected by the reader.\n");
         break;
      case W_VIRTUAL_TAG_EVENT_READER_LEFT:
         printf("The reader left the tag without reading the content.\n");
         break;
      case W_VIRTUAL_TAG_EVENT_READER_READ_ONLY :
         printf("The reader read the tag.\nPresent again a reader to read the virtual tag or press Ctrl+C to stop the application\n");
         break;
      case W_VIRTUAL_TAG_EVENT_READER_WRITE:
      default:
         printf("This event should not occur for a read-only virtual tag.\n");
         break;
   }
}

/**
 * GetSpecificTestSyntax
 *
 * @return     String describing the command line syntax specific to this test
 **/
char16_t * GetSpecificTestSyntax( void )
{
   return L"[url <URL> | virtualurl <URL>\n"
          L" - If the parameter is not present, the application waits to read a tag with an URL.\n"
          L" - If \"url\" is present, the application waits for a Tag to write the URL.\n"
          L" - If \"virtualurl\" is present, the application simulates a tag containing the URL.\n\n"
          L"The following tags are supported: Type 2, Type 4-A, Type 4-B, Type 5-2K, Type 5-32K and Type 6.\n\n" ;
}

/**
 * VerifyTestConditions
 *
 * @param[in]  nArgc       number of arguments
 *
 * @param[in]  pArgv       arguments array
 *
 * @return     W_SUCCESS   Arguments syntax is correct
 *             W_ERROR_xxx is syntax error is detected
 **/

W_ERROR VerifyTestConditions( int32_t nArgc, char16_t* pArgv[] )
{
   if(nArgc == 3)
   {
      if(wcscmp(pArgv[1], L"url") == 0)
      {
         g_testCtx.pURL = pArgv[2];
      }
      else if(wcscmp(pArgv[1], L"virtualurl") == 0)
      {
         g_testCtx.bVirtualTag = W_TRUE;
         g_testCtx.pURL = pArgv[2];
      }
      else
      {
         nArgc = 0;
      }
   }

   if((nArgc != 1) && (nArgc != 3))
   {
      return W_ERROR_BAD_PARAMETER;
   }

   g_testCtx.bReadTest = (nArgc == 1) ? W_TRUE : W_FALSE ;

   return W_SUCCESS;
}

/**
 * LaunchTest
 *
 **/

W_ERROR LaunchTest( void )
{
   W_HANDLE hMessage = W_NULL_HANDLE;

   if(g_testCtx.bReadTest != W_FALSE)
   {
      WNDEFReadMessageOnAnyTag(ReadMessageOnAnyTagCompletedURL, null, W_PRIORITY_MAXIMUM,
         W_NDEF_TNF_WELL_KNOWN, L"U", &g_testCtx.hRegistry);

      printf("Present the tag to be read or press Ctrl+C to stop the application\n");
   }
   else
   {
      if(WNDEFCreateNewMessage(&hMessage) != W_SUCCESS)
      {
         printf("Error: Cannot create the new message.\n");
         goto return_function;
      }
      if(WRTDURIAddRecord(hMessage, g_testCtx.pURL) != W_SUCCESS)
      {
         printf("Error: Cannot add the URI record in the message.\n");
         goto return_function;
      }

      if(g_testCtx.bVirtualTag == W_FALSE)
      {
         printf("Ready to write the URL \"%S\"\n", g_testCtx.pURL);

         WNDEFWriteMessageOnAnyTag(WriteMessageOnAnyTagCompleted, null, W_PRIORITY_MAXIMUM, hMessage, W_NDEF_ACTION_BIT_ERASE | W_NDEF_ACTION_BIT_FORMAT_ALL, &g_testCtx.hRegistry);

         printf("Present the tag to be written or press Ctrl+C to stop the application\n");
      }
      else
      {
         W_ERROR nError;
         W_HANDLE hVirtualTag;
         static const uint8_t aPUPI[] = { 0x01, 0x02, 0x03, 0x04 };

         printf("Ready to simulate a tag with the URL \"%S\"\n", g_testCtx.pURL);

         /* Create the virtual tag, the maximum length is set to the size of the message */
         nError = WVirtualTagCreate(W_PROP_NFC_TAG_TYPE_4_B, aPUPI, sizeof(aPUPI),
            WNDEFGetMessageLength(hMessage), &hVirtualTag);
         if(nError != W_SUCCESS)
         {
            printf("Cannot create the virtual tag\n");
         }
         else
         {
            /* Write the message in the virtual tag */
            nError = WNDEFWriteMessageSync(hVirtualTag, hMessage, W_NDEF_ACTION_BIT_ERASE | W_NDEF_ACTION_BIT_LOCK | W_NDEF_ACTION_BIT_FORMAT_ALL);
            if(nError != W_SUCCESS)
            {
               printf("Cannot write the message in the virtual tag\n");
               WBasicCloseHandle(hVirtualTag);
            }
            else
            {
               /* Start the tag simulation */
               nError = WVirtualTagStartSync(hVirtualTag, VirtualTagEventHandler, null, W_TRUE);
               if(nError != W_SUCCESS)
               {
                  printf("Cannot activate the virtual tag\n");
                  WBasicCloseHandle(hVirtualTag);
               }
               else
               {
                  printf("Present a reader to read the virtual tag or press Ctrl+C to stop the application\n");
               }
            }
         }
      }
   }

return_function:

   WBasicCloseHandle(hMessage);

   /* Go pending on WaitForSingleObject (Card/Tag operation completed or Ctrl-C) */

   return W_SUCCESS;
}

/**
 * CloseTest
 *
 **/
void CloseTest( void )
{
   WBasicCloseHandle( g_testCtx.hRegistry );
}

The part of the code is defined shellapi.h where ShellExecute is:

    #include <winapifamily.h>

/*****************************************************************************\
*                                                                             *
* shellapi.h -  SHELL.DLL functions, types, and definitions                   *
*                                                                             *
* Copyright (c) Microsoft Corporation. All rights reserved.                   *
*                                                                             *
\*****************************************************************************/

#ifndef _INC_SHELLAPI
#define _INC_SHELLAPI

#include <SpecStrings.h>

//
// Define API decoration for direct importing of DLL references.
//
#ifndef WINSHELLAPI
#if !defined(_SHELL32_)
#define WINSHELLAPI       DECLSPEC_IMPORT
#else
#define WINSHELLAPI
#endif
#endif // WINSHELLAPI

#ifndef SHSTDAPI
#if !defined(_SHELL32_)
#define SHSTDAPI          EXTERN_C DECLSPEC_IMPORT HRESULT STDAPICALLTYPE
#define SHSTDAPI_(type)   EXTERN_C DECLSPEC_IMPORT type STDAPICALLTYPE
#else
#define SHSTDAPI          STDAPI
#define SHSTDAPI_(type)   STDAPI_(type)
#endif
#endif // SHSTDAPI

#ifndef SHDOCAPI
#if !defined(_SHDOCVW_)
#define SHDOCAPI          EXTERN_C DECLSPEC_IMPORT HRESULT STDAPICALLTYPE
#define SHDOCAPI_(type)   EXTERN_C DECLSPEC_IMPORT type STDAPICALLTYPE
#else
#define SHDOCAPI          STDAPI
#define SHDOCAPI_(type)   STDAPI_(type)
#endif
#endif // SHDOCAPI


#if !defined(_WIN64)
#include <pshpack1.h>
#endif

#ifdef __cplusplus
extern "C" {            /* Assume C declarations for C++ */
#endif  /* __cplusplus */


#pragma region Desktop Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)



DECLARE_HANDLE(HDROP);

_Success_(return != 0)
SHSTDAPI_(UINT) DragQueryFileA(_In_ HDROP hDrop, _In_ UINT iFile, _Out_writes_opt_(cch) LPSTR lpszFile, _In_ UINT cch);
_Success_(return != 0)
SHSTDAPI_(UINT) DragQueryFileW(_In_ HDROP hDrop, _In_ UINT iFile, _Out_writes_opt_(cch) LPWSTR lpszFile, _In_ UINT cch);
#ifdef UNICODE
#define DragQueryFile  DragQueryFileW
#else
#define DragQueryFile  DragQueryFileA
#endif // !UNICODE
SHSTDAPI_(BOOL) DragQueryPoint(_In_ HDROP hDrop, _Out_ POINT *ppt);
SHSTDAPI_(void) DragFinish(_In_ HDROP hDrop);
SHSTDAPI_(void) DragAcceptFiles(_In_ HWND hWnd, _In_ BOOL fAccept);

SHSTDAPI_(HINSTANCE) ShellExecuteA(_In_opt_ HWND hwnd, _In_opt_ LPCSTR lpOperation, _In_ LPCSTR lpFile, _In_opt_ LPCSTR lpParameters,
    _In_opt_ LPCSTR lpDirectory, _In_ INT nShowCmd);
SHSTDAPI_(HINSTANCE) ShellExecuteW(_In_opt_ HWND hwnd, _In_opt_ LPCWSTR lpOperation, _In_ LPCWSTR lpFile, _In_opt_ LPCWSTR lpParameters,
    _In_opt_ LPCWSTR lpDirectory, _In_ INT nShowCmd);
#ifdef UNICODE
#define ShellExecute  ShellExecuteW
#else
#define ShellExecute  ShellExecuteA
#endif // !UNICODE
_Success_(return > 32) // SE_ERR_DLLNOTFOUND
SHSTDAPI_(HINSTANCE) FindExecutableA(_In_ LPCSTR lpFile, _In_opt_ LPCSTR lpDirectory, _Out_writes_(MAX_PATH) LPSTR lpResult);
_Success_(return > 32) // SE_ERR_DLLNOTFOUND
SHSTDAPI_(HINSTANCE) FindExecutableW(_In_ LPCWSTR lpFile, _In_opt_ LPCWSTR lpDirectory, _Out_writes_(MAX_PATH) LPWSTR lpResult);
#ifdef UNICODE
#define FindExecutable  FindExecutableW
#else
#define FindExecutable  FindExecutableA

And that tells me the error Microsoft Visual Studio 2013 is:

Warning 1   warning LNK4075: ignoring '/EDITANDCONTINUE' due to '/SAFESEH' specification    File:win32_test_core.obj    Project:test_ndef_url

Error   2   error LNK2019: unresolved external symbol __imp__ShellExecuteW@24 referenced in function _ReadMessageOnAnyTagCompletedURL   File:win32_test_ndef_url.obj    Project:test_ndef_url

Error   3   error LNK1120: 1 unresolved externals   File:test_ndef_url.exe  Project:test_ndef_url

Please, I need help with this.

1

There are 1 answers

1
yzt On BEST ANSWER

You are missing the actual body (or a placeholder for a function body) of the ShellExecute function (OK, ShellExecuteW, but that's a detail you don't need to worry about now.) What's in the header is the declaration, but you still need the definition. In this case, that's in a library.

Do these:

  1. Google for "MSDN ShellExecute". MSDN is Microsoft Developer Network, and the place to go for Windows API documentation.
  2. The first link is to https://msdn.microsoft.com/en-us/library/windows/desktop/bb762153%28v=vs.85%29.aspx (for now; Microsoft's websites have been awful at keeping links stable for future reference.)
  3. That's the documentation of the function. Scroll down to the bottom of the page. There is a table that lists the "requirements" for this function. One of them is the "Library", which is "Shell32.lib" in this case.
  4. You need to link to this library. The easiest way, in Visual C++ (which you are using) and for a standard Windows library (which this is) is to add the following line to one of your source files (maybe at the very top): #pragma comment (lib, "Shell32"). (Note the lack of a semicolon.)

and that's it. There are better (more scalable, flexible and portable) methods for larger projects, but you should be fine for this small sample.