Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

Class instantiation blowup

Posted on 2014-04-22
5
Medium Priority
?
299 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

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Question has a verified solution.

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

Q&A with Course Creator, Mark Lassoff, on the importance of HTML5 in the career of a modern-day developer.
This article will show how Aten was able to supply easy management and control for Artear's video walls and wide range display configurations of their newsroom.
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …
Progress

664 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