Calling Legacy C function from Managed C++

B

briankirkpatrick

Forgive me if my post seems a little amateurish...

I'm requesting assistance from some of you smart folks out there to
get the managed calls write that meet the specification in the esa.h
for Esa_Init. When I make a call, VS2005 reports
"AccessViolationException" and refers to the 4th parameter
(EsaT_State_Handle). I don't know how to define nor pass this
reference correctly to the legacy DLL. What am I doing wrong? Thanks
in advance!!


esa.h
**********************************
#define SIM_EXPORT DLLIMPORT

typedef struct EsaT_State EsaT_State;
typedef EsaT_State * EsaT_State_Handle;

typedef enum EsaT_Compcode
{
EsaC_Comp_Failure,
EsaC_Comp_Success
} EsaT_Compcode;

DLLIMPORT EsaT_Compcode Esa_Init (int argc, char *argv[], int options,
EsaT_State_Handle * esa_state_pptr);
************************************


CPP Wrapper in DLL:
****************************
#include "stdafx.h" //Includes esa.h above

//Globals
EsaT_State_Handle esaHandle;

extern "C" __declspec(dllexport) int __stdcall main(int argc, char*
argv[])
{
Esa_Init(argc, argv, ESAC_OPTS_NONE, &esaHandle);
return 0;
}
*****************************
 
B

Bruno van Dooren [MVP - VC++]

I'm requesting assistance from some of you smart folks out there to
get the managed calls write that meet the specification in the esa.h
for Esa_Init. When I make a call, VS2005 reports
"AccessViolationException" and refers to the 4th parameter
(EsaT_State_Handle). I don't know how to define nor pass this
reference correctly to the legacy DLL. What am I doing wrong? Thanks
in advance!!

What does the documentation for that function say?
Are you passing the correct values?
An access violation means that that function is reading or writing in a
place that it has no access to. Probebly due to bad pointer values.
esa.h
**********************************
#define SIM_EXPORT DLLIMPORT

typedef struct EsaT_State EsaT_State;
typedef EsaT_State * EsaT_State_Handle;

typedef enum EsaT_Compcode
{
EsaC_Comp_Failure,
EsaC_Comp_Success
} EsaT_Compcode;

DLLIMPORT EsaT_Compcode Esa_Init (int argc, char *argv[], int options,
EsaT_State_Handle * esa_state_pptr);
************************************


CPP Wrapper in DLL:
****************************
#include "stdafx.h" //Includes esa.h above

//Globals
EsaT_State_Handle esaHandle;

extern "C" __declspec(dllexport) int __stdcall main(int argc, char*
argv[])
{
Esa_Init(argc, argv, ESAC_OPTS_NONE, &esaHandle);
return 0;
}
*****************************

Why do you export main here?
main is the entry point of your application.
What does Esa_init expect in argc and argv?
Does esaHandle need to be a valid pointer value?
 
B

Ben Voigt [C++ MVP]

Forgive me if my post seems a little amateurish...

I'm requesting assistance from some of you smart folks out there to
get the managed calls write that meet the specification in the esa.h
for Esa_Init. When I make a call, VS2005 reports
"AccessViolationException" and refers to the 4th parameter
(EsaT_State_Handle). I don't know how to define nor pass this
reference correctly to the legacy DLL. What am I doing wrong? Thanks
in advance!!

Is this the Esa_Init for OPNET which I see with a google? Other people
observe that OPNET ESA isn't threadsafe. Also, it looks like Esa_Main is
called before Esa_Init.

Mostly though, I think you have bad indirection.
esa.h
**********************************
#define SIM_EXPORT DLLIMPORT

typedef struct EsaT_State EsaT_State;
typedef EsaT_State * EsaT_State_Handle;

typedef enum EsaT_Compcode
{
EsaC_Comp_Failure,
EsaC_Comp_Success
} EsaT_Compcode;

I don't like that... same name for a type and a typedef? Get rid of the
enum identifier, like "typedef enum { ... } EsaT_Compcode". Don't like the
same thing done with EsaT_State above either.
DLLIMPORT EsaT_Compcode Esa_Init (int argc, char *argv[], int options,
EsaT_State_Handle * esa_state_pptr);

Calling convention isn't specified... you should do that.
************************************


CPP Wrapper in DLL:
****************************
#include "stdafx.h" //Includes esa.h above

