Solved

Class instantiation blowup

Posted on 2014-04-22
5
295 Views
Last Modified: 2014-05-03
I can create this class without error in a console .EXE, and it works fine. The class is in a .DLL called by the console app.

When I compile the app as a .DLL, with no other changes at all, the class can't be instantiated, and blows up with Invalid Access deep in Windows.

The app is native C++ without /clr. The .DLL containing the class is C++ with /clr.

In both cases, everything compiles without error.

Here's the code:

    YahooAPIWrapper yahoo;    // <--- this line blows up.



The class is declared in a separate .DLL as:

class YahooAPIWrapperPrivate;

class YahooAPIWrapper
{
    private: YahooAPIWrapperPrivate* _private;

    public: YahooAPIWrapper();
   
    public: double GetBid(const char* symbol);

    public: double GetAsk(const char* symbol);
   
    public: const char* GetCapitalization(const char* symbol);
   
    public: const char** GetValues(const char* symbol, const char* fields);

    public: ~YahooAPIWrapper();
};

How could there be any difference between a console app and .DLL in the way they initialize a class? There must be something wrong in my setup, but I can't see what it might be, the code is so simple.
0
Comment
Question by:PacificaResearch
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 2
5 Comments
 
LVL 31

Expert Comment

by:Zoppo
ID: 40016803
Hi PacificaResearch,

could you post the implementation of the constructor YahooAPIWrapper::YahooAPIWrapper()?

Where do you instantiate the object? If it's i.e. in a DllMain there may be some reasons for a failure.

BTW: I'm not sure if you know it, but you don't need to add a 'public:' for every function, it's ok to just write it once to mark all following members/functions as public, i.e.:
class YahooAPIWrapper
{
    // default is 'private'
    YahooAPIWrapperPrivate* _private;

    public:
        YahooAPIWrapper();
   
        double GetBid(const char* symbol);

        double GetAsk(const char* symbol);
   
        const char* GetCapitalization(const char* symbol);
   
        const char** GetValues(const char* symbol, const char* fields);

        ~YahooAPIWrapper();
};

Open in new window

ZOPPO
0
 

Author Comment

by:PacificaResearch
ID: 40018030
Thank you for the advice. I don't have a lot of experience with classes. This is sample code I downloaded.

Here's the main program. It works when compiled as console, fails when compiled as .DLL.

// yahoo_look.cpp
//
#include "..\YahooAPIWrapper.h"
extern "C"
{
int yahoo_lookup();
}

int yahoo_lookup()
//int main()
{
const char *bid,*ask,*cap;

    YahooAPIWrapper yahoo;    // <-- this line blows up.
    
    const char* stock = "MSFT";
    const char** bidAskCapi = yahoo.GetValues(stock, "b3b2j1");
    bid = bidAskCapi[0];
    ask = bidAskCapi[1];
    cap = bidAskCapi[2];
	return (0);
}

Open in new window



Here's the code containing the constructor and all methods. It's C++ with /clr, compiled as a .DLL:

#using "YahooAPI.dll"

#include <msclr\auto_gcroot.h>

using namespace System::Runtime::InteropServices; // Marshal

class YahooAPIWrapperPrivate
{
    public: msclr::auto_gcroot<YahooAPI^> yahooAPI;
};

class __declspec(dllexport) YahooAPIWrapper
{
    private: YahooAPIWrapperPrivate* _private;

    public: YahooAPIWrapper()
    {
        _private = new YahooAPIWrapperPrivate();
        _private->yahooAPI = gcnew YahooAPI();
    }
    
    public: double GetBid(const char* symbol)
    {
        return _private->yahooAPI->GetBid(gcnew System::String(symbol));
    }

    public: double GetAsk(const char* symbol)
    {
        return _private->yahooAPI->GetAsk(gcnew System::String(symbol));
    }
    
    public: const char* GetCapitalization(const char* symbol)
    {
        System::String^ managedCapi = _private->yahooAPI->GetCapitalization(gcnew System::String(symbol));
    
        return (const char*)Marshal::StringToHGlobalAnsi(managedCapi).ToPointer();
    }
    
    public: const char** GetValues(const char* symbol, const char* fields)
    {
        cli::array<System::String^>^ managedValues = _private->yahooAPI->GetValues(gcnew System::String(symbol), gcnew System::String(fields));
        
        const char** unmanagedValues = new const char*[managedValues->Length];
        
        for (int i = 0; i < managedValues->Length; ++i)
        {
            unmanagedValues[i] = (const char*)Marshal::StringToHGlobalAnsi(managedValues[i]).ToPointer();
        }
        
        return unmanagedValues;
    }
    
    public: ~YahooAPIWrapper()
    {
        delete _private;
    }
};

Open in new window




Here's the C# code called by the code above, compiled for .NET 4.

using System.Net; // WebClient
using System.Globalization; // CultureInfo

public class YahooAPI
{
    private static readonly WebClient webClient = new WebClient();

    private const string UrlTemplate = "http://finance.yahoo.com/d/quotes.csv?s={0}&f={1}";

    private static double ParseDouble(string value)
    {
         return double.Parse(value.Trim(), CultureInfo.InvariantCulture);
    }
    
    private static string[] GetDataFromYahoo(string symbol, string fields)
    {
        string request = string.Format(UrlTemplate, symbol, fields);

        string rawData = webClient.DownloadString(request).Trim();
        
        return rawData.Split(',');
    }

    public double GetBid(string symbol)
    {
        return ParseDouble(GetDataFromYahoo(symbol, "b3")[0]);
    }

    public double GetAsk(string symbol)
    {
        return ParseDouble(GetDataFromYahoo(symbol, "b2")[0]);
    }
    
    public string GetCapitalization(string symbol)
    {
        return GetDataFromYahoo(symbol, "j1")[0];
    }
    
    public string[] GetValues(string symbol, string fields)
    {
        return GetDataFromYahoo(symbol, fields);
    }
}

Open in new window

0
 
LVL 31

Expert Comment

by:Zoppo
ID: 40019366
Hm, sorry, I'm not very familiar with mixing managed and none-managed C++/C# code this way. I don't see any obvious problem, but I'm not sure how this works at all because if I understand it correctly the DLL is used as if it is implicit linked, but the class in the header always is declared as exported with class __declspec(dllexport) YahooAPIWrapper. IMO in the EXE where you want to use this class from the DLL the directive has to be class __declspec(dllimport) YahooAPIWrapper in order the functions can be linked at runtime.

Usually this is done via preprocessor macros. For this only in the DLL a symbol is defined (i.e. MYDLL_EXPORT) and the class is declared like this:
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif

class MYDLL_API MyClass
{
 ...
};

Open in new window

IMO without this the first code can't compile (or, better said, linking should fail), so as told I don't understand how this can work at all.

ZOPPO
0
 

Accepted Solution

by:
PacificaResearch earned 0 total points
ID: 40027883
It turns out to be a compiler or linker setting in the .exe project that calls the .dll. I can't tell what settings are actually correct, because I just fooled around making random changes until it began to work. There was nothing about the final settings that were better than any of the others. Something flakey about C++ interop, maybe.
0
 

Author Closing Comment

by:PacificaResearch
ID: 40039036
No expert offered helpful advice.
0

Featured Post

Salesforce Made Easy to Use

On-screen guidance at the moment of need enables you & your employees to focus on the core, you can now boost your adoption rates swiftly and simply with one easy tool.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

If you’re thinking to yourself “That description sounds a lot like two people doing the work that one could accomplish,” you’re not alone.
Part One of the two-part Q&A series with MalwareTech.
Progress
Starting up a Project

628 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question