<2019年7月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

文章分类

导航

订阅

实战C与C++符号差异导致的连接错误

为了测试一个与系统电源有关的问题,我写了一个小程序(MFC)使用了如下API:

BOOLEAN GetPwrCapabilities(
  PSYSTEM_POWER_CAPABILITIES lpSystemPowerCapabilities
);
MSDN对该函数的文档中有如下提示:

Requirements

Client: Included in Windows XP, Windows 2000 Professional, Windows Me, and Windows 98.
Server: Included in Windows Server 2003 and Windows 2000 Server.
Header: Declared in Powrprof.h.
Library: Use Powrprof.lib.
也就是需要包含Powrprof.h头文件,并在连接时使用Powrprof.lib。
但是以上两步都做了以后,编译没有问题,连接时却遇到以下错误:

BatViewDlg.obj : error LNK2001: unresolved external symbol "unsigned char __stdcall GetPwrCapabilities(struct SYSTEM_POWER_CAPABILITIES *)" (?GetPwrCapabilities@@YGEPAUSYSTEM_POWER_CAPABILITIES@@@Z)
BatViewDlg.obj : error LNK2001: unresolved external symbol "long __stdcall CallNtPowerInformation(enum POWER_INFORMATION_LEVEL,void *,unsigned long,void *,unsigned long)" (?CallNtPowerInformation@@YGJW4POWER_INFORMATION_LEVEL@@PAXK1K@Z)
Release/BatView.exe : fatal error LNK1120: 2 unresolved externals


开始怀疑是VC6的路径设置有问题,但检查并调整后也不见效,GOOGLE一下也没见什么有用信息。于是只好回过头来仔细分析错误信息。

首先看找不到函数符号(Symbol)是如下形式的。

unsigned char __stdcall GetPwrCapabilities(struct SYSTEM_POWER_CAPABILITIES *)" (?GetPwrCapabilities@@YGEPAUSYSTEM_POWER_CAPABILITIES@@@Z)

然后使用dumpbin工具观察powrprof.lib的输出:

c:\Program Files\Microsoft SDK\Lib>dumpbin /exports powrprof.lib
Microsoft (R) COFF Binary File Dumper Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.


Dump of file powrprof.lib

File Type: LIBRARY

     Exports

       ordinal    name

                  _CallNtPowerInformation@20
                  _CanUserWritePwrScheme@0
                  _DeletePwrScheme@4
                  _EnumPwrSchemes@8
                  _GetActivePwrScheme@4
                  _GetCurrentPowerPolicies@8
                  _GetPwrCapabilities@4
                  _GetPwrDiskSpindownRange@8
                  _IsAdminOverrideActive@4
                  _IsPwrHibernateAllowed@0
                  _IsPwrShutdownAllowed@0
                  _IsPwrSuspendAllowed@0
                  _LoadCurrentPwrScheme@16
                  _MergeLegacyPwrScheme@16
                  _ReadGlobalPwrPolicy@4
                  _ReadProcessorPwrScheme@8
                  _ReadPwrScheme@8
                  _SetActivePwrScheme@12
                  _SetSuspendState@12
                  _ValidatePowerPolicies@8
                  _WriteGlobalPwrPolicy@4
                  _WriteProcessorPwrScheme@8
                  _WritePwrScheme@16

  Summary

          C9 .debug$S
          14 .idata$2
          14 .idata$3
           4 .idata$4
           4 .idata$5
           E .idata$6

c:\Program Files\Microsoft SDK\Lib>


看了以后,恍然大悟。二者的符号形式大不相同,显然VC6需要的是C++格式的符号(有所谓的微软的装饰符 decoration),而powerprof.lib导出的是C语言的形式,不待任何修饰。

那么为什么会出现这个差异呢?一定是头文件出问题了。于是打开powerprof.h看,其前面的部分如下:


/*****************************************************************************\
*                                                                             *
* powrprof.h - - Interface for powrprof.dll, the power policy applicator      *
*                                                                             *
* Version 1.0                                                                 *
*                                                                             *
* Copyright (c) Microsoft Corporation. All rights reserved.                   *
*                                                                             *
\*****************************************************************************/


// Registry storage structures for the GLOBAL_POWER_POLICY data. There are two
// structures, GLOBAL_MACHINE_POWER_POLICY and GLOBAL_USER_POWER_POLICY. the
// GLOBAL_MACHINE_POWER_POLICY stores per machine data for which there is no UI.
// GLOBAL_USER_POWER_POLICY stores the per user data.

typedef struct _GLOBAL_MACHINE_POWER_POLICY{
    ULONG                   Revision;
    SYSTEM_POWER_STATE      LidOpenWakeAc;
    SYSTEM_POWER_STATE      LidOpenWakeDc;
    ULONG                   BroadcastCapacityResolution;
} GLOBAL_MACHINE_POWER_POLICY, *PGLOBAL_MACHINE_POWER_POLICY;
。。。。。。

果然缺少一般API头文件所常见的编译声明,比如:

/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    setupapi.h

Abstract:

    Public header file for Windows NT Setup and Device Installer services Dll.

--*/

#ifndef _INC_SETUPAPI
#define _INC_SETUPAPI

#if _MSC_VER > 1000
#pragma once
#endif

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

//
// determine version of setupapi based on _WIN32_WINDOWS and _WIN32_WINNT
//
// NT4 version of setupapi   (0x0400) is earliest, and installed onto Win95 by IE.
// Win2k version of setupapi (0x0500) also shipped in WinME
// we'll use "0x0410" to indicate version of setupapi shipped with Win98
//
#ifndef _SETUPAPI_VER
#if defined(_WIN32_WINNT) && (!defined(_WIN32_WINDOWS) || (_WIN32_WINNT < _WIN32_WINDOWS))
#define _SETUPAPI_VER _WIN32_WINNT  // SetupAPI version follows Windows NT version
#elif defined(_WIN32_WINDOWS)
#if _WIN32_WINDOWS >= 0x0490
#define _SETUPAPI_VER 0x0500        // WinME uses same version of SetupAPI as Win2k
#elif _WIN32_WINDOWS >= 0x0410
#define _SETUPAPI_VER 0x0410        // Indicates version of SetupAPI shipped with Win98
#else
#define _SETUPAPI_VER 0x0400        // Earliest SetupAPI version
#endif // _WIN32_WINDOWS
#else // _WIN32_WINNT/_WIN32_WINDOWS
#define _SETUPAPI_VER 0x0501
#endif // _WIN32_WINNT/_WIN32_WINDOWS
#endif // !_SETUPAPI_VER

#ifndef __LPGUID_DEFINED__
#define __LPGUID_DEFINED__
typedef GUID *LPGUID;
#endif

//
// Include commctrl.h for our use of HIMAGELIST and wizard support.
//
#include <commctrl.h>

#ifdef _WIN64
#include <pshpack8.h>   // Assume 8-byte (64-bit) packing throughout
#else
#include <pshpack1.h>   // Assume byte packing throughout (32-bit processor)
#endif

#ifdef __cplusplus
extern "C" {
#endif

//

其实最关键就是

#ifdef __cplusplus
extern "C" {
#endif

也就是告诉C++编译器后面的声明是C规范的。

于是将本来的直接包含头文件#include <Powrprof.h>修改为如下形式:

#ifdef  __cplusplus 
extern  "C"  { 
#endif  
   #include <Powrprof.h>
#ifdef  __cplusplus 

#endif 


问题果然消失。像这样的问题能不能说是微软头文件的BUG呢?如果不是,那么文档也应该有比较明显的说明吧?或许最新的SDK已经有了。但是无论如何我都感觉到SDK在变得越来越大的同时,问题也在增加。

 

 

posted on 2006年1月19日 15:32 由 Raymond

Powered by Community Server Powered by CnForums.Net