1

我正在尝试创建一个用于 VBA 程序的 C++ DLL。我正在关注这个示例,并成功编译了示例代码并使用了生成的 DLL。但是,我需要向 DLL 添加一些附加功能,因此我在示例代码中创建了更多功能并重新编译了它。然后我制作了一个测试程序来测试我的新功能。当我尝试从我的测试项目中调用一些 DLL 函数时,我得到类似于以下的链接器错误:

error LNK2019: unresolved external symbol "int __stdcall PWCreateDocument(long,char *,char *)" (?PWCreateDocument@@YGHJPAD0@Z) referenced in function _wmain

当我调用函数来初始化 ProjectWise、CVbaHelperApp::InitInstance() 和我的自定义函数 PWCreateDocument 时,会发生此错误。

当我调用 PWGetLastErrorMessage() 时,不会发生此错误。我可以从我的测试程序中访问此函数,但不能访问 DLL 中的任何其他函数。

我已经排除了任何常见的链接器错误,例如函数头和定义之间的拼写错误/错误类型。

我觉得奇怪的是我可以成功调用 PWGetLastErrorMesssage 但不能成功调用任何其他函数。

这是我的测试程序 vbaHelperTest3.cpp 的代码:

#include "stdafx.h"

typedef long LONG;
typedef int BOOL;


int _tmain(int argc, _TCHAR* argv[])
{
    char* filePath = "C:\\pwworking\\cemvn\\b2edsjga\\d0572507\\";
    char* fileName = "BUMP Imagery 2009.xwms";
    LONG projID = 572507;
    char* errorMsg;

    std::cout << "Hello World" << std::endl;
    CVbaHelperApp myApp;
    BOOL isInit = myApp.InitInstance();
    std::cout << "Is Initialized? " << isInit << std::endl;

    errorMsg = PWGetLastErrorMessage();
    std::cout << "Error Message: " << errorMsg << std::endl;

    BOOL results = PWCreateDocument(projID, filePath, fileName);
    std::cout << "PWCreateDocument Result: " << results << std::endl;
    return 0;
}

标头 stdafx.h 包含我的 DLL 的标头 vbaHelper.h。这是 vbaHelper.h 的代码:

// vbaHelper.h : main header file for the VBAHELPER DLL
//
#include "stdafx.h"
#if      !defined(AFX_VBAHELPER_H__3FBDA9E4_EE8B_4AA1_8FD0_21A0A8B6C590__INCLUDED_)
#define AFX_VBAHELPER_H__3FBDA9E4_EE8B_4AA1_8FD0_21A0A8B6C590__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif

#include "resource.h"       // main symbols


/////////////////////////////////////////////////////////////////////////////
// CVbaHelperApp
// See vbaHelper.cpp for the implementation of this class
//

class CVbaHelperApp : public CWinApp
{
public:
    CVbaHelperApp();

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CVbaHelperApp)
    public:
    virtual BOOL InitInstance();
    virtual int ExitInstance();
    //}}AFX_VIRTUAL

    //{{AFX_MSG(CVbaHelperApp)
        // NOTE - the ClassWizard will add and remove member functions here.
        //    DO NOT EDIT what you see in these blocks of generated code !
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};


/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.


//Function definitions added by me
typedef int BOOL;
typedef long LONG;
LONG __stdcall PWGetDocumentName( LONG , LONG , VOID **);
LONG __stdcall PWGetDocumentIDs( TCHAR **, LONG *, LONG *);
BOOL __stdcall PWCreateDocument( LONG, char*, char*);
char * __stdcall PWGetLastErrorMessage(void);


#endif //     !defined(AFX_VBAHELPER_H__3FBDA9E4_EE8B_4AA1_8FD0_21A0A8B6C590__INCLUDED_)

最后,这里是 vbaHelper.cpp 的代码,DLL:

/****************************************************************************
*
*             ProjectWise(TM) Software Development Kit
*             Sample Application
*             Copyright (C) 2003 Bentley Systems, Incorporated
*             All Rights Reserved
*
****************************************************************************/

/****************************************************************************
*
* Project Name: VbaHelper
*
* Project Description: This example is used in conjunction with MicroStation's
*                      VBA to extract a Design file's attributes.
*
* File name: VbaHelper.cpp
*
* File description: Custom Module implementation
*
****************************************************************************/

/*---------------------------------------------------------------------------
        Copyright (C) 2003 Bentley Systems, Incorporated
                  All Rights Reserved

      THIS IS AN OPEN SOURCE CODE OF BENTLEY SYSTEMS, INCORPORATED 

  You have a royalty-free right to use, modify, reproduce and distribute
  the Sample Applications (and/or any modified version) in any way you find
  useful, provided that you agree that Bentley Systems, Incorporated has no
  warranty obligations or liability for any Sample Application files which
  are modified.

  No guarantees of performance accompany Sample Application, nor is any 
  responsibility assumed on the part of the author(s). The software has
  been tested extensively and every effort has been made to insure its
  reliability.

---------------------------------------------------------------------------*/

/****************************************************************************
*
*   Include Files
*
****************************************************************************/
#include "stdafx.h"
#include "vbaHelper.h"
#include "aaatypes.h"
#include "aadmsdef.h"
#include "aawddef.h"
#include "aawindef.h"   
#include "aaodsdef.h"
#include "stdtypes.h"

#include "aadmsapi.fdf"
#include "aawinapi.fdf"
#include "aawindms.fdf"
#include "aaodsapi.fdf"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define DLLEXPORT __declspec( dllexport )
#define WINAPI __stdcall

//
//  Note!
//
//      If this DLL is dynamically linked against the MFC
//      DLLs, any functions exported from this DLL which
//      call into MFC must have the AFX_MANAGE_STATE macro
//      added at the very beginning of the function.
//
//      For example:
//
//      extern "C" BOOL PASCAL EXPORT ExportedFunction()
//      {
//          AFX_MANAGE_STATE(AfxGetStaticModuleState());
//          // normal function body here
//      }
//
//      It is very important that this macro appear in each
//      function, prior to any calls into MFC.  This means that
//      it must appear as the first statement within the 
//      function, even before any object variable declarations
//      as their constructors may generate calls into the MFC
//      DLL.
//
//      Please see MFC Technical Notes 33 and 58 for additional
//      details.
//

/////////////////////////////////////////////////////////////////////////////
// CVbaHelperApp

BEGIN_MESSAGE_MAP(CVbaHelperApp, CWinApp)
    //{{AFX_MSG_MAP(CVbaHelperApp)
        // NOTE - the ClassWizard will add and remove mapping macros here.
        //    DO NOT EDIT what you see in these blocks of generated code!
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CVbaHelperApp construction

CVbaHelperApp::CVbaHelperApp()
    {
    // TODO: add construction code here,
    // Place all significant initialization in InitInstance
    }

/////////////////////////////////////////////////////////////////////////////
// The one and only CVbaHelperApp object

CVbaHelperApp theApp;

/*----------------------------------------------------------------------+
|
| name          mcmMain_GetDocumentIdByFilePath
|
| author        BSI                                      04/2003
|
| Description   This function finds document and project 
|               numbers of the document specified by its path.
| 
+----------------------------------------------------------------------*/
extern "C" int mcmMain_GetDocumentIdByFilePath
(
LPWSTR pchFilePath, /* i  full file path to search */
long   *plProNo,    /* o  project id               */
long   *plDocNo     /* o  document id              */
);

/*----------------------------------------------------------------------+
|
| name          HooksInitialize
|
| author        BSI                                      04/2003
|
| Description   Dll entry function for ProjectWise.
| 
+----------------------------------------------------------------------*/
extern "C" LONG HooksInitialize
(
ULONG   ulMask,     // i Application Mask   
LPVOID  lpReserved  // i Reserved (must be NULL)
)
    {
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    return IDOK;
    }