//Globals
EsaT_State_Handle esaHandle;

extern "C" __declspec(dllexport) int __stdcall main(int argc, char*
argv[])

Why is main exported from a dll? That's probably not what you intended.
 
K

kirkpabk

Yes--OPNET ESA. Just didn't know of another better way to "control"
the simulation process. Please forgive me--VC is not my cup of tea.
Is there any way to "wrap" the code to make it thread safe? I assume
by calling convention, you mean __stdcall (don't know why I left it
out)... As far as the type/typedef decl, the convention was used
by OPNET--I obviously don't understand the construct enough. My goal
is to be able to call the C API from C++. My ignorance of both
languages is preventing me from getting the call right--or maybe I am
and just as you said, threading issues are causing the protected
memory issue. So, any other suggestions?

I appreciate your help--this has been both a painful and rewarding
experience!

Forgive me if my post seems a little amateurish...
I'm requesting assistance from some of you smart folks out there to
get the managed calls write that meet the specification in the esa.h
for Esa_Init. When I make a call, VS2005 reports
"AccessViolationException" and refers to the 4th parameter
(EsaT_State_Handle). I don't know how to define nor pass this
reference correctly to the legacy DLL. What am I doing wrong? Thanks
in advance!!

Is this the Esa_Init for OPNET which I see with a google? Other people
observe that OPNET ESA isn't threadsafe. Also, it looks like Esa_Main is
called before Esa_Init.

Mostly though, I think you have bad indirection.


esa.h
**********************************
#define SIM_EXPORT DLLIMPORT
typedef struct EsaT_State EsaT_State;
typedef EsaT_State * EsaT_State_Handle;
typedef enum EsaT_Compcode
{
EsaC_Comp_Failure,
EsaC_Comp_Success
} EsaT_Compcode;

I don't like that... same name for a type and a typedef? Get rid of the
enum identifier, like "typedef enum { ... } EsaT_Compcode". Don't like the
same thing done with EsaT_State above either.


DLLIMPORT EsaT_Compcode Esa_Init (int argc, char *argv[], int options,
EsaT_State_Handle * esa_state_pptr);

Calling convention isn't specified... you should do that.
************************************
CPP Wrapper in DLL:
****************************
#include "stdafx.h" //Includes esa.h above
//Globals
EsaT_State_Handle esaHandle;
extern "C" __declspec(dllexport) int __stdcall main(int argc, char*
argv[])

Why is main exported from a dll? That's probably not what you intended.


{
Esa_Init(argc, argv, ESAC_OPTS_NONE, &esaHandle);
return 0;
}
*****************************- Hide quoted text -

- Show quoted text -
 
K

kirkpabk

I'm not able to pass the 4th parameter correctly. If I make the DLL
an Application, with the simulation model passed in the command line,
the same function call to Esa_Init() works correctly.

The argc and argv are the standard command line parameter count and
parameter array values. The esaHandle is an "opaque" reference--I
understand that it allows provides a handle to the simulation to be
passed to other functions called within OPNET ESA API.

So... If this is a threading issue (AccessViolationException) then
how do I make the call in a thread-safe manner?

DOCUMENTATION:
Esa_Init (argc, argv, options, esa_state_pptr)

Argument Type Description
argc int size of argv array
argv char ** array of strings, typically as
provided to the main() entry point
options int one of:
ESAC_INIT_OPTS_TRACE-trace all subsequent ESA calls (similar to the
tracing of kernel procedures in ODB)
ESAC_INIT_OPTS_DEBUG-run the simulation with the ODB interface
ESAC_OPTS_NONE-no special options
esa_state_pptr EsaT_State_Handle * will be filled with an ESA
opaque data structure used by the other ESA APIs.

Return Type
EsaT_Compcode -- EsaC_Comp_Success if initialization succeeds;
EsaC_Comp_Failure otherwise.

Details
This function performs the main initialization of the ESA library. It
sets up the OPNET analysis software environment based on the argument
list, loads preference files (env_db, .ef), and so on.



I'm requesting assistance from some of you smart folks out there to
get the managed calls write that meet the specification in the esa.h
for Esa_Init. When I make a call, VS2005 reports
"AccessViolationException" and refers to the 4th parameter
(EsaT_State_Handle). I don't know how to define nor pass this
reference correctly to the legacy DLL. What am I doing wrong? Thanks
in advance!!

