Black Friday = Pricewatch Bekijk onze selectie van de beste Black Friday-deals en voorkom een miskoop.

[C++] Probleem met constructor/globale class instance

Pagina: 1
Acties:

  • danslo
  • Registratie: Januari 2003
  • Nu online
Beetje vaag uitgelegd, maar dit is zo ongeveer mijn probleem:

Ik ben op het moment een beetje aan het rommelen met Win32 GUI (CreateWindow) zooi icm. sockets om zo een asynchrone client te maken.

Dit werkt allemaal prima als ik alle connect code in mijn class constructor gooi. Nu vind ik dat sowieso niet een geweldige oplossing, en daar komt nog bij dat ik ook de hwnd van de huidige window mee wil geven aan de class zodat ik WSAAsyncSelect kan callen. Dit is een probleem want dan zal ik mijn class instance moeten definieren in de private scope van de WinMain functie, waardoor ik de class instance bijvoorbeeld niet meer kan bereiken in WndProc (wanneer ik callbacks krijg voor FD_READ etc).

Nu is mijn oplossing hiervoor, dat ik alleen WSAStartup() in mijn class constructor aanroep. Vervolgens definieer ik een class instance in de global scope (zonder parameters), waarna ik vervolgens een nieuwe Connect functie aanroep als WM_CREATE wordt getriggered. Op die manier heb ik namelijk wel een globale class instance, en kan ik hem ook aanroepen met HWND parameter. Ook hier heb ik een probleem, namelijk dat mijn socket() call _altijd_ SOCKET_ERROR returned.

WSAGetLastError() geeft gewoon 0 terug. De class constructor (met WSAStartup) wordt gewoon aangeroepen voordat Connect() wordt aangeroepen, dus in principe wordt precies dezelfde code uitgevoerd als wanneer ik alles in de constructor had.

Pseudo code:


Werkt WEL:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class blah { 

private:
    WSAData wsa
    SOCKET s

public:
    constructor(parameters) {
        WSAStartup
        Maak socket aan
        verbind naar (parameters)
    }

}

WinMain { 
    blah instance(parameters);
}


Werkt NIET:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class blah { 

private:
    WSAData wsa
    SOCKET s

public:
    constructor() {
        WSAStartup
    }
    connect(parameters) {
        Maak socket aan
        Verbin naar (parameters)
    }
};




GLOBAAL: blah instance

WndProc {
    WM_CREATE:
    instance.Connect(parameters)
    break
}


Ik hoop dat het een beetje duidelijk is... Als er nog vragen zijn hoor ik het graag, wordt er onderhand een beetje gek van.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

En als je de WSAStartup() naar connect() verplaatst, werkt het dan weer wel?

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • danslo
  • Registratie: Januari 2003
  • Nu online
Sorry dat ik dat er niet meteen bij vermeld heb, dat heb ik inderdaad ook geprobeerd. Krijg echter nog steeds SOCKET_ERROR terug op socket() call.

edit:

Als ik die WSAStartup() call verplaats naar Connect() heb ik dus precies de code in Connect() zitten die WEL werkt als ik alles in de constructor gooi. Geen flauw idee waar dat aan ligt.

[ Voor 40% gewijzigd door danslo op 09-09-2008 03:05 ]


  • Dricus
  • Registratie: Februari 2002
  • Laatst online: 08:32

Dricus

ils sont fous, ces tweakers

