22
33When, for example, marshaling is enabled and ` useSafeHandles ` is ` true ` , the code can be different than that in C++. Here we show a few code examples of using CsWin32.
44
5- ``` json
6- {
7- "$schema" : " https://aka.ms/CsWin32.schema.json" ,
8- "useSafeHandles" : false ,
9- "comInterop" : {
10- "preserveSigMethods" : [" *" ]
11- }
12- }
13- ```
14-
15- ## Scenario 1: Retrieving the last-write time
5+ ## Retrieving the last-write time
166
177Based on: [ Retrieving the Last-Write Time] ( https://learn.microsoft.com/en-us/windows/win32/sysinfo/retrieving-the-last-write-time )
188
@@ -29,7 +19,6 @@ MAX_PATH
2919``` cs
3020static void Main (string [] args )
3121{
32- SafeHandle hFile ;
3322 Span < char > szBuf = stackalloc char [(int )PInvoke .MAX_PATH ];
3423
3524 if (args .Length is not 1 )
@@ -38,7 +27,7 @@ static void Main(string[] args)
3827 return ;
3928 }
4029
41- hFile = PInvoke .CreateFile (
30+ using SafeHandle hFile = PInvoke .CreateFile (
4231 args [0 ],
4332 (uint )GENERIC_ACCESS_RIGHTS .GENERIC_READ ,
4433 FILE_SHARE_MODE .FILE_SHARE_READ ,
@@ -52,8 +41,6 @@ static void Main(string[] args)
5241
5342 if (GetLastWriteTime (hFile , szBuf ))
5443 Console .WriteLine (" Last write time is: {0}\n " , szBuf .ToString ());
55-
56- hFile .Close ();
5744}
5845
5946static unsafe bool GetLastWriteTime (SafeHandle hFile , Span < char > lpszString )
@@ -77,7 +64,7 @@ static unsafe bool GetLastWriteTime(SafeHandle hFile, Span<char> lpszString)
7764}
7865```
7966
80- ## Scenario 2: Retrieving information about all of the display monitors
67+ ## Retrieving information about all of the display monitors
8168
8269```
8370EnumDisplayMonitors
@@ -130,7 +117,7 @@ static unsafe BOOL MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, RECT* lprc
130117 }
131118```
132119
133- ## Scenario 3: Use of SHCreateItemFromParsingName
120+ ## Use of SHCreateItemFromParsingName
134121
135122```
136123IShellItem
@@ -185,3 +172,152 @@ static unsafe void Main(string[] args)
185172 psi ->Release ();
186173}
187174```
175+
176+ ## Using COM classes (NetFwMgr, INetFwMgr)
177+
178+ In this example we see how to activate a COM class and call methods on it via its primary interface. When marshalling
179+ is enabled, we can use C# cast to do the "QueryInterface" that you would have seen in a native C++ sample.
180+
181+ ```
182+ NetFwMgr
183+ INetFwMgr
184+ IEnumVARIANT
185+ INetFwAuthorizedApplication
186+ ```
187+
188+ ### Marshalling enabled (built-in COM Interop, not AOT-compatible)
189+
190+ ``` cs
191+ var fwMgr = (INetFwMgr )new NetFwMgr ();
192+ var authorizedApplications = fwMgr .LocalPolicy .CurrentProfile .AuthorizedApplications ;
193+ var aaObjects = new object [authorizedApplications .Count ];
194+ var applicationsEnum = (IEnumVARIANT )authorizedApplications ._NewEnum ;
195+ applicationsEnum .Next ((uint )authorizedApplications .Count , aaObjects , out uint fetched );
196+ foreach (var aaObject in aaObjects )
197+ {
198+ var app = (INetFwAuthorizedApplication )aaObject ;
199+ Console .WriteLine (" ---" );
200+ Console .WriteLine ($" Name: {app .Name .ToString ()}" );
201+ Console .WriteLine ($" Enabled: {(bool )app .Enabled }" );
202+ Console .WriteLine ($" Remote Addresses: {app .RemoteAddresses .ToString ()}" );
203+ Console .WriteLine ($" Scope: {app .Scope }" );
204+ Console .WriteLine ($" Process Image Filename: {app .ProcessImageFileName .ToString ()}" );
205+ Console .WriteLine ($" IP Version: {app .IpVersion }" );
206+ }
207+ ```
208+
209+ ### Marshalling enabled (COM wrappers, AOT compatible)
210+
211+ Note that in COM wrappers mode, the generated interfaces have get_ methods instead of properties. Some parameters
212+ are also ComVariant instead of object because source generated COM does not support as much automatic marshalling
213+ as built-in COM does.
214+
215+ ``` cs
216+ var fwMgr = NetFwMgr .CreateInstance <INetFwMgr >();
217+ var authorizedApplications = fwMgr .get_LocalPolicy ().get_CurrentProfile ().get_AuthorizedApplications ();
218+ var aaObjects = new ComVariant [authorizedApplications .get_Count ()];
219+ var applicationsEnum = (IEnumVARIANT )authorizedApplications .get__NewEnum ();
220+ applicationsEnum .Next ((uint )authorizedApplications .get_Count (), aaObjects , out uint fetched );
221+ foreach (var aaObject in aaObjects )
222+ {
223+ var app = (INetFwAuthorizedApplication )ComVariantMarshaller .ConvertToManaged (aaObject )! ;
224+
225+ Console .WriteLine (" ---" );
226+ Console .WriteLine ($" Name: {app .get_Name ().ToString ()}" );
227+ Console .WriteLine ($" Enabled: {(bool )app .get_Enabled ()}" );
228+ Console .WriteLine ($" Remote Addresses: {app .get_RemoteAddresses ().ToString ()}" );
229+ Console .WriteLine ($" Scope: {app .get_Scope ()}" );
230+ Console .WriteLine ($" Process Image Filename: {app .get_ProcessImageFileName ().ToString ()}" );
231+ Console .WriteLine ($" IP Version: {app .get_IpVersion ()}" );
232+
233+ aaObject .Dispose ();
234+ }
235+ ```
236+
237+ ## Use PNP APIs (shows omitted optional params and cbSize-d struct)
238+
239+ This sample shows how to call an API where we've omitted some optional params -- note that we must
240+ use named parameters when passing parameters past the omitted optional ones. This also shows how to
241+ use Span APIs, and in this case one where we first call the API to get the buffer size, create the buffer
242+ and then call again to populate the buffer.
243+
244+ ```
245+ SetupDiGetClassDevs
246+ SetupDiEnumDeviceInfo
247+ SetupDiGetDeviceInstanceId
248+ ```
249+
250+ ``` cs
251+ using SafeHandle hDevInfo = PInvoke .SetupDiGetClassDevs (
252+ Flags : SETUP_DI_GET_CLASS_DEVS_FLAGS .DIGCF_ALLCLASSES | SETUP_DI_GET_CLASS_DEVS_FLAGS .DIGCF_PRESENT );
253+
254+ var devInfo = new SP_DEVINFO_DATA { cbSize = (uint )sizeof (SP_DEVINFO_DATA ) };
255+
256+ uint index = 0 ;
257+ while (PInvoke .SetupDiEnumDeviceInfo (hDevInfo , index ++ , ref devInfo ))
258+ {
259+ PInvoke .SetupDiGetDeviceInstanceId (hDevInfo , in devInfo , RequiredSize : out uint requiredSize );
260+
261+ Span < char > instanceIdSpan = new char [(int )requiredSize ];
262+ PInvoke .SetupDiGetDeviceInstanceId (hDevInfo , in devInfo , instanceIdSpan );
263+
264+ Console .WriteLine ($" Device {devInfo .ClassGuid } Instance ID: {instanceIdSpan .ToString ()}" );
265+ }
266+ ```
267+
268+ ## Pass struct as a Span<byte >
269+
270+ In this short example, we see how to pass a struct to a method that accepts a ` Span<byte> ` . ` new Span<SHFILEINFOW>(ref fileInfo) ` lets us
271+ get a ` Span<SHFILEINFOW> ` and then ` MemoryMarshal.AsBytes ` reinterprets that same Span as a ` Span<byte> ` with the expected size. The cswin32
272+ method will pass the Span's length to the native method as the "cb" count bytes parameter.
273+
274+ ``` cs
275+ SHFILEINFOW fileInfo = default ;
276+ PInvoke .SHGetFileInfo (
277+ " c:\\ windows\\ notepad.exe" ,
278+ FILE_FLAGS_AND_ATTRIBUTES .FILE_ATTRIBUTE_NORMAL ,
279+ MemoryMarshal .AsBytes (new Span <SHFILEINFOW >(ref fileInfo )),
280+ SHGFI_FLAGS .SHGFI_DISPLAYNAME );
281+ ```
282+
283+ ## Omitting optional out/ref parameters
284+
285+ APIs with optional ` in ` parameters are tagged with ` [Optional] ` attribute and such parameters can be omitted, but APIs
286+ with optional ` out ` or ` ref ` parameters must always be passed. When the native method has these as ` [optional] ` and you need
287+ to pass _ some_ but not all of those parameters, you can pass "null" to the native method using ` ref Unsafe.NullRef<T>() ` .
288+
289+ This sample also shows passing ` null ` for SafeHandle-typed parameters which are not optional per the SDK headers but the
290+ implementation allows for them to be null.
291+
292+ This sample shows a number of advanced COM marshalling scenarios
293+
294+ ### Marshalling enabled (COM wrappers, AOT compatible)
295+
296+ ``` cs
297+ // CoCreateInstance CLSID_WbemLocator
298+ IWbemLocator locator = WbemLocator .CreateInstance <IWbemLocator >();
299+
300+ var ns = new SysFreeStringSafeHandle (Marshal .StringToBSTR (@" ROOT\Microsoft\Windows\Defender" ), true );
301+ locator .ConnectServer (ns , new SysFreeStringSafeHandle (), new SysFreeStringSafeHandle (), new SysFreeStringSafeHandle (), 0 , new SafeFileHandle (), null , out IWbemServices services );
302+
303+ unsafe
304+ {
305+ PInvoke .CoSetProxyBlanket (
306+ services ,
307+ 10 , // RPC_C_AUTHN_WINNT is 10
308+ 0 , // RPC_C_AUTHZ_NONE is 0
309+ pServerPrincName : null ,
310+ dwAuthnLevel : RPC_C_AUTHN_LEVEL .RPC_C_AUTHN_LEVEL_CALL ,
311+ dwImpLevel : RPC_C_IMP_LEVEL .RPC_C_IMP_LEVEL_IMPERSONATE ,
312+ pAuthInfo : null ,
313+ dwCapabilities : EOLE_AUTHENTICATION_CAPABILITIES .EOAC_NONE );
314+ }
315+
316+ var className = new SysFreeStringSafeHandle (Marshal .StringToBSTR (" MSFT_MpScan" ), true );
317+ IWbemClassObject ? classObj = null ; // out param
318+
319+ services .GetObject (className , WBEM_GENERIC_FLAG_TYPE .WBEM_FLAG_RETURN_WBEM_COMPLETE , null , ref classObj , ref Unsafe .NullRef <IWbemCallResult >());
320+
321+ classObj .GetMethod (" Start" , 0 , out IWbemClassObject pInParamsSignature , out IWbemClassObject ppOutSignature );
322+
323+ ```
0 commit comments