Solved

Class instantiation blowup

Posted on 2014-04-22
5
272 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
  • 3
  • 2
5 Comments
 
LVL 30

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 30

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

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
sumDigits  challenge 7 60
Explain Unit of Work pattern 2 48
advertisement module in core php 4 87
Currency Conversion? 1 37
A theme is a collection of property settings that allow you to define the look of pages and controls, and then apply the look consistently across pages in an application. Themes can be made up of a set of elements: skins, style sheets, images, and o…
This article is meant to give a basic understanding of how to use R Sweave as a way to merge LaTeX and R code seamlessly into one presentable document.
In this fourth video of the Xpdf series, we discuss and demonstrate the PDFinfo utility, which retrieves the contents of a PDF's Info Dictionary, as well as some other information, including the page count. We show how to isolate the page count in a…
In this fifth video of the Xpdf series, we discuss and demonstrate the PDFdetach utility, which is able to list and, more importantly, extract attachments that are embedded in PDF files. It does this via a command line interface, making it suitable …

705 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

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now