Misschien kun je even wat echt code posten. Zoals je het in je pseudocode laat zien zou het volgens mij gewoon moeten werken ('k heb de MSDN library er nog even op nageslagen). Mijn conclusie is dat dit ruikt naar een verborgen bugje :).

Stel niet uit tot morgen wat je vandaag nog tot morgen kunt uitstellen...


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Dat laatste zou ik niet doen. Je hebt weinig garanties wanneer wsastartup precies gecalled wordt; misschien wel voor andere belangrijke windows initialisatie.

Het kan ook een threading issue zijn. Lopen die callbacks in dezelfde, main thread?
Dit werkt allemaal prima als ik alle connect code in mijn class constructor gooi. Nu vind ik dat sowieso niet een geweldige oplossing,
Een prachtige oplossing, een resource acquisition in een ctor.

[ Voor 10% gewijzigd door Zoijar op 09-09-2008 09:52 ]


  • Dricus
  • Registratie: Februari 2002
  • Laatst online: 08:32

Dricus

ils sont fous, ces tweakers

Volgens de MSDN initialiseert WSAStartup de winsock DLL voor een proces. Over threading wordt niet gesproken. Het lijkt mij dus niet heel waarschijnlijk dat het een threading issue is.

Of het een threading issue is hangt trouwens ook af hoe de message loop van de applicatie geïmplementeerd is. Als deze in de main thread van de applicatie draait zit er qua threading geen verschil tussen de twee oplossingen. Als hiervoor een aparte thread gemaakt is dan zit hem daar het verschil.

Als je je message loop als aparte thread hebt geïmplementeerd dan zou je eens kunnen kijken wat er gebeurt met de nu-niet-werkende code als je de message loop in de main thread van je applicatie laat draaien.

Stel niet uit tot morgen wat je vandaag nog tot morgen kunt uitstellen...


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

cls schreef op dinsdag 09 september 2008 @ 03:02:
Sorry dat ik dat er niet meteen bij vermeld heb, dat heb ik inderdaad ook geprobeerd. Krijg echter nog steeds SOCKET_ERROR terug op socket() call.

edit:

Als ik die WSAStartup() call verplaats naar Connect() heb ik dus precies de code in Connect() zitten die WEL werkt als ik alles in de constructor gooi. Geen flauw idee waar dat aan ligt.
Euh, eerst zeg je dat je het al geprobeerd hebt, en daarna zeg je dat als je dat doet dat het dan wel werkt 8)7.
Maar goed, first of all, die WSAStartup() hoort eigenlijk sowieso niet in je class, maar in je main(). Maar goed, dat is een design kwestie. Maar wat returnt die functie? En welke parameters geef je mee?

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • epic007
  • Registratie: Februari 2004
  • Laatst online: 15:31
.oisyn schreef op dinsdag 09 september 2008 @ 11:09:
..first of all, die WSAStartup() hoort eigenlijk sowieso niet in je class, maar in je main()..
Volgens mij zit hier wel ergens de fout.

als je een globale declaratie maakt van je klasse, dan wordt de constructor volgens mij vóór main() aangeroepen. Het kan zijn dat WSAStartup() hier failed doordat je window/applicatie nog niet is geinitialiseerd.

  • danslo
  • Registratie: Januari 2003
  • Nu online
Goed, ik zal het eens met wat code doen om het wat duidelijker uit te leggen...

Dit werkt dus wel:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include "blah.h"

