Piszę bibliotekę w C++/CLI, żeby spiąć ze sobą dwa światy. W założeniu ma ona mieć minimalną ilość kodu, aby zbędnie nie generować błędów, dlatego zdecydowałem się na odwzorowanie klas 1:1. Gdy wywołam ConnectToServer aplikacja działa jeszcze przez dwie sekundy, potem zawiesza się i po około minucie otrzymuję Exception:
Exception thrown at 0x00000000 in ClrWrapperTest.exe: 0xC0000005: Access violation executing location 0x00000000.
Po śladach wywnioskowałem, że wywala się po jednym cyklu próby połączenia z serwerem. Możliwe, że coś jest z tym SDK (napisałem do autorów - czekam na odpowiedź), ale mam przykładową aplikację w czystym C++ gdzie wykonanie tych samych operacji działa. Co ważne brak połączenia z serwerm nie zawiesza tam aplikacji, więc prawdopodobnie powinno to działać w innym wątku. Mi to wygląda na problem z utworzeniem wątku do zestawiania połączeń w kodzie natywnym. Zastanawiam się czy gdzieś nie popełniłem błędu, albo jest jakiś mankament o którym nie wiem. Nie mam tutaj żadnego speca od C++, a sam to Kali jeść, Kali pić. Będę wdzięczny za wszelkie sugestie. Poniżej zamieszczam kod:
Nagłówek klasy natywnej (okroiłem z nieistotnych metod):
class ICmsServer : public IUnknown
{
public:
// connection type
enum CONNECTION_TYPE {
TYPE_1 = 1U<<1,
TYPE_2 = 1U<<2,
TYPE_3 = 1U<<3 };
// connects to remote server,
// client auto reconnects after an error.
virtual void ConnectToServer(
LPCOLESTR szAddress, int nPort,
LPCOLESTR szLogin,
LPCOLESTR szPassword,
int nConnectionType = TYPE_1,
BOOL bArchBrowser = FALSE ) = 0;
// disconnect
virtual void DisconnectServer() = 0;
// current connection status
// it is always DISCONNECTED after DisconnectServer(),
// but it can be any value after ConnectToServer() - Example:
// If client can't connect to a server status will be like this:
// CONNECTING,ERROR_OCURRED,DISCONNECTED, .... CONNECTING,....
enum STATUS {
DISCONNECTED = 0x01,
CONNECTING = 0x02,
CONNECTED = 0x03,
ERROR_OCCURED = 0x04 };
// return connection status
virtual int GetStatus() = 0;
};
class __declspec( uuid("{C48B8E60-ABB7-4e86-8399-8E5EBE926EFA}") ) ICmsServer;
Tak wygląda mój kod:
Plik .h
#pragma once
#include "SDKHeaders\ICmsClient.h" //to jest includowane dlatego, że includowanie "ICmsServer.h" powoduje błędy - widocznie mają to spaprane
using namespace System;
namespace SDKClrWrapper
{
public ref class CmsServer
{
public:
enum class Status
{
Disconnected = ICmsServer::DISCONNECTED,
Connecting = ICmsServer::CONNECTING,
Connected = ICmsServer::CONNECTED,
ErrorOccured = ICmsServer::ERROR_OCCURED
};
private:
ICmsServer * server;
internal:
CmsServer(ICmsServer * nativeServer);
public:
~CmsServer();
void ConnectToServer(String^ address, int port, String^ login, String^ password);
void DisconnectServer();
CmsServer::Status GetStatus();
protected:
!CmsServer();
private:
LPOLESTR Convert(String^ s);
};
}
i plik cpp:
#include "stdafx.h"
#include "CmsServer.h"
#include <vcclr.h>
namespace SDKClrWrapper
{
CmsServer::CmsServer(ICmsServer * nativeServer)
{
server = nativeServer;
}
CmsServer::~CmsServer()
{
this->!CmsServer();
}
void CmsServer::ConnectToServer(String^ address, int port, String^ login, String^ password)
{
LPOLESTR lpAddress = Convert(address);
LPOLESTR lpLogin = Convert(login);
LPOLESTR lpPassword = Convert(password);
server->ConnectToServer(lpAddress, port, lpLogin, lpPassword);
}
void CmsServer::DisconnectServer()
{
if (server == NULL || GetStatus() != Status::Connected)
return;
server->DisconnectServer();
}
CmsServer::Status CmsServer::GetStatus()
{
if (server == NULL)
return Status::Disconnected;
return (Status)server->GetStatus();
}
CmsServer::!CmsServer()
{
if (server == NULL)
return;
if (GetStatus() == Status::Connected)
server->DisconnectServer();
server->Release();
}
LPOLESTR CmsServer::Convert(String^ s)
{
pin_ptr<const wchar_t> wch = PtrToStringChars(s);
return (LPOLESTR)wch;
}
}
plik Form1.cs
using System;
using System.Windows.Forms;
using SDKClrWrapper;
namespace ClrWrapperTest
{
public partial class Form1 : Form
{
private SdkLibrary _library;
private CmsClientFactory _factory;
private CmsClient _client;
private CmsServer _server;
private CmsVideoView _videoView;
private uint _counter = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
_server?.Dispose();
_videoView?.Dispose();
_client?.Dispose();
_factory?.Dispose();
_library?.Dispose();
}
private void Form1_Shown(object sender, EventArgs e)
{
_library = new SdkLibrary();
_factory = _library.GetClientFactory();
_client = _factory.GetCmsClient();
_server = _client.GetServer(0);
_server.ConnectToServer("localhost", 9001, "admin", "admin");
}
}
}
Wydaje mi się, że to jest cały potrzebny kod, ale resztę mogę dorzucić na żądanie.