How do I start a COM server? Code is in Python - python

I want to run Python code as a COM server. Eventually I want to run an RTD server available here. But first I want to know what exactly you have to do to getting any COM server running. So let's focus on this example.
class HelloWorld:
_reg_clsid_ = "{7CC9F362-486D-11D1-BB48-0000E838A65F}"
_reg_desc_ = "Python Test COM Server"
_reg_progid_ = "Python.TestServer"
_public_methods_ = ['Hello']
_public_attrs_ = ['softspace', 'noCalls']
_readonly_attrs_ = ['noCalls']
def __init__(self):
self.softspace = 1
self.noCalls = 0
def Hello(self, who):
self.noCalls = self.noCalls + 1
# insert "softspace" number of spaces
return "Hello" + " " * self.softspace + who
if __name__=='__main__':
import win32com.server.register
win32com.server.register.UseCommandLine(HelloWorld)
Ok, this works in the way that there were no errors and server is registered, hence it is available in the HKEY_CLASSES_ROOT registry. But what can I do with this? Some say you have to compile a instance and have a .dll or .exe file. WHat else do I have to do?

Well, I ran your example. The registry key for the server is at:
HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{7CC9F362-486D-11D1-BB48-0000E838A65F}
It has two subkeys... one for LocalServer32 and one for InProcServer32
I created a simple VBA macro in Excel:
Sub d()
Set obj = CreateObject("Python.TestServer")
MsgBox obj.Hello("joe")
End Sub
Macro ran just fine. My version of Excel is 64-bit. I ran the macro and then fired up Task Manager while the message box was being displayed. I could see pythonw.exe running in the background.
The only difference between my python script and yours is probably the name and also that I added a line to print to make sure I was executing the function:
if __name__=='__main__':
import win32com.server.register
print("Going to register...")
win32com.server.register.UseCommandLine(HelloWorld)
When I ran the 64-bit csript.exe test, it worked... as expected... when I ran the 32-bit version it failed.
I know why...sort of...
The registry entry for InProcServer32 is pythoncom36.dll
That's no good. It is an incomplete path. I tried modifying the path variable on my shell to add to one of the 3 places where the DLL existed on my system, but it didn't work. Also, tried coding the path in the InProcServer32. That didn't work.. kept saying it couldn't find the file.
I ran procmon, and then I observerved that it couldn't load vcruntime140.dll. Found the directory under python where those files were, and added to my path. It got further along. If I cared enough, I might try more. Eventually using procmon, I could find all the problems. But you can do that.
My simple solution was to rename the key InProcServer32 for the CLSID to be _InProcServer32. How does that work? Well, the system can't find InProcServer32 so it always uses LocalServer32--for 32-bit and 64-bit processes. If you need the speed of in process then you'd need to fix the problem by using procmon and being relentless until you solved all the File Not Found errors and such. But, if you don't need the speed of in process, then just using the LocalServer32 might solve the problem.
Caveats I'm using an Anaconda distro that my employer limits access to and I can only install it from the employee store. YMMV.

Related

Why can't VSCode load MicroPython 'machine'?

I have installed the MicroPython IDE and Python extension in accordance with the VSCode instructions. This is my first use of VSCode and I have searched for a solution and failed. when I try to debug this code:
import machine
import time
led = machine.Pin(2, machine.Pin.OUT)
button = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_UP)
while button.value():
led.on()
time.sleep(1)
led.off()
time.sleep(1)
led.on()
I get this result: Module Not Found Error "no module named 'machine' "
I would very much appreciate your assistance.
Regards
John
You are dealing with two separate Python environments:
The "PC Python" (also called CPython) on your development machine
MicroPython running on embedded device like ESP32.
When testing on PC, you are running your code in CPython. But if you transfer it to the device, it runs within MicroPython. Both environments are very similar but some differences exist.
The machine module is one such difference. It exists only in MicroPython and allows actual access to the hardware. Thus, if you run your program in CPython, you get exactly the error you described.
You can either test your program on an actual device. Or you "mock" the machine module, i.e. you create that module as "dummy" implementation for testing on PC. It would at least need to contain the Pin() class, which could for example print the state changes to command line.
There is an example for such a mock on GitHub: https://github.com/tflander/esp32-machine-emulator
A minimal example for your case: Create machine.py containing:
class Pin:
IN = 0
OUT = 0
PULL_UP = 0
def __init__(self, number, mode=-1, pull=-1):
self.number = number
def on(self):
print('Pin %d switches ON' % self.number)
def off(self):
print('Pin %d switches OFF' % self.number)
def value(self):
return 1
# ... add other methods when needed ...
... and put it somewhere where your script can import it from.

