Excel — повреждение кучи, когда VBA выполняет объявленную функцию, но консольное приложение C ++ работает нормально?

Я использую Visual C ++ 2012 для создания функции-оболочки для вызова API из VBA. Если это имеет значение, то это для 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, настроив отладчик так, чтобы он входил в код C ++ после вызова VBA selectFile(), Я сделал это, установив EXCEL.EXE как Debugging-> Command, и C / C ++ -> Browse Information-> Enable Browse Information, установив «Да (/ FR)».

То, что озадачивает то, что единственный элемент функции, который отличается (или так кажется) при вызове из VBA, это docArray аргумент, который даже не используется selectFile() пока после вызова API. Когда я достигну aaApi_DocumentSelectDlg() Строка и шаг в нее, я получаю следующие сообщения об ошибках:

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).

Почему VBA вызывает эту кучную коррупцию? Я передаю указатель на первый элемент docArray так что я могу передать массив из VBA в функцию DLL без использования SAFEARRAY, но я не думаю, что это проблема, потому что вызов aaApi_DocumentSelectDlg() не использует 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;
}
}

1

Решение

Попробуйте изменить свой 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

Это явно отправляет функции адрес первого элемента массива.

0

Другие решения

Так как вы проходите в массив из Longs к вашему вызову DLL, похоже, что он изменяется при вызове, я предполагаю, что он должен быть передан по адресу.
декларирование (ByVal lpdocArray As Long) передает содержимое 32-битного значения.
Я думаю, что вам лучше использовать ByRef вместо ByVal, который затем автоматически передаст адрес.

Теперь, когда вы использовали ByRef, вы можете просто вызывать идентификатор переменной:
result = selectFile(docArray)

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector