Home Help Center

Access Violation using ICE with Automation DLL

amrufonamrufon Member AlexOrganization: ISTProject: jDatabase ✭✭
Hello.

Backgrounder first. I'm a VB programmer who's trying out ICE with VB so that I can integrate it with our applications. You can categorize me as a newbie with regards to C++. Also, this post is in connection with Using ICE with Single Threaded DLL post.

Ok, now with the problem:
1.) I created a VB ActiveX DLL which only has one interface. A function wich accepts a string and returns a string. Pretty simple.
2.) I made the following slice definition to support the DLL.
interface jSockets
{
string jclCommand(string command);
};
3.) I then proceeded to make a client and a server classes to manage the connection. Here is a code snippit on the server:
jSocketsI.h
#include <jSockets.h>
#include <IceUtil/Mutex.h>

#define INITGUID
#import "..\jIceServer\bin\jIceServer.dll"
#include "icrsint.h"

using namespace jIceServer;

class jSocketsI : public jSockets {
public:
virtual ::std::string jclCommand(const ::std::string& command, const Ice::Current&);
jSocketsI();
~jSocketsI();
private:
bool initialize();
_jServerPtr pjServer;
IceUtil::Mutex _jServerMutex;
};

jSockets.cpp
#include "stdafx.h"
#include "jSocketsI.h"

using namespace std;

std::string jSocketsI::jclCommand(const ::std::string& command, const Ice::Current&) {
// Make sure that were thread safe
IceUtil::Mutex::Lock lock(_jServerMutex);

// Declare used variables
std::string retval;
_bstr_t data = command.c_str();

// Call the J Object and get the return parameter
retval = pjServer->jclCommand(data);

// Return the values
return retval;
}

bool jSocketsI::initialize() {
// Make sure that were thread safe
IceUtil::Mutex::Lock lock(_jServerMutex);

// Make sure that were connected
CoInitialize(NULL);
pjServer.CreateInstance(__uuidof(jServer));

// Return that all is well
return true;
}

jSocketsI::jSocketsI() {
// Initialize the COM environment, this
// extra call ensures that the Adapter Class will
// not go out of scope.
initialize();
}

jSocketsI::~jSocketsI() {
CoUninitialize();

Just for completeness, here's the code for the main application
IceServer.cpp
#include <Ice/Ice.h>
#include <jSocketsI.h>

using namespace std;

class IceServer : virtual public Ice::Application {
public:
virtual int run(int, char* []) {
// Server code
Ice::ObjectAdapterPtr adapter = communicator()->createObjectAdapterWithEndpoints("jclCommandAdapter", "default -p 7012");
Ice::ObjectPtr object = new jSocketsI;
adapter->add(object, Ice::stringToIdentity("jclCommand"));
adapter->activate();

// Wait until all operations are completed
communicator()->waitForShutdown();
if (interrupted()) {
cerr << appName() << ": received signal, shutting down" << endl;
}
return 0;
};
};

int main(int argc, char* argv[]) {
IceServer app;
return app.main(argc,argv);
}

This code will actually compile and run but the problem is that when the clients connects and actually calls the "jclCommand", it will fail with an access violation in the code:
// Call the J Object and get the return parameter
retval = pjServer->jclCommand(data);

I don't know what to do anymore. I'm close to just giving up and walking away.

Any help?

Thanks.

Alex

Comments

  • marcmarc FloridaAdministrators, ZeroC Staff Marc LaukienOrganization: ZeroC, Inc.Project: The Internet Communications Engine ZeroC Staff
    I'm afraid from the code fragments alone, it's not possible to say what is going on. I can't see anything obvious wrong. We would need to see a complete and compilable example, since we don't know what's going on in jclCommand().
  • amrufonamrufon Member AlexOrganization: ISTProject: jDatabase ✭✭
    Hello Marc,

    Its a good thing you replied to this post and said that everything looks Ok. Because when I was trying to trim down the VB code so I can post it here ... I thought of a solution which worked!!!

    Ok, first off, what I was trying to do was something like this:
    ice client -->> internet --> ice server -->> vb dll --> proprietary COM libraries

    What I was doing from the code above is that the IceServer is creating an instance of the vb dll when it initializes. If you would recall your documentation on Chapter 14: Threads and Concurrency with C++, you guys kept pointing out that ICE is a multi-threaded system. So from my own testing, it seems that each calls made by the client to the server is given its own thread. So the Access Violation occurs when the newly created thread for the client is trying to access the variable initialized by the main application thread:

    The pjServer was initialized by the Main Application
    bool jSocketsI::initialize() {
    // Make sure that were thread safe
    IceUtil::Mutex::Lock lock(_jServerMutex);

    // Make sure that were connected
    CoInitialize(NULL);
    pjServer.CreateInstance(__uuidof(jServer));

    // Return that all is well
    return true;
    }

    jSocketsI::jSocketsI() {
    // Initialize the COM environment, this
    // extra call ensures that the Adapter Class will
    // not go out of scope.
    initialize();
    }

    and was access by the new thread here ... where it fails
    std::string jSocketsI::jclCommand(const ::std::string& command, const Ice::Current&) {
    // Make sure that were thread safe
    IceUtil::Mutex::Lock lock(_jServerMutex);

    // Declare used variables
    std::string retval;
    _bstr_t data = command.c_str();

    // Call the J Object and get the return parameter
    retval = pjServer->jclCommand(data);

    // Return the values
    return retval;
    }

    My Solution
    So my solution was to modify my VB DLL from an ActiveX Apartment Threaded DLL to an ActiveX EXE library (please refer to the attached image on how I configured the VB project for this). Then I modified the my Ice Adapter in such a way that each new call made by the client will create a new instance of the ActiveX EXE library. Here's the change codes:

    jSocketsI.h
    #include <jSockets.h>
    #include <IceUtil/Mutex.h>

    #define INITGUID
    #import "..\jIceServer\bin\jIceServer.exe"
    #include "icrsint.h"

    using namespace jIceServer;

    class jSocketsI : public jSockets {
    public:
    virtual ::std::string jclCommand(const ::std::string& command, const Ice::Current&);
    jSocketsI();
    ~jSocketsI();
    private:
    _jServerPtr pjServer;
    IceUtil::Mutex _jServerMutex;
    };

    jSocketsI.cpp
    #include "stdafx.h"
    #include "jSocketsI.h"

    using namespace std;

    std::string jSocketsI::jclCommand(const ::std::string& command, const Ice::Current&) {
    // Make sure that were thread safe
    IceUtil::Mutex::Lock lock(_jServerMutex);

    // Declare used variables
    std::string retval;
    _bstr_t data = command.c_str();
    _bstr_t temp;
    _jServerPtr lpjServer;

    // Make a new connection to the J Server ONLY for this thread
    CoInitialize(NULL);
    lpjServer.CreateInstance(__uuidof(jServer));

    // Call the J Object and get the return parameter
    temp = lpjServer->jclCommand(data);
    retval = temp;

    // Uninitalize the COM Automation for this thread
    CoUninitialize();

    // Return the values
    return retval;
    }

    jSocketsI::jSocketsI() {
    // Initialize the COM environment, this call
    // ensures that an instance of J will be running
    // until this application quit
    CoInitialize(NULL);
    pjServer.CreateInstance(__uuidof(jServer));
    }

    jSocketsI::~jSocketsI() {
    CoUninitialize();
    }

    So, if you would look closely in the jSocketsI::jclCommand function, I create a local variable called lpjServer that is initialized and closed at that function level.

    Summary
    As I say, this works. Unfortunately, I'm not satisfied with my solutions. Let me put it this way: To get into a living room of a house, all you need to do is walk up to the door and go in. What I just did was make my own ladder, climb up to the second floor, crawl through the window and go down the stairs just to get to the living room. :rolleyes:

    Although, because of time constraints, I'll have to stick with my current solution (until a better one comes up) and move to making an IceClient DLL that can be called by our proprietary libraries. I was wondering if anybody has insights, thought and/or ideas on this issue of mine. Also, did I really understand how ICE works or I'm just fooling myself?

    Thanks.

    Alex
Sign In or Register to comment.