1 van 1

MicroSoft VS C++ 2010 - hoe los ik dit op?

Geplaatst: wo 04 feb 2015, 03:13
door Doctor Who
Lieve dames en beste heren,
 
Wat ik aan het doen ben is een "windows-service" bouwen die fout-geadresseerde email "bounces." (retourneert).
Die service op zich draait al prachtig - no problem - maar ik wil 'm wat te doen geven natuurlijk.
 
Ik baseer dat op de "Micro$oft Collaboration Data Objects" (CDO) en op zich werkt dat wel.
Als Header gebruik ik b.v. deze code:
 
CDONoNameSpace.h

Code: Selecteer alles

#import "C:\Program Files\Common Files\System\ado\msado15.dll"\
        no_namespace rename("EOF","adoEOF")
#import <cdosys.dll> no_namespace

#include <cdosysstr.h>
#include <cdosyserr.h>
en dat werkt subliem hoor - geen probleem daarmee.
 
Maar nu wil ik een email-bestand (.eml-file) inlezen volgens dit:
 
CDOReadMail.cpp

Code: Selecteer alles

#include "CDONoNameSpace.h"

IMessagePtr readMailMessage(_bstr_t filePath)
{
    // Creëer een (ADODB) data-stream object
    _StreamPtr pStm(__uuidof(Stream));

    // Source is niet van belang - komt later wel
    _variant_t varOptional(DISP_E_PARAMNOTFOUND,VT_ERROR);
    try {
        // Open die stream
        pStm->raw_Open(
        varOptional,
        adModeUnknown,
        adOpenStreamUnspecified,
        NULL,
        NULL);

        // Lees data uit een gespecificeerd file
        pStm->LoadFromFile(filePath);

        // en zorg dat dat bewaard blijft
        pStm->Flush();
    }
    catch(_com_error e)
    {
        // Iets in het bovenstaande ging niet goed
        // en deze afhandeling vind ik erg slordig
        throw e;
    }

    // Nu gaan we kijken wat die steam ons te bieden heeft:
    // Creëer een Message-object
    IMessagePtr iMsg(__uuidof(Message));

    // Maak dat dat ingelezen kan worden
    IDataSourcePtr iDsrc = iMsg;

    try {
        // en probeer dat ook
        iDsrc->OpenObject(pStm,_bstr_t("_Stream"));
    }
    catch(_com_error e)
    {
        // Mislukt:
        // ook deze afhandeling kan beter
        throw e;
    }

    // Want in die Catch-statements wordt de geopende stream niet gesloten
    // en na een throw-statement wordt deze opdracht niet meer bereikt
    pStm->raw_Close();

    // Maar DIT is het probleem:
    // hoe lever ik dit af aan een aanroepende functie?
    return iMsg;
}
En ook dat werkt.
 
Maar wat beslist niet werkt is dus dit:

Code: Selecteer alles

int main(int argc, char* argv[])
{
    CoInitialize(NULL);
    HRESULT hr = S_OK;
    IMessagePtr imp;
    try {
        imp = readMailMessage("c:\\sample_mail.eml");
    } catch( _com_error err) {
        //  ...
        hr = err.Error();
        wprintf(L"Exeption occured: %s\n", err.ErrorMessage());
    }

    CoUninitialize();
    return (int)hr != S_OK;
}

want dan krijg ik dus mooi deze error:

Code: Selecteer alles

Unhandled exception at 0x<where_ever> in CDOReadMail.exe: 0xC0000005: Access violation reading location 0x<where_ever>.
Weet iemand wat ik hier over het hoofd zie?
Alvast bedankt. ;)

Re: MicroSoft VS C++ 2010 - hoe los ik dit op?

Geplaatst: wo 04 feb 2015, 08:24
door physicalattraction
Ik ben onbekend met CDO, en zie dus ook niet gelijk een probleem. Heb je al eens geprobeerd met een debugger stap voor stap door je code te lopen, en zo ja, in welke regel gaat het precies mis?