What does the documentation for that function say?
Are you passing the correct values?
An access violation means that that function is reading or writing in a
place that it has no access to. Probebly due to bad pointer values.




esa.h
**********************************
#define SIM_EXPORT DLLIMPORT
typedef struct EsaT_State EsaT_State;
typedef EsaT_State * EsaT_State_Handle;
typedef enum EsaT_Compcode
{
EsaC_Comp_Failure,
EsaC_Comp_Success
} EsaT_Compcode;
DLLIMPORT EsaT_Compcode Esa_Init (int argc, char *argv[], int options,
EsaT_State_Handle * esa_state_pptr);
************************************
CPP Wrapper in DLL:
****************************
#include "stdafx.h" //Includes esa.h above
//Globals
EsaT_State_Handle esaHandle;
extern "C" __declspec(dllexport) int __stdcall main(int argc, char*
argv[])
{
Esa_Init(argc, argv, ESAC_OPTS_NONE, &esaHandle);
return 0;
}
*****************************

Why do you export main here?
main is the entry point of your application.
What does Esa_init expect in argc and argv?
Does esaHandle need to be a valid pointer value?

--
Kind regards,
Bruno van Dooren MVP - VC++
http://msmvps.com/blogs/vanDooren
(e-mail address removed)- Hide quoted text -

- Show quoted text -
 
B

Ben Voigt [C++ MVP]

kirkpabk said:
Yes--OPNET ESA. Just didn't know of another better way to "control"
the simulation process. Please forgive me--VC is not my cup of tea.
Is there any way to "wrap" the code to make it thread safe? I assume

You just have to make sure you don't call from multiple threads at once.
That might mean you are calling from the wrong place to begin with, or it
might mean you need a critical section, mutex, or other lock.
by calling convention, you mean __stdcall (don't know why I left it
out)... As far as the type/typedef decl, the convention was used
by OPNET--I obviously don't understand the construct enough. My goal

Don't worry about that.
is to be able to call the C API from C++. My ignorance of both
languages is preventing me from getting the call right--or maybe I am
and just as you said, threading issues are causing the protected
memory issue. So, any other suggestions?

What is the actual name of the function calling Esa_Init, I assume it isn't
main when you are using a DLL? And where does that get called from? If you
are using DllMain, for example, make sure you are checking the dwReason
argument and understand what each of the four reasons means (PROCESS_ATTACH,
THREAD_ATTACH, PROCESS_DETACH, THREAD_DETACH).
I appreciate your help--this has been both a painful and rewarding
experience!

Forgive me if my post seems a little amateurish...
I'm requesting assistance from some of you smart folks out there to
get the managed calls write that meet the specification in the esa.h
for Esa_Init. When I make a call, VS2005 reports
"AccessViolationException" and refers to the 4th parameter
(EsaT_State_Handle). I don't know how to define nor pass this
reference correctly to the legacy DLL. What am I doing wrong? Thanks
in advance!!

Is this the Esa_Init for OPNET which I see with a google? Other people
observe that OPNET ESA isn't threadsafe. Also, it looks like Esa_Main is
called before Esa_Init.

Mostly though, I think you have bad indirection.


esa.h
**********************************
#define SIM_EXPORT DLLIMPORT
typedef struct EsaT_State EsaT_State;
typedef EsaT_State * EsaT_State_Handle;
typedef enum EsaT_Compcode
{
EsaC_Comp_Failure,
EsaC_Comp_Success
} EsaT_Compcode;

I don't like that... same name for a type and a typedef? Get rid of the
enum identifier, like "typedef enum { ... } EsaT_Compcode". Don't like
the
same thing done with EsaT_State above either.


DLLIMPORT EsaT_Compcode Esa_Init (int argc, char *argv[], int options,
EsaT_State_Handle * esa_state_pptr);

Calling convention isn't specified... you should do that.
************************************
CPP Wrapper in DLL:
****************************
#include "stdafx.h" //Includes esa.h above
//Globals
EsaT_State_Handle esaHandle;
extern "C" __declspec(dllexport) int __stdcall main(int argc, char*
argv[])

Why is main exported from a dll? That's probably not what you intended.


