当VBA运行声明的函数但C ++控制台应用运行正常时,堆损坏?

时间:2013-12-03 17:38:55

标签: c++ excel vba excel-vba dll

我正在使用Visual C ++ 2012来创建一个包装函数,用于从VBA进行API调用。如果重要,那就是Bentley ProjectWise API,我正在调用aaApi_DocumentSelectDlg()

当我从C ++控制台应用程序运行它时,此函数有效,但是当我从VBA调用它并逐步执行该函数时,API调用会导致堆损坏错误。

这是包装函数:

//function for opening a file select dialog and getting the IDs associated with
//that file
long __stdcall FileOperator::selectFile(long* docArray)
{
 try
 {
  //set parameters for dialog
  AAOPENDOCSDLG2_PARAM param={0};
  param.ulMask |= AAOPENDLG2_MASK_FLAGS|AAOPENDLG2_MASK_TITLE|AAOPENDLG2_MASK_PROJECTID|
  AAOPENDLG2_MASK_DOCUMENTID|AAOPENDLG2_MASK_FILENAME;
  param.ulFlags = AAOPENDLG2_SINGLE_SELECTION | AAOPENDLG2_GET_IDS_ONLY | AAOPENDLG2_HIDEREADONLY | AAOPENDLG2_NO_USE_LASTPROJ;
  param.lpctstrTitle = L"Select a file";

  //open dialog, get result of user's selection
  long result=aaApi_DocumentSelectDlg(&param);

  //set array values to pass file info out of function
  if(result==IDOK)
  {
   docArray[0]=param.plProjectIds[0];
   docArray[1]=param.plDocumentIds[0];
  }
  else
  {
   docArray[0]=0;
   docArray[1]=0;
  }
  return 1;
 }
 catch (exception& e)
 {
  //error string not returned, just used for debugging
  string error=e.what();
  return 0;
 }
}

这是成功执行的控制台应用程序:

int main()
{
 //initialization function for opening session with database.  no problem here.
 long initResult=TestDLL::FileOperator::initialize("Test.QA.com:PWOPPID_XYZ");

 long selectResultArray[2]={0}; 
 TestDLL::FileOperator::selectFile(selectResultArray);

 return 0;
}

这是导致堆损坏的VBA代码:

Private Declare Function selectFile _
Lib "C:\Program Files (x86)\Bentley\ProjectWise\bin\TestDLL.dll" _
Alias "?selectFile@FileOperator@TestDLL@@SGJPAJ@Z" _
(ByRef docArray As Long) As Long

Public Function selectPWFolder() As Long

Dim docArray(1) As Long
Dim result As Long

docArray(0) = 0
docArray(1) = 0
result = selectFile(docArray(LBound(docArray)))
selectPWFolder = docArray(1)

End Function

我通过设置调试器将其缩小到API调用,以便在VBA调用selectFile()后它进入C ++代码。我通过将EXCEL.EXE设置为Debugging-> Command和C / C ++ - >浏览信息 - >启用浏览信息设置为“是(/ FR)”来完成此操作。

令人费解的是,从VBA调用时,函数的唯一元素(或看起来不同)是docArray参数,selectFile()甚至不会被aaApi_DocumentSelectDlg()使用。 API调用。当我到达Critical error detected c0000374 EXCEL.EXE has triggered a breakpoint. First-chance exception at 0x77E0E753 (ntdll.dll) in EXCEL.EXE: 0xC0000374: A heap has been corrupted (parameters: 0x77E44270). 行并进入它时,我收到以下错误消息:

docArray

为什么VBA会导致此堆损坏?我正在传递指向aaApi_DocumentSelectDlg()的第一个元素的指针,这样我就可以将数组从VBA传递给DLL函数,而不必使用SAFEARRAY,但我不认为这是问题,因为调用{ {1}}不使用docArray

编辑:我制作了另一个版本的selectFile(),它不接受任何参数,以测试错误是否在没有docArray的情况下发生。仍然发生堆损坏错误。因此它与API调用本身有关,与传递数组无关。

初始化函数的代码是:

long FileOperator::initialize(char* dbName)
    {
        LPCWSTR user=L"";
        LPCWSTR pwd=L"";
        LPCWSTR schema=L"";
        std::string dbNameStr=std::string(dbName);
        std::wstring sTemp=std::wstring(dbNameStr.begin(),dbNameStr.end());
        LPCWSTR dbName_L=sTemp.c_str();
        bool resultInit=aaApi_Initialize(AAMODULE_ALL);
        bool resultLogin=aaApi_Login(AAAPIDB_UNKNOWN,dbName_L,user,pwd,schema);
        if (resultInit&&resultLogin)
        {
            return 11;
        }
        else if (resultInit&&!resultLogin)
        {
            return 10;
        }
        else if ((!resultInit)&&resultLogin)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }

2 个答案:

答案 0 :(得分:0)

尝试将您的VBA更改为:

Private Declare Function selectFile _
Lib "C:\Program Files (x86)\Bentley\ProjectWise\bin\TestDLL.dll" _
Alias "?selectFile@FileOperator@TestDLL@@SGJPAJ@Z" _
(ByVal lpdocArray As Long) As Long

Public Function selectPWFolder() As Long

Dim docArray(1) As Long
Dim result As Long

docArray(0) = 0
docArray(1) = 0
result = selectFile(VarPtr(docArray(0)))
selectPWFolder = docArray(1)

End Function

这显式地向函数发送数组的第一个元素的地址。

答案 1 :(得分:0)

由于您将Longs的数组传递给您的DLL调用,看起来它被调用改变了,我猜它应该通过地址传递。
声明(ByVal lpdocArray As Long)传递32位值的内容。 我认为你用ByRef而不是ByVal会更好 - 然后会自动传递地址。

既然你已经使用了ByRef,你应该能够使用变量标识符进行调用:
result = selectFile(docArray)