I am using the .NET DateTime to get the current date and time. I am converting it to a string to use as part of a file name. The problem is the OpenCV command to save an image requires a char * not a string type, and DateTime will only output a String^ type. How do I make this work? Heres the code not completed
String^ nowString = DateTime::Now.ToString("yyyy-MM-dd-HH:mm");
IplImage* toSave;
CvCapture* capture = cvCreateCameraCapture(0);
toSave = cvQueryFrame( capture );
cvSaveImage(nowString, toSave);
cvReleaseImage(&toSave);
cvReleaseCapture(&capture);
Your best bet is to use StringToHGlobalAnsi
. Here is complete code showing how its done and remembering to free the memory allocated.
using namespace System::Runtime::InteropServices;
void MethodName()
{
String^ nowString = DateTime::Now.ToString("yyyy-MM-dd-HH:mm");
IntPtr ptrToNativeString = Marshal::StringToHGlobalAnsi(nowString);
try
{
CvCapture* capture = cvCreateCameraCapture(0);
IplImage* toSave = cvQueryFrame(capture);
cvSaveImage(static_cast<char*>(ptrToNativeString.ToPointer()), toSave);
cvReleaseImage(&toSave);
cvReleaseCapture(&capture);
}
catch (...)
{
Marshal::FreeHGlobal(ptrToNativeString);
throw;
}
Marshal::FreeHGlobal(ptrToNativeString);
}
You might want to rethink using a ':' character in the filename, as I don't believe windows likes this very much.
Actually, I found the easiest way to get a char *
from a String^
is to use good ol' sprintf()
. So in your case, you can simple do this:
char cNow[17] = { 0 };
String^ nowString = DateTime::Now.ToString("yyyy-MM-dd-HH:mm");
if (nowString->Length < sizeof(cNow)) // make sure it fits & allow space for null terminator
sprintf(cNow, "%s", nowString);
No need to call the Marshal
functions!
Update
So it appears that VS 2015 adheres more closely to the C++11 standards, so using sprintf()
with the .NET String won't work. The easiest way is to use the marshal_as()
function like this:
Include these lines before your code:
#include <msclr/marshal_cppstd.h>
using namespace msclr::interop;
Then this should work:
char cNow[17] = { 0 };
String^ nowString = DateTime::Now.ToString("yyyy-MM-dd-HH:mm");
string sNow = marshal_as<string>(nowString);
if (sNow.length() < sizeof(cNow)) // make sure it fits & allow space for null terminator
sprintf(cNow, "%s", sNow.c_str());
Otherwise, if you don't want to use the marshal_as()
function, you can copy the string character by character like this:
char cNow[17] = { 0 };
String^ nowString = DateTime::Now.ToString("yyyy-MM-dd-HH:mm");
if (nowString->Length < sizeof(cNow)) // make sure it fits & allow space for null terminator
{
for (int i = 0; i < nowString->Length; i++)
cNow[i] = static_cast<char>(nowString[i]);
}
You need to read about C++ interop and data marshalling.
Basically: you need to "cast" the .NET String
to a C++ TCHAR
array.
See this: http://msdn.microsoft.com/en-us/library/ef4c3t39(VS.80).aspx
Use the StringToXxxAnsi
functions in the Marshal
class to allocate a char*
buffer, then the appropriate functions from the same class to free them.
Random Googling got me this. Maybe someone can shorten it?
cli::array<char>^ bytes = Encoding::ASCII::GetBytes(nowString);
pin_ptr<char> pinned = &bytes[0];
std::string nativeString((char*)pinned, bytes->Length);
char const* chars = nativeString.c_str();
Edit: This is longer than the operations of the Marshal class, but works with more encodings. In your case, it sounds like the simpler StringToHGlobalAnsi
approach will do everything you need.