Can't call DLL function that makes ProjectWise API calls

Using Visual C++ (in Visual Studio 2012), I've made a DLL with a function openDoc(int projID,int docID) that calls the ProjectWise API to open a document.

I'm trying to call this function from either C# or VBA, but haven't succeeded with either one.

The C++ code is as follows:

#include "stdafx.h"
#include "AAAPI.H"
#include "AAWINAPI.H"
#include "AADMSAPI.H"
#include "AAWINDMS.H"

extern "C"
__declspec(dllexport)
int
__cdecl

openDoc(int projectID,int docID)
{

LPCWSTR dbName=L"abc.def:ghi";
LPCWSTR user=L"";
LPCWSTR pwd=L"";
LPCWSTR schema=L"";


bool resultInit=aaApi_Initialize(AAMODULE_ALL);


bool resultLogin=aaApi_Login(AAAPIDB_UNKNOWN,dbName,user,pwd,schema);
bool resultOpen=aaApi_OpenDocument(projectID,docID,false);
return resultOpen;
}
This builds successfully.  I have tested the body of openDoc() in a C++ Win32 console application and it works.
I tried with C# as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace ProjectWise_Csharp
{
class Program
{

public const string dllPath = @"U:\Software Development\c++ projects\ProjectWise2\Debug\ProjectWise2.dll";
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
public extern static int openDoc(int projID, int docID);

static void Main(string[] args)
{
Console.WriteLine(openDoc(1799, 29));
Console.ReadLine();

}
}
}
This throws a System.BadImageFormatException, with the message:
An attempt was made to load a program with an incorrect format.

This only happens when one of the API calls occurs; if I rebuild the DLL with the API calls commented out, then the C# code runs fine.  

Everything I've read online indicates that this exception gets thrown when a 64-bit program tries to call a 32-bit DLL, but my DLL is Win32 and I've set my C# project's platform target to x86.

I tried it with VBA as follows:

Public Declare Function openDoc Lib "U:\Software Development\c++ projects\ProjectWise2\Debug\ProjectWise2.dll" (ByVal projectID As Long, ByVal docID As Long) As Variant
Sub TestDLL()
 Debug.Print openDoc(1799, 29)
Exit Sub
This gave the following set of errors:
Last Error [50126]
Cannot execute document action
Specified document does not exist.
and
Run-time error '49':
Bad DLL calling convention
Since I can run the code fine from C++, I know that's not the problem.  Is there a problem with how I've set up my DLL export?  
Parents
  • Have you tried using stdCall instead of Cdecl?

  • I tried replacing "__cdecl" with "__stdcall" in the C++ code, but I got the same error.

  • any luck with other things you have tried?  Does even the Initiallize work? all it does is spin up the dlls.

  • Ok, I got a test function to execute, but I'm still getting this error:

    Run-time error '49':

    Bad DLL calling convention

    I modified my code by splitting it into a header file and .cpp file, as follows:

    Header (myProject.h):

    namespace myProject
    {
    class FileOperator
    {
    public:
    static __declspec(dllexport) int openDoc(int projectID,int docID);
    };
    }

    .cpp file (myProject.cpp):

    #include "stdafx.h"
    #include "AAAPI.H"
    #include "AAWINAPI.H"
    #include "AADMSAPI.H"
    #include "AAWINDMS.H"
    #include "myProject.h"

    using namespace std;

    namespace myProject
    {


    int FileOperator::openDoc(int projectID,int docID)
    {

    LPCWSTR dbName=L"abc.def:ghi";
    LPCWSTR user=L"";
    LPCWSTR pwd=L"";
    LPCWSTR schema=L"";


    bool resultInit=aaApi_Initialize(AAMODULE_ALL);
    bool resultLogin=aaApi_Login(AAAPIDB_UNKNOWN,dbName,user,pwd,schema);
    bool resultOpen=aaApi_OpenDocument(projectID,docID,false);
    return resultOpen;

    }
    }

    And here is the VBA declaration and function call:

    Declare Function openDoc Lib "U:\Software Development\c++ projects\myProject\Debug\myProject.dll" _
    Alias "?openDoc@FileOperator@myProject@@SAHHH@Z" _
    (ByVal projectID As Long, ByVal docID As Long) As Long

    Sub test()

    openDoc 1799, 29

    End Sub

    When I run this, the document successfully opens; adding ByVal made the difference between this and my previous attempt.  But I still get that run-time error, even though it seems that the API call has successfully executed.  What's going on here?

    Also, is there a way to change the header so that I don't have to use that elaborate Alias in my Declare statement?

