# # Scans a header file for interfaces and generates # proxies and stubs for the methods encountered. # # NOTE: This script's parser is very simple and targeted # at the formatting used in dbgeng.h. It will not work # with general header files. # $gPre = "__drpc_"; # Parameter flags. $cPfIn = 1; $cPfOut = 2; $cPfOpt = 4; $cPfSizeIn = 8; # Parameter data array indices. $cParamFlags = 0; $cParamType = 1; $cParamName = 2; $cParamSizeIs = 3; $cParamAlignIs = 4; # The method returns no data and the return value # is uninteresting so do no send a return packet. # This is a significant savings in network activity # for often-called methods like output callbacks. $cNoReturn = 1; # Proxy code is provided elsewhere. $cCustomProxy = 2; # A template implementation in $gProxy/StubRepl should be used # instead of standard code generation. $cReplImplementation = 4; # Destroy in-parameter objects. $cDestroyInObjects = 8; # Omit the thread check. $cNoThreadCheck = 16; # Method object arguments are known to be proxies. $cObjectsAreProxies = 32; # Stub code is provided elsewhere. $cCustomStub = 64; # Method does not need a stub. $cNoStub = 128 | $cCustomStub; # Method cannot be remoted so generate proxy code that fails # and don't bother with a stub. $cNotRemotable = 256 | $cNoStub; # Method can be assumed to be thread safe as it is protected # by a higher-level lock. $cCallLocked = 512; $cCustomImplementation = $cCustomProxy | $cCustomStub; $gMethodFlagsTable{"*::QueryInterface"} = $cReplImplementation; $gMethodFlagsTable{"*::AddRef"} = $cReplImplementation | $cNoStub; $gMethodFlagsTable{"*::Release"} = $cReplImplementation; $gMethodFlagsTable{"IDebugBreakpoint::GetAdder"} = $cObjectsAreProxies; $gMethodFlagsTable{"IDebugClient::CreateClient"} = $cCustomProxy | $cObjectsAreProxies; $gMethodFlagsTable{"IDebugClient::ExitDispatch"} = $cNoThreadCheck | $cNoReturn | $cObjectsAreProxies; $gMethodFlagsTable{"IDebugClient::GetOtherOutputMask"} = $cObjectsAreProxies; $gMethodFlagsTable{"IDebugClient::SetOtherOutputMask"} = $cObjectsAreProxies; $gMethodFlagsTable{"IDebugClient::StartProcessServer"} = $cCustomProxy | $cCustomStub; $gMethodFlagsTable{"IDebugControl::AddBreakpoint"} = $cObjectsAreProxies; $gMethodFlagsTable{"IDebugControl::ControlledOutputVaList"} = $cCustomImplementation; $gMethodFlagsTable{"IDebugControl::GetBreakpointById"} = $cObjectsAreProxies; $gMethodFlagsTable{"IDebugControl::GetBreakpointByIndex"} = $cObjectsAreProxies; $gMethodFlagsTable{"IDebugControl::GetExtensionFunction"} = $cNotRemotable; $gMethodFlagsTable{"IDebugControl::GetWindbgExtensionApis32"} = $cNotRemotable; $gMethodFlagsTable{"IDebugControl::GetWindbgExtensionApis64"} = $cNotRemotable; $gMethodFlagsTable{"IDebugControl::OutputPromptVaList"} = $cCustomImplementation; $gMethodFlagsTable{"IDebugControl::OutputVaList"} = $cCustomImplementation; $gMethodFlagsTable{"IDebugControl::RemoveBreakpoint"} = $cDestroyInObjects | $cObjectsAreProxies; $gMethodFlagsTable{"IDebugControl::ReturnInput"} = $cNoThreadCheck | $cNoReturn; $gMethodFlagsTable{"IDebugControl::SetInterrupt"} = $cNoThreadCheck | $cNoReturn; $gMethodFlagsTable{"IDebugSymbols::CreateSymbolGroup"} = $cObjectsAreProxies; $gMethodFlagsTable{"IDebugSymbols::GetScopeSymbolGroup"} = $cObjectsAreProxies | $cDestroyInObjects; # All callback proxy methods need to allow any thread # but use the owning thread's connection because the # engine wants to dispatch callbacks to all clients # from a single thread. $gMethodFlagsTable{"IDebugEventCallbacks::ChangeDebuggeeState"} = $cNoThreadCheck | $cNoReturn; $gMethodFlagsTable{"IDebugEventCallbacks::ChangeEngineState"} = $cNoThreadCheck | $cNoReturn; $gMethodFlagsTable{"IDebugEventCallbacks::ChangeSymbolState"} = $cNoThreadCheck | $cNoReturn; $gMethodFlagsTable{"IDebugInputCallbacks::*"} = $cNoThreadCheck | $cNoReturn; $gMethodFlagsTable{"IDebugOutputCallbacks::*"} = $cNoThreadCheck | $cNoReturn; # RequestBreakIn operates asynchronously. $gMethodFlagsTable{"IUserDebugServices::RequestBreakIn"} = $cNoThreadCheck | $cNoReturn; # All low-level debug services routines are thread-safe # as they are only called when the engine lock is held. $gMethodFlagsTable{"IUserDebugServices::*"} = $cNoThreadCheck | $cCallLocked; $cTypeStringSize = -1; $cTypeBufferSize = -2; $gTypeSize{"LONG"} = 4; $gAddrOf{"LONG"} = "&"; $gTypeSize{"PLONG"} = 4; $gTypeSize{"ULONG"} = 4; $gAddrOf{"ULONG"} = "&"; $gTypeSize{"PULONG"} = 4; $gTypeSize{"BOOL"} = 4; $gAddrOf{"BOOL"} = "&"; $gTypeSize{"PBOOL"} = 4; $gTypeSize{"ULONG64"} = 8; $gAddrOf{"ULONG64"} = "&"; $gTypeSize{"PULONG64"} = 8; $gTypeSize{"PSTR"} = $cTypeStringSize; $gTypeSize{"PCSTR"} = $cTypeStringSize; $gTypeSize{"PVOID"} = $cTypeBufferSize; $gTypeSize{"PDEBUG_BREAKPOINT_PARAMETERS"} = 56; $gTypeAlign{"PDEBUG_BREAKPOINT_PARAMETERS"} = 8; $gTypeSize{"PDEBUG_STACK_FRAME"} = 128; $gTypeAlign{"PDEBUG_STACK_FRAME"} = 8; $gTypeSize{"PDEBUG_VALUE"} = 32; $gTypeAlign{"PDEBUG_VALUE"} = 8; $gTypeSize{"PDEBUG_REGISTER_DESCRIPTION"} = 32; $gTypeAlign{"PDEBUG_REGISTER_DESCRIPTION"} = 8; $gTypeSize{"PDEBUG_SYMBOL_PARAMETERS"} = 32; $gTypeAlign{"PDEBUG_SYMBOL_PARAMETERS"} = 8; $gTypeSize{"PDEBUG_MODULE_PARAMETERS"} = 64; $gTypeAlign{"PDEBUG_MODULE_PARAMETERS"} = 8; $gTypeSize{"PDEBUG_SPECIFIC_FILTER_PARAMETERS"} = 20; $gTypeAlign{"PDEBUG_SPECIFIC_FILTER_PARAMETERS"} = 4; $gTypeSize{"PDEBUG_EXCEPTION_FILTER_PARAMETERS"} = 24; $gTypeAlign{"PDEBUG_EXCEPTION_FILTER_PARAMETERS"} = 4; $gTypeSize{"PEXCEPTION_RECORD64"} = 152; $gTypeAlign{"PEXCEPTION_RECORD64"} = 8; $gTypeSize{"PMEMORY_BASIC_INFORMATION64"} = 48; $gTypeAlign{"PMEMORY_BASIC_INFORMATION64"} = 8; $gTypeSize{"PUSER_THREAD_INFO"} = 16; $gTypeAlign{"PUSER_THREAD_INFO"} = 16; $gProxyRepl{"*::QueryInterface"} = <<"EOQ"; HRESULT Status; DbgRpcConnection* Conn; DbgRpcCall Call; PUCHAR Data; DbgRpcProxy* Proxy; IUnknown* Unk; if ((Conn = DbgRpcGetConnection(m_OwningThread)) == NULL || (Data = Conn->StartCall(&Call, m_ObjectId, DBGRPC_STUB_INDEX(m_InterfaceIndex, DBGRPC_SMTH__QueryInterface), sizeof(IID) + sizeof(ULONG), sizeof(DbgRpcObjectId))) == NULL) { Status = E_OUTOFMEMORY; } else { ULONG IfUnique; if ((Status = DbgRpcPreallocProxy_(InterfaceId, (void **)&Unk, &Proxy, &IfUnique)) == S_OK) { *(LPIID)Data = InterfaceId; // IfUnique is a per-interface unique code generated // automatically based on the methods and parameters // in an attempt to create a version-specific identifier. // This gives us some interface-level protection // against version mismatches. *(PULONG)(Data + sizeof(IID)) = IfUnique; Status = Conn->SendReceive(&Call, &Data); if (Status == S_OK) { *Interface = (PVOID) Proxy->InitializeProxy(*(DbgRpcObjectId*)Data, Unk); } else { delete Unk; } } Conn->FreeData(Data); } return Status; EOQ # # Originally AddRefs were never sent across the wire as a # performance optimization. However, this caused a mismatch # in behavior between local and remote cases because in the # remote case objects stored by the engine do not receive # refcounts since the proxy at the engine side does not # forward the AddRef. This was changed, but AddRef could # not simply be added as a method as that would throw # method tables off. Instead the Release method is overloaded # to allow both AddRef and Release depending on whether # in arguments are given. Old binaries will never send # in arguments so the new stub will only do Release. New # binaries need to check for the DBGRPC_FULL_REMOTE_UNKNOWN flag # before sending new-style AddRef-through-Release calls. # $gProxyRepl{"*::AddRef"} = <<"EOQ"; DbgRpcConnection* Conn; DbgRpcCall Call; PUCHAR Data; HRESULT Status; if ((Conn = DbgRpcGetConnection(m_OwningThread)) == NULL) { // Nothing that can really be done. return m_LocalRefs + m_RemoteRefs; } if ((Conn->m_Flags & DBGRPC_FULL_REMOTE_UNKNOWN) == 0) { return ++m_LocalRefs + m_RemoteRefs; } if ((Data = Conn->StartCall(&Call, m_ObjectId, DBGRPC_STUB_INDEX(m_InterfaceIndex, DBGRPC_SMTH__Release), sizeof(INT32), 0)) == NULL) { // Nothing that can really be done. return m_RemoteRefs; } *(INT32*)Data = 1; Status = Conn->SendReceive(&Call, &Data); Conn->FreeData(Data); return ++m_RemoteRefs; EOQ $gProxyRepl{"*::Release"} = <<"EOQ"; if (m_LocalRefs > 0) { return --m_LocalRefs + m_RemoteRefs; } else { DbgRpcConnection* Conn; DbgRpcCall Call; PUCHAR Data; HRESULT Status; if ((Conn = DbgRpcGetConnection(m_OwningThread)) == NULL || (Data = Conn->StartCall(&Call, m_ObjectId, DBGRPC_STUB_INDEX(m_InterfaceIndex, DBGRPC_SMTH__Release), 0, 0)) == NULL) { // Nothing that can really be done. return m_RemoteRefs; } Status = Conn->SendReceive(&Call, &Data); Conn->FreeData(Data); if (--m_RemoteRefs == 0) { delete this; return 0; } else { return m_RemoteRefs; } } EOQ $gStubRepl{"*::QueryInterface"} = <<"EOQ"; ULONG IfUnique = *(PULONG)(${gPre}InData + sizeof(IID)); if (IfUnique != DbgRpcGetIfUnique_(*(LPIID)${gPre}InData)) { // Version mismatch between client and server. return RPC_E_VERSION_MISMATCH; } *(DbgRpcObjectId*)${gPre}OutData = 0; return ${gPre}If->QueryInterface(*(LPIID)${gPre}InData, (void **)${gPre}OutData); EOQ $gStubRepl{"*::Release"} = <<"EOQ"; if (${gPre}InData > 0) { return (HRESULT)${gPre}If->AddRef(); } else { return (HRESULT)${gPre}If->Release(); } EOQ sub Error($) { warn "BUILDMSG: $0: $gHeaderFile($.): $_[0]\n"; } sub ErrorExit($) { Error($_[0]); die; } sub Line($) { if (($gFile == \*PROXC && ($gMethodFlags & $cCustomProxy)) || ($gFile == \*STUBC && ($gMethodFlags & $cCustomStub))) { return; } print $gFile " " x $gIndent; print $gFile $_[0]; } sub CLine($) { if (($gMethodFlags & $cCustomProxy) == 0) { print PROXC " " x $gIndent; print PROXC $_[0]; } if (($gMethodFlags & $cCustomStub) == 0) { print STUBC " " x $gIndent; print STUBC $_[0]; } } sub CClean($) { if (($gMethodFlags & $cCustomProxy) == 0) { print PROXC " " x $gIndent; print PROXC $_[0]; } if (($gMethodFlags & $cCustomStub) == 0 && $gStubHasCleanup) { print STUBC " " x $gIndent; print STUBC $_[0]; } } sub Print($) { if (($gFile == \*PROXC && ($gMethodFlags & $cCustomProxy)) || ($gFile == \*STUBC && ($gMethodFlags & $cCustomStub))) { return; } print $gFile $_[0]; } sub AddIfType($$) { my $IfType = $_[0]; my $IfTypedef = $_[1]; push @gIfTypeList, $IfType; $gIfTypes{$IfTypedef} = $IfType; $gIfTypes{"$IfTypedef*"} = $IfType; # Interfaces are represented by # DbgRpcObjectIds in the stream so # create artificial type size entries # that match that size. $gTypeSize{$IfTypedef} = 8; $gTypeSize{"$IfTypedef*"} = 8; # Every interface has a proxy class that # has some global manipulation code associated # with it. print PROXH "$IfType*\nDbgRpcPrealloc${IfType}Proxy(void);\n"; } sub GenerateIfTypes() { my($IfType, $First, $IfCount); $IfCount = $#gIfTypeList + 1; $gFile = \*PROXC; $gIndent = 0; # # Generate trailing interface proxy global code for # each registered interface type. # Line("\n"); Line("ULONG\n"); Line("DbgRpcGetIfUnique_$gFileBase(REFIID InterfaceId)\n"); Line("{\n"); $gIndent += 4; $First = 1; foreach $IfType (@gIfTypeList) { if ($First) { $First = 0; Line(""); } else { Line("else "); } Print("if (DbgIsEqualIID(IID_$IfType, InterfaceId))\n"); Line("{\n"); Line(" return DBGRPC_UNIQUE_$IfType;\n"); Line("}\n"); } Line("else\n"); Line("{\n"); Line(" return 0;\n"); Line("}\n"); $gIndent -= 4; Line("}\n"); Line("\n"); Line("HRESULT\n"); Line("DbgRpcPreallocProxy_$gFileBase(REFIID InterfaceId, PVOID* Interface,\n"); Line(" DbgRpcProxy** Proxy, PULONG IfUnique)\n"); Line("{\n"); $gIndent += 4; $First = 1; foreach $IfType (@gIfTypeList) { if ($First) { $First = 0; Line(""); } else { Line("else "); } Print("if (DbgIsEqualIID(IID_$IfType, InterfaceId))\n"); Line("{\n"); $gIndent += 4; Line("Proxy$IfType* IProxy = new Proxy$IfType;\n"); Line("*Interface = IProxy;\n"); Line("*Proxy = IProxy;\n"); Line("*IfUnique = DBGRPC_UNIQUE_$IfType;\n"); $gIndent -= 4; Line("}\n"); } Line("else\n"); Line("{\n"); Line(" return E_NOINTERFACE;\n"); Line("}\n"); Line("\n"); Line("return *Interface != NULL ? S_OK : E_OUTOFMEMORY;\n"); $gIndent -= 4; Line("}\n"); foreach $IfType (@gIfTypeList) { Line("\n"); Line("$IfType*\n"); Line("DbgRpcPrealloc${IfType}Proxy(void)\n"); Line("{\n"); Line(" Proxy$IfType* Proxy = new Proxy$IfType;\n"); Line(" return Proxy;\n"); Line("}\n"); } # # Now generate stub tables and their initialization functions. # $gFile = \*STUBC; Line("\n"); Line("DbgRpcStubFunctionTable g_DbgRpcStubs_${gFileBase}[$IfCount];\n"); Line("#if DBG\n"); Line("PCSTR* g_DbgRpcStubNames_${gFileBase}[$IfCount];\n"); Line("#endif // #if DBG\n"); Line("\n"); Line("void\n"); Line("DbgRpcInitializeStubTables_$gFileBase(ULONG Base)\n"); Line("{\n"); $gIndent += 4; foreach $IfType (@gIfTypeList) { Line("g_DbgRpcStubs_${gFileBase}[DBGRPC_SIF_$IfType - Base]" . ".Functions = \n"); Line(" g_DbgRpcStubs_${gFileBase}_$IfType;\n"); Line("g_DbgRpcStubs_${gFileBase}[DBGRPC_SIF_$IfType - Base]" . ".Count = \n"); Line(" DBGRPC_SMTH_${IfType}_COUNT;\n"); } $gIndent -= 4; Line("\n"); Line("#if DBG\n"); $gIndent += 4; foreach $IfType (@gIfTypeList) { Line("g_DbgRpcStubNames_${gFileBase}[DBGRPC_SIF_$IfType - Base] = \n"); Line(" g_DbgRpcStubNames_${gFileBase}_$IfType;\n"); } $gIndent -= 4; Line("#endif // #if DBG\n"); Line("}\n"); } sub AccumulateUnique($) { my($Value, @Values); @Values = unpack("C*", $_[0]); foreach $Value (@Values) { $gIfUnique = ($gIfUnique + $Value) & 0xffffffff; } } sub BeginInterface($) { $gIface = $_[0]; $gStubList = ""; $gIfUnique = 0; print PROXH "\n"; print PROXH "class Proxy$gIface : public $gIface, public DbgRpcProxy\n"; print PROXH "{\n"; print PROXH "public:\n"; print PROXH " Proxy$gIface(void) : DbgRpcProxy(DBGRPC_SIF_$gIface) {}\n"; } sub FindSizeParam($) { my $MatchParam = $_[0]; my($Param); foreach $Param (@gParams) { # Look for the parameter named by size_is. if ($Param->[$cParamName] eq $MatchParam->[$cParamSizeIs]) { return $Param; } } # Size parameters were already validated so just return a default. return ""; } sub AddSize($$$) { my $Which = $_[0]; my $TypeAlign = $_[1]; my $AddSize = $_[2]; Line("${gPre}${Which}Size = "); if ($TypeAlign > 1) { Print("INT_ALIGN2(${gPre}${Which}Size, $TypeAlign)"); } else { Print("${gPre}${Which}Size"); } Print(" + $AddSize;\n"); } sub InParamObjectId($$) { my $Name = $_[0]; my $IfName = $_[1]; if ($gMethodFlags & $cObjectsAreProxies) { return "((Proxy$IfName*)$Name)->m_ObjectId"; } else { return "(DbgRpcObjectId)$Name"; } } sub AlignInData($) { my $TypeAlign = $_[0]; if ($TypeAlign > 1) { Line("${gPre}InData = " . "PTR_ALIGN2(PUCHAR, ${gPre}InData, $TypeAlign);\n"); } } sub AlignOutData($) { my $TypeAlign = $_[0]; if ($TypeAlign > 1) { Line("${gPre}OutData = " . "PTR_ALIGN2(PUCHAR, ${gPre}OutData, $TypeAlign);\n"); } } sub CollectParams() { my($LastParam, $Param); $LastParam = 0; @gParams = (); PARAMS: while (!$LastParam) { # Collect lines up to ',' or end. $Param = ""; PARAM: for (;;) { $_ =
; chop($_); $_ =~ s/^\s*//; if (/^\)( PURE)?;$/) { if ($1 ne " PURE") { Error("Expecting PURE"); } $LastParam = 1; last PARAM; } if ($Param) { $Param .= " "; } $Param .= $_; if (/,$/) { last PARAM; } } if (!$Param) { # No arguments. last PARAMS; } print PROXH " " . $Param . "\n"; Line("$Param\n"); AccumulateUnique($Param); # Skip ... if ($Param eq "...") { next PARAMS; } # Trim off trailing chars. $Param =~ s/,?\s*$//; # # Analyze parameter and extract mode, type and name. # my($ParamParts, $Part, $InOut, $SizeIs, $AlignIs); my($ParamType, $ParamName); @ParamParts = split / /, $Param; $InOut = 0; $SizeIs = ""; $AlignIs = ""; $Part = shift @ParamParts; if ($Part eq "IN") { $InOut |= $cPfIn; $Part = shift @ParamParts; } if ($Part eq "OUT") { $InOut |= $cPfOut; $Part = shift @ParamParts; } if ($Part eq "OPTIONAL") { $InOut |= $cPfOpt; $Part = shift @ParamParts; } if ($Part eq "/*") { $Part = shift @ParamParts; if ($Part =~ /^size_is\((\w+)\)$/) { $SizeIs = $1; $Part = shift @ParamParts; } if ($Part =~ /^align_is\(([0-9]+)\)$/) { $AlignIs = $1; $Part = shift @ParamParts; } while ($Part ne "*/") { Error("Unknown directive $Part"); if ($Part eq "") { ErrorExit("Unrecoverable error in directives\n"); } $Part = shift @ParamParts; } # Get next part. $Part = shift @ParamParts; } if (($InOut & ($cPfIn | $cPfOut)) == 0) { Error("$gIfaceMethod: No in/out given"); } $ParamType = $Part; $Part = shift @ParamParts; $ParamName = $Part; if (($InOut & ($cPfIn | $cPfOut)) == ($cPfIn | $cPfOut)) { if ($gIfTypes{$ParamType} ne "") { Error("$gIfaceMethod: In-out interfaces not supported"); } elsif ($gTypeSize{$ParamType} == $cTypeStringSize || $gTypeSize{$ParamType} == $cTypeBufferSize) { if ($InOut & $cPfOpt) { Error("$gIfaceMethod: In-out optional buffers " . "not supported"); } } } if (!$SizeIs) { # Buffers and out strings have unknown sizes initially. # By convention buffers and out strings must have a size_is # parameter of the same name with the suffix Size. $SizeIs = $gTypeSize{$ParamType}; if ($SizeIs == $cTypeBufferSize || ($SizeIs == $cTypeStringSize && ($InOut & $cPfOut))) { $SizeIs = $ParamName . "Size"; } else { $SizeIs = ""; } } elsif ($gVerbose) { warn "$gIfaceMethod: size_is($SizeIs) $ParamName\n"; } # If no align_is was given use the natural alignment # for the type. if (!$AlignIs) { $AlignIs = $gTypeAlign{$ParamType}; if (!$AlignIs) { $AlignIs = $gTypeSize{$ParamType}; if ($AlignIs < 1) { $AlignIs = 1; } } } elsif ($gVerbose) { warn "$gIfaceMethod: align_is($AlignIs) $ParamName\n"; } push @gParams, [($InOut, $ParamType, $ParamName, $SizeIs, $AlignIs)]; } my($SizeParam); # For every sized in-parameter look up and mark the # corresponding sizing parameter. # This mark is used later to keep the sizing parameter before # the sized parameter and not in whatever arbitrary parameter # position it happens to be in. foreach $Param (@gParams) { if ($Param->[$cParamSizeIs]) { $SizeParam = FindSizeParam($Param); if ($SizeParam) { if (($SizeParam->[$cParamFlags] & ($cPfIn | $cPfOut | $cPfOpt)) != $cPfIn) { Error("$gIfaceMethod $Param->[$cParamName] " . "size_is($Param->[$cParamSizeIs]), " . "size_is must be in-only\n"); } # Optional parameters always have their own size # to mark their optionality so don't suppress # the sizing parameter in that case. if (($Param->[$cParamFlags] & ($cPfIn | $cPfOpt)) == $cPfIn) { if ($gVerbose) { warn "$gIfaceMethod: Mark $SizeParam->[$cParamName] " . "SizeIn for $Param->[$cParamName]\n"; } $SizeParam->[$cParamFlags] |= $cPfSizeIn; } } elsif (($gMethodFlags & ($cCustomProxy | $cCustomStub)) != ($cCustomProxy | $cCustomStub)) { Error("Sized param $Param->[$cParamName] " . "has no matching size argument"); } } } return @gParams; } # Prepare for object proxies and stubs. # Compute data size for in and out. sub ProxyInitializeParams($) { my $Param = $_[0]; my($Size, $IfName, $SizeParam); $Size = $gTypeSize{$Param->[$cParamType]}; $IfName = $gIfTypes{$Param->[$cParamType]}; if ($IfName ne "") { if ($gVerbose) { warn "$gIfaceMethod: Object param $Param->[$cParamName]\n"; } if ($Param->[$cParamFlags] & $cPfIn) { AddSize("In", $Param->[$cParamAlignIs], $Size); } elsif ($Param->[$cParamFlags] & $cPfOut) { if ($gMethodFlags & $cObjectsAreProxies) { # Prepare out parameters for proxies. Line("*$Param->[$cParamName] = " . "DbgRpcPrealloc${IfName}Proxy();\n"); } AddSize("Out", $Param->[$cParamAlignIs], $Size); } } else { if ($Param->[$cParamFlags] & $cPfIn) { if ($Size > 0) { if ($Param->[$cParamSizeIs]) { $SizeParam = FindSizeParam($Param); AddSize("In", $SizeParam->[$cParamAlignIs], $gTypeSize{$SizeParam->[$cParamType]}); $Size .= " * $Param->[$cParamSizeIs]"; } elsif ($Param->[$cParamFlags] & $cPfOpt) { # Optional pointer parameters generate # a size to mark whether they're non-NULL. AddSize("In", $gTypeSize{"ULONG"}, $gTypeSize{"ULONG"}); } if ($Param->[$cParamFlags] & $cPfOpt) { Line("if ($Param->[$cParamName] != NULL)\n"); Line("{\n"); $gIndent += 4; } AddSize("In", $Param->[$cParamAlignIs], $Size); if ($Param->[$cParamFlags] & $cPfOpt) { $gIndent -= 4; Line("}\n"); } } elsif ($Size == $cTypeStringSize) { if ($Param->[$cParamFlags] & $cPfOpt) { # Pass both a length and the string # to handle NULLs. Line("ULONG ${gPre}Len_$Param->[$cParamName] = " . "$Param->[$cParamName] != NULL ? " . "strlen($Param->[$cParamName]) + 1 : 0;\n"); AddSize("In", $gTypeSize{"ULONG"}, "${gPre}Len_$Param->[$cParamName] + " . $gTypeSize{"ULONG"}); } else { Line("ULONG ${gPre}Len_$Param->[$cParamName] = " . "strlen($Param->[$cParamName]) + 1;\n"); AddSize("In", 1, "${gPre}Len_$Param->[$cParamName]"); } } elsif ($Size == $cTypeBufferSize) { $SizeParam = FindSizeParam($Param); if ($Param->[$cParamFlags] & $cPfOpt) { Line("$SizeParam->[$cParamName] = " . "$Param->[$cParamName] != NULL ? " . "$SizeParam->[$cParamName] : 0;\n"); } # Include the size parameter. AddSize("In", $SizeParam->[$cParamAlignIs], $gTypeSize{$SizeParam->[$cParamType]}); AddSize("In", $Param->[$cParamAlignIs], $SizeParam->[$cParamName]); } else { Error("Unknown in param size: $Param->[$cParamName]"); } } if ($Param->[$cParamFlags] & $cPfOut) { if ($Size > 0) { # Non-size_is out parameters are always passed # on the stub side so always include space for # them even if they're optional and NULL. The # NULL check happens in the out-param write-back # code. if ($Param->[$cParamSizeIs]) { # size_is pointers do need optionality checks. if ($Param->[$cParamFlags] & $cPfOpt) { Line("if ($Param->[$cParamName] != NULL)\n"); Line("{\n"); $gIndent += 4; } $Size .= " * $Param->[$cParamSizeIs]"; } AddSize("Out", $Param->[$cParamAlignIs], $Size); if ($Param->[$cParamSizeIs] && ($Param->[$cParamFlags] & $cPfOpt)) { $gIndent -= 4; Line("}\n"); } } elsif ($Size == $cTypeStringSize || $Size == $cTypeBufferSize) { $SizeParam = FindSizeParam($Param); if ($Param->[$cParamFlags] & $cPfOpt) { Line("$SizeParam->[$cParamName] = " . "$Param->[$cParamName] != NULL ? " . "$SizeParam->[$cParamName] : 0;\n"); } AddSize("Out", $Param->[$cParamAlignIs], $SizeParam->[$cParamName]); } else { Error("Unknown out param size: $Param->[$cParamName]"); } } } } # Check object proxies. sub ProxyValidateParams($) { my $Param = $_[0]; my($IfName); if ($Param->[$cParamFlags] & $cPfOut) { $IfName = $gIfTypes{$Param->[$cParamType]}; if ($IfName ne "") { Line("if (*$Param->[$cParamName] == NULL)\n"); Line("{\n"); Line(" goto CheckStatus;\n"); Line("}\n"); } } } # Copy in-parameter data into packet. sub ProxyCopyInParams($) { my $Param = $_[0]; my($Size, $IfName); $Size = $gTypeSize{$Param->[$cParamType]}; $IfName = $gIfTypes{$Param->[$cParamType]}; if ($IfName ne "") { AlignInData($Param->[$cParamAlignIs]); if ($Param->[$cParamFlags] & $cPfOpt) { Line("if ($Param->[$cParamName] == NULL)\n"); Line("{\n"); Line(" *(DbgRpcObjectId*)${gPre}InData = 0;\n"); Line("}\n"); Line("else\n"); Line("{\n"); $gIndent += 4; } Line("*(DbgRpcObjectId*)${gPre}InData = " . InParamObjectId($Param->[$cParamName], $IfName) . ";\n"); if ($Param->[$cParamFlags] & $cPfOpt) { $gIndent -= 4; Line("}\n"); } Line("${gPre}InData += $Size;\n"); } elsif ($Size == $cTypeStringSize) { if ($Param->[$cParamFlags] & $cPfOpt) { # Always write out the size. AlignInData($gTypeSize{"ULONG"}); Line("*(ULONG*)${gPre}InData = " . "${gPre}Len_$Param->[$cParamName];\n"); Line("${gPre}InData += " . $gTypeSize{"ULONG"} . ";\n"); } else { AlignInData($Param->[$cParamAlignIs]); } Line("memcpy(${gPre}InData, $Param->[$cParamName], " . "${gPre}Len_$Param->[$cParamName]);\n"); Line("${gPre}InData += " . "${gPre}Len_$Param->[$cParamName];\n"); } elsif ($Size == $cTypeBufferSize) { $SizeParam = FindSizeParam($Param); AlignInData($SizeParam->[$cParamAlignIs]); Line("*($SizeParam->[$cParamType]*)${gPre}InData = " . "$SizeParam->[$cParamName];\n"); Line("${gPre}InData += " . $gTypeSize{$SizeParam->[$cParamType]} . ";\n"); AlignInData($Param->[$cParamAlignIs]); Line("memcpy(${gPre}InData, $Param->[$cParamName], " . "$Param->[$cParamSizeIs]);\n"); Line("${gPre}InData += $Param->[$cParamSizeIs];\n"); } else { my($AddrOf); $AddrOf = $gAddrOf{$Param->[$cParamType]}; if ($Param->[$cParamSizeIs]) { if ($AddrOf) { # size_is parameters should always be arrays. ErrorExit("AddrOf for size_is parameter\n"); } # Always write out the size. $SizeParam = FindSizeParam($Param); AlignInData($SizeParam->[$cParamAlignIs]); if ($Param->[$cParamFlags] & $cPfOpt) { Line("*($SizeParam->[$cParamType]*)${gPre}InData = " . "$Param->[$cParamName] != NULL ? " . "$SizeParam->[$cParamName] : 0;\n"); } else { Line("*($SizeParam->[$cParamType]*)${gPre}InData = " . "$SizeParam->[$cParamName];\n"); } Line("${gPre}InData += " . $gTypeSize{$SizeParam->[$cParamType]} . ";\n"); $Size .= " * $SizeParam->[$cParamName]"; } elsif ($Param->[$cParamFlags] & $cPfOpt) { AlignInData($gTypeSize{"ULONG"}); Line("*(ULONG*)${gPre}InData = " . "$Param->[$cParamName] != NULL ? $Size : 0;\n"); Line("${gPre}InData += " . $gTypeSize{"ULONG"} . ";\n"); } if ($Param->[$cParamFlags] & $cPfOpt) { Line("if ($Param->[$cParamName] != NULL)\n"); Line("{\n"); $gIndent += 4; } AlignInData($Param->[$cParamAlignIs]); if ($AddrOf) { # If there's an AddrOf this is an atomic type # so copy using the type rather than memcpy as # the compiler can generate more efficient code # since it can assume alignment. Line("*($Param->[$cParamType]*)${gPre}InData = " . "$Param->[$cParamName];\n"); } else { Line("memcpy(${gPre}InData, $AddrOf" . "$Param->[$cParamName], $Size);\n"); } Line("${gPre}InData += $Size;\n"); if ($Param->[$cParamFlags] & $cPfOpt) { $gIndent -= 4; Line("}\n"); } } } # Copy out-parameter data to real out parameters. # Initialize or clean up object proxies. sub ProxyFinalizeParamsSuccess($) { my $Param = $_[0]; my($Size, $IfName, $SizeParam); $Size = $gTypeSize{$Param->[$cParamType]}; $IfName = $gIfTypes{$Param->[$cParamType]}; if ($IfName ne "") { if ($Param->[$cParamFlags] & $cPfOut) { AlignOutData($Param->[$cParamAlignIs]); if ($gMethodFlags & $cObjectsAreProxies) { Line("*$Param->[$cParamName] = ($IfName*)\n"); Line(" ((Proxy$IfName*)(*$Param->[$cParamName]))->\n"); Line(" InitializeProxy" . "(*(DbgRpcObjectId*)${gPre}OutData,\n"); Line(" *(IUnknown **)$Param->[$cParamName]);\n"); } else { Line("*$Param->[$cParamName] = ($IfName*)" . "*(DbgRpcObjectId*)${gPre}OutData;\n"); } Line("${gPre}OutData += $Size;\n"); } } elsif ($Param->[$cParamFlags] & $cPfOut) { if ($Size == $cTypeStringSize || $Size == $cTypeBufferSize) { AlignOutData($Param->[$cParamAlignIs]); $SizeParam = FindSizeParam($Param); Line("memcpy($Param->[$cParamName], ${gPre}OutData, " . "$SizeParam->[$cParamName]);\n"); Line("${gPre}OutData += $SizeParam->[$cParamName];\n"); } else { my($IsAtomic); $IsAtomic = $Size <= 8; if ($Param->[$cParamSizeIs]) { $Size .= " * $Param->[$cParamSizeIs]"; $IsAtomic = 0; } if ($Param->[$cParamFlags] & $cPfOpt) { # If there's no size_is the out size is always # added even if the parameter is NULL. if (!$Param->[$cParamSizeIs]) { AlignOutData($Param->[$cParamAlignIs]); } Line("if ($Param->[$cParamName] != NULL)\n"); Line("{\n"); $gIndent += 4; if ($Param->[$cParamSizeIs]) { AlignOutData($Param->[$cParamAlignIs]); } } else { AlignOutData($Param->[$cParamAlignIs]); } if ($IsAtomic) { # If there's an AddrOf this is an atomic type # so copy using the type rather than memcpy as # the compiler can generate more efficient code # since it can assume alignment. Line("*$Param->[$cParamName] = *($Param->[$cParamType])" . "${gPre}OutData;\n"); } else { Line("memcpy($Param->[$cParamName], ${gPre}OutData, " . "$Size);\n"); } if (($Param->[$cParamFlags] & $cPfOpt) == 0) { Line("${gPre}OutData += $Size;\n"); } else { if ($Param->[$cParamSizeIs]) { Line("${gPre}OutData += $Size;\n"); } $gIndent -= 4; Line("}\n"); if (!$Param->[$cParamSizeIs]) { Line("${gPre}OutData += $Size;\n"); } } } } } sub ProxyFinalizeParamsFailure($) { my $Param = $_[0]; my($IfName); # Clean up unused proxies and stubs. if (($gMethodFlags & $cObjectsAreProxies) && ($Param->[$cParamFlags] & $cPfOut)) { $IfName = $gIfTypes{$Param->[$cParamType]}; if ($IfName ne "") { Line("if (*$Param->[$cParamName] != NULL)\n"); Line("{\n"); Line(" delete (Proxy$IfName*)(*$Param->[$cParamName]);\n"); Line(" *$Param->[$cParamName] = NULL;\n"); Line("}\n"); } } } # Get rid of proxies representing objects that # were freed by the call. sub ProxyDestroyInObjects($) { my $Param = $_[0]; my($IfName); # Special case for cleaning up stubs for methods # that destroy their input objects. This # flag should only be set for cases where it is # known that the objects are proxies. if ($gMethodFlags & $cDestroyInObjects) { if ($Param->[$cParamFlags] & $cPfIn) { $IfName = $gIfTypes{$Param->[$cParamType]}; if ($IfName ne "") { Line("delete (Proxy$IfName *)$Param->[$cParamName];\n"); } } } } # Declares parameters and creates proxies for incoming objects. sub StubInitializeParams($) { my $Param = $_[0]; my($IfName); Line("$Param->[$cParamType] $Param->[$cParamName];\n"); if (($gMethodFlags & $cObjectsAreProxies) == 0 && ($Param->[$cParamFlags] & $cPfIn)) { $IfName = $gIfTypes{$Param->[$cParamType]}; if ($IfName ne "") { Line("$Param->[$cParamName] = ($Param->[$cParamType])\n"); Line(" DbgRpcPrealloc${IfName}Proxy();\n"); $gStubHasCleanup = 1; } } } # Check object proxies. sub StubValidateParams($) { my $Param = $_[0]; my($IfName); if (($gMethodFlags & $cObjectsAreProxies) == 0 && ($Param->[$cParamFlags] & $cPfIn)) { $IfName = $gIfTypes{$Param->[$cParamType]}; if ($IfName ne "") { Line("if ($Param->[$cParamName] == NULL)\n"); Line("{\n"); Line(" goto CheckStatus;\n"); Line("}\n"); } } } sub StubOptPointer($$$$$) { my $DataPre = $_[0]; my $Param = $_[1]; my $SizeName = $_[2]; my $SizeType = $_[3]; my $SizeAdd = $_[4]; if (!$SizeAdd) { $SizeAdd = $SizeName; } # All optional parameters are assumed to be pointer # variables preceeded by a ULONG size. If the # size is zero, use NULL for the argument. # In parameters need to retrieve the size now. # Out parameters are done after all in parameters # so their size should already be available. if ($Param->[$cParamFlags] & $cPfIn) { if ($DataPre eq "Out") { AlignOutData($gTypeSize{$SizeType}); } else { AlignInData($gTypeSize{$SizeType}); } Line("$SizeName = *($SizeType*)${gPre}${DataPre}Data;\n"); Line("${gPre}${DataPre}Data += $gTypeSize{$SizeType};\n"); } Line("if ($SizeName == 0)\n"); Line("{\n"); Line(" $Param->[$cParamName] = NULL;\n"); Line("}\n"); Line("else\n"); Line("{\n"); $gIndent += 4; if ($DataPre eq "Out") { AlignOutData($Param->[$cParamAlignIs]); } else { AlignInData($Param->[$cParamAlignIs]); } Line("$Param->[$cParamName] = " . "($Param->[$cParamType])${gPre}${DataPre}Data;\n"); Line("${gPre}${DataPre}Data += $SizeAdd;\n"); $gIndent -= 4; Line("}\n"); } # Assign in parameter values from incoming packet data. sub StubAssignInParams($) { my $Param = $_[0]; my($Size, $IfName); $Size = $gTypeSize{$Param->[$cParamType]}; $IfName = $gIfTypes{$Param->[$cParamType]}; if ($IfName ne "") { AlignInData($Param->[$cParamAlignIs]); if ($gMethodFlags & $cObjectsAreProxies) { Line("$Param->[$cParamName] = " . "*($Param->[$cParamType]*)${gPre}InData;\n"); } else { Line("$Param->[$cParamName] = ($Param->[$cParamType])" . "((Proxy$IfName*)$Param->[$cParamName])->\n"); Line(" InitializeProxy(*(DbgRpcObjectId*)${gPre}InData, " . "(IUnknown*)$Param->[$cParamName]);\n"); } Line("${gPre}InData += $Size;\n"); } elsif ($Size == $cTypeStringSize) { if ($Param->[$cParamFlags] & $cPfOpt) { Line("ULONG ${gPre}Len_$Param->[$cParamName];\n"); StubOptPointer("In", $Param, "${gPre}Len_$Param->[$cParamName]", "ULONG", ""); } else { AlignInData($Param->[$cParamAlignIs]); Line("$Param->[$cParamName] = " . "($Param->[$cParamType])${gPre}InData;\n"); Line("${gPre}InData += " . "strlen($Param->[$cParamName]) + 1;\n"); } } elsif ($Size == $cTypeBufferSize) { $SizeParam = FindSizeParam($Param); Line("$SizeParam->[$cParamType] ${gPre}Len_$Param->[$cParamName];\n"); if ($Param->[$cParamFlags] & $cPfOpt) { StubOptPointer("In", $Param, "${gPre}Len_$Param->[$cParamName]", $SizeParam->[$cParamType], ""); } else { # Get size parameter first. AlignInData($gTypeSize{$SizeParam->[$cParamType]}); Line("${gPre}Len_$Param->[$cParamName] = " . "*($SizeParam->[$cParamType]*)${gPre}InData;\n"); Line("${gPre}InData += $gTypeSize{$SizeParam->[$cParamType]};\n"); AlignInData($Param->[$cParamAlignIs]); Line("$Param->[$cParamName] = " . "($Param->[$cParamType])${gPre}InData;\n"); Line("${gPre}InData += ${gPre}Len_$Param->[$cParamName];\n"); } } else { if ($Param->[$cParamFlags] & $cPfOpt) { if ($Param->[$cParamSizeIs]) { $SizeParam = FindSizeParam($Param); Line("$SizeParam->[$cParamType] " . "${gPre}Len_$Param->[$cParamName];\n"); $Size .= " * ${gPre}Len_$Param->[$cParamName]"; StubOptPointer("In", $Param, "${gPre}Len_$Param->[$cParamName]", $SizeParam->[$cParamType], $Size); } else { Line("ULONG ${gPre}Len_$Param->[$cParamName];\n"); StubOptPointer("In", $Param, "${gPre}Len_$Param->[$cParamName]", "ULONG", ""); } } else { my($AddrOf); $AddrOf = $gAddrOf{$Param->[$cParamType]}; if ($Param->[$cParamSizeIs]) { # Get size parameter first. $SizeParam = FindSizeParam($Param); Line("$SizeParam->[$cParamType] " . "${gPre}Len_$Param->[$cParamName];\n"); AlignInData($gTypeSize{$SizeParam->[$cParamType]}); Line("${gPre}Len_$Param->[$cParamName] = " . "*($SizeParam->[$cParamType]*)${gPre}InData;\n"); Line("${gPre}InData += " . "$gTypeSize{$SizeParam->[$cParamType]};\n"); $Size .= " * ${gPre}Len_$Param->[$cParamName]"; } AlignInData($Param->[$cParamAlignIs]); if ($AddrOf) { # If there's an AddrOf this is an atomic type # so copy using the type rather than memcpy as # the compiler can generate more efficient code # since it can assume alignment. Line("$Param->[$cParamName] = " . "*($Param->[$cParamType]*)${gPre}InData;\n"); } else { Line("$Param->[$cParamName] = ($Param->[$cParamType])" . "${gPre}InData;\n"); } Line("${gPre}InData += $Size;\n"); } } } # Assign out parameter values by pointing into outgoing packet data. sub StubAssignOutParams($) { my $Param = $_[0]; my($Size, $IfName); $Size = $gTypeSize{$Param->[$cParamType]}; $IfName = $gIfTypes{$Param->[$cParamType]}; if ($IfName ne "") { AlignOutData($Param->[$cParamAlignIs]); Line("*(DbgRpcObjectId*)${gPre}OutData = 0;\n"); Line("$Param->[$cParamName] = " . "($Param->[$cParamType])${gPre}OutData;\n"); Line("${gPre}OutData += $Size;\n"); # If objects aren't proxies on the proxy side that # means they're proxies on the stub side and # need to be unwrapped when returning them. if (($gMethodFlags & $cObjectsAreProxies) == 0) { $gStubReturnsProxies = 1; } } elsif ($Size == $cTypeStringSize || $Size == $cTypeBufferSize) { $SizeParam = FindSizeParam($Param); if ($Param->[$cParamFlags] & $cPfOpt) { StubOptPointer("Out", $Param, $SizeParam->[$cParamName], $SizeParam->[$cParamType], ""); } else { AlignOutData($Param->[$cParamAlignIs]); if ($Param->[$cParamFlags] & $cPfIn) { # This is an in-out parameter so copy the # input data over the output area to initialize # it before the call. # OutData is pointing to the output area and # the parameter name has already been initialized # to the input data from the input parameter pass. if ($Size == $cTypeStringSize) { Line("strcpy((PSTR)${gPre}OutData, " . "$Param->[$cParamName]);\n"); } else { Line("memcpy(${gPre}OutData, $Param->[$cParamName], " . "$SizeParam->[$cParamName]);\n"); } } Line("$Param->[$cParamName] = " . "($Param->[$cParamType])${gPre}OutData;\n"); Line("${gPre}OutData += $SizeParam->[$cParamName];\n"); } } else { if ($Param->[$cParamSizeIs]) { $SizeParam = FindSizeParam($Param); $Size .= " * $SizeParam->[$cParamName]"; if ($Param->[$cParamFlags] & $cPfOpt) { StubOptPointer("Out", $Param, $SizeParam->[$cParamName], $SizeParam->[$cParamType], $Size); } else { AlignOutData($Param->[$cParamAlignIs]); Line("$Param->[$cParamName] = " . "($Param->[$cParamType])${gPre}OutData;\n"); Line("${gPre}OutData += $Size;\n"); } } else { # Atomic out parameters are always given space in the # output data even if they are optional. Always # assign them to their appropriate out packet # location and pass them in. AlignOutData($Param->[$cParamAlignIs]); Line("$Param->[$cParamName] = " . "($Param->[$cParamType])${gPre}OutData;\n"); Line("${gPre}OutData += $Size;\n"); } } } # Unwrap proxies if necessary. sub StubFinalizeParamsSuccess($) { my $Param = $_[0]; my($IfName); if ($Param->[$cParamFlags] & $cPfOut) { $IfName = $gIfTypes{$Param->[$cParamType]}; if ($IfName ne "") { Line("*(DbgRpcObjectId*)$Param->[$cParamName] = " . "((Proxy$IfName*)*$Param->[$cParamName])->m_ObjectId;\n"); } } } # Clean up any allocated proxies. sub StubFinalizeParamsFailure($) { my $Param = $_[0]; my($IfName); if (($gMethodFlags & $cObjectsAreProxies) == 0 && ($Param->[$cParamFlags] & $cPfIn)) { $IfName = $gIfTypes{$Param->[$cParamType]}; if ($IfName ne "") { Line("delete (Proxy$IfName*)$Param->[$cParamName];\n"); } } } sub IfMethod($$$) { my $Method = $_[0]; my $CallType = $_[1]; my $RetType = $_[2]; my($TableName, $Param, $Repl, $IfaceBase, $IfaceVer); AccumulateUnique($Method); AccumulateUnique($CallType); AccumulateUnique($RetType); $gMethod = $Method; $gIfaceMethod = $gIface . "::" . $gMethod; # # Look for method flags for a method specific # to this interface, then all previous interface # versions, then a generic method. # # Get the interface version. if ($gIface =~ /^(\w+)([0-9]+)$/) { $IfaceBase = $1; $IfaceVer = $2; } else { $IfaceBase = $gIface; $IfaceVer = 1; } # Search from the current version backwards. while ($IfaceVer >= 1) { if ($IfaceVer == 1) { $TableName = $IfaceBase . "::" . $gMethod; } else { $TableName = $IfaceBase . $IfaceVer . "::" . $gMethod; } $gMethodFlags = $gMethodFlagsTable{$TableName}; if ($gMethodFlags) { break; } $IfaceVer--; } # Check for a generic method. if ($gMethodFlags == 0) { $TableName = "*::" . $gMethod; $gMethodFlags = $gMethodFlagsTable{$TableName}; if ($gMethodFlags == 0) { $TableName = $gIface . "::*"; $gMethodFlags = $gMethodFlagsTable{$TableName}; } } # # Begin prototype and implementation. # STDMETHODV methods are assumed to have a custom # implementation as it is impossible to autogenerate # code for them. # if ($CallType eq "V") { $gMethodFlags |= $cCustomProxy | $cNoStub; } $gFile = \*PROXC; $gIndent = 0; Line("\n"); if ($RetType) { print PROXH " STDMETHOD${CallType}_($RetType, $gMethod)(\n"; Line("STDMETHODIMP${CallType}_($RetType)\n"); } else { print PROXH " STDMETHOD${CallType}($gMethod)(\n"; Line("STDMETHODIMP${CallType}\n"); } Line("Proxy$gIfaceMethod(\n"); if (($gMethodFlags & $cNoStub) != $cNoStub) { $gStubList .= " DBGRPC_SMTH_${gIface}_${gMethod},\n"; if ($gMethodFlags & $cCustomStub) { $gFile = \*STUBH; } else { $gFile = \*STUBC; } Line("\n"); Line("HRESULT\nSFN_${gIface}_${gMethod}(\n"); Line(" IUnknown* ${gPre}If,\n"); Line(" DbgRpcConnection* ${gPre}Conn,\n"); Line(" DbgRpcCall* ${gPre}Call,\n"); Line(" PUCHAR ${gPre}InData,\n"); Line(" PUCHAR ${gPre}OutData\n"); } # # Process arguments and complete declarations. # # Skip THIS. Unnecessary since implementation is C++. $_ =
; if (!/^\s+THIS_?\s*$/) { Error("Expecting THIS"); } $gFile = \*PROXC; $gIndent += 4; CollectParams(); print PROXH " );\n"; if (($gMethodFlags & $cNoStub) == $cCustomStub) { print STUBH " );\n"; } if (($gMethodFlags & $cCustomImplementation) == $cCustomImplementation) { # Both the proxy and stub are custom so skip # all code generation. return; } CLine(")\n"); $gIndent -= 4; CLine("{\n"); $gIndent += 4; # # Generate code body. # if (($gMethodFlags & $cNotRemotable) == $cNotRemotable) { Line("// This method is not remotable.\n"); Line("return E_UNEXPECTED;\n"); goto FinishMethod; } if (($gMethodFlags & $cNoThreadCheck) == 0) { Line("if (::GetCurrentThreadId() != m_OwningThread)\n"); Line("{\n"); Line(" return E_INVALIDARG;\n"); Line("}\n"); } else { Line("// Any thread allowed.\n"); } if ($gMethodFlags & $cReplImplementation) { if (($gMethodFlags & $cCustomProxy) == 0) { $Repl = $gProxyRepl{$TableName}; $Repl =~ s//$gIface/g; $Repl =~ s//$gFileBase/g; print PROXC $Repl; } if (($gMethodFlags & $cCustomStub) == 0) { $Repl = $gStubRepl{$TableName}; $Repl =~ s//$gIface/g; $Repl =~ s//$gFileBase/g; print STUBC $Repl; } goto FinishMethod; } Line("DbgRpcConnection* ${gPre}Conn;\n"); Line("if ((${gPre}Conn = DbgRpcGetConnection(m_OwningThread)) == NULL)\n"); Line("{\n"); Line(" return RPC_E_CONNECTION_TERMINATED;\n"); Line("}\n"); CLine("HRESULT ${gPre}Status;\n"); Line("DbgRpcCall ${gPre}Call;\n"); Line("PUCHAR ${gPre}Data, ${gPre}InData, ${gPre}OutData;\n"); Line("ULONG ${gPre}InSize = 0, ${gPre}OutSize = 0;\n"); # First parameter pass. # Prepare for object proxies and stubs. # Compute data size for in and out. $gStubHasCleanup = 0; $gStubReturnsProxies = 0; $gFile = \*STUBC; foreach $Param (@gParams) { StubInitializeParams($Param); } $gFile = \*PROXC; foreach $Param (@gParams) { ProxyInitializeParams($Param); } CClean("${gPre}Status = E_OUTOFMEMORY;\n"); # Start RPC call. Line("if ((${gPre}Data = " . "${gPre}Conn->StartCall(&${gPre}Call, m_ObjectId,\n"); Line(" DBGRPC_STUB_INDEX(m_InterfaceIndex,\n"); Line(" DBGRPC_SMTH_${gIface}_${gMethod}),\n"); Line(" ${gPre}InSize, ${gPre}OutSize)) == NULL)\n"); Line("{\n"); Line(" goto CheckStatus;\n"); Line("}\n"); if ($gMethodFlags & $cNoReturn) { Line("${gPre}Call.Flags |= DBGRPC_NO_RETURN;\n"); } if ($gMethodFlags & $cCallLocked) { Line("${gPre}Call.Flags |= DBGRPC_LOCKED;\n"); } # Second parameter pass. # Check object proxies and stubs. # Declare call variables. if ($gStubHasCleanup) { $gFile = \*STUBC; foreach $Param (@gParams) { StubValidateParams($Param); } } if ($gMethodFlags & $cObjectsAreProxies) { $gFile = \*PROXC; foreach $Param (@gParams) { ProxyValidateParams($Param); } } # Third parameter pass. # Copy parameter data into or out of packet. $gFile = \*STUBC; foreach $Param (@gParams) { if ($Param->[$cParamFlags] & $cPfIn) { StubAssignInParams($Param); } } foreach $Param (@gParams) { if ($Param->[$cParamFlags] & $cPfOut) { StubAssignOutParams($Param); } } $gFile = \*PROXC; Line("${gPre}InData = ${gPre}Data;\n"); foreach $Param (@gParams) { if ($Param->[$cParamFlags] & $cPfIn) { ProxyCopyInParams($Param); } } # # Call real interface. # Line("${gPre}Status = ${gPre}Conn->SendReceive(&${gPre}Call, " . "&${gPre}Data);\n"); my($FirstParam); $gFile = \*STUBC; Line("${gPre}Status = (($gIface*)${gPre}If)->$gMethod(\n"); $gIndent += 4; $FirstParam = 1; foreach $Param (@gParams) { if (!$FirstParam) { Print(",\n"); } else { $FirstParam = 0; } Line("$Param->[$cParamName]"); } Print("\n );\n"); $gIndent -= 4; # Fourth parameter pass. # Copy out-parameter data to real out parameters. # Initialize or clean up object proxies. $gIndent -= 4; CClean("CheckStatus:\n"); $gIndent += 4; if ($gStubReturnsProxies) { $gStubHasCleanup = 1; } CClean("if (SUCCEEDED(${gPre}Status))\n"); CClean("{\n"); $gIndent += 4; if ($gStubReturnsProxies) { $gFile = \*STUBC; foreach $Param (@gParams) { StubFinalizeParamsSuccess($Param); } } $gFile = \*PROXC; Line("${gPre}OutData = ${gPre}Data;\n"); foreach $Param (@gParams) { ProxyFinalizeParamsSuccess($Param); } foreach $Param (@gParams) { ProxyDestroyInObjects($Param); } $gIndent -= 4; CClean("}\n"); CClean("else\n"); CClean("{\n"); $gIndent += 4; if ($gStubHasCleanup) { $gFile = \*STUBC; foreach $Param (@gParams) { StubFinalizeParamsFailure($Param); } } $gFile = \*PROXC; foreach $Param (@gParams) { ProxyFinalizeParamsFailure($Param); } $gIndent -= 4; CClean("}\n"); Line("${gPre}Conn->FreeData(${gPre}Data);\n"); CLine("return ${gPre}Status;\n"); # # Finish up. # FinishMethod: $gIndent -= 4; CLine("}\n"); } sub EndInterface() { print PROXH "};\n"; print STUBH "\n"; print STUBH "#define DBGRPC_UNIQUE_$gIface $gIfUnique\n"; print STUBH "\n"; print STUBH "enum DBGRPC_SMTH_$gIface\n"; print STUBH "{\n"; print STUBH $gStubList; print STUBH " DBGRPC_SMTH_${gIface}_COUNT\n"; print STUBH "};\n"; $gStubList =~ s/ DBGRPC_SMTH_/ SFN_/g; print STUBC "\n"; print STUBC "DbgRpcStubFunction g_DbgRpcStubs_${gFileBase}_${gIface}[] =\n"; print STUBC "{\n"; print STUBC $gStubList; print STUBC "};\n"; $gStubList =~ s/ SFN_(\w+)_(\w+),/ "$1::$2",/g; print STUBC "\n"; print STUBC "#if DBG\n"; print STUBC "PCSTR g_DbgRpcStubNames_${gFileBase}_${gIface}[] =\n"; print STUBC "{\n"; print STUBC $gStubList; print STUBC "};\n"; print STUBC "#endif // #if DBG\n"; $gIface = ""; $gMethodFlags = 0; } sub OpenGenFile(*$) { my $File = $_[0]; my $Name = $_[1]; open($File, ">$gGenDir\\" . $Name) || ErrorExit("Can't open $gGenDir\\$Name\n"); print $File "//-------------------------------------". "--------------------------------------\n"; print $File "//\n"; print $File "// IMPORTANT: This file is automatically generated.\n"; print $File "// Do not edit by hand.\n"; print $File "//\n"; print $File "// Generated from $gHeaderFile " . localtime() . "\n"; print $File "//\n"; print $File "//-------------------------------------". "--------------------------------------\n"; } sub ScanHeader($) { my $File = $_[0]; my($InTypedef); $File =~ /(?:[a-zA-Z]:(?:[\\\/])?)?(?:.+[\\\/])*(.+)(\.[a-zA-Z]+)/; $gFileBase = $1; $gHeaderFile = $gFileBase . $2; open(HEADER, "<" . $File) || ErrorExit("Can't open " . $File); OpenGenFile(\*PROXH, "${gFileBase}_p.hpp"); OpenGenFile(\*PROXC, "${gFileBase}_p.cpp"); OpenGenFile(\*STUBH, "${gFileBase}_s.hpp"); OpenGenFile(\*STUBC, "${gFileBase}_s.cpp"); print PROXH "\n"; print PROXH "ULONG DbgRpcGetIfUnique_$gFileBase(REFIID InterfaceId);\n"; print PROXH "HRESULT DbgRpcPreallocProxy_$gFileBase(REFIID InterfaceId, " . "PVOID* Interface,\n"; print PROXH " DbgRpcProxy** Proxy, " . "PULONG IfUnique);\n"; print PROXH "\n"; print STUBH "\n"; print STUBH "extern DbgRpcStubFunctionTable g_DbgRpcStubs_${gFileBase}[];\n"; print STUBH "#if DBG\n"; print STUBH "extern PCSTR* g_DbgRpcStubNames_${gFileBase}[];\n"; print STUBH "#endif\n"; print STUBH "void DbgRpcInitializeStubTables_${gFileBase}(ULONG Base);\n"; # Not in an interface or typedef. $gIface = ""; $InTypedef = 0; while (
) { if (!$gIface) { # Not in an interface. # Look for interface typedefs to automatically # recognize interfaces. # Look for interface declarations. if ($InTypedef) { if (/^ (\w+)\*/) { my($IfType); $IfType = $1; # Accumulate lines until full statement. LINES: for (;;) { if (/;/) { last LINES; } else { $_ .=
; } } # Extract typedef name. $_ =~ /\*\s+(\w+)\s*;\s*$/; AddIfType($IfType, $1); } $InTypedef = 0; } elsif (/^typedef interface DECLSPEC_UUID/) { $InTypedef = 1; } elsif (/^DECLARE_INTERFACE_\((\w+), \w+\)$/) { BeginInterface($1); } } else { # In an interface. # Look for method declarations. # Look for interface close. if (/^\s+STDMETHOD(V?)\((\w+)\)\($/) { IfMethod($2, $1, ""); } elsif (/^\s+STDMETHOD(V?)_\((\w+), (\w+)\)\($/) { IfMethod($3, $1, $2); } elsif (/^};$/) { EndInterface(); } } } GenerateIfTypes(); close(HEADER); close(PROXH); close(PROXC); close(STUBH); close(STUBC); } ############################################################################### # # Main. # ############################################################################### $gVerbose = 0; $gGenDir = "."; ARG: while ($Arg = shift(@ARGV)) { if ($Arg eq "-g") { $gGenDir = shift; } elsif ($Arg eq "-v") { $gVerbose = 1; } else { last ARG; } } ScanHeader($Arg);