Re: MicroSoft VS C++ 2010 - hoe los ik dit op?

Geplaatst: wo 04 feb 2015, 11:20
door Xenion
Ook geen ervaring met CDO, maar op het eerste zicht ziet het ernaar uit dat je ergens geheugen probeert aan te spreken dat niet gealloceerd is. Check eens of je niet met NULL pointers zit te werken.

Re: MicroSoft VS C++ 2010 - hoe los ik dit op?

Geplaatst: wo 04 feb 2015, 22:51
door Doctor Who
Bedankt voor jullie repliek jongens, en ik zal wat duidelijker zijn (ik heb niet stil gezeten vandaag).
Wat ik eerder gaf als main-functie kun je vervangen door dit:

Code: Selecteer alles

void mesg_print(IMessagePtr pMsg)
{
	wprintf(L"To: %s\n", (wchar_t *)pMsg->To);
	wprintf(L"From: %s\n", (wchar_t *)pMsg->From);
	wprintf(L"Sender: %s\n", (wchar_t *)pMsg->Sender);
	wprintf(L"Subject: %s\n", (wchar_t *)pMsg->Subject);
}

int main(int argc, char* argv[])
{
    CoInitialize(NULL);
    HRESULT hr = S_OK;
    IMessagePtr imp;
    try {
        imp = readMailMessage("c:\\sample_mail.eml");
    } catch( _com_error err) {
        //  ...
        hr = err.Error();
        wprintf(L"Exeption occured: %s\n", err.ErrorMessage());
    }
    mesg_print(imp);

    CoUninitialize();
    return (int)hr != S_OK; // Hier gaat het mis
}

Als je dit uitvoert met de debugger gaat het fout, en komt juist de debugger met een popup-melding.
De inhoud daarvan gaf ik eerder (Access violation reading location 0x<where_ever>).
En je kunt stoppen met debuggen - het spel is uit.
Maar wel zijn de gevonden waarden op scherm afgedrukt - geen NULL-pointer dus.
 
Wat ik zelf denk - het is idd een allocatie-probleem:
die subfunctie "readMailMessage" doet natuurlijk wel wat-ie doen moet, echter, na uitvoering ervan zijn al z'n lokale variabelen foetsie c.q. gealloceerd geheugen is vrijgegeven.
Ik heb eerder geprobeerd de lokale variabele "iMsg" als static te declareren, maar dat ging ook niet.
 
FYI: Ik ben trouwens niet de eerste met dit probleem - in 2008 was iemand mij al voor:
 
why-this-does-not-work-at-vc6?
 
Nou... in VC 2010 werkt dat nog steeds niet hoor. ;)
 
Maar ik vond vandaag iets iets dat wèl werkt, en daar zien jullie vast de logica wel van in.
Het werkt als volgt:
 
1. verander de declaratie van "readMailMessage" van

Code: Selecteer alles

IMessagePtr readMailMessage(_bstr_t filePath)
in

Code: Selecteer alles

IMessagePtr readMailMessage(IMessagePtr iMsg, _bstr_t filePath)
en verwijder/becommentarieer binnen die functie dit statement

Code: Selecteer alles

IMessagePtr iMsg(__uuidof(Message));
2. gebruik dit als main-functie:

Code: Selecteer alles

int main(int argc, char* argv[])
{
    CoInitialize(NULL);
    HRESULT hr = S_OK;
    IMessage* pMsg = NULL;
    CoCreateInstance( __uuidof(Message),
                      NULL,
                      CLSCTX_INPROC_SERVER,
                      __uuidof(IMessage),
                     (void**) &pMsg
                     );
    try {
        readMailMessage(pMsg, "c:\\sample_mail.eml");
    } catch( _com_error err) {
        //  ...
        hr = err.Error();
        wprintf(L"Exeption occured: %s\n", err.ErrorMessage());
    }
    mesg_print(pMsg);

    pMsg->Release();
    CoUninitialize();
    return (int)hr != S_OK;
}
... en verd@md - het werkt! :oops:
 
Jullie zien wat er gebeurt:
het object wordt gecreëerd en het adres ervan wordt doorgegeven aan de manipulerende functie (standaard C truc).
 
Drie opmerkingen nog voor als je dit thuis probeert:
  1. verander de declaratie "IMessage* pMsg" NIET in "IMessagePtr pMsg", want dan gaat het feest niet door;
  2. v.w.b. de header - weet zeker dat je de goede locatie invoert voor bestand "msado15.dll" (bij mij "C:\Program Files\Common Files\System\ado\msado15.dll");
  3. het in te lezen .eml-bestand (in mijn geval "c:\\sample_mail.eml") moet fysiek aanwezig zijn. Je kunt dit doen door bv een inkomende e-mail op te slaan in .eml-format. Een beetje zichzelf-respecterend mail-programma kan dat.
 
Tot slot:
  • Ik ben nog steeds niet trots of eufoor of zo, want ik vind dat dit netter kan.
  • Met CDO heb ik alleen ervaring middels JavaScript via de Windows Scripting Host, en dat werkt best leuk.
  • Het is enorm bruikbaar om in webpagina's in te bouwen mgv ASP, PHP, etc.
  • Onbekend ben ik dus niet met dat object, maar het heeft nog wel geheimen voor me hoor..
  • Daarnaast weet ik m'n wetje van "C" maar veel minder van "C++".
  • Om met dat laatste vertrouwd te raken lijkt me dit een leuke instap, want ik denk dat jullie meer van C++ weten dan ik.
 
Ik hoop dat jullie (en ik) hier iets mee kunnen.
 
Greetz,
DW

Re: MicroSoft VS C++ 2010 - hoe los ik dit op?

Geplaatst: do 05 feb 2015, 22:23
door Xenion
In dit topic heeft iemand blijkbaar een gelijkaardig probleem: http://forums.codeguru.com/showthread.php?193792-Access-violation-releasing-IMessagePtr
Blijkbaar is die CoUninitialize() de boosdoener. Die CoUnitialize() gaat waarschijnlijk geheugen de-alloceren en wanneer je smart pointer zijn destructor wil aanroepen gaat die geheugen proberen vrij te geven dat al vrij is.

Re: MicroSoft VS C++ 2010 - hoe los ik dit op?

Geplaatst: vr 06 feb 2015, 23:06
door Doctor Who
@Xenion
 
Ik heb een donkerbruin vermoeden dat dit klopt, ja.
Het is mij eerder overkomen (niet zozeer bij de codes hier) dat de debugger een "native C++ code" binnenstapte.
Dat kan ik helaas niet meer zo snel naar boven halen.
Maar het betrof een bestand "iomanip.h" met een "Release" opdracht er in - voorzien van een "throw statement".
Krijg ik dit ooit weer, dan richt ik m'n aandacht er op en zal het melden.
Een bug lijkt het mij niet, maar ook niet echt gebruiksvriendelijk.
 
Bedankt voor de link die je gaf: daar heb ik veel aan.
Als ik klaar ben met m'n test-routines wil ik ze netjes maken en in een class onder brengen.
Hoe dat te doen gaf een topic-deelnemer (Filbert Fox) daar heel mooi aan:
ik ga beslist stoeien met dat concept (leuke tutorial voor mij).
 
Ik vond op mijn beurt deze link:
 
Using Native COM Support in Microsoft Visual C++
 
zie de eerste voorbeeld-code, die voorziet in wat ik in beginsel wilde:
gooi die smartpointer(?) "IMessagePtr" overboord en gebruik een "classic pointer".
En dan werkt een code als deze probleemloos:

Code: Selecteer alles

#import "c:\program files\common files\system\ado\msado15.dll" \
        rename("EOF","adoEOF") no_namespace
#import <cdosys.dll> no_namespace

#include "cdosysstr.h"
#include "cdosyserr.h"