Extend the functionality of a compiled Python script

I'm not a programmer, so I don't even know in what terms should I ask this. Let's say I've compiled a Python script to have an .exe (I use py2exe to do this). This is the major program. Now, I want to add some extra functionality to it, but I don't want to recompile the entire script with the added functionality. I tried to search something on the web, and I found examples of extending a C++ or other application with Python scripts (like a sort of plugin). But I can't figure out how to do it with an application already written in Python.
I tried this: I wrote major.py (this is the script from where I build the executable) and stuff.py. In major I wrote this:
def generic():
import stuff
while True:
param=input('what did you say? ')
stuff.speak(param)
generic()
And in stuff I wrote this:
def speak(param):
print(param)
Then I created a .exe with py2exe. It works as expected, when I run the program in the command line says "what did you say?" and waits until I type something, then it prints what I typed.
Then, I changed stuff.py with this:
def speak(param):
print('I said '+param)
Hoping that now upon the execution of the .exe created earlier it would print "I said.." plus whatever I typed. Obviously, it didn't work, the program continued to behave like before. So I'm guessing that once I imported stuff and created the .exe file, that import is permanent, not allowing me to change whatever is in stuff. What should I do?
py2exe packs the compiled scripts in the executable.
You need to recreate the executable (which will recompile any changed script) to see the changes take effect.
EDIT following the comments:
You can do it if you dynamically import/reimport the module from inside the executable.
In your main script you do (see code below)
mod, error = loadmodule('mystuff.py')
if mod is not None:
# loading succeeded you can now proceed and do things with it
pass
Of course you have to leave mystuff.py out of the scripts that py2exe packs into the executable. In the above example mystuff.py would be in the same directory as the executable.
The loading code:
def loadmodule(modpath, modname=''):
if not modpath.endswith('.py'):
modpath += '.py'
# generate a random name for the module
if not modname:
modpathbase = os.path.basename(modpath)
modname, _ = os.path.splitext(modpathbase)
version = (sys.version_info[0], sys.version_info[1])
if version < (3, 3):
mod, e = loadmodule2(modpath, modname)
else:
mod, e = loadmodule3(modpath, modname)
return mod, e
def loadmodule2(modpath, modname):
import imp
try:
mod = imp.load_source(modname, modpath)
except Exception as e:
return (None, e)
return (mod, None)
def loadmodule3(modpath, modname):
import importlib.machinery
try:
loader = importlib.machinery.SourceFileLoader(modname, modpath)
mod = loader.load_module()
except Exception as e:
return (None, e)
return (mod, None)
If you run your script from Python, instead of compiling it as an executable, you can make changes and run them without having to recompile. The py2exe is mostly for allowing you to distribute your application to other Windows computers that don't have Python installed. After you have finished developing it, then compile it as an executable so you can run it on other computers.
There is no way to do what you want. py2exe builds a standalone python interpreter (In the file named python.dll) with just the dependencies of your project. Then the .exe file runs your script using that interpreter
I suggest you that if you really need to provide regular upgrades then you should recompile it, or install python in the target machine, or make an updating routine that checks for updates and compiles it in the target machine (With py2exe)

IDLE subprocess startup error