Blah::Blah(char* host, unsigned short port, HWND hwnd) {
    WSAStartup(MAKEWORD(2, 2),&wsa);
    target.sin_port         = htons(port);
    target.sin_family       = AF_INET;
    target.sin_addr.s_addr  = inet_addr(host);
    if ((s=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET){
        MessageBox(hwnd, L"Socket returned invalid socket. :(", L"Error", MB_OK);
    }
    if (connect(s,(SOCKADDR*)&target,sizeof(target))!=0) {
        MessageBox(hwnd, L"Couldn't connect to server. :(", L"Error", MB_OK);
    } else {
        WSAAsyncSelect(s, hwnd, WM_USER+2, FD_READ | FD_CLOSE);
    }
}

Blah::~Blah() {
    WSACleanup();
}

Om het vervolgens zo aan te roepen:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

    {...}

    Blah Two("IP", 1337, hwnd);

    // Message loop.
    while(GetMessage(&msg,  NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;

}


En dit niet:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include "blah.h"

Blah::Blah() {
    WSAStartup(MAKEWORD(2, 2),&wsa);
}

Blah::~Blah() {
    WSACleanup();
}

bool Blah::Connect(char* host, unsigned short port, HWND hwnd) {
    target.sin_port         = htons(port);
    target.sin_family       = AF_INET;
    target.sin_addr.s_addr  = inet_addr(host);
    if ((s=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET){
        MessageBox(hwnd, L"Socket returned invalid socket. :(", L"Error", MB_OK);
        return false;
    }
    if (connect(s,(SOCKADDR*)&target,sizeof(target))!=0) {
        MessageBox(hwnd, L"Couldn't connect to server. :(", L"Error", MB_OK);
        return false;
    } else {
        WSAAsyncSelect(s, hwnd, WM_USER+2, FD_READ | FD_CLOSE);
        return true;
    }
}

Om het vervolgens zo aan te roepen:

Blah Two;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch(msg) {
        case WM_CREATE:
            Two.Connect("IP", 1337, hwnd);
            break;
        case WM_CLOSE:
            DestroyWindow(hwnd);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        case WM_USER+2:
            switch(lParam) {
                case FD_CLOSE:
                    break;
                case FD_READ:
                    break;
            }
            break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

    {...}

    // Message loop.
    while(GetMessage(&msg,  NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;

}

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

Maar wat returnt die functie?
The WSAStartup function returns zero if successful. Otherwise, it returns one of the error codes listed in the following.
An application cannot call WSAGetLastError to determine the error code as is normally done in Windows Sockets if WSAStartup fails. The WS2_32.DLL will not have been loaded in the case of a failure so the client data area where the last error information is stored could not be established.

[ Voor 85% gewijzigd door .oisyn op 09-09-2008 15:28 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • epic007
  • Registratie: Februari 2004
  • Laatst online: 15:31
Ik zou in je 2e voorbeeld.
C++:
1
2
    WSAStartup(MAKEWORD(2, 2),&wsa);//van regel 4 naar regel 60 verplaatsen.
    WSACleanup(); //van regel 8 naar regel 66 verplaatsen.

Zoiets dus:
C++:
1
2
3
4
5
6
    WSAStartup(MAKEWORD(2, 2),&wsa); :> 
    while(GetMessage(&msg,  NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    } 
    WSACleanup();

  • danslo
  • Registratie: Januari 2003
  • Nu online
@epic007

Probleem daarmee is dat wsa een private variabele in mijn class is.
Het probleem is trouwens opgelost, geen idee hoe en wat het was, maar het werkt opeens :?

Toch twee kleine vraagjes:
- Kan ik beter van die private wsa variabele een static public member maken en die vanuit mijn main zo aanroepen: WSAStartup(x, Blah::wsa); ?
- Hiervoor heb ik alleen met console schermpjes gewerkt in C++, is er behalve fwrite/messageboxes ook een manier waarmee ik data in een debug schermpje kan gooien ergens? :P

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

De vraag is meer, wat moet Blah in hemelsnaam met de verantwoordelijkheid voor het initialiseren van de netwerk API?
WSAData maak je gewoon een lokale variabele in main(). Blah kan zich voor de rest gewoon met z'n eigen zaken bemoeien, namelijk het creëren van sockets e.d. :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Dricus
  • Registratie: Februari 2002
  • Laatst online: 08:32

Dricus

ils sont fous, ces tweakers

WSAStartup hoef je maar 1x aan te roepen voor je proces. Het lijkt me dan ook onlogisch om dat in de constructor van een class aan te roepen die je (op z'n minst in theorie) meerdere keren kunt instantiëren. Aangezien je die WSADATA struct bij mijn weten verder niet nodig hebt voor je Winsock calls heb je die variabele ook niet nodig in je class. Sterker nog, je hoeft hem niet eens te bewaren.

Stel niet uit tot morgen wat je vandaag nog tot morgen kunt uitstellen...

Pagina: 1