{
Esa_Init(argc, argv, ESAC_OPTS_NONE, &esaHandle);
return 0;
}
*****************************- Hide quoted text -

- Show quoted text -
 
K

kirkpabk

Ben,

I'm sure there is some snickering in the background because of my
ignorance <grin>. Yes, in the DLL I do have the call to Esa_Init in
the main(). I converted from an EXE to DLL--so because of my
ignorance (again), I didn't know enough to make it a DllMain (what
does that do for me?). I am calling the DLL main function from a C#
application. When I execute the function within it, I get the
"Unhandled exeception at 0x7cblah (msvcr71.dll) in giss_clr.exe:
0xC00blah: Unwind exception code." <Another problem--not necessarily
related to the original problem>. Meanwhile, I'll look into the
dwReason elements you made reference to.

Again thanks for lending your expertise!
v/r
Brian



Yes--OPNET ESA. Just didn't know of another better way to "control"
the simulation process. Please forgive me--VC is not my cup of tea.
Is there any way to "wrap" the code to make it thread safe? I assume

You just have to make sure you don't call from multiple threads at once.
That might mean you are calling from the wrong place to begin with, or it
might mean you need a critical section, mutex, or other lock.
by calling convention, you mean __stdcall (don't know why I left it
out)... As far as the type/typedef decl, the convention was used
by OPNET--I obviously don't understand the construct enough. My goal

Don't worry about that.
is to be able to call the C API from C++. My ignorance of both
languages is preventing me from getting the call right--or maybe I am
and just as you said, threading issues are causing the protected
memory issue. So, any other suggestions?

What is the actual name of the function calling Esa_Init, I assume it isn't
main when you are using a DLL? And where does that get called from? If you
are using DllMain, for example, make sure you are checking the dwReason
argument and understand what each of the four reasons means (PROCESS_ATTACH,
THREAD_ATTACH, PROCESS_DETACH, THREAD_DETACH).




I appreciate your help--this has been both a painful and rewarding
experience!
Forgive me if my post seems a little amateurish...
I'm requesting assistance from some of you smart folks out there to
get the managed calls write that meet the specification in the esa.h
for Esa_Init. When I make a call, VS2005 reports
"AccessViolationException" and refers to the 4th parameter
(EsaT_State_Handle). I don't know how to define nor pass this
reference correctly to the legacy DLL. What am I doing wrong? Thanks
in advance!!
Is this the Esa_Init for OPNET which I see with a google? Other people
observe that OPNET ESA isn't threadsafe. Also, it looks like Esa_Main is
called before Esa_Init.
Mostly though, I think you have bad indirection.
esa.h
**********************************
#define SIM_EXPORT DLLIMPORT
typedef struct EsaT_State EsaT_State;
typedef EsaT_State * EsaT_State_Handle;
typedef enum EsaT_Compcode
{
EsaC_Comp_Failure,
EsaC_Comp_Success
} EsaT_Compcode;
I don't like that... same name for a type and a typedef? Get rid of the
enum identifier, like "typedef enum { ... } EsaT_Compcode". Don't like
the
same thing done with EsaT_State above either.
DLLIMPORT EsaT_Compcode Esa_Init (int argc, char *argv[], int options,
EsaT_State_Handle * esa_state_pptr);
Calling convention isn't specified... you should do that.
************************************
CPP Wrapper in DLL:
****************************
#include "stdafx.h" //Includes esa.h above
//Globals
EsaT_State_Handle esaHandle;
extern "C" __declspec(dllexport) int __stdcall main(int argc, char*
argv[])
Why is main exported from a dll? That's probably not what you intended.
{
Esa_Init(argc, argv, ESAC_OPTS_NONE, &esaHandle);
return 0;
}
*****************************- Hide quoted text -
- Show quoted text -- Hide quoted text -

- Show quoted text -
 
B

Ben Voigt [C++ MVP]

kirkpabk said:
Ben,

I'm sure there is some snickering in the background because of my

None whatsoever. I'm just trying to figure out what's going on.
ignorance <grin>. Yes, in the DLL I do have the call to Esa_Init in
the main(). I converted from an EXE to DLL--so because of my
ignorance (again), I didn't know enough to make it a DllMain (what
does that do for me?). I am calling the DLL main function from a C#