I have the code below in a file called code.py. I am using IDLE to edit the file. When I click Run>Run Module I get the error:
"IDLE's subprocess didn't make connection. Either IDLE can't start a
subprocess of personal firewall software is blocking the connection."
I am using Windows 7 Ultimate 64bit, but I have the 32bit version of Python 2.7 installed.
I have looked for a solution on this site as well as others but all of them seem to recommend deleting something called tkinter.py (I have no idea what this is) or to turn off my firewalls (I have none enabled aside from Microsoft Security Essentials which isn't a firewall.)
#Globals
#-------------------
x_pad = 476
y_pad = 444
import ImageGrab
import os
import time
import win32api, win32con
def screenGrab():
box = (x_pad+1,y_pad+1,x_pad+641,y_pad+480)
im = ImageGrab.grab(box)
im.save(os.getcwd() + '\\full_snap__' + str(int(time.time())) +
'.png', 'PNG')
def main():
pass
if __name__ == '__main__':
main()
def leftClick():
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0)
time.sleep(.1)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0)
print 'Click.' #completely optional. But nice for debugging purposes.
def leftDown():
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0)
time.sleep(.1)
print 'left Down'
def leftUp():
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0)
time.sleep(.1)
print 'left release'
def mousePos(cord):
win32api.SetCursorPos((x_pad + cord[0], y_pad + cord[1])
def get_cords():
x,y = win32api.GetCursorPos()
x = x - x_pad
y = y - y_pad
print x,y
The thing is "python.exe" is being obstructed by "tkinter.py") that you created(i.e., you have written a program with Tk() and named it as tkinter.py and saved that in the root folder of python). And that's it just make sure that you don't save any program file directly in the root folder of python.
Another fix!!! Hopefully this will help someone.
I had the same problem and noticed something quite interesting. I had accidentally named a file (inside the desktop folder I was working in) "tkinter" (it will cause the same problem if you rename a file by any reserved keyword, I assume). Everytime I ran or attempted to run this file, it created a pycache folder, and the error you mention above came up. Deleting the erroneously named python file solved the problem.
So - look for ANY files (in the folder you are working with or indeed the root folder) that are named after any reserved words. Delete them. Hopefully it'll work!
I had the same problem. what i did that solved it, was to move every .py file that i had created in "C:\Python33" folder, to a sub-folder that i named "Examples". seems like one of my files was the cause of this problem.
I also had the following problem. My file was named code.py, and was working fine untill I installed Canopy, and numpy.
I tried reinstalling python, but what solved the problem for me was simply renaming the file. I called my file myCode.py, everything started working fine. Strange problem...
I made a python file and named it "socket.py" so then python IDLE showing an error on startup that 'startup failure'
so the problem is that if we are using python reserved keywords or module names as our python file name that it conflicts with built-in modules.
the solution is: go to path C:\Users\sony\AppData\Local\Programs\Python\Python38 where your python files are saved and just renamed that file.
then start IDLE.
Happily using IDLE continously under python36 and windows10, it has suddenly given this error on all the programs I'm working on, with no new files created.
I terminated IDLE and tried to restart it with idle.bat but that no longer works.
Happily I have been able to restart it successfully with Lib\idlelib\idle.pyw.
All my recent programs are there and they can again be run without problems.
No need to reinstall python.

How can I add a command to the Python interactive shell?