IMessage* het_Mooiste_Message_Object_Ter_Wereld(void)
{
  IMessage* retVal = NULL;
  CoCreateInstance( __uuidof(Message),
                      NULL,
                      CLSCTX_INPROC_SERVER,
                      __uuidof(IMessage),
                     (void**) &retVal
                     );
    return retVal;
}

void main()
{
  CoInitialize(NULL);
  IMessage* pMsg = het_Mooiste_Message_Object_Ter_Wereld();
  if(pMsg)
  // het waarom kan me niet schelen - die pointer is NULL als het is mislukt
  {
      // Doe wat je wilt
      //...
      
      // en geef het weer vrij
      pMsg->Release();
  }
  CoUninitialize();
}
Dat dit werkt vind ik natuurlijk heel erg leuk.
Maar ik heb nog heel wat te leren als het gaat om C++:
  • SmartPointers;
  • Abstract-classes;
  • Managed and UnManaged code;
  • en er zal nog wel meer zijn.
Daar ga ik zelf wel achteraan en vraag dat niet hier.
Ik heb wel eens een "derived class" geschreven mgv PHP - maar dat ligt wat simpeler.
Van C/C++ weet ik dat dit soort zaken wat strenger gehanteerd worden.
 
In die zin zal ik nog wel eens wat te melden en te vragen hebben. ;)
 
 

Re: MicroSoft VS C++ 2010 - hoe los ik dit op?

Geplaatst: di 24 feb 2015, 00:17
door Doctor Who
En ja hoor, ik heb weer een probleem. #-o
 
Die "windows-service" die ik heb gemaakt (zie post #1) heeft een vervelend neven-effect:

het affecteert het laden van webpagina's en dat ziet er uit als een caching-probleem naar mijn idee.
 
Wat actueel gebeurt?
Wanneer web-pages gebruik maken van geneste "onload-statements" zoals dit:

Code: Selecteer alles

if (window.addEventListener) window.addEventListener('load', myFunction, false);
else window.attachEvent('onload', myFunction );
een reeds aanwezig (on)load-event niet in z'n geheel wordt uitgevoerd.
Zo wordt b.v. een (achtergrond)afbeelding niet in z'n geheel geladen.
De broncode die ik voor mijn service heb gebruikt staat hier.
 
Het eerste waar ik dan aan denk is het verlagen van de prioriteit van het proces waarin mijn service wordt uitgevoerd.

Maar dat is niet eenvoudig en het lost wellicht ook niks op.

Wat schijnt te werken is het volgende:
zie het bestand "ThreadPool.h";
  • hierin wordt een functie QueueUserWorkItem  aangeroepen om mijn service (proces?) een plaats te geven tussen de reeds aanwezige (naar wat ik ervan begrijp);
  • de laatste parameter (Flags) heb ik op "WT_EXECUTEDEFAULT" (ofwel 0 c.q. alle vlaggen staan 'uit') gezet;
  • dit stond op "WT_EXECUTELONGFUNCTION" - die een specifieke vlag "aan" zet.
Hiermee lijkt het probleem opgelost, maar ik ben verre van zeker.
Toegegeven,
die service wacht nou steeds 2 seconden om er achter te komen dat-ie niks te doen heeft.
Zie "MainService.cpp":

Code: Selecteer alles

//   FUNCTION: CMainService::ServiceWorkerThread(void)
//
//   PURPOSE: The method performs the main function of the service. It runs 
//   on a thread pool worker thread.
//
void CMainService::ServiceWorkerThread(void)
{
    // Periodically check if the service is stopping.
    while (!m_fStopping)
    {
        // Perform main service function here...

        ::Sleep(2000);  // Simulate some lengthy operations.
    }

    // Signal the stopped event.
    SetEvent(m_hStoppedEvent);
}
Maar wat staat mij te wachten als ik daar m'n eigen procedure(s) aan ga koppelen? :-k
 
Dat is mijn vraag, maar...
doe lekker rustig aan v.w.b. repliek hoor.
 
Ik neem hier zelf ook wel eens afstand van. ;)
 
BTW: het OS is Windows Server 2008 Enterprise - SP2.