Я пытаюсь создать C ++ DLL для использования в программе VBA. я следую этот пример и успешно компилировал пример кода и использовал полученную 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
Я не верю, что это должно квалифицироваться как дубликат других нерешенных вопросов о внешних символах, поскольку я рассмотрел все распространенные причины этой ошибки. Кроме того, теперь я исправил ошибку компоновщика и ищу причину, по которой я получаю ошибки «идентификатор не найден».
Вы должны добавить свои определения функций с extern "C"
если вы хотите иметь возможность позвонить им с. У вас уже есть такой пример в коде, который вы опубликовали.
extern "C" LONG HooksInitialize
BOOL WINAPI PWCreateDocument
это должно быть
BOOL DLLEXPORT PWCreateDocument