I'm trying to save myself just a few keystrokes for a command I type fairly regularly in Python.
In my python startup script, I define a function called load which is similar to import, but adds some functionality. It takes a single string:
def load(s):
# Do some stuff
return something
In order to call this function I have to type
>>> load('something')
I would rather be able to simply type:
>>> load something
I am running Python with readline support, so I know there exists some programmability there, but I don't know if this sort of thing is possible using it.
I attempted to get around this by using the InteractivConsole and creating an instance of it in my startup file, like so:
import code, re, traceback
class LoadingInteractiveConsole(code.InteractiveConsole):
def raw_input(self, prompt = ""):
s = raw_input(prompt)
match = re.match('^load\s+(.+)', s)
if match:
module = match.group(1)
try:
load(module)
print "Loaded " + module
except ImportError:
traceback.print_exc()
return ''
else:
return s
console = LoadingInteractiveConsole()
console.interact("")
This works with the caveat that I have to hit Ctrl-D twice to exit the python interpreter: once to get out of my custom console, once to get out of the real one.
Is there a way to do this without writing a custom C program and embedding the interpreter into it?
Edit
Out of channel, I had the suggestion of appending this to the end of my startup file:
import sys
sys.exit()
It works well enough, but I'm still interested in alternative solutions.
You could try ipython - which gives a python shell which does allow many things including automatic parentheses which gives you the function call as you requested.
I think you want the cmd module.
See a tutorial here:
http://wiki.python.org/moin/CmdModule
Hate to answer my own question, but there hasn't been an answer that works for all the versions of Python I use. Aside from the solution I posted in my question edit (which is what I'm now using), here's another:
Edit .bashrc to contain the following lines:
alias python3='python3 ~/py/shellreplace.py'
alias python='python ~/py/shellreplace.py'
alias python27='python27 ~/py/shellreplace.py'
Then simply move all of the LoadingInteractiveConsole code into the file ~/py/shellreplace.py Once the script finishes executing, python will cease executing, and the improved interactive session will be seamless.

Runing bcdedit from python in Windows 2008 SP2

I do not know windows well, so that may explain my dilemma ...
I am trying to run bcdedit in Windows 2008R2 from Python 2.6.
My Python routine to run a command looks like this:
def run_program(cmd_str):
"""Run the specified command, returning its output as an array of lines"""
dprint("run_program(%s): entering" % cmd_str)
cmd_args = cmd_str.split()
subproc = subprocess.Popen(cmd_args, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
(outf, errf) = (subproc.stdout, subproc.stderr)
olines = outf.readlines()
elines = errf.readlines()
if Options.debug:
if elines:
dprint('Error output:')
for line in elines:
dprint(line.rstrip())
if olines:
dprint('Normal output:')
for line in olines:
dprint(line.rstrip())
errf.close()
outf.close()
res = subproc.wait()
dprint('wait result=', res)
return (res, olines)
I call this function thusly:
(res, o) = run_program('bcdedit /set {current} MSI forcedisable')
This command works when I type it from a cmd window, and it works when I put it in a batch file and run it from a command window (as Administrator, of course).
But when I run it from Python (as Administrator), Python claims it can't find the command, returning:
bcdedit is not recognized as an internal or external command,
operable program or batch file
Also, if I trying running my batch file from Python (which works from the command line), it also fails. I've also tried it with the full path to bcdedit, with the same results.
What is it about calling bcdedit from Python that makes it not found?
Note that I can call other EXE files from Python, so I have some level of confidence that my Python code is sane ... but who knows.
Any help would be most appreciated.
Windows 2008 R2 is 64-bit-only, yes? Python's a 32-bit process. When a 32-bit app runs something from C:\Windows\System32, Windows actually looks in C:\Windows\SysWOW64. Use C:\Windows\SysNative.
Perhaps the path to bcdedit.exe isn't in your system path when Python is running for some reason (a different user account, for example). You can find this out by printing:
os.environ.get("PATH")
It's semicolon-delimited, so os.environ.get("PATH").split(';') might be more useful.
I can't see any reason why it wouldn't be there, but just in case, you should be looking for C:\Windows\System32, where C is your Windows drive letter.
Check your PATH variable and see if C:\windows\system32 is there. (use set in DOS)
For some reason I experiment the same trouble from c#. If I list the files it was not here, but when I was looking from Explorer it was there. maybe it is some kind of protected file. To call bcdedit.exe, I manually copied it from system32 to my application folder and it worked. There is also another one in windows\winsxs folder. I can start it from my application, but I`m not sure it is the same path on all computers.
Hope it helps!

Categories

Resources