The name "main" is C is reserved for the application entry point. I
wouldn't export it from a DLL, and I wouldn't use it for a function invoked
from user code.
application. When I execute the function within it, I get the
"Unhandled exeception at 0x7cblah (msvcr71.dll) in giss_clr.exe:
0xC00blah: Unwind exception code." <Another problem--not necessarily
related to the original problem>. Meanwhile, I'll look into the
dwReason elements you made reference to.

Can you start by renaming your function to something other than "main"?
Again thanks for lending your expertise!
v/r
Brian



Yes--OPNET ESA. Just didn't know of another better way to "control"
the simulation process. Please forgive me--VC is not my cup of tea.
Is there any way to "wrap" the code to make it thread safe? I assume

You just have to make sure you don't call from multiple threads at once.
That might mean you are calling from the wrong place to begin with, or it
might mean you need a critical section, mutex, or other lock.
by calling convention, you mean __stdcall (don't know why I left it
out)... As far as the type/typedef decl, the convention was used
by OPNET--I obviously don't understand the construct enough. My goal

Don't worry about that.
is to be able to call the C API from C++. My ignorance of both
languages is preventing me from getting the call right--or maybe I am
and just as you said, threading issues are causing the protected
memory issue. So, any other suggestions?

What is the actual name of the function calling Esa_Init, I assume it
isn't
main when you are using a DLL? And where does that get called from? If
you
are using DllMain, for example, make sure you are checking the dwReason
argument and understand what each of the four reasons means
(PROCESS_ATTACH,
THREAD_ATTACH, PROCESS_DETACH, THREAD_DETACH).




I appreciate your help--this has been both a painful and rewarding
experience!
Forgive me if my post seems a little amateurish...
I'm requesting assistance from some of you smart folks out there to
get the managed calls write that meet the specification in the esa.h
for Esa_Init. When I make a call, VS2005 reports
"AccessViolationException" and refers to the 4th parameter
(EsaT_State_Handle). I don't know how to define nor pass this
reference correctly to the legacy DLL. What am I doing wrong?
Thanks
in advance!!
Is this the Esa_Init for OPNET which I see with a google? Other
people
observe that OPNET ESA isn't threadsafe. Also, it looks like Esa_Main
is
called before Esa_Init.
Mostly though, I think you have bad indirection.
esa.h
**********************************
#define SIM_EXPORT DLLIMPORT
typedef struct EsaT_State EsaT_State;
typedef EsaT_State * EsaT_State_Handle;
typedef enum EsaT_Compcode
{
EsaC_Comp_Failure,
EsaC_Comp_Success
} EsaT_Compcode;
I don't like that... same name for a type and a typedef? Get rid of
the
enum identifier, like "typedef enum { ... } EsaT_Compcode". Don't
like
the
same thing done with EsaT_State above either.
DLLIMPORT EsaT_Compcode Esa_Init (int argc, char *argv[], int
options,
EsaT_State_Handle * esa_state_pptr);
Calling convention isn't specified... you should do that.

CPP Wrapper in DLL:
****************************
#include "stdafx.h" //Includes esa.h above
//Globals
EsaT_State_Handle esaHandle;
extern "C" __declspec(dllexport) int __stdcall main(int argc, char*
argv[])
Why is main exported from a dll? That's probably not what you
intended.
{
Esa_Init(argc, argv, ESAC_OPTS_NONE, &esaHandle);
return 0;
}
*****************************- Hide quoted text -
- Show quoted text -- Hide quoted text -

- Show quoted text -
 
B

Ben Voigt [C++ MVP]

kirkpabk said:
Ben,

I'm sure there is some snickering in the background because of my
ignorance <grin>. Yes, in the DLL I do have the call to Esa_Init in
the main(). I converted from an EXE to DLL--so because of my
ignorance (again), I didn't know enough to make it a DllMain (what
does that do for me?). I am calling the DLL main function from a C#
application. When I execute the function within it, I get the
"Unhandled exeception at 0x7cblah (msvcr71.dll) in giss_clr.exe:
0xC00blah: Unwind exception code." <Another problem--not necessarily
related to the original problem>. Meanwhile, I'll look into the
dwReason elements you made reference to.

Again thanks for lending your expertise!

