I have tried the tricks posted here (at least the ones I have found), but I still can't seem to rid myself of the dreaded "Bad DLL Calling Convention" error. Here is my (very simple) c++ code:
VRTTCONVERSION_API long controlVTT(BYTE controlVal, BYTE valueVal)
{
LPSTR controlStr = new char[256];
PCHAR convstrout = new char[256];
if (controlVal == 1) {
controlStr = "Ping";
}else if (controlVal == 2) {
controlStr = "0 Init Tool Default";
}
if (bGetGeneralAccessFunctionPointer()) {
return (pGeneral)(controlStr, convstrout, 256);
}
else {
printf("failed to acquire function pointer.\n");
return -1;
}
}
This SHOULD accept bytes as inputs and return a long. I know the return works as I have another function that doesn't accept any inputs and returns the long and that one works fine.
Here is the VBA code (again, simple):
Public Declare PtrSafe Function controlVTT Lib "VRTTConversionDLL.dll" Alias "#4" (ByVal controlVal As Byte, ByVal valueVal As Byte) As Long
Sub testControl()
Dim retval As Long
Dim bFunc As Byte
Dim bVal As Byte
bVal = 0
bFunc = 1
retval = controlVTT(bFunc, bVal)
MsgBox retval
bFunc = 2
retval = controlVTT(bFunc, bVal)
MsgBox retval
End Sub
I know the #4 alias is correct (from using DLL Export Viewer). This function also works when I call it from another C++ function (see below):
int main()
{
long retVal;
retVal = controlVTT(1, 0);
printf("Return: %08X \n", retVal);
retVal = controlVTT(2, 0);
printf("Return: %08X \n", retVal);
return 0; return 0;
}
Obviously, I'm not a professional programmer and I'm probably missing something basic, but I am truly stuck.
Related
I'm currently working on a project where I want to be able to manipulate and graph data with relative ease in Excel. However, a lot of the data involved exceeds Excels row limits considerably and it needs a degree of preprocessing before I can work with it.
To solve this I decided to write a backend in C++ to handle the preprocessing so that it's acceptable for Excel. The aim is to be able to pick several files within excel which are then sent to the .dll to be parsed, preprocessed, and have some averaging applied to make it more easily handled in excel. The data is then passed back to Excel to be graphed, etc.
I've already worked out how to send arrays of data from the .dll to Excel reliably. However, sending data from Excel to the .dll, usually an array of BSTR which hold file paths, has proven more difficult.
After reading through a few questions:
How to create an array of strings in VBA/Excel and send it to a C++ DLL so that it can be iterated through in the DLL
VBA/Excel, and C++ DLL, specifically problems with strings
Calling C++ function from Excel and VBA using DLL created in C++
I thought the following code should work:
// lib.cpp
/*
* clear_log() & write_log(...) both write to a specified log file
*/
inline std::string WINAPI
bstr_string_convert( const BSTR bstr ) {
const auto bstrlen = SysStringLen(bstr);
const auto buffer = new char[bstrlen + 1];
size_t n_char_converted{ 0 };
if ( bstrlen > 0 ) {
const auto err =
wcstombs_s( &n_char_converted,
buffer, bstrlen + 1,
bstr, bstrlen );
}
else {
buffer[0] = '\0';
}
return std::string{ buffer };
}
const std::string log_location{
"C:\\\\path\\to\\log\\location\\"
};
void WINAPI
clear_log( const std::string_view filename ) {
std::fstream log_file( log_location + filename,
std::ios_base::out | std::ios_base::trunc );
log_file.close();
}
void WINAPI
write_log( const std::string_view filename, const std::string_view str ) {
std::fstream log_file( log_location + filename,
std::ios_base::out | std::ios_base::app );
log_file << str << "\n";
log_file.close();
}
// This works
__declspec(dllexport) LPSAFEARRAY WINAPI
get_double_array( _In_ const LPSAFEARRAY* ppsa ) {
CComSafeArray<double> csa(*ppsa);
clear_log("double_log.txt");
write_log( "double_log.txt",
std::format("size: {}", csa.GetCount()) );
for ( LONG i{ csa.GetLowerBound() }; i < csa.GetUpperBound(); ++i ) {
write_log( "double_log.txt",
std::format("{}: {}", i, csa.GetAt(i)) );
}
return csa.Detach();
}
// This doesn't
__declspec(dllexport) LPSAFEARRAY WINAPI
get_str_array( _In_ const LPSAFEARRAY* ppsa ) {
CComSafeArray<BSTR> csa(*ppsa);
clear_log("string_log.txt");
write_log( "string_log.txt",
std::format("size: {}", csa.GetCount()));
for (LONG i{ csa.GetLowerBound() }; i < csa.GetUpperBound(); ++i ) {
write_log( "string_log.txt",
std::format( "{}: {}",
i,
bstr_string_convert(csa.GetAt(i))
) );
}
return csa.Detach();
}
Declare PtrSafe Function send_string_array _
Lib "path\to\dll" _
Alias "_get_str_array#4" _
(ByRef first_element() As String) As String()
Declare PtrSafe Function send_double_array _
Lib "path\to\dll" _
Alias "_get_double_array#4" _
(ByRef ptr() As Double) As Double()
(Not sure what's making the syntax highlighting weird here)
Sub test()
Dim doubles(3) As Double
doubles(0) = 3.141592
doubles(1) = 1235.12617
doubles(2) = -1266.2346
Dim d_result() As Double
d_result = send_double_array(doubles)
Dim n As Long
For n = 0 To 2
Debug.Print d_result(n)
Next n
Dim strings(3) As String
strings(0) = "This"
strings(1) = "is a "
strings(2) = "test."
Dim result() As String
result = send_string_array(strings)
For n = 0 To 2
Debug.Print result(n)
Next n
End Sub
Immediate Window:
3.141592
1235.12617
-1266.2346
??
??
??
double_log.txt:
size: 4
0: 3.141592
1: 1235.12617
2: -1266.2346
string_log.txt:
size: 4
0:
1:
2:
So my question is what am I missing that causes get_double_array(...) to work, but get_str_array(...) doesn't?
As an aside, why does CComSafeArray::GetCount() return 4 for an array declared with 3 elements? Seems like something weird is going on there.
I tried to use GetDiskFreeSpaceExA function, but it doesn't work:
int drvNbr = PathGetDriveNumber(db7zfolderw);
if (drvNbr == -1) // fn returns -1 on error
{
const char * errmsg = "error occured during get drive number";
strcpy_s(retmsg, strlen(errmsg) + 1, errmsg);
return -3;
}
char driverletter = (char)(65 + drvNbr);
string driverstr(1, driverletter);
driverstr = driverstr + ":";
PULARGE_INTEGER freespace = 0;
PULARGE_INTEGER totalnumbtype = 0;
PULARGE_INTEGER totalnumberfreebyte = 0;
fileSize = SzArEx_GetFileSize(&db, i);
BOOL myresult=GetDiskFreeSpaceExA(
driverstr.c_str(),
freespace,
totalnumbtype,
totalnumberfreebyte
);
The value of variable freespace is 0. I have no idea why it didn't work if the value of variable which is driverstr.c_str() was D:?
Thanks for your help.
You need to supply pointers to variables that will hold the value returned. Right now you a re supplying null pointers so nothing is retured:
::ULARGE_INTEGER freespace{};
::ULARGE_INTEGER totalnumbtype{};
::ULARGE_INTEGER totalnumberfreebyte{};
::BOOL myresult
{
::GetDiskFreeSpaceExA
(
driverstr.c_str()
, &freespace
, &totalnumbtype
, &totalnumberfreebyte
)
};
It would also be a good idea to use wide char versions of these functions.
I have written a C++ COM which is running as COM+ application.
I am trying to access COM functionality from VBScript (ASP application).
I am able to call a function of COM from VBScript which takes a string. But when I try to call a COM function which takes an array of string, I could get length of array but I could not retrieve elements from that array at COM side.
VBScript (ASP application)
dim myComObj
Set myComObj = Server.CreateObject("ProgId_PerlCOMSimple.1")
Dim myArray(3)
myArray(0) = "Clean Underwear"
myArray(1) = "Vacuum Cleaner"
myArray(2) = "New Computer"
myArray(3) = "Talking Bass"
strDfStatus = myComObj.TestArray1 (myArray)
C++ COM which runs as COM+ application (through dllHost.exe)
STDMETHODIMP CPerlCOMSimple::TestArray1(VARIANT* testArray, LONG* lResult)
{
// TODO: Add your implementation code here
*lResult = testArray->parray->rgsabound->cElements;
BSTR** StrPtr = 0;
//LONG* pVals;
long LowerBound = 0;
long UpperBound = 0;
int i;
SafeArrayGetLBound(testArray->parray, 1, &LowerBound);
SafeArrayGetUBound(testArray->parray, 1, &UpperBound);
SafeArrayAccessData(testArray->parray, (void**)&pVals);
for (i = LowerBound; i <= UpperBound; ++i)
{
BSTR* lVal = StrPtr[i];
lVal++;
}
SafeArrayUnaccessData(testArray->parray);
return S_OK;
}
VBScript will not generate a SAFEARRAY with vartype VT_BSTR, which is what you are expecting. It will have VARTYPE VT_VARIANT.
// check all your parameters
if(testarray == null) return E_INVALIDARG;
if(testarray->vartype != VT_ARRAY|VT_BSTR
&& testarray->vartype != VT_ARRAY|VT_VARIANT)
return E_INVALIDARG;
if(testarray->parray == null) return E_INVALIDARG;
// Now we have established we have an array, and that it
// is either a string array or a variant array.
VARTYPE vt = VT_EMPTY;
SafeArrayGetVarType(testarray->pArray, &vt);
// Now we need to take different actions based on the vartype.
if(vt == VT_BSTR){
// we have an array of strings
// Proceed as above.
}else if(vt == VT_VARIANT){
// we have an array of variants, probably supplied by VBScript
// Read them one by one and use VariantChangeType to get a string
}else{
// We have some other array type we don't support
return E_INVALIDARG;
}
When calling WinAPI functions that take callbacks as arguments, there's usually a special parameter to pass some arbitrary data to the callback. In case there's no such thing (e.g. SetWinEventHook) the only way we can understand which of the API calls resulted in the call of the given callback is to have distinct callbacks. When we know all the cases in which the given API is called at compile-time, we can always create a class template with static method and instantiate it with different template arguments in different call sides. That's a hell of a work, and I don't like doing so.
How do I create callback functions at runtime so that they have different function pointers?
I saw a solution (sorry, in Russian) with runtime assembly generation, but it wasn't portable across x86/x64 archtectures.
You can use the closure API of libffi. It allows you to create trampolines each with a different address. I implemented a wrapping class here, though that's not finished yet (only supports int arguments and return type, you can specialize detail::type to support more than just int). A more heavyweight alternative is LLVM, though if you're dealing only with C types, libffi will do the job fine.
I've come up with this solution which should be portable (but I haven't tested it):
#define ID_PATTERN 0x11223344
#define SIZE_OF_BLUEPRINT 128 // needs to be adopted if uniqueCallbackBlueprint is complex...
typedef int (__cdecl * UNIQUE_CALLBACK)(int arg);
/* blueprint for unique callback function */
int uniqueCallbackBlueprint(int arg)
{
int id = ID_PATTERN;
printf("%x: Hello unique callback (arg=%d)...\n", id, arg);
return (id);
}
/* create a new unique callback */
UNIQUE_CALLBACK createUniqueCallback(int id)
{
UNIQUE_CALLBACK result = NULL;
char *pUniqueCallback;
char *pFunction;
int pattern = ID_PATTERN;
char *pPattern;
char *startOfId;
int i;
int patterns = 0;
pUniqueCallback = malloc(SIZE_OF_BLUEPRINT);
if (pUniqueCallback != NULL)
{
pFunction = (char *)uniqueCallbackBlueprint;
#if defined(_DEBUG)
pFunction += 0x256; // variable offset depending on debug information????
#endif /* _DEBUG */
memcpy(pUniqueCallback, pFunction, SIZE_OF_BLUEPRINT);
result = (UNIQUE_CALLBACK)pUniqueCallback;
/* replace ID_PATTERN with requested id */
pPattern = (char *)&pattern;
startOfId = NULL;
for (i = 0; i < SIZE_OF_BLUEPRINT; i++)
{
if (pUniqueCallback[i] == *pPattern)
{
if (pPattern == (char *)&pattern)
startOfId = &(pUniqueCallback[i]);
if (pPattern == ((char *)&pattern) + sizeof(int) - 1)
{
pPattern = (char *)&id;
for (i = 0; i < sizeof(int); i++)
{
*startOfId++ = *pPattern++;
}
patterns++;
break;
}
pPattern++;
}
else
{
pPattern = (char *)&pattern;
startOfId = NULL;
}
}
printf("%d pattern(s) replaced\n", patterns);
if (patterns == 0)
{
free(pUniqueCallback);
result = NULL;
}
}
return (result);
}
Usage is as follows:
int main(void)
{
UNIQUE_CALLBACK callback;
int id;
int i;
id = uniqueCallbackBlueprint(5);
printf(" -> id = %x\n", id);
callback = createUniqueCallback(0x4711);
if (callback != NULL)
{
id = callback(25);
printf(" -> id = %x\n", id);
}
id = uniqueCallbackBlueprint(15);
printf(" -> id = %x\n", id);
getch();
return (0);
}
I've noted an interresting behavior if compiling with debug information (Visual Studio). The address obtained by pFunction = (char *)uniqueCallbackBlueprint; is off by a variable number of bytes. The difference can be obtained using the debugger which displays the correct address. This offset changes from build to build and I assume it has something to do with the debug information? This is no problem for the release build. So maybe this should be put into a library which is build as "release".
Another thing to consider whould be byte alignment of pUniqueCallback which may be an issue. But an alignment of the beginning of the function to 64bit boundaries is not hard to add to this code.
Within pUniqueCallback you can implement anything you want (note to update SIZE_OF_BLUEPRINT so you don't miss the tail of your function). The function is compiled and the generated code is re-used during runtime. The initial value of id is replaced when creating the unique function so the blueprint function can process it.
How can I get the value of a primitive literal using libclang?
For example, if I have a CXCursor of cursor kind CXCursor_IntegerLiteral, how can I extract the literal value.
UPDATE:
I've run into so many problems using libclang. I highly recommend avoiding it entirely and instead use the C++ interface clang provides. The C++ interface is highly useable and very well documented: http://clang.llvm.org/doxygen/annotated.html
The only purpose I see of libclang now is to generate the ASTUnit object for you as with the following code (it's not exactly easy otherwise):
ASTUnit * astUnit;
{
index = clang_createIndex(0, 0);
tu = clang_parseTranslationUnit(
index, 0,
clangArgs, nClangArgs,
0, 0, CXTranslationUnit_None
);
astUnit = static_cast<ASTUnit *>(tu->TUData);
}
Now you might say that libclang is stable and the C++ interface isn't. That hardly matters, as the time you spend figuring out the AST with libclang and creating kludges with it wastes so much of your time anyway. I'd just as soon spend a few hours fixing up code that does not compile after a version upgrade (if even needed).
Instead of reparsing the original, you already have all the information you need inside the translation unit :
if (kind == CXCursor_IntegerLiteral)
{
CXSourceRange range = clang_getCursorExtent(cursor);
CXToken *tokens = 0;
unsigned int nTokens = 0;
clang_tokenize(tu, range, &tokens, &nTokens);
for (unsigned int i = 0; i < nTokens; i++)
{
CXString spelling = clang_getTokenSpelling(tu, tokens[i]);
printf("token = %s\n", clang_getCString(spelling));
clang_disposeString(spelling);
}
clang_disposeTokens(tu, tokens, nTokens);
}
You will see that the first token is the integer itself, the next one is not relevant (eg. it's ; for int i = 42;.
If you have access to a CXCursor, you can make use of the clang_Cursor_Evaluate function, for example:
CXChildVisitResult var_decl_visitor(
CXCursor cursor, CXCursor parent, CXClientData data) {
auto kind = clang_getCursorKind(cursor);
switch (kind) {
case CXCursor_IntegerLiteral: {
auto res = clang_Cursor_Evaluate(cursor);
auto value = clang_EvalResult_getAsInt(res);
clang_EvalResult_dispose(res);
std::cout << "IntegerLiteral " << value << std::endl;
break;
}
default:
break;
}
return CXChildVisit_Recurse;
}
Outputs:
IntegerLiteral 42
I found a way to do this by referring to the original files:
std::string getCursorText (CXCursor cur) {
CXSourceRange range = clang_getCursorExtent(cur);
CXSourceLocation begin = clang_getRangeStart(range);
CXSourceLocation end = clang_getRangeEnd(range);
CXFile cxFile;
unsigned int beginOff;
unsigned int endOff;
clang_getExpansionLocation(begin, &cxFile, 0, 0, &beginOff);
clang_getExpansionLocation(end, 0, 0, 0, &endOff);
ClangString filename = clang_getFileName(cxFile);
unsigned int textSize = endOff - beginOff;
FILE * file = fopen(filename.c_str(), "r");
if (file == 0) {
exit(ExitCode::CANT_OPEN_FILE);
}
fseek(file, beginOff, SEEK_SET);
char buff[4096];
char * pBuff = buff;
if (textSize + 1 > sizeof(buff)) {
pBuff = new char[textSize + 1];
}
pBuff[textSize] = '\0';
fread(pBuff, 1, textSize, file);
std::string res(pBuff);
if (pBuff != buff) {
delete [] pBuff;
}
fclose(file);
return res;
}
You can actually use a combination of libclang and the C++ interface.
The libclang CXCursor type contains a data field which contains references to the underlying AST nodes.
I was able to successfully access the IntegerLiteral value by casting data[1] to the IntegerLiteral type.
I'm implementing this in Nim so I will provide Nim code, but you can likely do the same in C++.
let literal = cast[clang.IntegerLiteral](cursor.data[1])
echo literal.getValue().getLimitedValue()
The IntegerLiteral type is wrapped like so:
type
APIntObj* {.importcpp: "llvm::APInt", header: "llvm/ADT/APInt.h".} = object
# https://github.com/llvm-mirror/llvm/blob/master/include/llvm/ADT/APInt.h
APInt* = ptr APIntObj
IntegerLiteralObj* {.importcpp: "clang::IntegerLiteral", header: "clang/AST/Expr.h".} = object
IntegerLiteral* = ptr IntegerLiteralObj
proc getValue*(i: IntegerLiteral): APIntObj {.importcpp: "#.getValue()".}
# This is implemented by the superclass: https://clang.llvm.org/doxygen/classclang_1_1APIntStorage.html
proc getLimitedValue*(a: APInt | APIntObj): culonglong {.importcpp: "#.getLimitedValue()".}
Hope this helps someone :)