Most of the functions within the WIN32 API can easily be marshaled because their parameters have a similar managed data type. However, some functions may expect a struct as a parameter. To solve this, you must define a data structure within your source file whose members are equivalent to the data structure the DLL function expects to receive. To demonstrate this process, you will be using the WIN32 API function GetLocalTime, which expects a SYSTEM_TIME data structure as its only parameter. The process to do this is similar to creating the function definition.
When you define a structure that will be passed via P/Invoke, you need to be aware of two things. First of all, your structure or class must be a managed type specified by using the __gc keyword. Second, you must specify how the structure is laid out by using the StructLayout attribute. This attribute can contain one of three different parameters. A structure with a layout defined as LayoutKind::Automatic means that the runtime will choose an appropriate layout in memory for that object. Using this is not recommended because some functions expect the structure to be in a certain form when laid out in memory. A structure can also be described using LayoutKind::Sequential, which means the structure is laid out in memory the same way it is defined. In other words, the members of the structures follow each other sequentially in memory. This layout type is the most commonly used. The last layout type is LayoutKind::Explicit. By using this layout type, you must add additional parameters that describe the sizes of each of the data types. Although this gives you a greater level of control, it is error prone and would be hard to manage across different platforms.
Create a managed structure named SystemTime that is arranged using a StructLayout of LayoutKind::Sequential, as shown on line 16 of Listing 22.2. Because each of the member variables within the SYSTEM_TIME structure are WORDs, define each of the parameters of your structure using the UInt16 managed data type. Next, create the GetLocalTime function definition, which is contained in kernel32.dll, similar to the way you did for the MessageBox function. This can be seen on line 39 of Listing 22.2
Create another public member function within the CSystemTime class named DisplayTime with no parameters and a void return type. Within this function, create an instance of the SystemTime structure, because the GetLocalTime function expects the memory to already be allocated. Call the GetLocalTime function using the structure variable you just created. Finally, using String objects and the String::Format function, output the results you received from the GetLocalTime function.
1: #include "stdafx.h" 2: 3: #using <mscorlib.dll> 4: #include <vcclr.h> 5: #include <stdio.h> 6: #include <tchar.h> 7: 8: // enable memory leak detection 9: #define _CRTDBG_MAP_ALLOC 10: #include <stdlib.h> 11: #include <crtdbg.h> 12: 13: using namespace System; 14: using namespace System::Runtime::InteropServices; 15: 16: [StructLayout(LayoutKind::Sequential)] 17: __gc struct SystemTime 18: { 19: UInt16 wYear; 20: UInt16 wMonth; 21: UInt16 wDayOfWeek; 22: UInt16 wDay; 23: UInt16 wHour; 24: UInt16 wMinute; 25: UInt16 wSecond; 26: UInt16 wMilliseconds; 27: }; 28: 29: [DllImport("user32.dll", ExactSpelling=true, CharSet=CharSet::Ansi)] 30: extern int MessageBoxA(void* hWnd, 31: String* pText, 32: String* pCaption, 33: unsigned int uType); 34: 35: [DllImport("user32.dll", ExactSpelling=true, CharSet=CharSet::Unicode)] 36: extern int MessageBoxW(void* hWnd, String* pText, 37: String* pCaption,unsigned int uType); 38: 39: [DllImport("Kernel32.dll", CharSet=CharSet::Auto)] 40: extern void GetLocalTime(SystemTime* st); 41: 42: [DllImport("kernel32.dll")] 43: extern int MultiByteToWideChar( 44: unsigned int CodePage, 45: unsigned long dwFlags, 46: [MarshalAs(UnmanagedType::LPStr)] String* lpMultiByteStr, 47: int cbMultiByte, 48: [MarshalAs(UnmanagedType::LPWStr)] String* lpWideCharStr, 49: int cchWideChar ); 50: 51: __nogc class CSystemTime 52: { 53: public: 54: CSystemTime() 55: { 56: m_strHello = new String("CSystemTime says..."); 57: }; 58: 59: void SayHello() 60: { 61: Console::WriteLine(m_strHello); 62: MessageBoxA( 0, "Hello from CSystemTime", "CSystemTime", 0 ); 63: } 64: 65: void DisplayTime() 66: { 67: SystemTime* sysTime = new SystemTime; 68: GetLocalTime( sysTime ); 69: 70: String* sDate = new String(""); 71: String* sTime = new String(""); 72: String* sDateTime = new String(""); 73: String* sWideDateTime = new String(""); 74: 75: sDate = sDate->Format( S"{0}/{1}/{2}", 76: sysTime->wMonth.ToString(), 77: sysTime->wDay.ToString(), 78: sysTime->wYear.ToString()); 79: 80: sTime = sTime->Format( S"{0}:{1}", 81: sysTime->wHour.ToString(), 82: sysTime->wMinute.ToString()); 83: 84: sDateTime = sDateTime->Format( 85: "It is now {0} on {1}", sTime, sDate ); 86: 87: MessageBoxA(0, sDateTime, "Today", 0 ); 88: 89: int numChars = MultiByteToWideChar( 0, 0, sDateTime, 90: -1, sWideDateTime, sDateTime->Length*2 ); 91: 92: MessageBoxW(0, sWideDateTime, "Today", 0 ); 93: } 94: 95: private: 96: gcroot<String*> m_strHello; 97: }; 98: 99: // This is the entry point for this application 100: int _tmain(void) 101: { 102: CSystemTime* pSysTime = new CSystemTime; 103: 104: pSysTime->SayHello(); 105: 106: // comment the following line to create a memory leak 107: delete pSysTime; 108: 109: // dump memory leaks 110: _CrtDumpMemoryLeaks(); 111: 112: return 0; 113: }
Top |