Reply
  • Ok, I got a test function to execute, but I'm still getting this error:

    Run-time error '49':

    Bad DLL calling convention

    I modified my code by splitting it into a header file and .cpp file, as follows:

    Header (myProject.h):

    namespace myProject
    {
    class FileOperator
    {
    public:
    static __declspec(dllexport) int openDoc(int projectID,int docID);
    };
    }

    .cpp file (myProject.cpp):

    #include "stdafx.h"
    #include "AAAPI.H"
    #include "AAWINAPI.H"
    #include "AADMSAPI.H"
    #include "AAWINDMS.H"
    #include "myProject.h"

    using namespace std;

    namespace myProject
    {


    int FileOperator::openDoc(int projectID,int docID)
    {

    LPCWSTR dbName=L"abc.def:ghi";
    LPCWSTR user=L"";
    LPCWSTR pwd=L"";
    LPCWSTR schema=L"";


    bool resultInit=aaApi_Initialize(AAMODULE_ALL);
    bool resultLogin=aaApi_Login(AAAPIDB_UNKNOWN,dbName,user,pwd,schema);
    bool resultOpen=aaApi_OpenDocument(projectID,docID,false);
    return resultOpen;

    }
    }

    And here is the VBA declaration and function call:

    Declare Function openDoc Lib "U:\Software Development\c++ projects\myProject\Debug\myProject.dll" _
    Alias "?openDoc@FileOperator@myProject@@SAHHH@Z" _
    (ByVal projectID As Long, ByVal docID As Long) As Long

    Sub test()

    openDoc 1799, 29

    End Sub

    When I run this, the document successfully opens; adding ByVal made the difference between this and my previous attempt.  But I still get that run-time error, even though it seems that the API call has successfully executed.  What's going on here?

    Also, is there a way to change the header so that I don't have to use that elaborate Alias in my Declare statement?

Children
  • I suspect it may be because you are declaring the openDoc function parameters and return as "int" type, but in the VBA, you are telling it to pass long type and get a long in return. While longs are typically just "bigger" ints, it might be just enough to throw things in a case like this, even though it is successful in using the value passed. See also msdn.microsoft.com/.../aa232602(v=vs.60).aspx

    Try using LONG in your DLL code for the return and parameter types for openDoc, since that is also the type used in the API for aaApi_OpenDocument.

  • I changed all the INT references in my DLL code to LONG, but I still got the same result.

    I also tried changing the return type declaration from "long" to "long __stdcall" in the header and .cpp, but that didn't prevent the error either.

    Are you able to replicate this issue?

  • I think __stdcall is necessary for the function to work with VBA.  And as far as int vs. long goes, I guess I should have dug a little more. C++ "int" is supposedly equivalent to VBA "Long" (in most cases anyway), but C++ "long" is also equivalent to VBA Long. The one thing all cautioned against was using typedefs like "LONG", just use "long".

    As for the alias, you might look at using  .def file to export the dll functions. Not sure if that requires using the decorated name in there, but have a look at that.

    I don't deal much with VBA, but will see if I can test something next week when I'm back in the office. Just curious, what are you using to write/call your VBA? Excel? Not that it matters a whole lot, but I've read where some applications (Excel in particular) seem to have an a complier bug that will present error 49 sometimes.

  • As the problem appears to be related to calling conventions, you should simplify your test case a bit.  Remove/comment out all the ProjectWise headers and calls, use plain C types and function calling conventions, and focus on getting your C function callable from .NET.

    Here's an example of how we export a function in the ProjectWise AAAPI, and how that function is then subsequently exposed/called from C#. (NB: this is only one way of doing this, and I do not claim to be a C# programmer. YMMV.)

    #pragma once

    #ifdef __cplusplus
        extern "C" {
    #endif

    /* NB: 'AAAPI' is just a define for the real 'WINAPI' qualifier */
    int WINAPI myFunction (int x, int y);

    #ifdef __cplusplus
        }
    #endif


    mydll.cpp

    #include "stdafx.h"
    #include "mydll.h"

    int WINAPI myFunction (int x, int y)
        {
        return 1;
        }


    myDll.def (NB: adjust your compiler settings for your DLL to use a .def file to create the function exports.)

    LIBRARY      "MYDLL"

    EXPORTS
        myFunc


    myApp.cs

    namespace Foo
        {
        public class Foo_Interface
            {
            [DllImport("mydll.dll", CharSet = CharSet.Unicode)]
            public static extern int myFunc (int x, int y);
            }
        }

    //... your code here ...

        Foo_Interface.myFunc (1, 2)


    Once you get this working, then I would say you are free to use C++, exports via __declspec, alternate calling conventions, etc.

    HTH

    Mike