I am automating my work with SAP GUI script at the moment and whilst trying to recreate the recorded macro I am having an issue at one particular point which I don't know how to translate.
session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell").setCurrentCell 1,"MAKTX2"
session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell").doubleClickCurrentCell
session.findById("wnd[1]/tbar[0]/btn[0]").press
I have read through the SAP GUI Scripting API pdf and am struggling to see how I action the .setCurrentCell 1,"MAKTX2" part. I am accessing the container cell with the following:
GuiContainerShell materials = (GuiContainerShell)session.FindById("wnd[0]/shellcont/shell/shellcont[1]/shell");
How do I make "materials" double click "MAKTX2"?
Edit: Full SAP GUI script:
SapROTWr.CSapROTWrapper sapROTWrapper = new SapROTWr.CSapROTWrapper();
object SapGuilRot = sapROTWrapper.GetROTEntry("SAPGUI");
object engine = SapGuilRot.GetType().InvokeMember("GetScriptingEngine", System.Reflection.BindingFlags.InvokeMethod, null, SapGuilRot, null);
GuiApplication GuiApp = (GuiApplication)engine;
GuiConnection connection = (GuiConnection)GuiApp.Connections.ElementAt(0);
GuiSession session = (GuiSession)connection.Children.ElementAt(0);
GuiFrameWindow frame = (GuiFrameWindow)session.FindById("wnd[0]");
GuiTextField jobsite = (GuiTextField)session.FindById("wnd[0]/usr/subSA_0100_1:SAPMZCX_CSDSLSBM5001_OFS_OTS:2410/subSA_2410_1:SAPMZCX_CSDSLSBM5001_OFS_OTS:2510/ctxtKUWEV-KUNNR");
jobsite.Text = "I033";
frame.SendVKey(0);
GuiLabel aggregates = (GuiLabel)session.FindById("wnd[1]/usr/lbl[12,3]");
aggregates.SetFocus();
GuiFrameWindow frame2 = (GuiFrameWindow)session.FindById("wnd[1]");
frame2.SendVKey(1);
GuiContainerShell materials = (GuiContainerShell)session.FindById("wnd[0]/shellcont/shell/shellcont[1]/shell");
To be honest I can't help you with C#, but perhaps the SAP interface is generic enough anyway. Thing is, session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell") gives you a reference to an object of type GuiShell or GuiContainerShell or whatever it's called. On this reference, you can call the methods defined for this type. So in the same way, when you do
session.findById("wnd[0]/shellcont/shell/shellcont[1]/shell").setCurrentCell 1,"MAKTX2"
You're just getting the reference first, and then applying the method setCurrentCell on it, all on the same line.
When you did in C#
GuiContainerShell materials = (GuiContainerShell)session.FindById("wnd[0]/shellcont/shell/shellcont[1]/shell");
you gave this reference a name materials, and provided that line works correctly, I guess you can just say now:
materials.setCurrentCell(1, "MAKTX2")
materials.doubleClickCurrentCell
Related
I have the following script, which allows me to to control the SAP Gui with .NET (C#)
SapROTWr.CSapROTWrapper sapROT = new SapROTWr.CSapROTWrapper();
object objSapGui = sapROT.GetROTEntry("SAPGUI");
object objEngine = objSapGui.GetType().InvokeMember("GetScriptingEngine", System.Reflection.BindingFlags.InvokeMethod, null, objSapGui, null);
GuiConnection connection = (GuiConnection)(objEngine as GuiApplication).Connections.Item(0);
SapSession = (GuiSession)connection.Sessions.Item(0);
SapSession.SendCommand("/nN204");
As i figured out, i can start a transaction within the SAP Gui with GuiSession.StartTransaction("N204") or with GuiSession.SendCommand("/nN204")
Now I want to start the transaction with a parameter structure to go directly into a specific for example document at this point. How can I achieve this?
My first thought was the GuiApplication.CreateGuiCollection() but as I do not found any examples, I do not now what to do with it.
It would be nice if someone could share a approach to achieve my goal.
Background
What I'm trying to do is to have a scoped variable of one of my models in the xaml.
In my workflow project "MyProject.Workflows" I have created model classes, code activities and Xaml files. They are all under same namespace. In another project ("Engine"), I load and execute these workflows.
To load the workflows in the "Engine", I use ActivityXamlServices with ActivityXamlServicesSettings including CompileExpressions = true.
When loading the ActivityXamlServices, I use a XamlXmlReader with XamlXmlReaderSettings where I actually point to the "MyProject.Workflows" dll.
Since Both these projects are in the same solution I actually referred MyProject.Workflows in the "Engine".
Because Earlier, they were in different solutions, So when I tried to do this It gave me It cant find the "MyProject.Workflows" dll even though I point it in the XamlXmlReaderSettings.
Then I tried to load the dll to the app domain and then it worked.But I did not want to deal with App Domains so I decided to get both projects under one solution so I can refer the "MyProject.Workflows" in the "Engine".
Issue:
If I use one of those models inside of the Xaml as an expression like "Assign Activity" the Workflow isn't getting compiled when I try to execute this.
For example if I use this in an "Assign" activity having a scoped variable of type MyObject
Newtonsoft.Json.JsonConvert.DeserializeObject<MyProject.Workflows.Models.MyObject>(inputString);
I will get the below error message when I run the workflow.
NotSupportedException:'Expression Activity type 'CSharpValue`1' requires compilation in order to run. Please ensure that the workflow has been compiled.
If I remove these objects and just deal with strings or ints, it works fine.
Things I found in my research:
I found this was a bug in .Net Framework 4.5. But Im using 4.6
Even though I used CompileExpressions = true , I tried this compile method I found. But did not change a thing.
private static void Compile(DynamicActivity dynamicActivity)
{
TextExpressionCompilerSettings settings = new TextExpressionCompilerSettings
{
Activity = dynamicActivity,
Language = "C#",
ActivityName = dynamicActivity.Name.Split('.').Last() + "_CompiledExpressionRoot",
ActivityNamespace = string.Join(".", dynamicActivity.Name.Split('.').Reverse().Skip(1).Reverse()),
RootNamespace = null,
GenerateAsPartialClass = false,
AlwaysGenerateSource = true,
};
TextExpressionCompilerResults results =
new TextExpressionCompiler(settings).Compile();
if (results.HasErrors)
{
throw new Exception("Compilation failed.");
}
ICompiledExpressionRoot compiledExpressionRoot =
Activator.CreateInstance(results.ResultType,
new object[] { dynamicActivity }) as ICompiledExpressionRoot;
CompiledExpressionInvoker.SetCompiledExpressionRootForImplementation(
dynamicActivity, compiledExpressionRoot);
}
I read that some people faced this problem and they had to actually move the models to a diffrent namespace. I did that too. Didn't fix the problem.
My Xaml file has this entry added at the top.
xmlns:local="clr-namespace:MyProject.Workflows.Models"
Can someone please help me to get through this?
I am creating a custom transformation in C# to be used in SSIS. I have already been able creating and adding the custom component and receive and alter data from a db source but I need more data to register in a log table. This data can only be passed with variables but I can't find a good explanation of how to add a readonlyvariable to my component.
I have tried to use IDTSVariable100 and VariableDispenser but I can't make sense of how to.
public override void ProvideComponentProperties()
{
base.ProvideComponentProperties();
base.RemoveAllInputsOutputsAndCustomProperties();
VariableDispenser varDispenser = this.VariableDispenser();
IDTSVariable100 vr = this.VariableDispenser.GetVariables();
IDTSInput100 input = this.ComponentMetaData.InputCollection.New();
input.Name = "Input_B";
IDTSOutput100 output=this.ComponentMetaData.OutputCollection.New();
output.Name = "Output_B";
// the output is synchronous with the input
output.SynchronousInputID = input.ID;
}
Basically i want to define readonlyvariables that I can alter the value before my custom component runs like the original "script component" has.
Well i researched a bit more and stumbled on a answer:
It seems that to access the SSIS public variables we have to get them with code on the ProcessInput Method:
var dimSrcId ="";
IDTSVariables100 variables = null;
this.VariableDispenser.LockForRead("User::dimSrcId");
this.VariableDispenser.GetVariables(out variables);
dimSrcId = variables["User::dimSrcId"].Value.ToString();
variables.Unlock();
By using the VariableDispenser.LockForRead() we're capable of searching for our variables and access there value.
The host property of a familyInstance returns a RevitLinkInstance when the host is placed within a linked document. I there a way to get the real Element (or its ID) instead of the RevitLinkInstance?
I was hoping that the stableREpresentation could give me more information, but unfortunatly, it doesn't.
Reference hostFaceReference = instance.HostFace;
string stableRepresentation = hostFaceReference.ConvertToStableRepresentation(instance.Document);
this would give "ac669fa6-4686-4f47-b1d0-5d7de6a40550-000a6a4a:0:RVTLINK:234297:0:218" where 234297 is the ID of the referenced element, in this case, still the RevitLinkInstance.
Have you tried this?
ElementId hostFaceReferenceId = instance.HostFace.LinkedElementId;
You could then try getting the Element via the linkedDocument.
Document LinkedDoc = RevitLinkInstance01.GetLinkDocument();
Element linkedEl = LinkedDoc.GetElement(hostFaceReferenceId);
Depending on the host you may have to go about it a few ways. For example, with a wall you could try the following (this is using LINQ by the way):
// filter the Host's document's items
FilteredElementCollector linkdocfec = new FilteredElementCollector(elem_inst.Host.Document);
// establish the host's type
linkdocfec.OfClass(elem_inst.Host.GetType());
// find the host in the list by comparing the UNIQUEIDS
Element hostwallinlinkedfile = (from posshost in linkdocfec
where posshost.UniqueId.ToString().Equals(elem_inst.Host.UniqueId.ToString())
select posshost).First();
// check the different faces of the host (wall in this case) and select the exterior one
Reference linkrefface = HostObjectUtils.GetSideFaces((hostwallinlinkedfile as HostObject), ShellLayerType.Exterior).First<Reference>();
// create a reference to the linked face in the the CURRENT document (not the linked document)
Reference linkref = linkrefface.CreateLinkReference(rvtlink_other);
Ultimately, according to the docs anyway, you're supposed to utilize the CreateReferenceInLink method to get your item.
I am trying to set the print margins in a Word Doc I am printing from my c# application but I am having trouble accessing the methods that I need to call (based on what they are in MS Word VBA)
In VBA the code looks like this:
Options.printBackground = False
With ActiveDocument.PageSetup
.TopMargin = CentimetersToPoints(0.61)
.BottomMargin = CentimetersToPoints(0.43)
.LeftMargin = CentimetersToPoints(1.27)
.RightMargin = CentimetersToPoints(0.43)
.Gutter = CentimetersToPoints(0)
End With
Here is my c# code
oWordDoc.PageSetup.TopMargin = Microsoft.Office.Interop.Word.Application.CentimetersToPoints(float.Parse ("0.61")) ;
The error I am getting is:
An object reference is required for the non-static field, method, or property 'Microsoft.Office.Interop.Word._Application.CentimetersToPoints(float)'
I have tried several varaiations after searching the VS 2010 Object Browser for CentimetersToPoints
The available interfaces in the Object Browser are:
Microsoft.Office.Interop.Word._Application.CentimetersToPoints(float)
Microsoft.Office.Interop.Word.ApplicationClass.CentimetersToPoints(float)
Microsoft.Office.Interop.Word._Global.CentimetersToPoints(float)
Microsoft.Office.Interop.Word.GlobalClass.CentimetersToPoints(float)
how can I access methods like this?
thanks
As the error message indicates, you need a reference to the Word.Application object to access this method, since it is not static.
I guess you have a oWordApp somewhere that you use to create or open the oWordDoc, so you can access the method from this object.
Or you can retreive the instance of the Word.Application from your oWordDoc (Word.Document) object.
oWordDoc.PageSetup.TopMargin = oWordDoc.Application.CentimetersToPoints(float.Parse("0.61"));