Also, I'm trying to figure out where the "Managed C++" comes in... You
wouldn't be using DLL exports to call a C++/CLI (or even Managed Extensions
for C++ which is now dead) function from C#. C++/CLI makes .NET types that
you use the same as any Microsoft class, by adding a reference to the
assembly.
 
J

John

I'm sure there is some snickering in the background because of my

None whatsoever. I'm just trying to figure out what's going on.
ignorance <grin>. Yes, in the DLL I do have the call to Esa_Init in
the main(). I converted from an EXE to DLL--so because of my
ignorance (again), I didn't know enough to make it a DllMain (what
does that do for me?). I am calling the DLL main function from a C#

The name "main" is C is reserved for the application entry point. I
wouldn't export it from a DLL, and I wouldn't use it for a function invoked
from user code.
application. When I execute the function within it, I get the
"Unhandled exeception at 0x7cblah (msvcr71.dll) in giss_clr.exe:
0xC00blah: Unwind exception code." <Another problem--not necessarily
related to the original problem>. Meanwhile, I'll look into the
dwReason elements you made reference to.

Can you start by renaming your function to something other than "main"?




Again thanks for lending your expertise!
v/r
Brian
Yes--OPNET ESA. Just didn't know of another better way to "control"
the simulation process. Please forgive me--VC is not my cup of tea.
Is there any way to "wrap" the code to make it thread safe? I assume
You just have to make sure you don't call from multiple threads at once.
That might mean you are calling from the wrong place to begin with, or it
might mean you need a critical section, mutex, or other lock.
by calling convention, you mean __stdcall (don't know why I left it
out)... As far as the type/typedef decl, the convention was used
by OPNET--I obviously don't understand the construct enough. My goal
Don't worry about that.
is to be able to call the C API from C++. My ignorance of both
languages is preventing me from getting the call right--or maybe I am
and just as you said, threading issues are causing the protected
memory issue. So, any other suggestions?
What is the actual name of the function calling Esa_Init, I assume it
isn't
main when you are using a DLL? And where does that get called from? If
you
are using DllMain, for example, make sure you are checking the dwReason
argument and understand what each of the four reasons means
(PROCESS_ATTACH,
THREAD_ATTACH, PROCESS_DETACH, THREAD_DETACH).
I appreciate your help--this has been both a painful and rewarding
experience!

Forgive me if my post seems a little amateurish...
I'm requesting assistance from some of you smart folks out there to
get the managed calls write that meet the specification in the esa.h
for Esa_Init. When I make a call, VS2005 reports
"AccessViolationException" and refers to the 4th parameter
(EsaT_State_Handle). I don't know how to define nor pass this
reference correctly to the legacy DLL. What am I doing wrong?
Thanks
in advance!!
Is this the Esa_Init for OPNET which I see with a google? Other
people
observe that OPNET ESA isn't threadsafe. Also, it looks like Esa_Main
is
called before Esa_Init.
Mostly though, I think you have bad indirection.
esa.h
**********************************
#define SIM_EXPORT DLLIMPORT
typedef struct EsaT_State EsaT_State;
typedef EsaT_State * EsaT_State_Handle;
typedef enum EsaT_Compcode
{
EsaC_Comp_Failure,
EsaC_Comp_Success
} EsaT_Compcode;
I don't like that... same name for a type and a typedef? Get rid of
the
enum identifier, like "typedef enum { ... } EsaT_Compcode". Don't
like
the
same thing done with EsaT_State above either.
DLLIMPORT EsaT_Compcode Esa_Init (int argc, char *argv[], int
options,
EsaT_State_Handle * esa_state_pptr);
Calling convention isn't specified... you should do that.
************************************
CPP Wrapper in DLL:
****************************
#include "stdafx.h" //Includes esa.h above
//Globals
EsaT_State_Handle esaHandle;
extern "C" __declspec(dllexport) int __stdcall main(int argc, char*
argv[])
Why is main exported from a dll? That's probably not what you
intended.
{
Esa_Init(argc, argv, ESAC_OPTS_NONE, &esaHandle);
return 0;
}
*****************************- Hide quoted text -
- Show quoted text -- Hide quoted text -
- Show quoted text -- Hide quoted text -

- Show quoted text -

Could this be the typical DLL initialization issue when using
conventional DLL with CLR?
Do you have static variables in the DLL? If you do, you will have to
make the DLL gets initialized before being called from CLR envronment
(like C#).

John
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top