/*----------------------------------------------------------------------+
|
| name          PWGetDocumentName
|
| author        BSI                                      04/2003
|
| Description   A function that will populate documentName for the given 
|               DOCUMENT_ID.
|
| Return        SUCCESS -  The path and file name of the specified document 
|                          were built successfully.
|
|               -1      -  Failed to build the path and file name of the 
|                          specified document.
| 
+----------------------------------------------------------------------*/
LONG WINAPI PWGetDocumentName 
(
LONG  PROJECT_ID,       /* i Project ID*/
LONG  DOCUMENT_ID,      /* i Document ID */
VOID  **documentName    /* o Document Name*/
) 
    {
    BOOL    status = FALSE;
    TCHAR   tempDocName[MAX_STRING];

    // Extract the document's name
    status = aaApi_GetDocumentFileName (PROJECT_ID, DOCUMENT_ID, tempDocName, MAX_STRING);
    _tcscpy ((TCHAR*)(*documentName), tempDocName);

    return (status == TRUE ? SUCCESS : -1);
    }

/*----------------------------------------------------------------------+
|
| name          PWGetDocumentIDs
|
| author        BSI                                      04/2003
|
| Description   A function that will return the document's 
|               Project and Document IDs.
|
| Return        SUCCESS or error number
| 
+----------------------------------------------------------------------*/
LONG WINAPI PWGetDocumentIDs 
(
TCHAR   **fileName, /* i Desgin File Name */
LONG    *ProjectID, /* o Project ID */
LONG    *DocumentID /* o Document ID */
)
    {
    return mcmMain_GetDocumentIdByFilePath (*fileName, ProjectID, DocumentID);
    }

/*----------------------------------------------------------------------+
|
| name          convertCharArrayToLPCWSTR
|
| author        MY CUSTOM FUNCTION                             10/2015
|
| Description   Converts regular string to LPCWSTR, which
|               is required by projectwise.
|
| Return        The converted string.
| 
+----------------------------------------------------------------------*/
wchar_t *convertCharArrayToLPCWSTR(const char* charArray)
{
wchar_t * wString=new wchar_t[4096];
MultiByteToWideChar(CP_ACP, 0, charArray, -1, wString, 4096);
return wString;
}

/*----------------------------------------------------------------------+
|
| name          PWCreateDocument
|
| author        MY CUSTOM FUNCTION                             10/2015
|
| Description   A function that will create a new document in the
|               specified PW project.
|
| Return        SUCCESS or error number
| 
+----------------------------------------------------------------------*/

BOOL WINAPI PWCreateDocument
(
LONG  PROJECT_ID,       /* i Project ID*/ 
char* PATH_NAME,        /* path of document */
char* FILE_NAME     /* name of document */
)
    {

        LONG    docID = 0L;
        //LONG  lngAppID = aaApi_GetFExtensionApplication(L"xwms");
        LONG    lngAppID = aaApi_GetFExtensionApplication(L"pdf");
        LONG    lngWorkSpaceID = aaApi_GetWorkspaceProfileId(PROJECT_ID, 0);
        LPCWSTR _path_name = convertCharArrayToLPCWSTR(PATH_NAME);
        LPCWSTR _file_name = convertCharArrayToLPCWSTR(FILE_NAME);
        WCHAR strWorkingDir[_MAX_PATH];   // for checked out file locationmemset (strWorkingDir, '\0', _MAX_PATH);
        BOOL status = aaApi_CreateDocument(
            &docID, //new document's ID
            PROJECT_ID, //Passed in project ID
            0, //default
            0, //default
            0, //default
            lngAppID, //Applicaiton ID
            0, //no department
            lngWorkSpaceID, //workspace profile
            _path_name, //source file
            _file_name, //Name of file in PW, must be the same as Document Name
            _file_name, //Document Name
            NULL, //Document description
            NULL, //Document Version
            FALSE, //Specifies that this document is checked out to the user after it is create in PW.
            AADMSDOCCREF_DEFAULT, //Checks documentaiton for flags
            //_path_name, //location of the file if checked out
            strWorkingDir,
            _MAX_PATH - 1, //make sure the buffer is large enough
            0 //New attribute ID in environment if created
            );
        //???
        //long errorID=aaApi_GetLastErrorID();
        //LPCWSTR errorStr = aaApi_GetLastErrorDetail();

        return status;

    }


