/// <summary> /// Converts function parameters to C# and appends to sb like "(...);\r\n". /// _i must be at '('. Finally will be after ')'. /// </summary> void _ConvertParameters(StringBuilder sb, string parentName, _TypeContext context) { if (!_TokIsChar(_i, '(')) { _Err(_i, "unexpected"); } _i++; sb.Append('('); if (!_TokIsChar(_i, ')')) { for (int iParam = 0; ; iParam++, _i++) { //ignore ... if (_TokIsChar(_i, '.') && _TokIsChar(_i + 1, '.') && _TokIsChar(_i + 2, '.') && _TokIsChar(_i + 3, ')')) { _i += 3; sb.Remove(sb.Length - 2, 2); //", " break; } _PARAMDATA t; _ParseParamOrMember(context, out t, parentName, iParam + 1); //skip default value of optional parameter if (_TokIsChar(_i, '=')) { while (!_TokIsChar(++_i, ',', ')')) { } } //is ',' or ')'? bool isLast = _TokIsChar(_i, ')'); if (!isLast && !_TokIsChar(_i, ',')) { _Err(_i, "unexpected"); } if (t.attributes != null) { sb.Append(t.attributes); sb.Append(' '); } sb.Append(t.typeName); sb.Append(' '); sb.Append(t.name); if (!isLast) { sb.Append(", "); } if (isLast) { break; } } } _i++; //skip ')' sb.Append(");\r\n"); }
/// <summary> /// Converts type name/pointer to C# type name. /// The return value is type name, possibly with * or []. If parameter, also can have ref/out/in and [In]/[Out]. /// </summary> /// <param name="t">The type.</param> /// <param name="ptr">Pointer level. The function may adjust it depending on typedef pointer level etc.</param> /// <param name="isConst">Is const.</param> /// <param name="iTokTypename">Type name token index. Can be 0 if don't need to unalias etc; then just adds pointer/ref if need.</param> /// <param name="context">Depending on it char* will be converted to "string" etc.</param> /// <param name="attributes">Receives null or MarshalAs attribute.</param> /// <param name="iSAL">SAL token index, or 0 if no SAL.</param> string _ConvertTypeName(_Symbol t, ref int ptr, bool isConst, int iTokTypename, _TypeContext context, out string attributes, int iSAL = 0) { attributes = null; string name, marshalAs = null; bool isLangType = false, isParameter = false, isCOM = false, isBlittable = false, isRawPtr = false; switch (context) { case _TypeContext.Parameter: case _TypeContext.DelegateParameter: isParameter = true; break; case _TypeContext.ComParameter: isParameter = true; isCOM = true; break; case _TypeContext.ComReturn: isCOM = true; break; } bool _In_ = false, _Out_ = false, _Inout_ = false; if (iSAL > 0) { //AOutput.Write(_tok[iSAL]); if (_TokStarts(iSAL, "_In_")) { _In_ = true; } else if (_TokStarts(iSAL, "_Out")) { _Out_ = true; } else if (_TokStarts(iSAL, "_Inout_")) { _Inout_ = true; } else { _Err(iSAL, "unknown SAL"); } } if (iTokTypename != 0) { ptr += _Unalias(iTokTypename, ref t, ref isConst); name = t.csTypename; var c = t as _CppType; if (c != null) { isLangType = true; isBlittable = true; switch (name) { case "char": isBlittable = false; if (ptr == 1) //not 'if(ptr != 0)', because cannot be 'ref string' { ptr = 0; bool isBSTR = _TokIs(iTokTypename, "BSTR"); switch (context) { case _TypeContext.Member: if (isConst || isBSTR) { name = "string"; if (isBSTR) { marshalAs = "BStr"; } } else { //string dangerous, because if the callee changes member pointer, .NET tries to free the new string with CoTaskMemFree. ptr = 1; //AOutput.Write(_DebugGetLine(iTokTypename)); isRawPtr = true; } break; case _TypeContext.Return: case _TypeContext.ComReturn: ptr = 1; //if string, .NET tries to free the return value isRawPtr = true; break; case _TypeContext.DelegateParameter: ptr = 1; //because some are "LPSTR or WORD". Rarely used. isRawPtr = true; break; case _TypeContext.Parameter: case _TypeContext.ComParameter: Debug.Assert(!(isConst && (_Out_ || _Inout_))); if (isConst || _In_ || isBSTR) { name = "string"; if (isCOM) { //if(!isBSTR) AOutput.Write(_tok[iTokTypename]); if (!isBSTR) { marshalAs = "LPWStr"; } } else { if (isBSTR) { marshalAs = "BStr"; } } } else { ptr = 1; isRawPtr = true; //Could be StringBuilder, but the Au library does not use it, it is slow. //Or [Out] char[]. The Au library uses it with Util.AMemoryArray.Char_. } break; } } else if (ptr > 1) { isRawPtr = true; isConst = false; } break; case "int": if (_TokIs(iTokTypename, "BOOL")) { if (ptr == 0 || (ptr == 1 && isParameter)) { name = "bool"; if (isCOM) { marshalAs = "Bool"; } isBlittable = false; } } break; case "bool": marshalAs = "U1"; isBlittable = false; break; case "void": case "byte": case "sbyte": if (ptr > 0) { isRawPtr = true; } break; case "double": if (_TokIs(iTokTypename, "DATE")) { if (ptr == 0 || (ptr == 1 && isParameter)) { name = "DateTime"; isBlittable = false; } } break; } if (isRawPtr) { isBlittable = true; } } } else { name = t.csTypename; } if (!isLangType) { var ts = t as _Struct; if (ts != null) { if (ts.isInterface) { //OutList(ptr, ts.csTypename); if (ptr == 0) { _Err(iTokTypename, "unexpected"); } ptr--; switch (name) { case "IUnknown": marshalAs = name; name = "Object"; break; //case "IDispatch": //not sure should this be used, because we don't convert SDK interfaces to C# IDispatch interfaces // marshalAs = name; // name = "Object"; // break; } } else if (!ts.isClass) { switch (name) { case "AWnd": case "LPARAM": isBlittable = true; break; case "GUID": name = "Guid"; break; case "DECIMAL": name = "decimal"; break; case "VARIANT": if (context == _TypeContext.ComParameter) { name = "object"; } break; //case "SAFEARRAY": //in SDK used only with SafeArrayX functions, with several other not-important functions and as [PROP]VARIANT members //AOutput.Write(ptr); //break; case "ITEMIDLIST": if (ptr > 0) { ptr--; name = "IntPtr"; isBlittable = true; } //else _Err(iTokTypename, "example"); //0 in SDK break; case "POINTL": name = "POINT"; break; case "RECTL": name = "RECT"; break; } } } else if (t is _Enum) { isBlittable = true; } } if (ptr > 0) { __ctn.Clear(); bool isArray = false; if (!isRawPtr && isParameter) { string sal = null; if (iSAL > 0) { sal = _TokToString(iSAL); } if (iSAL > 0 && ptr == 1) //if ptr>1, can be TYPE[]* or TYPE*[] { if (_In_ && (sal.Contains("_reads_") || sal.Contains("count"))) { //OutList(_tok[iTokTypename], name, _DebugGetLine(iTokTypename)); isArray = true; __ctn.Append("[In] "); } if (_Inout_ && (sal.Contains("_updates_") || sal.Contains("count"))) { //OutList(_tok[iTokTypename], name, _DebugGetLine(iTokTypename)); isArray = true; __ctn.Append("[In,Out] "); } if (_Out_ && (sal.Contains("_writes_") || sal.Contains("count"))) { //OutList(_tok[iTokTypename], name, _DebugGetLine(iTokTypename)); isArray = true; __ctn.Append("[Out] "); } } if (_Inout_) { isConst = false; } if (isArray) { ptr--; } else { ptr--; if (_Out_) { __ctn.Append("out "); } else if (_In_ || isConst) { if (isBlittable) { if (isConst && ptr == 0 && name != "IntPtr") { //OutList(_tok[iTokTypename], name, _DebugGetLine(iTokTypename)); isArray = true; //usually array, because there is no sense for eg 'const int* param', unless it is a 64-bit value (SDK usually then uses LARGE_INTEGER etc, not __int64). Those with just _In_ usually are not arrays, because for arrays are used _In_reads_ etc. } else { __ctn.Append("in "); //OutList(_tok[iTokTypename], name, _DebugGetLine(iTokTypename)); } } else { if (isConst) { __ctn.Append("in "); //prevents copying non-blittable types back to the caller when don't need } else { //OutList(_tok[iTokTypename], name, _DebugGetLine(iTokTypename)); //__ctn.Append("in "); //no, because there are many errors in SDK where inout parameters have _In_ __ctn.Append("ref "); } } } else { __ctn.Append("ref "); } } if (isArray) { bool useMarshalAsSubtype = false; if (isCOM) { //by default marshals as SAFEARRAY if (marshalAs == null) { marshalAs = "LPArray"; } else { useMarshalAsSubtype = true; } } else if (context == _TypeContext.DelegateParameter) { isArray = false; ptr++; __ctn.Clear(); //maybe can be array, not tested. Never mind, in SDK only 4 rarely used. //OutList(_tok[iTokTypename], name, _DebugGetLine(iTokTypename)); } else if (marshalAs != null) { useMarshalAsSubtype = true; } if (useMarshalAsSubtype) { marshalAs = "LPArray, ArraySubType = UnmanagedType." + marshalAs; } } } //if(isParameter) __ctn.Append(name); if (ptr > 0) { __ctn.Append('*', ptr); } if (isArray) { __ctn.Append("[]"); } name = __ctn.ToString(); } if (marshalAs != null) { __ctn.Clear(); string ret = null; if (context == _TypeContext.Return || context == _TypeContext.ComReturn) { ret = "return: "; } attributes = $"[{ret}MarshalAs(UnmanagedType.{marshalAs})]"; } return(name); }
/// <summary> /// Converts C++ function parameter or struct member definition (type, name, const etc) to C# typename/name/etc. /// _i must be at its start. Finally it will be after the parsed part. Does not check whether it is followed by ',' etc. /// </summary> /// <param name="context">Member, Parameter or DelegateParameter.</param> /// <param name="t">Receives C# typename, name etc.</param> /// <param name="parentName">The function/struct name. Used to auto-create names for nameless parameters.</param> /// <param name="iParam">1-based index of parameter/member. Used to auto-create names for nameless parameters.</param> void _ParseParamOrMember(_TypeContext context, out _PARAMDATA t, string parentName, int iParam) { bool isMember = context == _TypeContext.Member; t = new _PARAMDATA(); int iSAL = 0; if (_TokIsChar(_i, '^') && _TokIsChar(_i + 1, '\"')) { _i++; if (!isMember) { iSAL = _i; _tok[iSAL] = new _Token(_tok[iSAL].s + 1, _tok[iSAL].len - 2); } _i++; } var d = new _FINDTYPEDATA(); _FindTypename(isMember, ref d); if (d.outIsNestedTypeDefinition) { if (_TokIsChar(_i, ';')) { t.isNestedTypeDefinitionWithoutVariables = true; t.isAnonymousTypeDefinitionWithoutVariables = d.outIsAnonymousTypeDefinition; t.typeName = d.outSym.csTypename; return; } } else { _i++; } //pointer int ptr = 0; while (_TokIsChar(_i, '*', '&')) { _i++; ptr++; } //suport inline function type definition bool isFunc = !d.outIsNestedTypeDefinition && _DetectIsFuncType(_i); if (isFunc) { var f = new _INLINEFUNCTYPEDATA(parentName, iParam); _DeclareTypedefFunc(d.outSym, ptr, d.outIsConst, d.outTypenameToken, f); t.typeName = f.typeName; t.name = f.paramName; } else { //typename, param/member name t.typeName = _ConvertTypeName(d.outSym, ref ptr, d.outIsConst, d.outTypenameToken, context, out t.attributes, iSAL); if (_TokIsIdent(_i)) { t.name = _TokToString(_i++); } else if (!isMember && _TokIsChar(_i, ",)[=")) { t.name = "param" + iParam; } else { _Err(_i, "no name"); } } if (_TokIsChar(_i, '[')) { _ConvertCArray(ref t.typeName, ref t.name, ref t.attributes, !isMember); } //escape names that are C# keywords if (_csKeywords.Contains(t.name)) { //AOutput.Write(t.name); t.name = "@" + t.name; } }