microsoft / cswin32 Goto Github PK
View Code? Open in Web Editor NEWA source generator to add a user-defined set of Win32 P/Invoke methods and supporting types to a C# project.
License: MIT License
A source generator to add a user-defined set of Win32 P/Invoke methods and supporting types to a C# project.
License: MIT License
To better support generation into existing projects or projects with references to assemblies that define interop types, we should link against those existing types instead of generating colliding types.
All the other fields have docs. Name is a fixed array field that requires special generation, so maybe that has something to do with it.
I'm trying to call the method LoadIcon
. The first parameter is documented to be null if you want a system resource.
However if I call it with the following:
var handle = PInvoke.LoadIcon(FreeLibrarySafeHandle.Null, resource_name);
I'll get an access violation exception, because it sets the pointer to -1
which becomes 0xffffffff
.
The workaround is to set it to the correct pointer value for a null:
var handle = PInvoke.LoadIcon(new FreeLibrarySafeHandle(IntPtr.Zero), resource_name);
It seems to me the Null
should be using 0 instead of -1.
*
next to the type rather than the identifierNull
property looks terribleIn consideration of projects that are maintained by developers that prefer to avoid pointers and the unsafe
keyword in C#, we might offer a setting to generate code that does not use pointers. In particular, code generation would:
extern
methods with pointer-free friendly overloads, we would generate just the extern
methods with the friendly signatures.Note that mixing the two varieties where marshalled structs and pointer-based extern
methods coexist is a non-goal as it would be very complex to maintain and perhaps generate confusing code as well.
APIs should produce/return specific SafeHandle-derived types, but accept the base class SafeHandle for better interop with other systems pre-existing SafeHandle types.
i.e. project the parameter as SafeHandle and the return type as SafeFileHandle
It should call Marshal.GetLastWin32Error()
instead.
This is an accuracy thing, because the only way to reliably get the last error from a recent p/invoke is to ask .NET for it, since .NET "backed up the value" you want before potentially overwriting it with another call within the runtime.
The BS_BOTTOM
constant for example is documented here.
Can we scrape that?
Blocking issues:
Soft-blocking issues: (I could workaround them, by suppressing behaviors or warnings that highlight some issues/regressions in the metadata, but then we won't discover such issues in the future)
Start from WinUI 3 Preview 3 template project
Add package reference to Microsoft.Windows.CsWin32
, with GetActiveWindow
in NativeMethods.txt
Make a call to the method anywhere.
The IDE can correctly analysis all the occurrence and references of the method. However, when building the project, the command line output shows error CS0234: No member 'Windows' in namespace 'Microsoft'.
I think this is dotnet/roslyn#46420 , in another way.
MonitorFromWindow
generates the following import:
internal static extern nint MonitorFromWindow(HWND hwnd, uint dwFlags);
However according to the doc, the return type should be HWND
. https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfromwindow
We ideally should have tests that exercise our source generator by way of the C# compiler so we're testing everything. Short of that, we should directly test the Generator
class and verify that the C# compiler produces no diagnostics given the emitted .cs files.
Win32 BOOL
should be represented as [MarshalAs(UnmanagedType.Bool)] bool
. C bool
should be represented as [MarshalAs(UnmanagedType.U1)] bool
.
The Boolean data type has multiple representations in unmanaged code. When the MarshalAsAttribute is not specified, the default marshaling behavior for the Boolean data type is System.Runtime.InteropServices.UnmanagedType. This is a 32-bit integer, which is not appropriate in all circumstances. The Boolean representation that is required by the unmanaged method should be determined and matched to the appropriate System.Runtime.InteropServices.UnmanagedType. UnmanagedType.Bool is the Win32 BOOL type, which is always 4 bytes. UnmanagedType.U1 should be used for C++ bool or other 1-byte types.
Source: https://docs.microsoft.com/en-us/visualstudio/code-quality/ca1414?view=vs-2019
This is required in order to keep taking metadata updates because otherwise there are type name collisions.
This depends on the metadata repo pushing to a public feed.
These structs date back to a time where C compilers didn't natively support 64-bit integers. I think these should be represented as System.Int64 and System.UInt64 in the metadata.
I tried having a go at this myself by adding:
LARGE_INTEGER=Int64
_LARGE_INTEGER=Int64
ULARGE_INTEGER=UInt64
_ULARGE_INTEGER=UInt64
To generation/baseRemap.rsp
, but to my surprise it just resulted in Microsoft.Windows.Sdk.Int64 and Microsoft.Windows.Sdk.UInt64 types being generated.
Do you think this is a reasonable change?
It would be helpful to add comments to the NativeMethods.txt file to make the list of lines more expressive.
For example, something like:
// GetLastInputInfo is used to determine if the user is idle in module xxx
GetLastInputInfo
// todo: remove SetWindowsHookEx and use class yyy instead
SetWindowsHookEx
Having read the code
CsWin32/src/Microsoft.Windows.CsWin32/SourceGenerator.cs
Lines 116 to 123 in d2f2d7f
this seems easy... I'll post a PR shortly.
If any add package scenario that works with this package will display it.
This is true for the return type and the last parameter.
I created a project and was able to successfully use the methods generated by CsWin32. However, when I try specifying a different metadata file, it no longer works -- Visual Studio gives an error on the using Microsoft.Windows.Sdk;
statement that says The type or namespace name 'Sdk' does not exist in the namespace 'Microsoft.Windows' (are you missing an assembly reference?)
.
To verify that it was not an issue with the metadata file, I extracted the metadata file from the NuGet package and pointed it to that, but I still get the same error about the namespace.
Source generators work in projects that target less than the C# 9 language version, so the generator should take the LangVersion
into account and generate valid code given that target version.
Examples of C# features the generated code may rely on that require newer language versions include:
Windows.Win32.PCWSTR.g.cs(52,15,52,19): error CS8370: Feature 'null pointer constant pattern' is not available in C# 7.3. Please use language version 8.0 or greater.
Windows.Win32.D2D_MATRIX_4X4_F.g.cs(66,22,66,30): error CS8370: Feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater.
Note that C# 9 does work on projects that target .NET Framework as well as .NET Core 3.1. But there are features of C# 8+ that do not work on .NET Framework, such as default interface methods from C# 8 or certain calling conventions of function pointers. By avoiding use of such features, many projects have successfully targeted .NET Framework while compiling with the C# 9 language version.
Repro steps:
.NET Framework 4.8
, then click the Create buttonMicrosoft.Windows.CsWin32
nuget prerelease packageNativeMethods.txt
file, with single line containing CreateFileW
Will it be added to .NetFrameork Libraries or just a third-party Nuget Package?
It's too inconvient to pack these libraries as I want to construct a one-file application.
If desired, the generated source can be made more readable:
By generating this instead:
#pragma warning disable CS1591, CS1573, CS0649, CS8019, CS1570
This is a tracking bug for: dotnet/roslyn#50559 dotnet/roslyn#48363
We currently workaround it by suppressing these two warnings. We should remove that suppression when the C# compiler bug is fixed.
When developing a WPF project, dotnet/wpf#810 and dotnet/wpf#3963 will cause the WPF "inner build" to omit important elements to source generators, leading to build breaks anywhere you use the generated code as reported by a *_wpftmp.csproj
project file.
For example you may see a compiler error as if the generated types do not exist:
D:\temp\WpfApp1\WpfApp1\MainWindow.xaml.cs(15,25): error CS0234: The type or namespace name 'Sdk' does not exist in the namespace 'Microsoft.Windows' (are you missing an assembly reference?) [D:\temp\WpfApp1\WpfApp1\WpfApp1_2gbqycls_wpftmp.csproj]
To fix this you must use the .NET 5.0.102 SDK and set the following property in your project:
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
To make sure you are using the SDK with the fix, you may need to add a global.json
file with this content to the root of your repo:
{
"sdk": {
"version": "5.0.102",
"allowPrerelease": false,
"rollForward": "major"
}
}
Here is a fully working minimal sample: wpfsample.zip
This API takes in a LPWStr
, scribbles a NUL on it, and returns the numeric component.
Shlwapi!PathParseIconLocationW per headers is defined as such:
int PathParseIconLocationW(
LPWSTR pszIconFile
);
Win32 metadata describes it as such:
[DllImport("SHLWAPI", ExactSpelling = true)]
public unsafe static extern int PathParseIconLocationW(
[In] [Out] [NativeTypeInfo(UnmanagedType.LPWStr)] ushort* pszIconFile);
CsWin32 generated:
[DllImport("ShlwApi", ExactSpelling = true, EntryPoint = "PathParseIconLocationW")]
internal static extern unsafe int PathParseIconLocation([In, Out] char *pszIconFile);
// ...
internal static unsafe int PathParseIconLocation(string pszIconFile)
{
fixed (char *pszIconFileLocal = pszIconFile)
{
return PInvoke.PathParseIconLocation(pszIconFileLocal);
}
}
A simpler, safer approach:
[DllImport("shlwapi.dll", ExactSpelling = true, PreserveSig = true)]
internal static extern int PathParseIconLocationW(
[MarshalAs(UnmanagedType.LPWStr)]
StringBuilder pszIconFile);
We might be able to get away with using StringBuilder in all cases where metadata describes a parameter as [In, Out] char *
. But I haven't had my coffee yet to say so definitively.
This is also a great example of how these wrappers dangerously hide the side effects of unsafe
, in this case mutating what was expected to be an immutable string.
This package contains source generator to add a user-defined set of Win32 p/invoke
methods and supporting types to a C# project.
To get started, create a "NativeMethods.txt" file in your project directory
that lists the names of methods you need p/invoke methods generated for, one per line.
Learn more from our README on GitHub: https://github.com/microsoft/CsWin32#readme
This package contains a source generator to add a user-defined set of Win32 P/Invoke
methods and supporting types to a C# project.
To get started, create a "NativeMethods.txt" file in your project directory
that lists the names of methods for which you need P/Invoke methods generated, one per line.
Learn more from our README on GitHub.
The docs for IMAGE_NT_HEADERS32
are missing in generated code. Possibly because the docs refer to it as IMAGE_NT_HEADERS32
but the metadata as IMAGE_NT_HEADERS
.
First of all I'm terrible at C++ and interop, so excuse me for any dumb remarks in this issue. I've used pinvoke.net before to use CopyFileEx
in our code base. I tried replacing the dllimport with CsWin32. I ran into several issues.
The callback argument lpProgressRoutine
is of type LPPROGRESS_ROUTINE
. When opening the reference of CopyFileEx
in Visual Studio after generating it by adding it to NativeMethods.txt the type LPPROGRESS_ROUTINE
remains white and cannot be opened using Go To Definition. In addition to this Visual Studio was unable to generate a method for the callback routine using quick actions. The only option was to create a field/property/variable as type object. Finally I found out that Go To Definition does work when you write out the type in a C# file in source file.
The generated type LARGE_INTEGER
cannot be casted to int
making it impossible to make heads or tails from the progress values in the LPPROGRESS_ROUTINE
calls.
The types LPVOID
and LPBOOL
are generated as void*
and bool*
correspondingly. Since these values aren't needed passing in null for both should work (should it?), but I was unable to use the argument types I was used to: IntPtr.Zero
and a bool
by ref
.
The last problem is that enum values in the return type of LPPROGRESS_ROUTINE
seem to be missing, for which I created the issue microsoft/win32metadata#170 in the win32metadata repository.
After messing around a bit it seems the CsWin32 source generator has stopped working entirely and even after a VS restart and NuGet uninstall/install it did not seem to want to generate any more code. I'm not sure what's going on and where to look for log files.
We can set AllowUnsafeBlocks=true
and add the AdditionalFiles
items (when those files exist).
Update readme to advise users as to this new default value for AllowUnsafeBlocks
.
The YamlDotNet.dll file is unsigned.
CsWin32/azure-pipelines/dotnet.yml
Lines 48 to 50 in 836882b
In the README.md it is mentioned that the structs and constants should be generated when the name is written in the NativeMethods.txt
I added the following structs, but they are not generated:
CWPRETSTRUCT
CWPSTRUCT
I checked in the winmd file, it is there.
Constants are also not generated, see: https://github.com/microsoft/win32metadata/issues/150
The can be found in the winmd, too.
For example:
namespace Windows.Win32.WindowsProgramming
{
public enum PROCESS_CREATION_FLAGS : uint
Wrapper structs are convenient. However, they are not always efficient nor compatible with the underlying ABI.
The best known case of this is with C++ instance members, where (on Windows) class C { MyStruct M(...); }
is actually MyStruct* M(C* pThis, MyStruct* retVal, ...)
and where this holds true for any struct, not just complex structs.
This means that if the native signature is, for example, class C { LRESULT M(...); };
where typedef intptr_t LRESULT;
then the C# side function pointer must be delegate* unmanaged[Thiscall]<T*, ..., nint>
and not delegate* unmanaged[Thiscall]<T*, ..., LRESULT>
This, in particular, crops up frequently when working with COM bindings, such as for DirectX and while I am not presently aware of any other OS/Architectures where this shows, it is not guaranteed that it never will.
This is likewise why the new CallConvMemberFunction modifier is being introduced, so function pointers for COM and other scenarios can be correctly annotated as delegate* unmanaged[Stdcall, MemberFunction]
and the JIT can automatically handle the required differences from the non-member function calling convention.
These differences can lead to subtle pit of failures, particularly when working with delegates and function pointers, and as such CsWin32
should ensure that its own generated signatures and exposed types are compatible (both with known cases today and potential future cases as more platforms are supported and more ABIs to support exist; such as the support for ARM32/ARM64 or future COM support).
When a project's properties indicate the WinSDK version it is targeting, we should generate only those APIs that are available on that Windows version.
When the app specifies a band of versions (a preferred target + a minimum required), we can provide the higher version's APIs, but may want to annotate them somehow to help the developer realize when "light up" version checks are required.
VS is throwing an error during compilation because of a backend file that I cannot see. Here are the details that I can see
CS0122: 'BOOL' is inaccessible due to its protection level
File: PInvoke.User32.cs, Line 36
The method endpoint is generated properly, Pinvoke.LockWorkStation() is available and no errors appear in my app code. "LockWorkStation" is the only method in my NativeMethods.txt
I was trying to update a non-trivial project (x_input gamepad library) to use this generator. The methods were generating properly, but I found that many of my existing data structs had custom members. It would be nice to have a way to extend the capabilities of auto-generated structs. I think adding the partial modifier could be an easy fix, but I'm not sure if that would cause any issues for marshaled objects
The source generator currently generates structs instead of interfaces. This makes things a little difficult when an API relies on being able to use inheritance and multiple interfaces, for example DWriteCreateFactory:
NativeMethods.txt
DWRITE.*
IUnknown
IDWriteFactory
IDWriteFactory1
IDWriteFactory2
IDWriteFactory3
IDWriteFactory4
IDWriteFactory5
IDWriteFactory6
IDWriteFactory7
Program.cs
using Microsoft.Windows.Sdk;
namespace InterFaceGenerationRepro
{
internal class Program
{
private static void Main(string[] args)
{
IDWriteFactory factory;
HRESULT result = PInvoke.DWriteCreateFactory(DWRITE_FACTORY_TYPE.DWRITE_FACTORY_TYPE_SHARED, typeof(IDWriteFactory).GUID, out factory);
}
}
}
The IDWriteFactory interface is based off of IUnknown, and has seven other interfaces that can be swapped in to provide additional methods that can be called, by changing the GUID in the DWriteCreateFactory iid parameter to communicate which interface to use.
Expected:
The source generator should generate interfaces with full inheritance hierarchy for interfaces.
Actual:
The source generator currently generates structs in place of interfaces, causing DWriteCreateFactory's factory out parameter to not be able to cast to any of the IDWriteFactory interfaces to the defined IUnknown type.
This is a security recommendation that is flagged by the built-in .NET analyzers
In the IDE the perf is ok, but build times noticeably suffer do the the startup cost of the source generator. What can be done to speed this up?
Ideas to evaluate:
// dxgi.h
HRESULT WINAPI CreateDXGIFactory1(REFIID riid, _COM_Outptr_ void **ppFactory);
// d3d12.h
HRESULT WINAPI D3D12GetDebugInterface( _In_ REFIID riid, _COM_Outptr_opt_ void** ppvDebug );
Generated C#
internal static unsafe HRESULT CreateDXGIFactory1(in System.Guid riid, out void *ppFactory)
internal static unsafe HRESULT D3D12GetDebugInterface(in System.Guid riid, void **ppvDebug)
Notice that both ppFactory
and ppvDebug
have _COM_Outptr_
or _COM_Outptr_opt_
, but in the generated file, only ppFactory
is out
, ppvDebug
should be too.
Therefore this always fails :
ID3D12Debug* debugController = null;
PInvoke.D3D12GetDebugInterface(typeof(ID3D12Debug).GUID, (void**) debugController);
debugController->EnableDebugLayer();
because debugController
stays null
EDIT : Should have done this instead
ID3D12Debug* debugController = null;
PInvoke.D3D12GetDebugInterface(typeof(ID3D12Debug).GUID, (void**) &debugController);
debugController->EnableDebugLayer();
Resolving this may resolve microsoft/win32metadata#21 as fixed.
Hi,
outside of this project the parameter type for handles is IntPtr, e.g.:
GetWindow(IntPtr hWnd, ...)
SetFocus(IntPtr hWnd)
That fits to the return type of functions which deliver these arguments like:
IntPtr Process.MainWindowHandle { get; }
IntPtr System.Windows.Interop.WindowInteropHelper.Handle { get; }
But win32metadata generates functions with Microsoft.Windows.Sdk.HWND as parameter type. E.g.:
PInvoke.GetWindow( HWND hwnd, ... );
PInvoke.SetFocus( HWND hwnd );
With the consequence, that we first have to convert IntPtr returns to HWND, before we can use win32metadata stuff.
What is the intension behind that type change ?
Whatever disadvantage is removed at HWND site isn't completely gone, its presumably just moved to that convert place.
thank you in advance for any candle light
Is there a technical reason why a library targeting net35;net48;netcoreapp5.0-windows
is adding generated source to only the net48
and netcoreapp5.0-windows
compilations? I didn't spot anything in the generated source that would not work on net35
.
I hoped to replace https://github.com/Techsola/InstantReplay/tree/main/src/Techsola.InstantReplay/Native.
.NET Framework 3.5 is in active support by Microsoft. Some of the projects at my day job integrate with a platform that still requires net35
, unfortunately, in a place where we would really benefit from using https://github.com/Techsola/InstantReplay.
I love the generated source! This is an awesome concept!
For better interop with .NET BCL APIs, we should produce and accept instances of SafeFileHandle
instead of our own CloseHandleSafeHandle
generated type.
This was astutely brought up here: dotnet/pinvoke#429
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.