/*----------------------------------------------------------------------+
|
| name          PWGetLastErrorMessage
|
| author        BSI                                      04/2003
|
| Description   A function that will return the last ProjectWise Error
|               message.
|
| Return        Last Error message.
| 
+----------------------------------------------------------------------*/
char * WINAPI PWGetLastErrorMessage 
(
void
)
    {
    char *errorMsg;
    TCHAR TerrorMsg [MAX_STRING];

    errorMsg = (char *)malloc  (sizeof (char) *MAX_STRING);

    _tcscpy (TerrorMsg, aaApi_GetLastErrorMessage());
    aaApi_UnicodeToAnsiStr (TerrorMsg, errorMsg,MAX_STRING);

    return errorMsg;
    }

/*----------------------------------------------------------------------+
|
| name          PWGetDocumentAttributes
|
| author        BSI                                      04/2003
|
| Description   A function that will return the documents attributes.
|
| Return        SUCCESS or -1 if error.
| 
+----------------------------------------------------------------------*/
LONG WINAPI PWGetDocumentAttributes 
(
LONG    ProjectID,      /* i Project ID */
LONG    DocumentID,     /* i Document ID */
void    **AttributeData /* o Document attributes */
)    
{
    CString message;
    LONG status = SUCCESS;
    LONG lEnvId = aaApi_GetEnvId (0);
    LONG lTabNo = aaApi_GetEnvNumericProperty (ENV_PROP_TABLEID, 0);
    LONG count = -1;
    int rowCount = -1;


    /* Select environment for given project */
    status = aaApi_SelectEnvByProjectId (ProjectID);

    if (status == -1 || status == 0)
        {
        return -1;
        }
    else
        {

        // Select the documents Attribute Data
        rowCount = aaApi_SelectLinkDataByObject (
                                                lTabNo,             /* i  Table identifier (required)   */
                                                AADMSLDT_DOCUMENT,  /* i  Reference Item type           */
                                                ProjectID,          /* i  First item identifier         */
                                                DocumentID,         /* i  Second item identifier        */
                                                NULL,               /* i  Where statement (optional)    */
                                                &count,             /* io Column count in lplColumnIds  */
                                                NULL,               /* i  Columns to fetch (NULL - all) */
                                                0                   /* i  Flags (AADMSLDSF_XXX)         */
                                                );


        if (rowCount <= 0)
            return -1;

        for (int colIndex= 0; colIndex<count; colIndex++)
            {
            message += aaApi_GetLinkDataColumnStringProperty (LINKDATA_PROP_COLUMN_NAME, colIndex);
            message += ": ";
            message += aaApi_GetLinkDataColumnValue (0, colIndex); 
            message +="\n";
            }// end for 

        _tcscpy ((TCHAR*)(*AttributeData), message);
        }

    return SUCCESS;
    }

/*----------------------------------------------------------------------+
|
| name          InitInstance
|
| author        BSI                                      04/2003
|
| Description   Initialize the PW API
|
| Return        Nonzero if initialization is successful; otherwise 0.
| 
+----------------------------------------------------------------------*/
BOOL CVbaHelperApp::InitInstance() 
    {

    // Initialize PW
    aaApi_Initialize (AAMODULE_ALL);

    return CWinApp::InitInstance();
    }

/*----------------------------------------------------------------------+
|
| name          ExitInstance
|
| author        BSI                                      04/2003
|
| Description   Remove the hook function on exit.
|
| Return        0 for success or > 0 for error.
| 
+----------------------------------------------------------------------*/
int CVbaHelperApp::ExitInstance() 
    {

    return CWinApp::ExitInstance();
    }

编辑更新 我意识到我的头文件不需要函数定义。该项目使用 vbaHelper.def 文件来定义函数。我从标题中删除了定义,现在有不同的错误:

error C3861: 'PWGetLastErrorMessage': identifier not found
error C3861: 'PWCreateDocument': identifier not found

编辑 2 我不认为这应该与其他未解决的外部符号问题重复,因为我已经审查了此错误的所有常见原因。另外,我现在已经解决了链接器错误,我正在寻找为什么会收到“找不到标识符”错误的原因。

4

2 回答 2

0

extern "C"如果您希望能够从 c 调用它们,则需要附加您的函数定义。您发布的代码中已经有这样的示例。

extern "C" LONG HooksInitialize
于 2015-11-02T16:55:06.417 回答
0

BOOL WINAPI PWCreateDocument

应该是

BOOL DLLEXPORT PWCreateDocument

于 2015-11-02T16:56:03.993 回答